From e6559fcf7eccc41f9ebf6c5ede29b6eec191edb6 Mon Sep 17 00:00:00 2001 From: Petr Krotov Date: Thu, 6 Nov 2025 15:24:23 +0300 Subject: [PATCH 1/2] chore(ErrorWrapper): add new stories and descriptions --- src/components/ErrorWrapper/README.md | 57 +++++++++++++++++++ .../ErrorWrapper/__stories__/ErrorWrapper.mdx | 3 +- .../__stories__/ErrorWrapper.stories.tsx | 50 ++++++++++++++-- .../ErrorWrapper/__stories__/data.json | 25 ++++++++ .../__tests__/ErrorWrapper.visual.test.tsx | 39 +++++++++++++ 5 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 src/components/ErrorWrapper/README.md create mode 100644 src/components/ErrorWrapper/__tests__/ErrorWrapper.visual.test.tsx diff --git a/src/components/ErrorWrapper/README.md b/src/components/ErrorWrapper/README.md new file mode 100644 index 000000000..8fb8dd985 --- /dev/null +++ b/src/components/ErrorWrapper/README.md @@ -0,0 +1,57 @@ +# ErrorWrapper + +The `ErrorWrapper` component is used to gracefully handle and display local errors in UI sections. +When the `isError` flag is set to `true`, it shows an error message and an optional retry button. +When `isError` is `false`, it renders its children content. + +## Installation / Import + +```tsx +import ErrorWrapper from './ErrorWrapper'; +``` + +## When to Use + +- Show a clear error state for a specific part of the interface (e.g., failed data load). + +- Wrap a component to display either content or an error depending on state. + +- Provide an inline retry mechanism. + +## Props + +| Name | Type | Required | Default | Description | +| ------------ | ----------------- | -------- | ------- | ------------------------------------------ | +| `text` | `string` | ✅ | — | Error message text. | +| `buttonText` | `string` | ✅ | — | Text for the retry button. | +| `handler` | `() => void` | ✅ | — | Callback triggered on retry button click. | +| `isError` | `boolean` | ✅ | — | Show error (`true`) or children (`false`). | +| `children` | `React.ReactNode` | ✅ | — | Content rendered when no error occurs. | +| `className` | `string` | ❌ | — | Optional CSS class for additional styling. | + +## Example + +```tsx +import React from 'react'; +import ErrorWrapper from './ErrorWrapper'; + +function DataPanel() { + const [isError, setIsError] = React.useState(true); + + const handleRetry = () => { + console.log('Retry clicked'); + setIsError(false); + }; + + return ( + +
Data loaded successfully!
+
+ ); +} +``` diff --git a/src/components/ErrorWrapper/__stories__/ErrorWrapper.mdx b/src/components/ErrorWrapper/__stories__/ErrorWrapper.mdx index 0134c9765..7e7179330 100644 --- a/src/components/ErrorWrapper/__stories__/ErrorWrapper.mdx +++ b/src/components/ErrorWrapper/__stories__/ErrorWrapper.mdx @@ -1,5 +1,4 @@ import {Meta} from '@storybook/blocks'; - import {StoryTemplate} from '../../../demo/StoryTemplate.mdx'; import * as ErrorWrapperStories from './ErrorWrapper.stories.tsx'; @@ -8,7 +7,7 @@ import * as ErrorWrapperStories from './ErrorWrapper.stories.tsx'; ## Usage -For a detailed usage guide of the ErrorWrapper component, see [ErrorWrapper Usage](https://github.com/gravity-ui/page-constructor/blob/main/memory-bank/usage/errorWrapper.md). +For a detailed usage guide of the **ErrorWrapper** component, see [ErrorWrapper Usage](https://github.com/gravity-ui/page-constructor/blob/main/memory-bank/usage/errorWrapper.md). ## Parameters diff --git a/src/components/ErrorWrapper/__stories__/ErrorWrapper.stories.tsx b/src/components/ErrorWrapper/__stories__/ErrorWrapper.stories.tsx index de40d7bbe..20ca9e495 100644 --- a/src/components/ErrorWrapper/__stories__/ErrorWrapper.stories.tsx +++ b/src/components/ErrorWrapper/__stories__/ErrorWrapper.stories.tsx @@ -5,12 +5,52 @@ import ErrorWrapper, {ErrorWrapperProps} from '../ErrorWrapper'; import data from './data.json'; export default { - component: ErrorWrapper, title: 'Components/ErrorWrapper', -} as Meta; + component: ErrorWrapper, + parameters: { + layout: 'centered', + }, + argTypes: { + isError: {control: 'boolean'}, + text: {control: 'text'}, + buttonText: {control: 'text'}, + className: {control: 'text'}, + handler: {action: 'onRetry'}, + }, +} as Meta; + +const Template: StoryFn = (args) => ; + +export const Default = Template.bind({}); +export const NoError = Template.bind({}); +export const CustomClass = Template.bind({}); +export const NoHandler = Template.bind({}); +export const Interactive = Template.bind({}); + +Default.args = { + ...data.default.content, + handler: () => alert('Retry clicked!'), +}; + +NoError.args = { + ...data.noError.content, + handler: () => console.log('Should not render button'), +}; -const DefaultTemplate: StoryFn = (args) => ; +CustomClass.args = { + ...data.customClass.content, + handler: () => console.log('Custom class retry'), +}; -export const Default = DefaultTemplate.bind({}); +NoHandler.args = { + ...data.noHandler.content, + handler: undefined, +}; -Default.args = data.default.content; +Interactive.args = { + text: 'Temporary error — click retry to fix', + isError: true, + buttonText: 'Fix', + handler: () => window.location.reload(), + children: 'Hidden content when error resolves', +}; diff --git a/src/components/ErrorWrapper/__stories__/data.json b/src/components/ErrorWrapper/__stories__/data.json index 0a629c957..c4c7197b1 100644 --- a/src/components/ErrorWrapper/__stories__/data.json +++ b/src/components/ErrorWrapper/__stories__/data.json @@ -6,5 +6,30 @@ "buttonText": "Try again", "children": "These are children" } + }, + "noError": { + "content": { + "text": "Something went wrong", + "isError": false, + "buttonText": "Try again", + "children": "Everything is fine, rendering content." + } + }, + "customClass": { + "content": { + "text": "Custom styled error", + "isError": true, + "buttonText": "Retry", + "className": "custom-error", + "children": "Child content" + } + }, + "noHandler": { + "content": { + "text": "Error without handler", + "isError": true, + "buttonText": "No action", + "children": "No handler provided" + } } } diff --git a/src/components/ErrorWrapper/__tests__/ErrorWrapper.visual.test.tsx b/src/components/ErrorWrapper/__tests__/ErrorWrapper.visual.test.tsx new file mode 100644 index 000000000..cfaaa2404 --- /dev/null +++ b/src/components/ErrorWrapper/__tests__/ErrorWrapper.visual.test.tsx @@ -0,0 +1,39 @@ +import {composeStories} from '@storybook/react'; + +import {test} from '../../../../playwright/core/index'; +import * as ErrorWrapperStories from '../__stories__/ErrorWrapper.stories'; + +export const {Default, NoError, CustomClass, NoHandler, Interactive} = + composeStories(ErrorWrapperStories); + +test.describe('ErrorWrapper', () => { + test('render ', async ({mount, expectScreenshot, defaultDelay}) => { + await mount(); + await defaultDelay(); + await expectScreenshot({skipTheme: 'dark'}); + }); + + test('render ', async ({mount, expectScreenshot, defaultDelay}) => { + await mount(); + await defaultDelay(); + await expectScreenshot({skipTheme: 'dark'}); + }); + + test('render ', async ({mount, expectScreenshot, defaultDelay}) => { + await mount(); + await defaultDelay(); + await expectScreenshot({skipTheme: 'dark'}); + }); + + test('render ', async ({mount, expectScreenshot, defaultDelay}) => { + await mount(); + await defaultDelay(); + await expectScreenshot({skipTheme: 'dark'}); + }); + + test('render ', async ({mount, expectScreenshot, defaultDelay}) => { + await mount(); + await defaultDelay(); + await expectScreenshot({skipTheme: 'dark'}); + }); +}); From e4d651d437e56938082de3351b6a50616e334a9d Mon Sep 17 00:00:00 2001 From: Petr Krotov Date: Thu, 6 Nov 2025 16:19:43 +0300 Subject: [PATCH 2/2] chore: add stories screenshots --- ...er-render-CustomClass-light-chromium-linux.png | Bin 0 -> 116 bytes ...pper-render-CustomClass-light-webkit-linux.png | Bin 0 -> 192 bytes ...rapper-render-Default-light-chromium-linux.png | Bin 0 -> 116 bytes ...rWrapper-render-Default-light-webkit-linux.png | Bin 0 -> 192 bytes ...er-render-Interactive-light-chromium-linux.png | Bin 0 -> 116 bytes ...pper-render-Interactive-light-webkit-linux.png | Bin 0 -> 192 bytes ...rapper-render-NoError-light-chromium-linux.png | Bin 0 -> 116 bytes ...rWrapper-render-NoError-light-webkit-linux.png | Bin 0 -> 192 bytes ...pper-render-NoHandler-light-chromium-linux.png | Bin 0 -> 116 bytes ...rapper-render-NoHandler-light-webkit-linux.png | Bin 0 -> 192 bytes 10 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-chromium-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-webkit-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-chromium-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-webkit-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-chromium-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-webkit-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-chromium-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-webkit-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-chromium-linux.png create mode 100644 src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-webkit-linux.png diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-chromium-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-chromium-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ea500ed4ac12decfb3b1a3787a1524cfbe47bdc8 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P#^NA%Cx&(BWL^R}dY&$hArY-_ z&oc5dFmNy{Y^{GTefX>+qm^gujLD%oV%SXYA^>bP0 Hl+XkK2&N!{ literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-webkit-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-webkit-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4e015d3ffad2c796633dda4be3c9a3979210b0 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51SA=YQ-1*|wj^(N7l!{JxM1({$v}}(PZ!6K ziaBr3Gx9Ph3LMz*{BN~gjDt}W>+M-H->d$A+i;oZy5u_JnDiaz3g$kvI$YZJnsNc@ YSMQkT9uO&x13G}g)78&qol`;+02BsJ`Tzg` literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-chromium-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-chromium-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ea500ed4ac12decfb3b1a3787a1524cfbe47bdc8 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P#^NA%Cx&(BWL^R}dY&$hArY-_ z&oc5dFmNy{Y^{GTefX>+qm^gujLD%oV%SXYA^>bP0 Hl+XkK2&N!{ literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-webkit-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-webkit-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4e015d3ffad2c796633dda4be3c9a3979210b0 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51SA=YQ-1*|wj^(N7l!{JxM1({$v}}(PZ!6K ziaBr3Gx9Ph3LMz*{BN~gjDt}W>+M-H->d$A+i;oZy5u_JnDiaz3g$kvI$YZJnsNc@ YSMQkT9uO&x13G}g)78&qol`;+02BsJ`Tzg` literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-chromium-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-chromium-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ea500ed4ac12decfb3b1a3787a1524cfbe47bdc8 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P#^NA%Cx&(BWL^R}dY&$hArY-_ z&oc5dFmNy{Y^{GTefX>+qm^gujLD%oV%SXYA^>bP0 Hl+XkK2&N!{ literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-webkit-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-webkit-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4e015d3ffad2c796633dda4be3c9a3979210b0 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51SA=YQ-1*|wj^(N7l!{JxM1({$v}}(PZ!6K ziaBr3Gx9Ph3LMz*{BN~gjDt}W>+M-H->d$A+i;oZy5u_JnDiaz3g$kvI$YZJnsNc@ YSMQkT9uO&x13G}g)78&qol`;+02BsJ`Tzg` literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-chromium-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-chromium-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ea500ed4ac12decfb3b1a3787a1524cfbe47bdc8 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P#^NA%Cx&(BWL^R}dY&$hArY-_ z&oc5dFmNy{Y^{GTefX>+qm^gujLD%oV%SXYA^>bP0 Hl+XkK2&N!{ literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-webkit-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-webkit-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4e015d3ffad2c796633dda4be3c9a3979210b0 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51SA=YQ-1*|wj^(N7l!{JxM1({$v}}(PZ!6K ziaBr3Gx9Ph3LMz*{BN~gjDt}W>+M-H->d$A+i;oZy5u_JnDiaz3g$kvI$YZJnsNc@ YSMQkT9uO&x13G}g)78&qol`;+02BsJ`Tzg` literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-chromium-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-chromium-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ea500ed4ac12decfb3b1a3787a1524cfbe47bdc8 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P#^NA%Cx&(BWL^R}dY&$hArY-_ z&oc5dFmNy{Y^{GTefX>+qm^gujLD%oV%SXYA^>bP0 Hl+XkK2&N!{ literal 0 HcmV?d00001 diff --git a/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-webkit-linux.png b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-webkit-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4e015d3ffad2c796633dda4be3c9a3979210b0 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51SA=YQ-1*|wj^(N7l!{JxM1({$v}}(PZ!6K ziaBr3Gx9Ph3LMz*{BN~gjDt}W>+M-H->d$A+i;oZy5u_JnDiaz3g$kvI$YZJnsNc@ YSMQkT9uO&x13G}g)78&qol`;+02BsJ`Tzg` literal 0 HcmV?d00001