Merge pull request #691 from smartxworks/feat/traitPropertiesDidUpdated

feat: add `traitPropertiesDidUpdated` lifecircle for trait
This commit is contained in:
tanbowensg 2023-03-02 11:32:40 +08:00 committed by GitHub
commit 516cc1dfd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 228 additions and 2 deletions

View File

@ -14,6 +14,7 @@ import {
TabsWithSlotsSchema,
ParentRerenderSchema,
MultiSlotsSchema,
UpdateTraitPropertiesSchema,
} from './mockSchema';
// A pure single sunmao component will render twice when it mount.
@ -222,3 +223,44 @@ describe('slot trait if condition', () => {
clearTesterMap();
});
});
describe('the `traitPropertiesDidUpdated` lifecircle for trait', () => {
it('it will only count once after the states are updated', () => {
const { App, stateManager } = initSunmaoUI({ libs: [TestLib] });
stateManager.mute = true;
const { unmount } = render(<App options={UpdateTraitPropertiesSchema} />);
const countBeforeClick = stateManager.store.button0.count;
act(() => {
screen.getByTestId('button0').click();
});
expect(stateManager.store.button0.count).toBe(countBeforeClick + 1);
unmount();
clearTesterMap();
});
it("it shouldn't count when update the states which the component depends on", () => {
const { App, stateManager } = initSunmaoUI({ libs: [TestLib] });
const NewUpdateTraitPropertiesSchema = produce(UpdateTraitPropertiesSchema, draft => {
const button = draft.spec.components.find(({ id }) => id === 'button0');
const handlers = button?.traits[1].properties.handlers as { disabled: boolean }[];
handlers[0].disabled = true;
handlers[1].disabled = true;
});
stateManager.mute = true;
const { unmount } = render(<App options={NewUpdateTraitPropertiesSchema} />);
const countBeforeClick = stateManager.store.button0.count;
act(() => {
screen.getByTestId('button0').click();
});
expect(stateManager.store.button0.count).toBe(countBeforeClick);
unmount();
clearTesterMap();
});
});

View File

@ -314,3 +314,135 @@ export const MultiSlotsSchema: Application = {
],
},
};
export const UpdateTraitPropertiesSchema: Application = {
version: 'sunmao/v1',
kind: 'Application',
metadata: {
name: 'some App',
},
spec: {
components: [
{
id: 'state0',
type: 'core/v1/dummy',
properties: {},
traits: [
{
type: 'core/v1/state',
properties: {
key: 'value',
initialValue: '',
},
},
],
},
{
id: 'button0',
type: 'test/v1/button',
properties: {
type: 'default',
status: 'default',
long: false,
size: 'default',
disabled: false,
loading: false,
shape: 'square',
text: '{{state2.value}}',
},
traits: [
{
type: 'test/v1/count',
properties: {
param1: '{{state0.value + state1.value}}',
param2: '{{!state0.value}}',
},
},
{
type: 'core/v1/event',
properties: {
handlers: [
{
type: 'click',
componentId: 'state0',
method: {
name: 'setValue',
parameters: {
key: 'value',
value: 'state0',
},
},
wait: {
type: 'debounce',
time: 0,
},
disabled: false,
},
{
type: 'click',
componentId: 'state1',
method: {
name: 'setValue',
parameters: {
key: 'value',
value: 'state1',
},
},
wait: {
type: 'debounce',
time: 0,
},
disabled: false,
},
{
type: 'click',
componentId: 'state2',
method: {
name: 'setValue',
parameters: {
key: 'value',
value: 'state2',
},
},
wait: {
type: 'debounce',
time: 0,
},
disabled: false,
},
],
},
},
],
},
{
id: 'state1',
type: 'core/v1/dummy',
properties: {},
traits: [
{
type: 'core/v1/state',
properties: {
key: 'value',
initialValue: '',
},
},
],
},
{
id: 'state2',
type: 'core/v1/dummy',
properties: {},
traits: [
{
type: 'core/v1/state',
properties: {
key: 'value',
initialValue: '',
},
},
],
},
],
},
};

View File

@ -25,8 +25,9 @@ export default implementRuntimeComponent({
},
})(({ callbackMap, component, elementRef }) => {
const onClick = () => {
callbackMap?.click();
callbackMap?.click?.();
};
return (
<div ref={elementRef}>
<button onClick={onClick} data-testid={component.id}>

View File

@ -0,0 +1,42 @@
import { Type } from '@sinclair/typebox';
import { implementRuntimeTrait } from '../../src';
const CountTraitPropertiesSpec = Type.Object({
param1: Type.Any(),
param2: Type.Any(),
});
export default implementRuntimeTrait({
version: 'test/v1',
metadata: {
name: 'count',
description: 'for test',
},
spec: {
properties: CountTraitPropertiesSpec,
methods: [],
state: Type.Object({
count: Type.String(),
}),
},
})(() => {
return ({ param1, param2, componentId, mergeState, services }) => {
const state = services.stateManager.store[componentId];
// read the states. don't remove this line
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const string = param1 + param2;
return {
props: {
traitPropertiesDidUpdated: [
() => {
mergeState({
count: (state.count || 0) + 1,
});
},
],
},
};
};
});

View File

@ -5,9 +5,10 @@ import TestInput from './Input';
import TestTabs from './Tabs';
import TestList from './TestList';
import TimeoutTrait from './TimeoutTrait';
import CountTrait from './CountTrait';
import { SunmaoLib } from '../../src';
export const TestLib: SunmaoLib = {
components: [TestButton, TestTester, TestInput, TestTabs, TestList],
traits: [TimeoutTrait],
traits: [TimeoutTrait, CountTrait],
};

View File

@ -149,6 +149,13 @@ export const ImplWrapperMain = React.forwardRef<HTMLDivElement, ImplWrapperProps
};
});
useEffect(() => {
const clearFunctions = propsFromTraits?.traitPropertiesDidUpdated?.map(e => e());
return () => {
clearFunctions?.forEach(func => func && func());
};
}, [propsFromTraits?.traitPropertiesDidUpdated]);
useDidUnmount(() => {
propsFromTraits?.componentDidUnmount?.forEach(e => e());
});

View File

@ -15,6 +15,7 @@ export type TraitResult<
componentDidUnmount?: Array<() => void>;
componentDidMount?: Array<() => Function | void>;
componentDidUpdate?: Array<() => Function | void>;
traitPropertiesDidUpdated?: Array<() => Function | void>;
} | null;
unmount?: boolean;
};