Skip to content

Commit

Permalink
alternative to attributesToProps that provides more context
Browse files Browse the repository at this point in the history
  • Loading branch information
danlunde committed Nov 21, 2020
1 parent 8bb148b commit e536505
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 76 deletions.
26 changes: 20 additions & 6 deletions packages/slate-plugins/src/common/types/PluginOptions.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Element } from 'slate';
import { RenderElementProps } from 'slate-react';
import {
GetElementDeserializerOptions,
GetLeafDeserializerOptions,
Expand All @@ -24,18 +26,30 @@ export interface RenderNodePropsOptions {
className?: string;

as?: any;
}

export type DeserializedAttributes = { [key: string]: any } | undefined;

export interface ElementWithAttributes extends Element {
attributes?: DeserializedAttributes;
}

export interface RenderElementPropsWithAttributes extends RenderElementProps {
element: ElementWithAttributes;
}

export interface NodeToPropsOptions
extends RenderElementPropsWithAttributes,
RootProps<RenderNodePropsOptions> {}

export interface NodeToProps<T> {
/**
* Function to evaluate any stored attributes on the element and return as props
* Function to evaluate a node and return props
*/
attributesToProps?: AttributesToProps;
nodeToProps?: (options: T) => HtmlAttributes;
}

export type DeserializedAttributes = { [key: string]: any } | undefined;
export type HtmlAttributes = { [key: string]: any } | undefined;
export type AttributesToProps = (
attributes: DeserializedAttributes
) => HtmlAttributes;

export interface HtmlAttributesProps {
htmlAttributes?: HtmlAttributes;
Expand Down
86 changes: 28 additions & 58 deletions packages/slate-plugins/src/common/utils/getRenderElement.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,15 @@
import * as React from 'react';
import pickBy from 'lodash/pickBy';
import { Element } from 'slate';
import { RenderElementProps } from 'slate-react';
import {
AttributesToProps,
DeserializedAttributes,
NodeToProps,
NodeToPropsOptions,
RenderElementPropsWithAttributes,
RenderNodeOptions,
} from '../types/PluginOptions.types';

export interface GetRenderElementOptions {
/**
* Type of the element.
*/
type: string;
/**
* React component to render the element.
*/
component: any;

/**
* Options passed to the component as props.
*/
[key: string]: any;
}

export interface ElementWithAttributes extends Element {
attributes?: DeserializedAttributes;
}

export interface RenderElementPropsWithAttributes extends RenderElementProps {
element: ElementWithAttributes;
}

export interface GetHtmlAttributes {
attributes?: DeserializedAttributes;
attributesToProps?: AttributesToProps;
}

const getHtmlAttributes = ({
attributes,
attributesToProps,
}: GetHtmlAttributes) => {
if (attributes && attributesToProps)
return pickBy(attributesToProps(attributes));
if (attributes) return pickBy(attributes);
};
export interface GetRenderElementOptions
extends Required<RenderNodeOptions>,
NodeToProps<NodeToPropsOptions> {}

/**
* Get a `renderElement` handler for a single type.
Expand All @@ -55,42 +20,47 @@ export const getRenderElement = ({
type,
component: Component,
rootProps,
}: Required<RenderNodeOptions>) => ({
nodeToProps,
}: GetRenderElementOptions) => ({
attributes,
...props
element,
children,
}: RenderElementPropsWithAttributes) => {
if (props.element.type === type) {
const htmlAttributes = getHtmlAttributes({
attributes: props.element?.attributes,
attributesToProps: rootProps?.attributesToProps,
});

if (element.type === type) {
const htmlAttributes =
nodeToProps?.({ attributes, element, children, rootProps }) ??
element?.attributes;
return (
<Component
attributes={attributes}
htmlAttributes={htmlAttributes}
{...props}
element={element}
{...pickBy(rootProps)}
/>
>
{children}
</Component>
);
}
};

/**
* Get a `renderElement` handler for multiple types.
*/
export const getRenderElements = (options: Required<RenderNodeOptions>[]) => ({
export const getRenderElements = (options: GetRenderElementOptions[]) => ({
attributes,
element,
children,
}: RenderElementPropsWithAttributes) => {
for (const { type, component: Component, rootProps } of options) {
for (const {
type,
component: Component,
rootProps,
nodeToProps,
} of options) {
if (element.type === type) {
const htmlAttributes = getHtmlAttributes({
attributes: element?.attributes,
attributesToProps: rootProps?.attributesToProps,
});

const htmlAttributes =
nodeToProps?.({ attributes, element, children, rootProps }) ??
element?.attributes;
return (
<Component
attributes={attributes}
Expand Down
13 changes: 11 additions & 2 deletions packages/slate-plugins/src/elements/link/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { IStyle } from '@uifabric/styling';
import { IStyleFunctionOrObject } from '@uifabric/utilities';
import { Element } from 'slate';
import { RenderElementProps } from 'slate-react';
import { RangeBeforeOptions } from '../../common/queries/getRangeBefore';
import {
Deserialize,
ElementWithAttributes,
HtmlAttributesProps,
NodeToProps,
NodeToPropsOptions,
RenderElementPropsWithAttributes,
RenderNodeOptions,
RenderNodePropsOptions,
RootProps,
Expand All @@ -20,7 +23,12 @@ export interface LinkNodeData {
url: string;
}
// Element node
export interface LinkNode extends Element, LinkNodeData {}
export interface LinkNode extends ElementWithAttributes, LinkNodeData {}

export type LinkNodeToPropsOptions = NodeToPropsOptions &
RootProps<LinkRenderElementPropsOptions> & {
element: LinkNode;
};

// renderElement options given as props
export interface LinkRenderElementPropsOptions {
Expand All @@ -47,6 +55,7 @@ export type LinkKeyOption = 'link';
// Plugin options
export type LinkPluginOptionsValues = RenderNodeOptions &
RootProps<LinkRenderElementPropsOptions> &
NodeToProps<LinkNodeToPropsOptions> &
Deserialize & {
/**
* Callback to validate an url.
Expand Down
16 changes: 8 additions & 8 deletions packages/slate-plugins/src/elements/table/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ export const DEFAULTS_TABLE: Record<
rootProps: {
className: 'slate-th',
as: 'th',
attributesToProps: (attributes) => ({
colSpan: attributes?.['colspan'],
rowSpan: attributes?.['rowspan'],
}),
styles: {
root: {
backgroundColor: 'rgb(244, 245, 247)',
Expand All @@ -52,17 +48,17 @@ export const DEFAULTS_TABLE: Record<
},
},
},
nodeToProps: ({ element }) => ({
colSpan: element?.attributes?.['colspan'],
rowSpan: element?.attributes?.['rowspan'],
}),
},
td: {
component: StyledElement,
type: ELEMENT_TD,
rootProps: {
className: 'slate-td',
as: 'td',
attributesToProps: (attributes) => ({
colSpan: attributes?.['colspan'],
rowSpan: attributes?.['rowspan'],
}),
styles: {
root: {
backgroundColor: 'rgb(255, 255, 255)',
Expand All @@ -77,5 +73,9 @@ export const DEFAULTS_TABLE: Record<
},
},
},
nodeToProps: ({ element }) => ({
colSpan: element?.attributes?.['colspan'],
rowSpan: element?.attributes?.['rowspan'],
}),
},
};
12 changes: 10 additions & 2 deletions packages/slate-plugins/src/elements/table/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { IStyle } from '@uifabric/styling';
import { IStyleFunctionOrObject } from '@uifabric/utilities';
import { Element } from 'slate';
import { RenderElementProps } from 'slate-react';
import {
Deserialize,
ElementWithAttributes,
HtmlAttributesProps,
NodeToProps,
NodeToPropsOptions,
RenderNodeOptions,
RenderNodePropsOptions,
RootProps,
Expand All @@ -13,7 +15,12 @@ import {
// Data of Element node
export interface TableNodeData {}
// Element node
export interface TableNode extends Element, TableNodeData {}
export interface TableNode extends ElementWithAttributes, TableNodeData {}

export type TableNodeToPropsOptions = NodeToPropsOptions &
RootProps<TableRenderElementPropsOptions> & {
element: TableNode;
};

// renderElement options given as props
export interface TableRenderElementPropsOptions {
Expand All @@ -37,6 +44,7 @@ export type TableKeyOption = 'table' | 'th' | 'tr' | 'td';
// Plugin options
export type TablePluginOptionsValues = RenderNodeOptions &
RootProps<TableRenderElementPropsOptions> &
NodeToProps<TableNodeToPropsOptions> &
Deserialize;
export type TablePluginOptionsKeys = keyof TablePluginOptionsValues;
export type TablePluginOptions<
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { htmlStringToDOMNode, ImagePlugin, LinkPlugin } from '../../../index';
import { serializeHTMLFromNodes } from '../index';

it('serialize link to html with attributes', () => {
expect(
serializeHTMLFromNodes({
plugins: [
LinkPlugin({
link: {
nodeToProps: ({ element }) =>
/^https?:\/\/slatejs.org\/?/.test(element.url)
? {}
: { target: '_blank' },
},
}),
],
nodes: [
{ text: 'An external ' },
{
type: 'a',
url: 'https://theuselessweb.com/',
children: [{ text: 'link' }],
},
{ text: ' and an internal ' },
{
type: 'a',
url: 'https://slatejs.org/',
children: [{ text: 'link' }],
},
{ text: '.' },
],
})
).toBe(
'An external <a href="https://theuselessweb.com/" class="slate-link" target="_blank">link</a> and an internal <a href="https://slatejs.orf/" class="slate-link">link</a>.'
);
});

0 comments on commit e536505

Please sign in to comment.