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/__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 000000000..ea500ed4a Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-chromium-linux.png differ 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 000000000..8c4e015d3 Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-CustomClass-light-webkit-linux.png differ 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 000000000..ea500ed4a Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-chromium-linux.png differ 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 000000000..8c4e015d3 Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Default-light-webkit-linux.png differ 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 000000000..ea500ed4a Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-chromium-linux.png differ 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 000000000..8c4e015d3 Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-Interactive-light-webkit-linux.png differ 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 000000000..ea500ed4a Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-chromium-linux.png differ 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 000000000..8c4e015d3 Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoError-light-webkit-linux.png differ 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 000000000..ea500ed4a Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-chromium-linux.png differ 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 000000000..8c4e015d3 Binary files /dev/null and b/src/components/ErrorWrapper/__snapshots__/ErrorWrapper.visual.test.tsx-snapshots/ErrorWrapper-render-NoHandler-light-webkit-linux.png differ 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'}); + }); +});