Skip to content

Commit

Permalink
feat(components): enable prefix override (#861)
Browse files Browse the repository at this point in the history
  • Loading branch information
yinonov authored Dec 4, 2022
1 parent ae64bf2 commit f023bd0
Show file tree
Hide file tree
Showing 80 changed files with 484 additions and 215 deletions.
6 changes: 3 additions & 3 deletions apps/docs/_data/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,9 @@
},
{
"title": "option",
"markdown": "./libs/components/src/lib/listbox-option/README.md",
"markdown": "./libs/components/src/lib/option/README.md",
"modules": [
"/assets/modules/components/listbox-option/index.js",
"/assets/modules/components/option/index.js",
"/assets/modules/components/listbox/index.js"
]
},
Expand All @@ -292,7 +292,7 @@
"status": "alpha",
"markdown": "./libs/components/src/lib/listbox/README.md",
"modules": [
"/assets/modules/components/listbox-option/index.js",
"/assets/modules/components/option/index.js",
"/assets/modules/components/listbox/index.js"
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"comment": "align with custom prefix for custom-elements support",
"type": "none",
"packageName": "@vonage/nx-vivid",
"email": "[email protected]",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "enable custom prefix for custom-elements",
"packageName": "@vonage/vivid",
"email": "[email protected]",
"dependentChangeType": "patch"
}
24 changes: 24 additions & 0 deletions libs/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ Note: scss users can simply [forward](https://sass-lang.com/documentation/at-rul
@forward 'node_modules/@vonage/vivid/styles/[path to file].css';
```

## Advanced Usage

### Scoped Elements

Custom elements, by browsers limitations, are registered globally, and thus may conflict when multiple versions of the library are used in the same application as all custom elements register under the same namespace.

This burdens micro frontend architecture and updates to outdated versions of the Vivid, enforcing a single version of the library to be used. Meaning, any update to the library will require a full application update.

To avoid this bottleneck, Vivid provides a way for authors' to scope each custom element namespace by setting a `prefix` query parameter to their import call.

The following example will register *badge* custom element as `dashboard-badge`:

```js
import '/node_modules/@vonage/vivid/badge/index.js?prefix=dashboard';
```

then use it as:

```html
<dashboard-badge text="I'm a custom prefixed badge"></dashboard-badge>
```

Even though the custom elements are registered under different namespaces, [npm packages version range handling](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies) can still be used for multiple versions saving.

## Support

This library is open source, developed and maintained by the [Vonage Vivid team](Vonage/vivid).
Expand Down
19 changes: 16 additions & 3 deletions libs/components/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,32 @@ module.exports = {
displayName: 'components',
preset: '../../jest.preset.js',
testEnvironment: 'jsdom',
extensionsToTreatAsEsm: ['.ts',],
globals: {
'ts-jest': {
useESM: true,
tsconfig: '<rootDir>/tsconfig.spec.json',
diagnostics: {
ignoreCodes: [1343]
},
astTransformers: {
before: [
{
path: 'node_modules/ts-jest-mock-import-meta',
options: { metaObjectReplacement: { url: 'https://www.url.com' } }
}
],
}
},
},
transform: {
'^.+\\.[tj]s?$': 'ts-jest',
},
transformIgnorePatterns: [
"/node_modules/(?!(@microsoft|exenv-es6)/)"
'/node_modules/(?!(@microsoft|exenv-es6|@vivid-nx)/)'
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/components',
testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec).[jt]s?(x)", "!**/?(*.)+(config.spec).[jt]s?(x)" ],
setupFilesAfterEnv: ["<rootDir>/setupJestTests.js"]
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec).[jt]s?(x)', '!**/?(*.)+(config.spec).[jt]s?(x)'],
setupFilesAfterEnv: ['<rootDir>/setupJestTests.js']
};
2 changes: 1 addition & 1 deletion libs/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"./nav-item": "./nav-item",
"./number-field": "./number-field",
"./note": "./note",
"./option": "./listbox-option",
"./option": "./option",
"./popup": "./popup",
"./progress": "./progress",
"./progress-ring": "./progress-ring",
Expand Down
28 changes: 25 additions & 3 deletions libs/components/setupJestTests.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import * as jestFetchMock from 'jest-fetch-mock';
// import * as jestFetchMock from 'jest-fetch-mock';
// import { enableFetchMocks } from 'jest-fetch-mock';
import { jest } from '@jest/globals';
import fetchMock from 'jest-fetch-mock';
import './src/shared/utils';

jestFetchMock.enableFetchMocks();
fetchMock.enableMocks();

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query: any) => ({
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
Expand All @@ -15,3 +19,21 @@ Object.defineProperty(window, 'matchMedia', {
dispatchEvent: jest.fn(),
})),
});

jest.mock('./src/shared/utils', () => {
const originalModule = jest.requireActual('./src/shared/utils');

//Mock the default export and named export 'foo'
return {
...originalModule,
loadComponentsModules: jest.fn((components) => {
components.forEach((component) => import(`./src/lib/${component}/index.ts`));

return Promise.all(
components.map(component =>
customElements.whenDefined(`vwc-${component}`)
)
);
}),
};
});
4 changes: 4 additions & 0 deletions libs/components/src/lib/accordion-item/accordion-item.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const COMPONENT_TAG = 'vwc-accordion-item';
describe('vwc-accordion-item', () => {
let element: AccordionItem;

beforeAll(async () => {
await customElements.whenDefined(COMPONENT_TAG);
});

beforeEach(async () => {
element = (await fixture(
`<${COMPONENT_TAG}></${COMPONENT_TAG}>`
Expand Down
15 changes: 10 additions & 5 deletions libs/components/src/lib/accordion-item/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import '../icon';
import '../focus';

import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import { loadComponentsModules } from '../../shared/utils';
import styles from './accordion-item.scss';

import { AccordionItem } from './accordion-item';
import { AccordionItemTemplate as template } from './accordion-item.template';

const prefix = getPrefix(import.meta.url);
const dependencies = ['icon', 'focus'];

export const vividAccordionItem =
AccordionItem.compose<FoundationElementDefinition>({
baseName: 'accordion-item',
Expand All @@ -18,4 +19,8 @@ export const vividAccordionItem =
},
});

designSystem.register(vividAccordionItem());
(async () => {
await loadComponentsModules(dependencies, prefix);
designSystem.withPrefix(prefix).register(vividAccordionItem());
})();

4 changes: 4 additions & 0 deletions libs/components/src/lib/accordion/accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ describe('vwc-accordion', () => {
let accordionItem1: AccordionItem;
let accordionItem2: AccordionItem;

beforeAll(async () => {
await customElements.whenDefined(COMPONENT_TAG);
});

beforeEach(async () => {
element = (await fixture(
`<${COMPONENT_TAG}>
Expand Down
4 changes: 2 additions & 2 deletions libs/components/src/lib/accordion/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import styles from './accordion.scss';

import { Accordion } from './accordion';
Expand All @@ -11,4 +11,4 @@ export const vividAccordion = Accordion.compose<FoundationElementDefinition>({
styles,
});

designSystem.register(vividAccordion());
designSystem.withPrefix(getPrefix(import.meta.url)).register(vividAccordion());
4 changes: 2 additions & 2 deletions libs/components/src/lib/action-group/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import styles from './action-group.scss';

import { ActionGroup } from './action-group';
Expand All @@ -11,4 +11,4 @@ export const vividActionGroup = ActionGroup.compose<FoundationElementDefinition>
styles,
});

designSystem.register(vividActionGroup());
designSystem.withPrefix(getPrefix(import.meta.url)).register(vividActionGroup());
4 changes: 4 additions & 0 deletions libs/components/src/lib/avatar/avatar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ describe('vwc-avatar', () => {
let baseElement: Element;
let element: Avatar;

beforeAll(async () => {
await customElements.whenDefined(COMPONENT_TAG);
});

beforeEach(async () => {
element = (await fixture(
`<${COMPONENT_TAG}></${COMPONENT_TAG}>`
Expand Down
13 changes: 9 additions & 4 deletions libs/components/src/lib/avatar/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import '../icon';

import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import { loadComponentsModules } from '../../shared/utils';
import styles from './avatar.scss';

import { Avatar } from './avatar';
import { AvatarTemplate as template } from './avatar.template';

const prefix = getPrefix(import.meta.url);
const dependencies = ['icon'];

export const vividAvatar = Avatar.compose<FoundationElementDefinition>({
baseName: 'avatar',
template: template as any,
styles,
});

designSystem.register(vividAvatar());
(async () => {
await loadComponentsModules(dependencies, prefix);
designSystem.withPrefix(prefix).register(vividAvatar());
})();
4 changes: 4 additions & 0 deletions libs/components/src/lib/badge/badge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const ICON_SELECTOR = 'vwc-icon';
describe('vwc-badge', () => {
let element: Badge;

beforeAll(async () => {
await customElements.whenDefined(COMPONENT_TAG);
});

beforeEach(async () => {
element = await fixture(`<${COMPONENT_TAG}></${COMPONENT_TAG}>`) as Badge;
});
Expand Down
11 changes: 7 additions & 4 deletions libs/components/src/lib/badge/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import '../icon';

import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import { loadComponentsModules } from '../../shared/utils';
import { Badge } from './badge';
import styles from './badge.scss';
import { badgeTemplate as template } from './badge.template';

const prefix = getPrefix(import.meta.url);

/**
* Represents a badge custom element.
Expand All @@ -20,4 +20,7 @@ export const vividBadge = Badge.compose<FoundationElementDefinition>({
styles,
});

designSystem.register(vividBadge());
(async () => {
await loadComponentsModules(['icon'], prefix);
designSystem.withPrefix(prefix).register(vividBadge());
})();
4 changes: 4 additions & 0 deletions libs/components/src/lib/banner/banner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ describe('vwc-banner', () => {

let element: Banner;

beforeAll(async () => {
await customElements.whenDefined(COMPONENT_TAG);
});

beforeEach(async () => {
element = (await fixture(
`<${COMPONENT_TAG}></${COMPONENT_TAG}>`
Expand Down
12 changes: 8 additions & 4 deletions libs/components/src/lib/banner/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import '../button';

import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import { loadComponentsModules } from '../../shared/utils';
import styles from './banner.scss';

import { Banner } from './banner';
import { BannerTemplate as template } from './banner.template';

const prefix = getPrefix(import.meta.url);

export const vividBanner = Banner.compose<FoundationElementDefinition>({
baseName: 'banner',
template: template as any,
styles,
});

designSystem.register(vividBanner());
(async () => {
await loadComponentsModules(['button'], prefix);
designSystem.withPrefix(prefix).register(vividBanner());
})();
13 changes: 8 additions & 5 deletions libs/components/src/lib/breadcrumb-item/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import '../icon';
import '../focus';

import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import {designSystem} from '../../shared/design-system';
import {designSystem, getPrefix} from '../../shared/design-system';
import { loadComponentsModules } from '../../shared/utils';
import styles from './breadcrumb-item.scss';

import {BreadcrumbItem} from './breadcrumb-item';
import {BreadcrumbItemTemplate as template} from './breadcrumb-item.template';

const prefix = getPrefix(import.meta.url);

export const vividBreadcrumbItem = BreadcrumbItem.compose<FoundationElementDefinition>({
baseName: 'breadcrumb-item',
template: template as any,
Expand All @@ -17,4 +17,7 @@ export const vividBreadcrumbItem = BreadcrumbItem.compose<FoundationElementDefin
},
});

designSystem.register(vividBreadcrumbItem());
(async () => {
await loadComponentsModules(['icon', 'focus'], prefix);
designSystem.withPrefix(prefix).register(vividBreadcrumbItem());
})();
5 changes: 3 additions & 2 deletions libs/components/src/lib/breadcrumb/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FoundationElementDefinition } from '@microsoft/fast-foundation';
import { designSystem } from '../../shared/design-system';
import { designSystem, getPrefix } from '../../shared/design-system';
import { breadcrumbTemplate as template } from './breadcrumb.template';
import styles from './breadcrumb.scss';
import { Breadcrumb } from './breadcrumb';
Expand All @@ -10,4 +10,5 @@ export const vividBreadcrumb = Breadcrumb.compose<FoundationElementDefinition>({
styles,
});

designSystem.register(vividBreadcrumb());
designSystem.withPrefix(getPrefix(import.meta.url)).register(vividBreadcrumb());

6 changes: 5 additions & 1 deletion libs/components/src/lib/button/button.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { elementUpdated, fixture } from '@vivid-nx/shared';
import type { Icon } from '../icon/icon';
import { Button } from './button';
import '.';
import '.';

const COMPONENT_TAG = 'vwc-button';
const ICON_SELECTOR = 'vwc-icon';

describe('vwc-button', () => {
let element: Button;

beforeAll(async () => {
await customElements.whenDefined(COMPONENT_TAG);
});

beforeEach(async () => {
element = await fixture(`<${COMPONENT_TAG}></${COMPONENT_TAG}>`) as Button;
});
Expand Down
Loading

0 comments on commit f023bd0

Please sign in to comment.