diff --git a/src/core/uui.tsx b/src/core/uui.tsx index 3759011..2dae5fa 100644 --- a/src/core/uui.tsx +++ b/src/core/uui.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { mapValues, pick, isString, omit, merge, clone, uniq, isEmpty } from 'lodash'; +import { mapValues, pick, isString, omit, merge, clone, uniq, isEmpty, chain } from 'lodash'; import classNames from 'classnames'; import { mergeRefs } from '../utils/mergeRefs'; @@ -32,10 +32,16 @@ export interface NodeCustomizeChildrenProps { extendChildrenBefore?: React.ReactNode; extendChildrenAfter?: React.ReactNode; } +export interface NodeCustomizeDataAttributesProps { + dataAttributes?: { + [key: string]: any; + }; +} export type NodeCustomizeProps = & NodeCustomizeClassNameProps & NodeCustomizeStyleProps & NodeCustomizeChildrenProps + & NodeCustomizeDataAttributesProps & React.RefAttributes // --------------------------------------------------------------- @@ -88,6 +94,18 @@ function IntrinsicNode( }) return isEmpty(data) ? undefined : data })() + const dataAttributes = (() => { + if (!customizeProps.customize?.dataAttributes) return {} + /** + * @reference https://www.w3.org/TR/2008/REC-xml-20081126/#NT-Name + * TODO: // fix regex for supporting unicode + */ + const validDataAttributesCharactersRegex = /^([A-Za-z0-9-])*$/ + return chain(customizeProps.customize.dataAttributes) + .pickBy((v, k) => validDataAttributesCharactersRegex.test(k)) + .mapKeys((v, k) => `data-${k}`) + .value() + })() const children = (() => { const noChildren = ['input', 'textarea', 'hr'].indexOf(tagName) !== -1 const isSelectOption = tagName === 'option' @@ -150,6 +168,7 @@ function IntrinsicNode( return React.createElement(tagName, { ...omit(_props, 'children', 'ref', 'className', 'style'), ...mergedCallbackFunctions, + ...dataAttributes, ref, className, style, }, children) @@ -242,6 +261,11 @@ export type UUIConvenienceProps = { * @default none */ style?: React.CSSProperties; + + /** + * React native support data-* attributes type, + * dont need to redeclare again convenience data attributes. + */ } export type UUIComponentProps = P & UUIConvenienceProps & UUIComponentCustomizeProps export type UUIFunctionComponentProps any> = Parameters[0] @@ -369,7 +393,16 @@ function compileProps(props: any, options: any, ref: any): any { ) { const rootCustomizeProps: any = (compiledProps.customize as any)['Root'] || {}; if (compiledProps.className) rootCustomizeProps.extendClassName = classNames(compiledProps.className, rootCustomizeProps.extendClassName); - if (compiledProps.style) rootCustomizeProps.extendStyle = Object.assign(compiledProps.style, rootCustomizeProps.extendStyle) as any; + if (compiledProps.style) rootCustomizeProps.extendStyle = Object.assign(compiledProps.style, rootCustomizeProps.extendStyle); + + const dataAttributes = chain(compiledProps) + .pickBy((v, k) => k.startsWith('data-')) + .mapKeys((v, k) => k.replace('data-', '')) + .value(); + if (!isEmpty(dataAttributes)) { + rootCustomizeProps.dataAttributes = Object.assign(dataAttributes, rootCustomizeProps.dataAttributes); + } + (compiledProps.customize as any)['Root'] = rootCustomizeProps; } compiledProps.ref = ref diff --git a/tests/core/__snapshots__/uui.test.tsx.snap b/tests/core/__snapshots__/uui.test.tsx.snap index cdfc096..751bff9 100644 --- a/tests/core/__snapshots__/uui.test.tsx.snap +++ b/tests/core/__snapshots__/uui.test.tsx.snap @@ -1,5 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`UUIComponent [dataAttributes] 1`] = ` +
+
+
+
+`; + exports[`UUIComponent [special IntrinsicNodes children] 1`] = `
{ expect(ref1.current).toBe(ref2.current); expect(ref3.current).toBe(ref4.current); +}) + +/** + * UUI Component dataAttributes + * + * 测试 props.customize[Node].dataAttributes 是否被正确插入 DOM + */ +it('UUIComponent [dataAttributes]', () => { + + const UUITestComponent = UUI.FunctionComponent({ + name: 'UUITestComponent', + nodes: { + Root: 'div', + Node1: 'div', + Node2: 'div', + } + }, (props: {}, nodes) => { + const { Root, Node1, Node2 } = nodes + return ( + + + + + ) + }) + + const tree = renderer + .create( + + ) + .toJSON(); + + expect(tree).toMatchSnapshot(); }) \ No newline at end of file