uui component meta props

This commit is contained in:
ShinCurry
2020-08-27 14:27:24 +08:00
parent ef6984a5ba
commit 2ca4525310
7 changed files with 175 additions and 62 deletions

View File

@@ -122,7 +122,7 @@ export class Button extends UUI.ClassComponent({
},
})<ButtonFeatureProps & ButtonStylingProps, {}> {
render() {
const { Root, LoadingSpinner, Content } = this.nodes
const { Root, LoadingSpinner, Content } = this.state.nodes
return (
<Root

View File

@@ -123,7 +123,7 @@ export class Button extends UUI.ClassComponent({
},
})<ButtonFeatureProps & ButtonStylingProps, {}> {
render() {
const { Root, LoadingSpinner, Content } = this.nodes
const { Root, LoadingSpinner, Content } = this.state.nodes
return (
<Root

View File

@@ -31,7 +31,7 @@ export const Button = UUI.FunctionComponent({
return (
<Root
role="button"
{...omit(props, 'customize', 'styling', 'className', 'style', 'loading')}
{...omit(props, 'customize', 'styling', 'className', 'style', 'loading', 'prefix', 'separator')}
className={classNames({
...(props.styling?.type ? {
[`TYPE_${props.styling?.type}`]: true,

View File

@@ -49,12 +49,9 @@ export class Toaster extends UUI.ClassComponent({
})<ToasterFeatureProps, ToasterState> {
constructor(props: ToasterFeatureProps) {
super(props)
this.state.toasts = [] as IToast[]
}
public state = {
toasts: [] as IToast[],
};
static create(props: ToasterFeatureProps, container = ReactHelper.document?.body) {
const containerElement = ReactHelper.document?.createElement("div");
if (!containerElement || !container) return null;
@@ -111,7 +108,7 @@ export class Toaster extends UUI.ClassComponent({
}
render() {
const { Root } = this.nodes
const { Root } = this.state.nodes
return (
<Root className={classNames(`position-${this.props.position || ToasterPosition.Top}`)}>

View File

@@ -7,7 +7,7 @@
*/
import React from 'react';
import React, { useMemo } from 'react';
import { mapValues, pick, isString, omit, merge, clone, uniq, isEmpty, chain, mergeWith } from 'lodash';
import classNames from 'classnames';
import { mergeRefs } from '../utils/mergeRefs';
@@ -227,6 +227,8 @@ function ComponentNode<P extends any, N extends string, M extends string>(Target
return <_Target
{...omit(_props, 'customize', 'ref')}
prefix={options.prefix}
separator={options.separator}
ref={ref}
className={_props.className}
customize={finalCustomize}
@@ -287,10 +289,14 @@ export type UUIConvenienceProps = {
style?: React.CSSProperties;
/**
* React native support data-* attributes type,
* React natively support data-* attributes type,
* dont need to redeclare again convenience data attributes.
*/
}
export type UUIMetaProps = {
prefix?: string;
separator?: string;
}
export type UUIComponentProps<P, X extends { [key in string]?: keyof IntrinsicNodeT | FunctionComponentNodeT | ClassComponentNodeT }> = P & UUIConvenienceProps & UUIComponentCustomizeProps<X>
export type UUIFunctionComponentProps<T extends (...args: any) => any> = Parameters<T>[0]
export type UUIClassComponentProps<T extends React.JSXElementConstructor<any>> = React.ComponentProps<T>
@@ -335,14 +341,19 @@ export abstract class UUI {
},
WrappedComponent: (props: P, nodes: UUIComponentNodes<X>) => React.ReactElement,
) {
const finalOptions: Required<typeof options> = getFinalOptions(options)
const nodes = compileNodes(finalOptions)
const component: React.FunctionComponent<P & UUIConvenienceProps & Z> = (props) => {
const component: React.FunctionComponent<P & UUIConvenienceProps & UUIMetaProps & Z> = (props) => {
const { prefix, separator } = props;
// eslint-disable-next-line react-hooks/rules-of-hooks
const { finalOptions, nodes } = useMemo(() => {
const finalOptions = getFinalOptions(options, { prefix, separator })
const nodes = compileNodes(finalOptions)
return { finalOptions, nodes }
}, [prefix, separator])
const compiledProps = compileProps(props, finalOptions, undefined)
injectCustomizeProps(nodes, compiledProps)
injectCustomizeProps(nodes, compiledProps);
return WrappedComponent(compiledProps, nodes)
}
component.displayName = `<UUI> [Component] ${finalOptions.name}`
component.displayName = `<UUI> [Component] ${options.name}`
return component
}
@@ -375,27 +386,40 @@ export abstract class UUI {
nodes: X;
},
) {
const finalOptions: Required<typeof options> = getFinalOptions(options)
const nodes = compileNodes(finalOptions)
return class WrappedComponent<P = {}, S = {}, SS = any> extends React.Component<P & UUIConvenienceProps & Z, S, SS> {
nodes: UUIComponentNodes<X>
displayName: string
return class WrappedComponent<P = {}, S = {}, SS = any> extends React.Component<P & UUIConvenienceProps & UUIMetaProps & Z, S & { nodes: UUIComponentNodes<X> }, SS> {
static displayName = `<UUI> [Component] ${options.name}`
state: S & { nodes: UUIComponentNodes<X> }
componentDidUpdate(prevProps: P & UUIConvenienceProps & UUIMetaProps & Z) {
if (
prevProps.prefix !== this.props.prefix ||
prevProps.separator !== this.props.separator
) {
const finalOptions = getFinalOptions(options, this.props)
this.setState({ nodes: compileNodes(finalOptions) })
const compiledProps = compileProps(this.props, finalOptions, (this.props as any).innerRef || undefined)
injectCustomizeProps(this.state.nodes, compiledProps)
}
}
constructor(props: P & UUIConvenienceProps & Z) {
super(props)
this.displayName = `<UUI> [Component] ${finalOptions.name}`
const finalOptions = getFinalOptions(options, props)
this.state = { nodes: compileNodes(finalOptions) } as any
this.state.nodes = compileNodes(finalOptions)
const compiledProps = compileProps(props, finalOptions, (props as any).innerRef || undefined)
injectCustomizeProps(nodes, compiledProps)
this.nodes = nodes
injectCustomizeProps(this.state.nodes, compiledProps)
}
}
}
}
function getFinalOptions(options: any) {
function getFinalOptions(options: any, props: any) {
return {
...options,
prefix: options.prefix || 'UUI',
separator: options.separator || '-',
nodes: options.nodes,
name: options.name,
prefix: props.prefix || options.prefix || 'UUI',
separator: props.separator || options.separator || '-',
}
}

View File

@@ -145,36 +145,6 @@ exports[`UUIComponent customize [convenience][className, style] 1`] = `
</div>
`;
exports[`UUIComponentHOC [more options] 1`] = `
<div
className="XUI=XUITestFunctionComponent=Root"
>
<div
className="XUI=XUITestFunctionComponent=Container"
>
<article
className="XUI=XUITestFunctionComponent=Article"
>
<h1
className="XUI=XUITestFunctionComponent=Title"
>
Lorem ipsum
</h1>
<p
className="XUI=XUITestFunctionComponent=Paragraph"
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sagittis magna finibus lorem semper malesuada. Maecenas vel tristique odio. Duis non nisi turpis. Nam malesuada condimentum ultrices. Mauris lectus ante, sollicitudin in odio ac, elementum euismod ipsum. Nulla semper mattis erat, nec sollicitudin turpis gravida ut. Donec aliquet sit amet enim at consequat. Duis lacinia libero ipsum, in faucibus sapien egestas quis. Pellentesque pretium gravida elit sed viverra. Morbi vitae enim at quam cursus dapibus non ac erat. Phasellus ligula lectus, tempor ut ultricies nec, ornare cursus mauris.
</p>
<p
className="XUI=XUITestFunctionComponent=Paragraph"
>
Proin mollis, dui in volutpat consectetur, quam sapien eleifend eros, ut consectetur neque mauris vel velit. Aliquam sed ultrices ex. Nam sed augue ligula. Cras et enim lorem. Maecenas eget lacus diam. Praesent nec lectus ut ante suscipit semper. Vestibulum congue justo eu rutrum tempus. Maecenas imperdiet neque sapien, vitae pretium dui aliquam et. Suspendisse potenti. Curabitur euismod nisi a urna auctor, quis viverra tellus euismod. Nulla facilisi. Proin eleifend nunc a nisi venenatis scelerisque. Aliquam erat volutpat. Etiam finibus laoreet ipsum, a rutrum odio venenatis vel.
</p>
</article>
</div>
</div>
`;
exports[`UUIComponentHOC [no Root node] 1`] = `
<div
className="UUI-UUITestComponent-Container TestingClassName"
@@ -186,6 +156,74 @@ exports[`UUIComponentHOC [no Root node] 1`] = `
/>
`;
exports[`UUIComponentHOC [options] 1`] = `
<div
className="XUI=TestXComponent=Root"
>
<div
className="XUI=TestXComponent=Container"
>
<article
className="XUI=TestXComponent=Article"
>
<h1
className="XUI=TestXComponent=Title"
>
Lorem ipsum
</h1>
<p
className="XUI=TestXComponent=Paragraph"
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sagittis magna finibus lorem semper malesuada. Maecenas vel tristique odio. Duis non nisi turpis. Nam malesuada condimentum ultrices. Mauris lectus ante, sollicitudin in odio ac, elementum euismod ipsum. Nulla semper mattis erat, nec sollicitudin turpis gravida ut. Donec aliquet sit amet enim at consequat. Duis lacinia libero ipsum, in faucibus sapien egestas quis. Pellentesque pretium gravida elit sed viverra. Morbi vitae enim at quam cursus dapibus non ac erat. Phasellus ligula lectus, tempor ut ultricies nec, ornare cursus mauris.
</p>
<p
className="XUI=TestXComponent=Paragraph"
>
Proin mollis, dui in volutpat consectetur, quam sapien eleifend eros, ut consectetur neque mauris vel velit. Aliquam sed ultrices ex. Nam sed augue ligula. Cras et enim lorem. Maecenas eget lacus diam. Praesent nec lectus ut ante suscipit semper. Vestibulum congue justo eu rutrum tempus. Maecenas imperdiet neque sapien, vitae pretium dui aliquam et. Suspendisse potenti. Curabitur euismod nisi a urna auctor, quis viverra tellus euismod. Nulla facilisi. Proin eleifend nunc a nisi venenatis scelerisque. Aliquam erat volutpat. Etiam finibus laoreet ipsum, a rutrum odio venenatis vel.
</p>
</article>
</div>
</div>
`;
exports[`UUIComponentHOC [options] 2`] = `
<div
className="YUI+TestXComponent+Root"
>
<div
className="YUI+TestXComponent+Container"
>
<article
className="YUI+TestXComponent+Article"
>
<h1
className="YUI+TestXComponent+Title"
>
Lorem ipsum
</h1>
<p
className="YUI+TestXComponent+Paragraph"
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sagittis magna finibus lorem semper malesuada. Maecenas vel tristique odio. Duis non nisi turpis. Nam malesuada condimentum ultrices. Mauris lectus ante, sollicitudin in odio ac, elementum euismod ipsum. Nulla semper mattis erat, nec sollicitudin turpis gravida ut. Donec aliquet sit amet enim at consequat. Duis lacinia libero ipsum, in faucibus sapien egestas quis. Pellentesque pretium gravida elit sed viverra. Morbi vitae enim at quam cursus dapibus non ac erat. Phasellus ligula lectus, tempor ut ultricies nec, ornare cursus mauris.
</p>
<p
className="YUI+TestXComponent+Paragraph"
>
Proin mollis, dui in volutpat consectetur, quam sapien eleifend eros, ut consectetur neque mauris vel velit. Aliquam sed ultrices ex. Nam sed augue ligula. Cras et enim lorem. Maecenas eget lacus diam. Praesent nec lectus ut ante suscipit semper. Vestibulum congue justo eu rutrum tempus. Maecenas imperdiet neque sapien, vitae pretium dui aliquam et. Suspendisse potenti. Curabitur euismod nisi a urna auctor, quis viverra tellus euismod. Nulla facilisi. Proin eleifend nunc a nisi venenatis scelerisque. Aliquam erat volutpat. Etiam finibus laoreet ipsum, a rutrum odio venenatis vel.
</p>
</article>
</div>
</div>
`;
exports[`UUIComponentHOC [options] 3`] = `
<div
className="GUI~TestZComponent~Root"
>
TestZComponent
</div>
`;
exports[`UUIComponentHOC - customize 1`] = `
<div
className="UUI-UUITestUnionComponent-Root"

View File

@@ -1,7 +1,7 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { UUI } from '../../src/core/uui';
import Enzyme, { mount } from 'enzyme';
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter })
@@ -47,7 +47,7 @@ it('UUIComponentHOC', () => {
}
}) {
render() {
const { Root, Container, Article, Title, Paragraph } = this.nodes
const { Root, Container, Article, Title, Paragraph } = this.state.nodes
return (
<Root>
<Container>
@@ -140,7 +140,7 @@ it('UUIComponentHOC - customize', () => {
}
}) {
render() {
const { Root, X } = this.nodes
const { Root, X } = this.state.nodes
return (
<Root>
<X className="TestFuncXInner" style={{ color: 'red' }}>
@@ -237,9 +237,9 @@ it('UUIComponentHOC - customize', () => {
expect(tree).toMatchSnapshot();
});
it('UUIComponentHOC [more options]', () => {
it('UUIComponentHOC [options]', () => {
const XUITestFunctionComponent = UUI.FunctionComponent({
name: 'XUITestFunctionComponent',
name: 'TestXComponent',
prefix: 'XUI',
separator: '=',
nodes: {
@@ -264,11 +264,65 @@ it('UUIComponentHOC [more options]', () => {
)
})
class UUITestClassComponent extends UUI.ClassComponent({
prefix: 'ZUI',
name: 'TestZComponent',
separator: '+',
nodes: {
Root: 'div',
}
}) {
render() {
const { Root } = this.state.nodes
return (
<Root>
TestZComponent
</Root>
)
}
}
const tree1 = renderer
.create(<XUITestFunctionComponent></XUITestFunctionComponent>)
.toJSON();
expect(tree1).toMatchSnapshot();
const tree2 = renderer
.create(<XUITestFunctionComponent
prefix={'YUI'}
separator={'+'}
></XUITestFunctionComponent>)
.toJSON();
expect(tree2).toMatchSnapshot();
const tree3 = renderer
.create(<UUITestClassComponent
prefix={'GUI'}
separator={'~'}
></UUITestClassComponent>)
.toJSON();
expect(tree3).toMatchSnapshot();
const wrapper1 = shallow(<XUITestFunctionComponent
prefix={'GUI'}
separator={'+'}
></XUITestFunctionComponent>) as any;
const before1 = wrapper1.html()
wrapper1.setProps({ prefix: 'HUI', name: 'ChangedTestHComponent', separator: '~' });
const after1 = wrapper1.html()
expect(before1).not.toEqual(after1);
const wrapper2 = shallow(<UUITestClassComponent
prefix={'BUI'}
separator={'+'}
></UUITestClassComponent>) as any;
const before2 = wrapper2.html()
wrapper2.setProps({ prefix: 'CUI', name: 'ChangedTestCComponent', separator: '~' });
const after2 = wrapper2.html()
expect(before2).not.toEqual(after2);
});
it('UUIComponentHOC [no Root node]', () => {