Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions packages/isolated-element/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,28 @@ describe('createIsolatedElement', () => {
document.querySelector('body')!.innerHTML = '';
});

it('should validate the custom element name', async () => {
it('should not allow invalid custom element names', async () => {
const invalidName = 'test';

await expect(createIsolatedElement({ name: invalidName })).rejects.toThrow(
`"${invalidName}" is not a valid custom element name`,
`"${invalidName}" cannot have a shadow root attached to it`,
);
});

it('should not allow certain built-in elements', async () => {
const invalidName = 'a';

await expect(createIsolatedElement({ name: invalidName })).rejects.toThrow(
`"${invalidName}" cannot have a shadow root attached to it`,
);
});

it('should allow certain built-in elements', async () => {
const validName = 'div';

await expect(createIsolatedElement({ name: validName })).resolves.toBeDefined();
});

it('should insert an app into the UI', async () => {
const text = 'Example';
const appId = 'app';
Expand Down
29 changes: 27 additions & 2 deletions packages/isolated-element/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import isPotentialCustomElementName from 'is-potential-custom-element-name';
import { CreateIsolatedElementOptions } from './options';

/**
* Built-in elements that can have a shadow root attached to them.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#elements_you_can_attach_a_shadow_to
*/
const ALLOWED_SHADOW_ELEMENTS = [
'article',
'aside',
'blockquote',
'body',
'div',
'footer',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'header',
'main',
'nav',
'p',
'section',
'span',
];

export type { CreateIsolatedElementOptions };

/**
Expand Down Expand Up @@ -33,9 +58,9 @@ export async function createIsolatedElement(options: CreateIsolatedElementOption
}> {
const { name, mode = 'closed', css, isolateEvents = false } = options;

if (!isPotentialCustomElementName(name)) {
if (!ALLOWED_SHADOW_ELEMENTS.includes(name) && !isPotentialCustomElementName(name)) {
throw Error(
`"${name}" is not a valid custom element name. It must be two words and kebab-case, with a few exceptions. See spec for more details: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name`,
`"${name}" cannot have a shadow root attached to it. It must be two words and kebab-case, with a few exceptions. See https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#elements_you_can_attach_a_shadow_to`,
);
}

Expand Down
7 changes: 6 additions & 1 deletion packages/isolated-element/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
*/
export interface CreateIsolatedElementOptions {
/**
* A unique HTML tag name (two words, kebab case - [see spec](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name)) used when defining the web component used internally. Don't use the same name twice for different UIs.
* An HTML tag name used for the shadow root container.
*
* Note that you can't attach a shadow root to every type of element. There are some that can't have a shadow DOM for security reasons (for example <a>).
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#elements_you_can_attach_a_shadow_to
* @example "sticky-note"
* @example "anime-skip-player"
* @example "github-better-line-count-diff"
* @example "div"
*/
name: string;
/**
Expand Down