From 4b7fe0c8bdfad405dce6e74e548ce34eb34e4bf9 Mon Sep 17 00:00:00 2001 From: Guillaume Chau Date: Sat, 3 Dec 2022 17:08:03 +0100 Subject: [PATCH] fix: state not syncing in iframe mode, fix #361 --- examples/vue3/cypress/integration/state.js | 49 +++++++++++++++++++ .../vue3/src/components/StateOption.story.vue | 2 +- packages/histoire-shared/src/state.ts | 9 +++- 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 examples/vue3/cypress/integration/state.js diff --git a/examples/vue3/cypress/integration/state.js b/examples/vue3/cypress/integration/state.js new file mode 100644 index 00000000..39bc014b --- /dev/null +++ b/examples/vue3/cypress/integration/state.js @@ -0,0 +1,49 @@ +/// + +describe('State Options API', () => { + const getIframeBody = () => cy.get('iframe[data-test-id="preview-iframe"]').iframe() + + beforeEach(() => { + cy.visit('/story/src-components-stateoption-story-vue?variantId=src-components-stateoption-story-vue-0') + }) + + it('syncs state', () => { + getIframeBody().find('.state-output').contains('"optionApiData": "OPTION API"') + cy.get('[data-test-id="story-controls"]').get('input[type="text"]').clear().type('Meow') + getIframeBody().find('.state-output').contains('"optionApiData": "Meow"') + }) +}) + +describe('State Setup API', () => { + const getIframeBody = () => cy.get('iframe[data-test-id="preview-iframe"]').iframe() + + beforeEach(() => { + cy.visit('/story/src-components-statesetup-story-vue?variantId=src-components-statesetup-story-vue-0') + }) + + it('syncs state', () => { + getIframeBody().find('pre').contains('"count": 0') + getIframeBody().find('pre').contains('"text": "Meow"') + cy.get('[data-test-id="story-controls"] .controls').contains('+1').click().click() + cy.get('[data-test-id="story-controls"] input[type="text"]').eq(0).clear().type('Waf') + getIframeBody().find('pre').contains('"count": 2') + getIframeBody().find('pre').contains('"text": "Waf"') + }) +}) + +describe('State Setup API (2)', () => { + const getIframeBody = () => cy.get('iframe[data-test-id="preview-iframe"]').iframe() + + beforeEach(() => { + cy.visit('/story/src-components-statesetup2-story-vue?variantId=src-components-statesetup2-story-vue-0') + }) + + it('syncs state', () => { + getIframeBody().find('pre').contains('"count": 0') + getIframeBody().find('pre').contains('"text": "Meow"') + cy.get('[data-test-id="story-controls"] .controls').contains('+1').click().click() + cy.get('[data-test-id="story-controls"] input[type="text"]').eq(0).clear().type('Waf') + getIframeBody().find('pre').contains('"count": 2') + getIframeBody().find('pre').contains('"text": "Waf"') + }) +}) diff --git a/examples/vue3/src/components/StateOption.story.vue b/examples/vue3/src/components/StateOption.story.vue index e142782f..9fa88eee 100644 --- a/examples/vue3/src/components/StateOption.story.vue +++ b/examples/vue3/src/components/StateOption.story.vue @@ -13,7 +13,7 @@ export default {

State

-
{{ { optionApiData } }}
+
{{ { optionApiData } }}
diff --git a/packages/histoire-shared/src/state.ts b/packages/histoire-shared/src/state.ts index db4db05e..29ac84f9 100644 --- a/packages/histoire-shared/src/state.ts +++ b/packages/histoire-shared/src/state.ts @@ -22,8 +22,13 @@ export function omit (data, keys: string[]) { return copy } -export function applyState (target: any, state: any) { +export function applyState (target: any, state: any, override = false) { for (const key in state) { - target[key] = state[key] + // iframe sync needs to update properties without overriding them + if (!override && target[key] && !key.startsWith('_h') && typeof target[key] === 'object' && !Array.isArray(target[key])) { + Object.assign(target[key], state[key]) + } else { + target[key] = state[key] + } } }