update error card

This commit is contained in:
Nate Kelley 2025-03-01 22:35:38 -07:00
parent 36c0b6da71
commit 000f66911c
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
7 changed files with 153 additions and 97 deletions

View File

@ -1,64 +1,47 @@
'use client';
import { Component, ErrorInfo, ReactNode } from 'react';
import React, { ReactNode } from 'react';
import { Button } from '@/components/ui/buttons/Button';
import { Card, CardContent, CardFooter } from '@/components/ui/card/CardBase';
import { ErrorBoundary } from '@/components/ui/error';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
}
const ErrorCard = () => {
return (
<div
className="bg-opacity-90 flex min-h-screen w-screen flex-col items-center justify-center bg-linear-to-br from-gray-50 to-gray-200 p-8 backdrop-blur-xs backdrop-brightness-95 backdrop-filter"
role="alert">
<Card className="-mt-10">
<CardContent>
<div className="flex flex-col gap-4">
<h1 className="animate-fade-in text-2xl font-semibold">
Looks like we hit an error! 😅
</h1>
export class GlobalErrorComponent extends Component<Props, State> {
state = {
hasError: false
};
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-600">
Don&apos;t worry, it&apos;s not you - it&apos;s us!
</h5>
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-500">
If this error persists, please contact Buster support!
</h5>
</div>
</CardContent>
static getDerivedStateFromError(_: Error): State {
return { hasError: true };
}
<CardFooter className="w-full pt-0">
<a href="/" className="w-full">
<Button variant="black" block size="tall">
Take Me Home 🏠
</Button>
</a>
</CardFooter>
</Card>
</div>
);
};
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Global error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div
className="bg-opacity-90 flex min-h-screen w-screen flex-col items-center justify-center bg-linear-to-br from-gray-50 to-gray-200 p-8 backdrop-blur-xs backdrop-brightness-95 backdrop-filter"
role="alert">
<Card className="-mt-10">
<CardContent>
<div className="flex flex-col gap-4">
<h1 className="animate-fade-in text-2xl font-semibold">
Looks like we hit an error! 😅
</h1>
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-600">
Don&apos;t worry, it&apos;s not you - it&apos;s us!
</h5>
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-500">
If this error persists, please contact Buster support!
</h5>
</div>
</CardContent>
<CardFooter className="w-full pt-0">
<a href="/" className="w-full">
<Button variant="black" block size="tall">
Take Me Home 🏠
</Button>
</a>
</CardFooter>
</Card>
</div>
);
}
return this.props.children;
}
}
export const GlobalErrorComponent = ({ children }: Props) => {
return <ErrorBoundary errorComponent={<ErrorCard />}>{children}</ErrorBoundary>;
};

View File

@ -23,20 +23,15 @@ type Story = StoryObj<typeof SidebarUserFooterComponent>;
export const Default: Story = {
args: {
name: 'John Doe',
email: 'john.doe@example.com'
}
};
export const WithoutAvatar: Story = {
args: {
name: 'Jane Smith',
email: 'jane.smith@example.com'
email: 'john.doe@example.com',
signOut: () => Promise.resolve({ error: '' })
}
};
export const LongName: Story = {
args: {
name: 'Alexander Bartholomew Christopherson III',
email: 'alexander@example.com'
email: 'alexander@example.com',
signOut: () => Promise.resolve({ error: '' })
}
};

View File

@ -25,7 +25,7 @@ const sampleCode = `function helloWorld() {
}`;
const longCode = `import React from 'react';
import { Button } from 'antd';
import { Button } from '../buttons';
export const MyComponent = () => {
const handleClick = () => {

View File

@ -1,40 +1,17 @@
import { Alert } from 'antd';
import { Component, ErrorInfo, ReactNode } from 'react';
import { ErrorCard } from '@/components/ui/error/ErrorCard';
import { ReactNode } from 'react';
import { ErrorBoundary } from '../error/ErrorBoundary';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
}
const ErrorCardComponent: React.FC = () => {
return (
<ErrorCard error="Something went wrong rendering the chart. This is likely an error on our end. Please contact Buster support." />
);
};
export class BusterChartErrorWrapper extends Component<Props, State> {
state = {
hasError: false
};
static getDerivedStateFromError(_: Error): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Chart Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="flex h-full w-full items-center justify-center" role="alert">
<Alert
message="Something went wrong rendering the chart. This is likely an error on our end. Please contact Buster support."
type="error"
showIcon
/>
</div>
);
}
return this.props.children;
}
}
export const BusterChartErrorWrapper: React.FC<Props> = ({ children }) => {
return <ErrorBoundary errorComponent={<ErrorCardComponent />}>{children}</ErrorBoundary>;
};

View File

@ -2,6 +2,7 @@ import React, { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
errorComponent?: ReactNode;
onError?: () => void;
}
@ -24,7 +25,7 @@ export class ErrorBoundary extends Component<Props, State> {
render() {
if (this.state.hasError) {
return <span className="hidden">Error boundary...</span>;
return this.props.errorComponent;
}
return this.props.children;

View File

@ -0,0 +1,58 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ErrorCard } from './ErrorCard';
const meta: Meta<typeof ErrorCard> = {
title: 'UI/Error/ErrorCard',
component: ErrorCard,
tags: ['autodocs'],
argTypes: {
error: {
control: 'text',
description: 'The error message to display'
},
title: {
control: 'text',
description: 'Optional title for the error alert'
},
variant: {
control: { type: 'select' },
options: ['danger', 'default'],
description: 'The visual style of the error alert'
}
}
};
export default meta;
type Story = StoryObj<typeof ErrorCard>;
export const Default: Story = {
args: {
error: 'Something went wrong. Please try again later.',
title: 'Error',
variant: 'default'
}
};
export const Danger: Story = {
args: {
error: 'Failed to save changes. Please check your connection and try again.',
title: 'Connection Error',
variant: 'danger'
}
};
export const WithoutTitle: Story = {
args: {
error: 'This is an error message without a title.',
variant: 'default'
}
};
export const LongErrorMessage: Story = {
args: {
error:
'This is a very long error message that might wrap to multiple lines. It contains detailed information about what went wrong and possibly some suggestions on how to fix the issue.',
title: 'Detailed Error',
variant: 'danger'
}
};

View File

@ -0,0 +1,42 @@
import { cva, VariantProps } from 'class-variance-authority';
import React from 'react';
import { cn } from '@/lib/classMerge';
import { Text } from '../typography';
import { Xmark } from '../icons';
const errorVariants = cva('shadow p-3 rounded', {
variants: {
variant: {
danger: 'bg-danger-foreground text-white',
default: 'bg-background text-foreground'
}
}
});
export const ErrorCard: React.FC<
{
error: string;
title?: string;
className?: string;
onClose?: () => void;
} & VariantProps<typeof errorVariants>
> = ({ error, title, variant, className, onClose }) => {
return (
<div className={cn('flex flex-col gap-1', errorVariants({ variant }), className)}>
{title && <Text variant={'inherit'}>{title}</Text>}
<Text
className={cn(
variant === 'danger' && 'text-white',
variant === 'default' && 'text-text-secondary',
'leading-[1.3]'
)}>
{error}
</Text>
{onClose && (
<div onClick={onClose} className="absolute top-2 right-2">
<Xmark />
</div>
)}
</div>
);
};