mirror of
https://github.com/HackPlan/UUI.git
synced 2026-06-14 22:50:07 +08:00
uui component meta props
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}`)}>
|
||||
|
||||
@@ -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 || '-',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]', () => {
|
||||
|
||||
Reference in New Issue
Block a user