Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/components/ErrorWrapper/README.md
Original file line number Diff line number Diff line change
@@ -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 (
<ErrorWrapper
text="Something went wrong"
buttonText="Try again"
isError={isError}
handler={handleRetry}
>
<div>Data loaded successfully!</div>
</ErrorWrapper>
);
}
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions src/components/ErrorWrapper/__stories__/ErrorWrapper.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {Meta} from '@storybook/blocks';

import {StoryTemplate} from '../../../demo/StoryTemplate.mdx';
import * as ErrorWrapperStories from './ErrorWrapper.stories.tsx';

Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work automatically. If it doesn't work, then you need to understand why.

Look at the reasons that I described in the comments, maybe it's them?
#1322

isError: {control: 'boolean'},
text: {control: 'text'},
buttonText: {control: 'text'},
className: {control: 'text'},
handler: {action: 'onRetry'},
},
} as Meta<ErrorWrapperProps>;

const Template: StoryFn<ErrorWrapperProps> = (args) => <ErrorWrapper {...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<ErrorWrapperProps> = (args) => <ErrorWrapper {...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',
};
25 changes: 25 additions & 0 deletions src/components/ErrorWrapper/__stories__/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
Original file line number Diff line number Diff line change
@@ -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 <Default>', async ({mount, expectScreenshot, defaultDelay}) => {
await mount(<Default />);
await defaultDelay();
await expectScreenshot({skipTheme: 'dark'});
});

test('render <NoError>', async ({mount, expectScreenshot, defaultDelay}) => {
await mount(<NoError />);
await defaultDelay();
await expectScreenshot({skipTheme: 'dark'});
});

test('render <CustomClass>', async ({mount, expectScreenshot, defaultDelay}) => {
await mount(<CustomClass />);
await defaultDelay();
await expectScreenshot({skipTheme: 'dark'});
});

test('render <NoHandler>', async ({mount, expectScreenshot, defaultDelay}) => {
await mount(<NoHandler />);
await defaultDelay();
await expectScreenshot({skipTheme: 'dark'});
});

test('render <Interactive>', async ({mount, expectScreenshot, defaultDelay}) => {
await mount(<Interactive />);
await defaultDelay();
await expectScreenshot({skipTheme: 'dark'});
});
});