From 9fc034c38c3cc21b06a0ab02835aa56aa741af5a Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 9 Oct 2024 14:07:17 +0200 Subject: [PATCH 1/6] Modal: Change styling --- .../components/Modal/Modal.stories.tsx | 16 +++++++++++++++- .../components/components/Modal/Modal.styled.tsx | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/code/core/src/components/components/Modal/Modal.stories.tsx b/code/core/src/components/components/Modal/Modal.stories.tsx index 0793d24c8db5..998e033d312e 100644 --- a/code/core/src/components/components/Modal/Modal.stories.tsx +++ b/code/core/src/components/components/Modal/Modal.stories.tsx @@ -10,7 +10,21 @@ type Story = StoryObj; const meta = { component: Modal, - decorators: [(storyFn) =>
{storyFn()}
], + decorators: [ + (storyFn) => ( +
+ {storyFn()} +
+ ), + ], } satisfies Meta; export default meta; diff --git a/code/core/src/components/components/Modal/Modal.styled.tsx b/code/core/src/components/components/Modal/Modal.styled.tsx index ca56fb05627e..37bb8be84404 100644 --- a/code/core/src/components/components/Modal/Modal.styled.tsx +++ b/code/core/src/components/components/Modal/Modal.styled.tsx @@ -30,7 +30,7 @@ const zoomIn = keyframes({ }); export const Overlay = styled.div({ - backgroundColor: 'rgba(27, 28, 29, 0.2)', + backdropFilter: 'blur(24px)', position: 'fixed', inset: 0, width: '100%', @@ -43,7 +43,7 @@ export const Container = styled.div<{ width?: number; height?: number }>( ({ theme, width, height }) => ({ backgroundColor: theme.background.bar, borderRadius: 6, - boxShadow: `rgba(255, 255, 255, 0.05) 0 0 0 1px inset, rgba(14, 18, 22, 0.35) 0px 10px 38px -10px`, + boxShadow: '0px 4px 67px 0px #00000040', position: 'fixed', top: '50%', left: '50%', From c8cdccdecbd0bdf5f39ae7347ec43e5eff1aca4e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 9 Oct 2024 14:08:33 +0200 Subject: [PATCH 2/6] Addon-Test: Implement GlobalErrorModal --- code/.storybook/main.ts | 4 + code/addons/test/package.json | 1 + .../components/GlobalErrorModal.stories.tsx | 69 ++++++++++++++++ .../test/src/components/GlobalErrorModal.tsx | 82 +++++++++++++++++++ code/yarn.lock | 1 + 5 files changed, 157 insertions(+) create mode 100644 code/addons/test/src/components/GlobalErrorModal.stories.tsx create mode 100644 code/addons/test/src/components/GlobalErrorModal.tsx diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index 513fde263a21..fffb7d18ddff 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -92,6 +92,10 @@ const config: StorybookConfig = { directory: '../addons/test/template/stories', titlePrefix: 'addons/test', }, + { + directory: '../addons/test/src', + titlePrefix: 'addons/test', + }, ], addons: [ '@storybook/addon-themes', diff --git a/code/addons/test/package.json b/code/addons/test/package.json index c0515a8dcb6f..3f53d0bca205 100644 --- a/code/addons/test/package.json +++ b/code/addons/test/package.json @@ -80,6 +80,7 @@ "@storybook/icons": "^1.2.12", "@storybook/instrumenter": "workspace:*", "@storybook/test": "workspace:*", + "@storybook/theming": "workspace:*", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, diff --git a/code/addons/test/src/components/GlobalErrorModal.stories.tsx b/code/addons/test/src/components/GlobalErrorModal.stories.tsx new file mode 100644 index 000000000000..effcbf158ece --- /dev/null +++ b/code/addons/test/src/components/GlobalErrorModal.stories.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; + +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, fn, userEvent, within } from '@storybook/test'; + +import dedent from 'ts-dedent'; + +import { GlobalErrorModal } from './GlobalErrorModal'; + +type Story = StoryObj; + +const meta = { + component: GlobalErrorModal, + decorators: [ + (storyFn) => ( +
+ {storyFn()} +
+ ), + ], + args: { + onRerun: fn(), + onClose: fn(), + open: false, + }, +} satisfies Meta; + +export default meta; + +export const Default: Story = { + args: { + error: dedent` + ReferenceError: FAIL is not defined + at Constraint.execute (the-best-file.js:525:2) + at Constraint.recalculate (the-best-file.js:424:21) + at Planner.addPropagate (the-best-file.js:701:6) + at Constraint.satisfy (the-best-file.js:184:15) + at Planner.incrementalAdd (the-best-file.js:591:21) + at Constraint.addConstraint (the-best-file.js:162:10) + at Constraint.BinaryConstraint (the-best-file.js:346:7) + at Constraint.EqualityConstraint (the-best-file.js:515:38) + at chainTest (the-best-file.js:807:6) + at deltaBlue (the-best-file.js:879:2)`, + }, + render: (props) => { + const [isOpen, setOpen] = useState(false); + + return ( + <> + + + + ); + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement.parentElement!); + const button = canvas.getByText('Open modal'); + await userEvent.click(button); + await expect(canvas.findByText('Storybook Tests error details')).resolves.toBeInTheDocument(); + }, +}; diff --git a/code/addons/test/src/components/GlobalErrorModal.tsx b/code/addons/test/src/components/GlobalErrorModal.tsx new file mode 100644 index 000000000000..cabfba9a0c4e --- /dev/null +++ b/code/addons/test/src/components/GlobalErrorModal.tsx @@ -0,0 +1,82 @@ +import React from 'react'; + +import { Button, IconButton, Modal } from 'storybook/internal/components'; + +import { CloseIcon, SyncIcon } from '@storybook/icons'; +import { styled } from '@storybook/theming'; + +interface GlobalErrorModalProps { + error: string; + open: boolean; + onClose: () => void; + onRerun: () => void; +} + +const ModalBar = styled.div({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: '6px 6px 6px 20px', +}); + +const ModalActionBar = styled.div({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', +}); + +const ModalTitle = styled.div(({ theme: { typography } }) => ({ + fontSize: typography.size.s2, + fontWeight: typography.weight.bold, +})); + +const ModalStackTrace = styled.pre(({ theme }) => ({ + whiteSpace: 'pre-wrap', + overflow: 'auto', + maxHeight: '60vh', + margin: 0, + padding: `20px`, + fontFamily: theme.typography.fonts.mono, + fontSize: '12px', + borderTop: `1px solid ${theme.appBorderColor}`, +})); + +const TroubleshootLink = styled.a(({ theme }) => ({ + color: theme.color.defaultText, +})); + +const TROUBLESHOOT_LINK = `https://storybook.js.org/docs/vitest/troubleshooting`; + +export function GlobalErrorModal({ onRerun, onClose, error, open }: GlobalErrorModalProps) { + return ( + + + Storybook Tests error details + + + + + + + + + + {error} +
+
+ Troubleshoot:{' '} + + {TROUBLESHOOT_LINK} + +
+
+ ); +} diff --git a/code/yarn.lock b/code/yarn.lock index 1b737436f043..4cb52a592b91 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6239,6 +6239,7 @@ __metadata: "@storybook/icons": "npm:^1.2.12" "@storybook/instrumenter": "workspace:*" "@storybook/test": "workspace:*" + "@storybook/theming": "workspace:*" "@types/node": "npm:^22.0.0" "@types/semver": "npm:^7" "@vitest/browser": "npm:^2.1.1" From 66700870a1e3e5eb9affc450410176056e3567da Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 9 Oct 2024 15:32:06 +0200 Subject: [PATCH 3/6] Fix documentation link and provide dynamic troubleshooting link --- .../components/GlobalErrorModal.stories.tsx | 35 +++++++++++++------ .../test/src/components/GlobalErrorModal.tsx | 20 +++++++---- code/addons/test/src/constants.ts | 1 + 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/code/addons/test/src/components/GlobalErrorModal.stories.tsx b/code/addons/test/src/components/GlobalErrorModal.stories.tsx index effcbf158ece..3cf0e4cf01d1 100644 --- a/code/addons/test/src/components/GlobalErrorModal.stories.tsx +++ b/code/addons/test/src/components/GlobalErrorModal.stories.tsx @@ -1,5 +1,7 @@ import React, { useState } from 'react'; +import { ManagerContext } from 'storybook/internal/manager-api'; + import type { Meta, StoryObj } from '@storybook/react'; import { expect, fn, userEvent, within } from '@storybook/test'; @@ -9,21 +11,32 @@ import { GlobalErrorModal } from './GlobalErrorModal'; type Story = StoryObj; +const managerContext: any = { + state: {}, + api: { + getDocsUrl: fn(({ subpath }) => `https://storybook.js.org/docs/${subpath}`).mockName( + 'api::getDocsUrl' + ), + }, +}; + const meta = { component: GlobalErrorModal, decorators: [ (storyFn) => ( -
- {storyFn()} -
+ +
+ {storyFn()} +
+
), ], args: { diff --git a/code/addons/test/src/components/GlobalErrorModal.tsx b/code/addons/test/src/components/GlobalErrorModal.tsx index cabfba9a0c4e..3dcd7ee881a6 100644 --- a/code/addons/test/src/components/GlobalErrorModal.tsx +++ b/code/addons/test/src/components/GlobalErrorModal.tsx @@ -1,10 +1,13 @@ import React from 'react'; import { Button, IconButton, Modal } from 'storybook/internal/components'; +import { useStorybookApi } from 'storybook/internal/manager-api'; import { CloseIcon, SyncIcon } from '@storybook/icons'; import { styled } from '@storybook/theming'; +import { TROUBLESHOOTING_LINK } from '../constants'; + interface GlobalErrorModalProps { error: string; open: boolean; @@ -45,9 +48,15 @@ const TroubleshootLink = styled.a(({ theme }) => ({ color: theme.color.defaultText, })); -const TROUBLESHOOT_LINK = `https://storybook.js.org/docs/vitest/troubleshooting`; - export function GlobalErrorModal({ onRerun, onClose, error, open }: GlobalErrorModalProps) { + const api = useStorybookApi(); + + const troubleshootURL = api.getDocsUrl({ + subpath: TROUBLESHOOTING_LINK, + versioned: true, + renderer: true, + }); + return ( @@ -58,8 +67,7 @@ export function GlobalErrorModal({ onRerun, onClose, error, open }: GlobalErrorM Rerun @@ -73,8 +81,8 @@ export function GlobalErrorModal({ onRerun, onClose, error, open }: GlobalErrorM

Troubleshoot:{' '} - - {TROUBLESHOOT_LINK} + + {troubleshootURL}
diff --git a/code/addons/test/src/constants.ts b/code/addons/test/src/constants.ts index 42995d81d183..bc90b7430b34 100644 --- a/code/addons/test/src/constants.ts +++ b/code/addons/test/src/constants.ts @@ -4,3 +4,4 @@ export const PANEL_ID = `${ADDON_ID}/panel`; export const TUTORIAL_VIDEO_LINK = 'https://youtu.be/Waht9qq7AoA'; export const DOCUMENTATION_LINK = 'writing-tests/component-testing'; +export const TROUBLESHOOTING_LINK = `${DOCUMENTATION_LINK}#troubleshooting`; From 6c3e57a815be52d27d9779ac6fb1aeef30f148d4 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 10 Oct 2024 10:06:47 +0200 Subject: [PATCH 4/6] Remove border radius on ModalStackTrace --- code/addons/test/src/components/GlobalErrorModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/code/addons/test/src/components/GlobalErrorModal.tsx b/code/addons/test/src/components/GlobalErrorModal.tsx index 3dcd7ee881a6..6aadd83a6d53 100644 --- a/code/addons/test/src/components/GlobalErrorModal.tsx +++ b/code/addons/test/src/components/GlobalErrorModal.tsx @@ -42,6 +42,7 @@ const ModalStackTrace = styled.pre(({ theme }) => ({ fontFamily: theme.typography.fonts.mono, fontSize: '12px', borderTop: `1px solid ${theme.appBorderColor}`, + borderRadius: 0, })); const TroubleshootLink = styled.a(({ theme }) => ({ From e6063d840a574f627218b414cd26d1b6d4559c96 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 10 Oct 2024 10:38:25 +0200 Subject: [PATCH 5/6] Remove unnecessary story config entry in Storybook's main.js --- code/.storybook/main.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index fffb7d18ddff..513fde263a21 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -92,10 +92,6 @@ const config: StorybookConfig = { directory: '../addons/test/template/stories', titlePrefix: 'addons/test', }, - { - directory: '../addons/test/src', - titlePrefix: 'addons/test', - }, ], addons: [ '@storybook/addon-themes', From 06836a0fcfbeeb13c2993a8e660c3d2150795504 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 10 Oct 2024 10:51:15 +0200 Subject: [PATCH 6/6] Fix tests --- .../addons/test/src/node/test-manager.test.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/code/addons/test/src/node/test-manager.test.ts b/code/addons/test/src/node/test-manager.test.ts index 1b87bdedf0a5..b5d6ecab179e 100644 --- a/code/addons/test/src/node/test-manager.test.ts +++ b/code/addons/test/src/node/test-manager.test.ts @@ -15,6 +15,7 @@ const vitest = vi.hoisted(() => ({ runFiles: vi.fn(), cancelCurrentRun: vi.fn(), globTestSpecs: vi.fn(), + getModuleProjects: vi.fn(() => []), })); vi.mock('vitest/node', () => ({ @@ -35,28 +36,34 @@ const tests = [ }, ]; +const options: ConstructorParameters[1] = { + onError: (message, error) => { + throw error; + }, + onReady: vi.fn(), +}; + describe('TestManager', () => { it('should create a vitest instance', async () => { - new TestManager(mockChannel); + new TestManager(mockChannel, options); await new Promise((r) => setTimeout(r, 1000)); expect(createVitest).toHaveBeenCalled(); }); it('should call onReady callback', async () => { - const onReady = vi.fn(); - new TestManager(mockChannel, { onReady }); + new TestManager(mockChannel, options); await new Promise((r) => setTimeout(r, 1000)); - expect(onReady).toHaveBeenCalled(); + expect(options.onReady).toHaveBeenCalled(); }); it('TestManager.start should start vitest and resolve when ready', async () => { - const testManager = await TestManager.start(mockChannel); + const testManager = await TestManager.start(mockChannel, options); expect(testManager).toBeInstanceOf(TestManager); expect(createVitest).toHaveBeenCalled(); }); it('should handle watch mode request', async () => { - const testManager = await TestManager.start(mockChannel); + const testManager = await TestManager.start(mockChannel, options); expect(testManager.watchMode).toBe(false); expect(createVitest).toHaveBeenCalledTimes(1); @@ -67,7 +74,7 @@ describe('TestManager', () => { it('should handle run request', async () => { vitest.globTestSpecs.mockImplementation(() => tests); - const testManager = await TestManager.start(mockChannel); + const testManager = await TestManager.start(mockChannel, options); expect(createVitest).toHaveBeenCalledTimes(1); await testManager.handleRunRequest({ @@ -91,7 +98,7 @@ describe('TestManager', () => { it('should filter tests', async () => { vitest.globTestSpecs.mockImplementation(() => tests); - const testManager = await TestManager.start(mockChannel); + const testManager = await TestManager.start(mockChannel, options); await testManager.handleRunRequest({ providerId: TEST_PROVIDER_ID, @@ -119,7 +126,7 @@ describe('TestManager', () => { }); it('should handle run all request', async () => { - const testManager = await TestManager.start(mockChannel); + const testManager = await TestManager.start(mockChannel, options); expect(createVitest).toHaveBeenCalledTimes(1); await testManager.handleRunAllRequest({ providerId: TEST_PROVIDER_ID });