mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-04-06 21:40:23 +08:00
Merge pull request #691 from smartxworks/feat/traitPropertiesDidUpdated
feat: add `traitPropertiesDidUpdated` lifecircle for trait
This commit is contained in:
commit
516cc1dfd1
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -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}>
|
||||
|
42
packages/runtime/__tests__/testLib/CountTrait.tsx
Normal file
42
packages/runtime/__tests__/testLib/CountTrait.tsx
Normal 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,
|
||||
});
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
});
|
@ -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],
|
||||
};
|
||||
|
@ -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());
|
||||
});
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user