Skip to content

Commit

Permalink
feat(main): Add renderErrorView prop for custom render and handle errors
Browse files Browse the repository at this point in the history
  • Loading branch information
EgorKluch committed Jun 20, 2023
1 parent ac0da94 commit 15e314f
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 20 deletions.
6 changes: 5 additions & 1 deletion src/components/ChartKit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ const ChartKitComponentWithErrorBoundary = React.forwardRef<
}, []);

return (
<ErrorBoundary onError={props.onError} resetError={handleResetError}>
<ErrorBoundary
onError={props.onError}
resetError={handleResetError}
renderErrorView={props.renderErrorView}
>
<ChartKitComponent instanceRef={ref} {...props} />
</ErrorBoundary>
);
Expand Down
27 changes: 25 additions & 2 deletions src/components/ErrorBoundary/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import React from 'react';
import type {ChartKitError} from '../../libs';
import type {ChartKitOnError} from '../../types';
import {ErrorView} from '../ErrorView/ErrorView';
import {getErrorMessage} from '../../utils/getErrorMessage';

type Props = {
onError?: ChartKitOnError;
resetError?(resetError: () => void): void;
renderErrorView?: ErrorBoundaryRenderErrorView;
};

type State = {
error?: ChartKitError | Error;
};

export type ErrorBoundaryRenderErrorViewOpts = {
message: string;
error: ChartKitError | Error;
resetError: () => void;
};

export type ErrorBoundaryRenderErrorView = (
opts: ErrorBoundaryRenderErrorViewOpts,
) => React.ReactNode;

export class ErrorBoundary extends React.Component<Props, State> {
static getDerivedStateFromError(error: Error) {
return {error};
Expand All @@ -37,7 +48,19 @@ export class ErrorBoundary extends React.Component<Props, State> {
const {error} = this.state;

if (error) {
return <ErrorView error={error} />;
const message = getErrorMessage(error);

if (this.props.renderErrorView) {
return this.props.renderErrorView({
error,
message,
resetError: () => {
this.setState({error: undefined});
},
});
}

return <div>{message}</div>;
}

return this.props.children;
Expand Down
14 changes: 0 additions & 14 deletions src/components/ErrorView/ErrorView.tsx

This file was deleted.

14 changes: 12 additions & 2 deletions src/plugins/highcharts/__stories__/components/ChartStory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ import {HighchartsPlugin} from '../../index';
import holidays from '../../mocks/holidays';
import {ChartKit} from '../../../../components/ChartKit';
import {HighchartsWidgetData} from '../../types';
import {ErrorBoundaryRenderErrorView} from '../../../../components/ErrorBoundary/ErrorBoundary';

const DEFAULT_STORY_HEIGHT = '300px';
const DEFAULT_STORY_WIDTH = '100%';

export type ChartStoryProps = {
data: HighchartsWidgetData;

withoutPlugin?: boolean;
visible?: boolean;
height?: string;
width?: string;
renderErrorView?: ErrorBoundaryRenderErrorView;
};
export const ChartStory: React.FC<ChartStoryProps> = (props: ChartStoryProps) => {
const {height, width, data} = props;
Expand All @@ -25,7 +28,9 @@ export const ChartStory: React.FC<ChartStoryProps> = (props: ChartStoryProps) =>
const chartKitRef = React.useRef<ChartKitRef>();

if (!initRef.current) {
settings.set({plugins: [HighchartsPlugin], extra: {holidays}});
if (!props.withoutPlugin) {
settings.set({plugins: [HighchartsPlugin], extra: {holidays}});
}
initRef.current = true;
}

Expand All @@ -40,7 +45,12 @@ export const ChartStory: React.FC<ChartStoryProps> = (props: ChartStoryProps) =>
width: width || DEFAULT_STORY_WIDTH,
}}
>
<ChartKit ref={chartKitRef} type="highcharts" data={data} />
<ChartKit
ref={chartKitRef}
type="highcharts"
data={data}
renderErrorView={props.renderErrorView}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import {Meta, Story} from '@storybook/react';
import {ChartKit} from '../../../../components/ChartKit';
import {ChartStory} from '../components/ChartStory';
import {Button} from '@gravity-ui/uikit';
import {ErrorBoundaryRenderErrorView} from '../../../../components/ErrorBoundary/ErrorBoundary';
import {CHARTKIT_ERROR_CODE, settings} from '../../../../libs';
import {HighchartsPlugin} from '../../index';
import holidays from '../../mocks/holidays';
import {noData, filledData} from '../../mocks/custom-error-render';

export default {
title: 'Plugins/Highcharts/CustomErrorRender',
component: ChartKit,
} as Meta;

const Template: Story = () => {
const [data, setData] = React.useState(noData);

const renderErrorView: ErrorBoundaryRenderErrorView = React.useCallback(
({error, message, resetError}) => {
function renderFixButton() {
if (!('code' in error)) {
return null;
}

switch (error.code) {
case CHARTKIT_ERROR_CODE.UNKNOWN_PLUGIN:
return (
<Button
onClick={() => {
settings.set({plugins: [HighchartsPlugin], extra: {holidays}});
resetError();
}}
>
Add highcharts plugin
</Button>
);
case CHARTKIT_ERROR_CODE.NO_DATA:
return (
<Button
onClick={() => {
setData(filledData);
}}
>
Add data
</Button>
);
default:
return null;
}
}

return (
<div>
<h2>{message}</h2>
{renderFixButton()}
</div>
);
},
[],
);

return (
<div>
<ChartStory
withoutPlugin={true}
data={data}
visible={true}
renderErrorView={renderErrorView}
/>
</div>
);
};

export const CustomErrorRender = Template.bind({});
80 changes: 80 additions & 0 deletions src/plugins/highcharts/mocks/custom-error-render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type {HighchartsWidgetData} from '../types';
import _ from 'lodash';

const baseData: Omit<HighchartsWidgetData, 'data'> = {
config: {
hideHolidays: false,
normalizeDiv: false,
normalizeSub: false,
},
libraryConfig: {
chart: {
type: 'arearange',
},
title: {
text: 'Temperature variation by day',
},
xAxis: {
type: 'datetime',
},
tooltip: {
valueSuffix: '°C',
},
},
};

export const noData: HighchartsWidgetData = {
...baseData,
data: {
graphs: [
{
name: 'Temperatures',
data: [],
},
],
},
};

export const filledData: HighchartsWidgetData = {
...baseData,
data: {
graphs: [
{
name: 'Temperatures',
data: [
[1246406400000, 10.4, 17],
[1246492800000, 10.3, 28.6],
[1246579200000, 14.8, 18.4],
[1246665600000, 11.5, 25.8],
[1246752000000, 11.1, 24.4],
[1246838400000, 17.7, 19.6],
[1246924800000, 15.1, 18.1],
[1247011200000, 15.1, 27.2],
[1247097600000, 17, 17.5],
[1247184000000, 12.6, 18.5],
[1247270400000, 12.2, 26],
[1247356800000, 15.9, 22.9],
[1247443200000, 17.1, 18.1],
[1247529600000, 13.3, 24.2],
[1247616000000, 17, 28.1],
[1247702400000, 16.2, 22.6],
[1247788800000, 10.6, 19],
[1247875200000, 11.3, 19.7],
[1247961600000, 14.1, 24.6],
[1248048000000, 14.2, 22.5],
[1248134400000, 14.1, 28.5],
[1248220800000, 14, 27],
[1248307200000, 10.2, 20.6],
[1248393600000, 13.1, 29.9],
[1248480000000, 13.7, 21.1],
[1248566400000, 15, 28.6],
[1248652800000, 12, 17.5],
[1248739200000, 17.8, 24.4],
[1248825600000, 11.7, 25.9],
[1248912000000, 13.6, 25.6],
[1248998400000, 17.3, 22.2],
],
},
],
},
};
4 changes: 3 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import type {ChartKitWidget} from './widget';
import type {ErrorBoundaryRenderErrorView} from '../components/ErrorBoundary/ErrorBoundary';

export type {ChartKitHolidays} from './misc';

Expand Down Expand Up @@ -34,6 +35,7 @@ export type ChartKitOnError = (data: {error: any}) => void;
export type ChartKitProps<T extends ChartKitType> = {
type: T;
data: ChartKitWidget[T]['data'];

id?: string;
isMobile?: boolean;
onLoad?: (data?: ChartKitOnLoadData<T>) => void;
Expand All @@ -47,7 +49,7 @@ export type ChartKitProps<T extends ChartKitType> = {
* @param data
*/
onChartLoad?: (data: ChartKitOnChartLoad<T>) => void;

renderErrorView?: ErrorBoundaryRenderErrorView;
onError?: ChartKitOnError;
} & {[key in keyof Omit<ChartKitWidget[T], 'data' | 'widget'>]: ChartKitWidget[T][key]};

Expand Down
7 changes: 7 additions & 0 deletions src/utils/getErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type {ChartKitError} from '../libs';
import {i18n} from '../i18n';

export function getErrorMessage(error: ChartKitError | Error) {
const code = 'code' in error && error.code;
return (error.message || code || i18n('error', 'label_unknown-error')).toString();
}

0 comments on commit 15e314f

Please sign in to comment.