diff --git a/code/e2e-tests/preview-web.spec.ts b/code/e2e-tests/preview-web.spec.ts new file mode 100644 index 000000000000..3dec28a68910 --- /dev/null +++ b/code/e2e-tests/preview-web.spec.ts @@ -0,0 +1,32 @@ +import { test, expect } from '@playwright/test'; +import process from 'process'; +import { SbPage } from './util'; + +const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:8001'; + +test.describe('preview-web', () => { + test.beforeEach(async ({ page }) => { + await page.goto(storybookUrl); + await new SbPage(page).waitUntilLoaded(); + }); + + test('should pass over shortcuts, but not from play functions, story', async ({ page }) => { + const sbPage = new SbPage(page); + await sbPage.navigateToStory('lib/store/shortcuts', 'keydown-during-play'); + + await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); + + await sbPage.previewRoot().locator('button').press('s'); + await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); + }); + + test('should pass over shortcuts, but not from play functions, docs', async ({ page }) => { + const sbPage = new SbPage(page); + await sbPage.navigateToStory('lib/store/shortcuts', 'docs'); + + await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); + + await sbPage.previewRoot().getByRole('button').getByText('Submit').press('s'); + await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); + }); +}); diff --git a/code/lib/preview-web/src/PreviewWeb.test.ts b/code/lib/preview-web/src/PreviewWeb.test.ts index 356d22018c37..36cf0b17ce72 100644 --- a/code/lib/preview-web/src/PreviewWeb.test.ts +++ b/code/lib/preview-web/src/PreviewWeb.test.ts @@ -3302,6 +3302,51 @@ describe('PreviewWeb', () => { expect.objectContaining({}) ); }); + + it('does not emit PREVIEW_KEYDOWN during story play functions', async () => { + document.location.search = '?id=component-one--a'; + + const [gate, openGate] = createGate(); + componentOneExports.a.play.mockImplementationOnce(async () => gate); + const preview = new PreviewWeb(); + await preview.initialize({ importFn, getProjectAnnotations }); + await waitForRenderPhase('playing'); + + await preview.onKeydown({ + target: { tagName: 'div', getAttribute: jest.fn().mockReturnValue(null) }, + } as any); + + expect(mockChannel.emit).not.toHaveBeenCalledWith( + PREVIEW_KEYDOWN, + expect.objectContaining({}) + ); + openGate(); + }); + + it('does not emit PREVIEW_KEYDOWN during docs play functions', async () => { + document.location.search = '?id=component-one--a'; + + const preview = await createAndRenderPreview(); + + mockChannel.emit.mockClear(); + const [gate, openGate] = createGate(); + componentOneExports.b.play.mockImplementationOnce(async () => gate); + preview.renderStoryToElement( + await preview.storyStore.loadStory({ storyId: 'component-one--b' }), + {} as any + ); + await waitForRenderPhase('playing'); + + await preview.onKeydown({ + target: { tagName: 'div', getAttribute: jest.fn().mockReturnValue(null) }, + } as any); + + expect(mockChannel.emit).not.toHaveBeenCalledWith( + PREVIEW_KEYDOWN, + expect.objectContaining({}) + ); + openGate(); + }); }); describe('extract', () => { diff --git a/code/lib/preview-web/src/PreviewWeb.tsx b/code/lib/preview-web/src/PreviewWeb.tsx index f8d5e15e5733..eba66fadac81 100644 --- a/code/lib/preview-web/src/PreviewWeb.tsx +++ b/code/lib/preview-web/src/PreviewWeb.tsx @@ -199,7 +199,7 @@ export class PreviewWeb extends Preview r.disableKeyListeners) && !focusInInput(event)) { // We have to pick off the keys of the event that we need on the other side const { altKey, ctrlKey, metaKey, shiftKey, key, code, keyCode } = event; this.channel.emit(PREVIEW_KEYDOWN, { diff --git a/code/lib/store/template/stories/shortcuts.stories.ts b/code/lib/store/template/stories/shortcuts.stories.ts new file mode 100644 index 000000000000..6a0ffec340bc --- /dev/null +++ b/code/lib/store/template/stories/shortcuts.stories.ts @@ -0,0 +1,23 @@ +import globalThis from 'global'; +import { userEvent, within } from '@storybook/testing-library'; +import { PREVIEW_KEYDOWN } from '@storybook/core-events'; +import { jest, expect } from '@storybook/jest'; +import type { PlayFunctionContext } from '@storybook/csf'; + +export default { + component: globalThis.Components.Form, + tags: ['docsPage'], +}; + +export const KeydownDuringPlay = { + play: async ({ canvasElement }: PlayFunctionContext) => { + const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__; + + const previewKeydown = jest.fn(); + channel.on(PREVIEW_KEYDOWN, previewKeydown); + const button = await within(canvasElement).findByText('Submit'); + await userEvent.type(button, 's'); + + expect(previewKeydown).not.toBeCalled(); + }, +};