Skip to content

Commit

Permalink
Issue #53: WIP: Ability to ignore common attributes which are also no…
Browse files Browse the repository at this point in the history
…t already defined on the component. Need unit tests...
  • Loading branch information
patricknelson committed Apr 23, 2024
1 parent ce856bf commit 5ebecd1
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 14 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,14 @@ the [Hello World demo](https://github.com/patricknelson/svelte-retag/tree/main/d

### Options 🛠

| Option | Default | Description |
|--------------|:------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `component` | _(required)_ | The constructor for your Svelte component (from `import`) |
| `tagname` | _(required)_ | The custom element tag name to use ([must contain a dash](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)) |
| `attributes` | `[]` | `Array` (legacy): Explicit list of attributes to reactively forward to your component. Attributes must be the lowercase version of your Svelte component props ([similar to Lit](https://lit.dev/docs/components/properties/#observed-attributes)). <br><br> `Boolean` (recommended): If set to `true`, will automatically forward all attributes to your component props. If `false`, will not forward anything. <br><br> **Note:** In v2, this option will be removed and all attributes will be forwarded by default (for consistency with Svelte 4's custom elements, see https://github.com/patricknelson/svelte-retag/issues/36). |
| `shadow` | `false` | Optional. Indicates if this component should use shadow DOM. <br/><br/> **Note:** Only basic support for shadow DOM is currently provided. See https://github.com/patricknelson/svelte-retag/issues/6. |
| `href` | `''` | Optional. URL to your stylesheet. Allows you to ensure your styles are included in the shadow DOM. This option is only useful when `shadow` is set to `true`. |
| Option | Default | Description |
|-------------------------------|:------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `component` | _(required)_ | The constructor for your Svelte component (from `import`) |
| `tagname` | _(required)_ | The custom element tag name to use ([must contain a dash](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)) |
| `attributes` | `[]` | `Array` (legacy): Explicit list of attributes to reactively forward to your component. Attributes must be the lowercase version of your Svelte component props ([similar to Lit](https://lit.dev/docs/components/properties/#observed-attributes)). <br><br> `Boolean` (recommended): If set to `true`, will automatically forward all attributes to your component props. If `false`, will not forward anything. <br><br> **Note:** In v2, this option will be removed and all attributes will be forwarded by default (for consistency with Svelte 4's custom elements, see https://github.com/patricknelson/svelte-retag/issues/36). |
| `shadow` | `false` | Optional. Indicates if this component should use shadow DOM. <br/><br/> **Note:** Only basic support for shadow DOM is currently provided. See https://github.com/patricknelson/svelte-retag/issues/6. |
| `ignoreCommonAttribWarnings` | `false` | Optional. Suppresses warnings in development mode about common attributes (such as `id`, `class`, `style` and `data-*`) if they don't already exist on the component. Set to an array to customize the list of ignored attributes.
| `href` | `''` | Optional. URL to your stylesheet. Allows you to ensure your styles are included in the shadow DOM. This option is only useful when `shadow` is set to `true`. |

**Note:** For portability, `svelte-retag`'s API is fully backward compatible
with [`svelte-tag@^1.0.0`](https://github.com/crisward/svelte-tag).
Expand Down
41 changes: 34 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ function renderElements(timestamp) {
* @property {CmpConstructor} component The Svelte component *class* constructor to incorporate into your custom element (this is the imported component class, *not* an instance)
* @property {string} tagname Name of the custom element tag you'd like to define.
* @property {string[]|boolean} [attributes=[]] Optional array of attributes that should be reactively forwarded to the component when modified. Set to true to automatically watch all attributes.
* @property {boolean|string[]} [ignoreCommonAttribWarnings=false] Suppresses warnings in development mode about common attributes (such as "id", "class" and "data-*") if they don't already exist on the component. Set to an array to customize the list of ignored attributes.
* @property {boolean} [shadow=false] Indicates if we should build the component in the shadow root instead of in the regular ("light") DOM.
* @property {string} [href=""] URL to the CSS stylesheet to incorporate into the shadow DOM (if enabled).
*
Expand Down Expand Up @@ -123,6 +124,19 @@ export default function svelteRetag(opts) {
});
}

// Filter for dynamically ignoring errors when using common attributes which might potentially be on a custom element
// but ALSO aren't already explicitly defined on the Svelte component. Default to false but allow user to enable.
let ignoreAttribFilter = () => false;
if (opts?.ignoreCommonAttribWarnings === true) {
ignoreAttribFilter = (name) => {
return (name === 'id' || name === 'class' || name === 'style' || name.startsWith('data-'));
};
} else if (Array.isArray(opts.ignoreCommonAttribWarnings)) {
ignoreAttribFilter = (name) => {
return opts.ignoreCommonAttribWarnings.includes(name);
};
}

/**
* Object containing keys pointing to slots: Either an actual <slot> element or a document fragment created to wrap
* default slot content.
Expand Down Expand Up @@ -320,7 +334,9 @@ export default function svelteRetag(opts) {
// If instance already available, pass it through immediately.
if (this.componentInstance) {
let translatedName = this._translateAttribute(name);
this.componentInstance.$set({ [translatedName]: value });
if (translatedName !== null) {
this.componentInstance.$set({ [translatedName]: value });
}
}
}

Expand Down Expand Up @@ -359,16 +375,23 @@ export default function svelteRetag(opts) {
* Converts the provided lowercase attribute name to the correct case-sensitive component prop name, if possible.
*
* @param {string} attributeName
* @returns {string}
* @returns {string|null}
*/
_translateAttribute(attributeName) {
// In the unlikely scenario that a browser somewhere doesn't do this for us (or maybe we're in a quirks mode or something...)
attributeName = attributeName.toLowerCase();
if (this.propMap && this.propMap.has(attributeName)) {
return this.propMap.get(attributeName);
} else {
this._debug(`_translateAttribute(): ${attributeName} not found`);
return attributeName;
// Return it unchanged but only if it's not in our "ignore attributes" filter.
if (!ignoreAttribFilter(attributeName)) {
this._debug(`_translateAttribute(): ${attributeName} not found on component, keeping unchanged`);
return attributeName;
} else {
// Ignored.
this._debug(`_translateAttribute(): ${attributeName} matched ignore filter, skipping entirely`);
return null;
}
}
}

Expand Down Expand Up @@ -515,9 +538,13 @@ export default function svelteRetag(opts) {
// props object with the correct case.
this.propMap = propMapCache.get(this.tagName);
for(let attr of [...this.attributes]) {
// Note: Skip svelte-retag specific attributes (used for hydration purposes).
if (attr.name.indexOf('data-svelte-retag') !== -1) continue;
props[this._translateAttribute(attr.name)] = attr.value;
// Note: Skip svelte-retag specific attributes (used for hydration purposes). This is not included in the ignored
// attributes filter since it's a special case and cannot be overridden.
if (attr.name.startsWith('data-svelte-retag')) continue;
const translatedName = this._translateAttribute(attr.name);
if (translatedName !== null) {
props[translatedName] = attr.value;
}
}
}

Expand Down

0 comments on commit 5ebecd1

Please sign in to comment.