Skip to content

Commit

Permalink
Prevent raw extra attributes being rendered to the DOM (#773)
Browse files Browse the repository at this point in the history
  • Loading branch information
whawker committed Nov 12, 2020
1 parent 5fd944c commit 47d10a8
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 9 deletions.
18 changes: 18 additions & 0 deletions docs/concepts/extra-attributes.md
Expand Up @@ -52,6 +52,24 @@ const paragraphExtension = new ParagraphExtension({

This example accomplishes the same things as the previous example and remirror is smart enough to automatically parse the dom and write to the dom the required values.

### Render as data attributes

You can return an array of a key value pair, to determine how your extra attribute is rendered in the DOM.

```ts
import { ParagraphExtension } from 'remirror/extension/paragraph';

const paragraphExtension = new ParagraphExtension({
extraAttributes: {
custom: {
default: 'my default',
parseDOM: (dom) => dom.getAttribute('data-custom'),
toDOM: (attrs) => ['data-custom', attrs.custom],
},
},
});
```

## RemirrorManager

Extra attributes can also be added via the `RemirrorManager`. This can set attributes for a collection of nodes, marks and tags. This is very useful when adding attributes to multiple places in one sweep.
Expand Down
18 changes: 18 additions & 0 deletions packages/@remirror/core-utils/src/core-utils.ts
Expand Up @@ -10,17 +10,21 @@ import {
isObject,
isString,
keys,
omit,
sort,
unset,
} from '@remirror/core-helpers';
import type {
AnchorHeadParameter,
AnyConstructor,
ApplySchemaAttributes,
DOMCompatibleAttributes,
EditorSchema,
EditorState,
FromToParameter,
MarkAttributes,
MarkTypeParameter,
NodeAttributes,
PrimitiveSelection,
ProsemirrorNode,
ProsemirrorNodeParameter,
Expand Down Expand Up @@ -1062,6 +1066,20 @@ export function areSchemasCompatible(schemaA: EditorSchema, schemaB: EditorSchem
return true;
}

/**
* Returns attributes for a node excluding those that were provided as extra attributes
*
* @param attrs - The source attributes
* @param extra - The extra attribute schema for this node
*/
export function omitExtraAttributes(
attrs: NodeAttributes,
extra: ApplySchemaAttributes,
): DOMCompatibleAttributes {
const extraAttributeNames = keys(extra.defaults());
return omit({ ...attrs }, extraAttributeNames) as DOMCompatibleAttributes;
}

/**
* A description of an invalid content block (representing a node or a mark).
*/
Expand Down
1 change: 1 addition & 0 deletions packages/@remirror/core-utils/src/index.ts
Expand Up @@ -65,6 +65,7 @@ export {
isTransaction,
shouldUseDomEnvironment,
startPositionOfParent,
omitExtraAttributes,
toDom,
toHtml,
getChangedRanges,
Expand Down
5 changes: 3 additions & 2 deletions packages/@remirror/core/src/builtins/schema-extension.ts
Expand Up @@ -76,7 +76,7 @@ import type { CombinedTags } from './tags-extension';
* awesome: {
* default: 'awesome',
* parseDOM: (domNode) => domNode.getAttribute('data-awesome'),
* toDOM: (node) => ({ 'data-awesome': node.attrs.awesome })
* toDOM: (attrs) => ([ 'data-awesome', attrs.awesome ])
* },
* },
* },
Expand Down Expand Up @@ -857,7 +857,8 @@ function createToDOM(extraAttributes: SchemaAttributes, shouldIgnore: boolean) {
}

if (isArray(value)) {
domAttributes[value[0]] = value[1] ?? (item.attrs[name] as string);
const [attr, val] = value;
domAttributes[attr] = val ?? (item.attrs[name] as string);
}

return;
Expand Down
Expand Up @@ -19,6 +19,7 @@ import {
markPasteRule,
MarkType,
MarkTypeParameter,
omitExtraAttributes,
ProsemirrorPlugin,
Static,
TransactionParameter,
Expand Down Expand Up @@ -68,11 +69,12 @@ export class AutoLinkExtension extends MarkExtension<AutoLinkOptions> {
},
],
toDOM: (node) => {
const attrs = omitExtraAttributes(node.attrs, extra);
return [
'a',
{
...extra.dom(node),
...node.attrs,
...attrs,
role: 'presentation',
},
0,
Expand Down
Expand Up @@ -8,6 +8,7 @@ import {
KeyBindings,
NodeExtension,
NodeExtensionSpec,
omitExtraAttributes,
toggleWrap,
} from '@remirror/core';
import { TextSelection } from '@remirror/pm/state';
Expand Down Expand Up @@ -54,7 +55,7 @@ export class CalloutExtension extends NodeExtension<CalloutOptions> {
},
],
toDOM: (node) => {
const { type, ...rest } = node.attrs as CalloutAttributes;
const { type, ...rest } = omitExtraAttributes(node.attrs, extra) as CalloutAttributes;
const attributes = { ...extra.dom(node), ...rest, [dataAttributeType]: type };

return ['div', attributes, 0];
Expand Down
4 changes: 3 additions & 1 deletion packages/@remirror/extension-image/src/image-extension.ts
Expand Up @@ -10,6 +10,7 @@ import {
NodeAttributes,
NodeExtension,
NodeExtensionSpec,
omitExtraAttributes,
} from '@remirror/core';
import type { ResolvedPos } from '@remirror/pm/model';

Expand Down Expand Up @@ -57,7 +58,8 @@ export class ImageExtension extends NodeExtension {
},
],
toDOM: (node) => {
return ['img', { ...extra.dom(node), ...node.attrs }];
const attrs = omitExtraAttributes(node.attrs, extra);
return ['img', { ...extra.dom(node), ...attrs }];
},
};
}
Expand Down
3 changes: 2 additions & 1 deletion packages/@remirror/extension-link/src/link-extension.ts
Expand Up @@ -22,6 +22,7 @@ import {
MarkExtension,
MarkExtensionSpec,
markPasteRule,
omitExtraAttributes,
OnSetOptionsParameter,
preserveSelection,
ProsemirrorNode,
Expand Down Expand Up @@ -152,7 +153,7 @@ export class LinkExtension extends MarkExtension<LinkOptions> {
},
],
toDOM: (node) => {
const { auto: _, ...rest } = node.attrs;
const { auto: _, ...rest } = omitExtraAttributes(node.attrs, extra);
const auto = node.attrs.auto ? { [AUTO_ATTRIBUTE]: '' } : {};
const rel = 'noopener noreferrer nofollow';
const attrs = { ...extra.dom(node), ...rest, rel, ...auto };
Expand Down
Expand Up @@ -12,6 +12,7 @@ import {
NodeAttributes,
NodeExtension,
NodeExtensionSpec,
omitExtraAttributes,
pick,
replaceText,
Static,
Expand Down Expand Up @@ -150,7 +151,7 @@ export class MentionAtomExtension extends NodeExtension<MentionAtomOptions> {
name,
range,
...rest
} = node.attrs as NamedMentionAtomNodeAttributes;
} = omitExtraAttributes(node.attrs, extra) as NamedMentionAtomNodeAttributes;
const matcher = this.options.matchers.find((matcher) => matcher.name === name);

const mentionClassName = matcher
Expand Down
8 changes: 6 additions & 2 deletions packages/@remirror/preset-embed/src/iframe-extension.ts
Expand Up @@ -10,6 +10,7 @@ import {
NodeExtension,
NodeExtensionSpec,
object,
omitExtraAttributes,
ProsemirrorAttributes,
Shape,
Static,
Expand Down Expand Up @@ -88,7 +89,10 @@ export class IframeExtension extends NodeExtension<IframeOptions> {
},
],
toDOM: (node) => {
const { frameBorder, allowFullScreen, src, type, ...rest } = node.attrs;
const { frameBorder, allowFullScreen, src, type, ...rest } = omitExtraAttributes(
node.attrs,
extra,
);
const { class: className } = this.options;

return [
Expand All @@ -100,7 +104,7 @@ export class IframeExtension extends NodeExtension<IframeOptions> {
src,
'data-embed-type': type,
allowfullscreen: allowFullScreen ? 'true' : 'false',
frameBorder: frameBorder.toString(),
frameBorder: frameBorder?.toString(),
},
];
},
Expand Down

1 comment on commit 47d10a8

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Published on https://remirror.io as production
🚀 Deployed on https://5facee3a34f08035a95222c9--remirror.netlify.app

Please sign in to comment.