From b35e627a964549213d6e24f90f760f80c93b6c6d Mon Sep 17 00:00:00 2001 From: MrWindlike Date: Wed, 1 Mar 2023 14:11:05 +0800 Subject: [PATCH] feat: add `traitPropertiesDidUpdated` lifecircle for trait --- .../ImplWrapper/ImplWrapper.spec.tsx | 42 ++++++ .../__tests__/ImplWrapper/mockSchema.ts | 132 ++++++++++++++++++ packages/runtime/__tests__/testLib/Button.tsx | 3 +- .../runtime/__tests__/testLib/CountTrait.tsx | 42 ++++++ packages/runtime/__tests__/testLib/index.ts | 3 +- .../_internal/ImplWrapper/ImplWrapperMain.tsx | 7 + packages/runtime/src/types/trait.ts | 1 + 7 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 packages/runtime/__tests__/testLib/CountTrait.tsx diff --git a/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx b/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx index 60d5b8d3..510e3a3a 100644 --- a/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx +++ b/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx @@ -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(); + 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(); + const countBeforeClick = stateManager.store.button0.count; + + act(() => { + screen.getByTestId('button0').click(); + }); + + expect(stateManager.store.button0.count).toBe(countBeforeClick); + + unmount(); + clearTesterMap(); + }); +}); diff --git a/packages/runtime/__tests__/ImplWrapper/mockSchema.ts b/packages/runtime/__tests__/ImplWrapper/mockSchema.ts index bf755b6c..5ada4f34 100644 --- a/packages/runtime/__tests__/ImplWrapper/mockSchema.ts +++ b/packages/runtime/__tests__/ImplWrapper/mockSchema.ts @@ -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: '', + }, + }, + ], + }, + ], + }, +}; diff --git a/packages/runtime/__tests__/testLib/Button.tsx b/packages/runtime/__tests__/testLib/Button.tsx index 2cc4c4c8..9de1d5ba 100644 --- a/packages/runtime/__tests__/testLib/Button.tsx +++ b/packages/runtime/__tests__/testLib/Button.tsx @@ -25,8 +25,9 @@ export default implementRuntimeComponent({ }, })(({ callbackMap, component, elementRef }) => { const onClick = () => { - callbackMap?.click(); + callbackMap?.click?.(); }; + return (