mirror of https://github.com/buster-so/buster.git
make more story components
This commit is contained in:
parent
0c66223ac6
commit
e240fadbb1
|
@ -178,9 +178,9 @@ const TitleCell = React.memo<{ title: string; status: VerificationStatus; chatId
|
|||
);
|
||||
TitleCell.displayName = 'TitleCell';
|
||||
|
||||
const OwnerCell = memo<{ name: string; image: string | null | undefined }>(({ name, image }) => (
|
||||
const OwnerCell = memo<{ name: string; image: string | undefined }>(({ name, image }) => (
|
||||
<div className="flex pl-0">
|
||||
<BusterUserAvatar image={image || undefined} name={name} size={18} />
|
||||
<BusterUserAvatar image={image} name={name} size={18} />
|
||||
</div>
|
||||
));
|
||||
OwnerCell.displayName = 'OwnerCell';
|
||||
|
|
|
@ -30,9 +30,9 @@ const roundingVariants = {
|
|||
};
|
||||
|
||||
const sizeVariants = {
|
||||
default: 'h-7',
|
||||
tall: 'h-8',
|
||||
small: 'h-6'
|
||||
default: 'h-6',
|
||||
tall: 'h-7',
|
||||
small: 'h-5'
|
||||
};
|
||||
|
||||
export const buttonVariants = cva(
|
||||
|
|
|
@ -20,7 +20,7 @@ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElemen
|
|||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-background text-foreground overflow-hidden rounded border shadow',
|
||||
'bg-background text-foreground flex flex-col overflow-hidden rounded border shadow',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { CodeCard } from './CodeCard';
|
||||
|
||||
const meta: Meta<typeof CodeCard> = {
|
||||
title: 'Base/Cards/CodeCard',
|
||||
component: CodeCard,
|
||||
parameters: {
|
||||
layout: 'centered'
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="w-full min-w-[500px]">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
]
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CodeCard>;
|
||||
|
||||
const sampleCode = `function helloWorld() {
|
||||
console.log('Hello, World!');
|
||||
}`;
|
||||
|
||||
const longCode = `import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleClick = () => {
|
||||
console.log('Button clicked!');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<h1>Hello World</h1>
|
||||
<Button onClick={handleClick}>
|
||||
Click me
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};`;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
code: sampleCode,
|
||||
language: 'javascript',
|
||||
fileName: 'example.js',
|
||||
className: 'w-[500px] h-[300px]'
|
||||
}
|
||||
};
|
||||
|
||||
export const ReadOnly: Story = {
|
||||
args: {
|
||||
code: sampleCode,
|
||||
language: 'javascript',
|
||||
fileName: 'readonly.js',
|
||||
readOnly: true,
|
||||
className: 'w-[500px] h-[300px]'
|
||||
}
|
||||
};
|
||||
|
||||
export const LargerEditor: Story = {
|
||||
args: {
|
||||
code: longCode,
|
||||
language: 'typescript',
|
||||
fileName: 'MyComponent.tsx',
|
||||
className: 'w-[600px] h-[400px]'
|
||||
}
|
||||
};
|
||||
|
||||
export const NoButtons: Story = {
|
||||
args: {
|
||||
code: sampleCode,
|
||||
language: 'javascript',
|
||||
fileName: 'no-buttons.js',
|
||||
buttons: false,
|
||||
className: 'w-[500px] h-[300px]'
|
||||
}
|
||||
};
|
||||
|
||||
export const CustomButtons: Story = {
|
||||
args: {
|
||||
code: sampleCode,
|
||||
language: 'javascript',
|
||||
fileName: 'custom-buttons.js',
|
||||
buttons: <div className="px-2">Custom Buttons</div>,
|
||||
className: 'w-[500px] h-[300px]'
|
||||
}
|
||||
};
|
|
@ -50,6 +50,7 @@ export const CodeCard: React.FC<{
|
|||
readOnly={readOnly}
|
||||
height="100%"
|
||||
onMetaEnter={onMetaEnter}
|
||||
className="border-none"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
|
@ -105,7 +105,7 @@ const LegendItemStandard = React.memo(
|
|||
onMouseEnter={onMouseEnterHandler}
|
||||
onMouseLeave={onMouseLeaveHandler}
|
||||
className={cn(
|
||||
'flex flex-col justify-center space-y-0',
|
||||
'flex flex-col justify-center space-y-1',
|
||||
'h-[24px] rounded-sm px-2.5',
|
||||
clickable && 'transition-background hover:bg-item-hover cursor-pointer duration-100'
|
||||
)}>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
export const useMeasureElement = (useMeasure?: boolean) => {
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const [dimensions, setDimensions] = useState({ fullWidth: 0, fullHeight: 0 });
|
||||
|
||||
const updateDimensions = useCallback(() => {
|
||||
if (elementRef.current) {
|
||||
const rect = elementRef.current.getBoundingClientRect();
|
||||
|
||||
setDimensions({
|
||||
fullWidth: rect.width,
|
||||
fullHeight: rect.height
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!elementRef.current || !useMeasure) return;
|
||||
|
||||
const resizeObserver = new ResizeObserver(updateDimensions);
|
||||
resizeObserver.observe(elementRef.current);
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [updateDimensions, useMeasure]);
|
||||
|
||||
return {
|
||||
elementRef,
|
||||
...dimensions
|
||||
};
|
||||
};
|
|
@ -0,0 +1,146 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { BusterChartLegend } from '../BusterChartLegend';
|
||||
import { ChartType } from '../../interfaces';
|
||||
|
||||
const meta: Meta<typeof BusterChartLegend> = {
|
||||
title: 'Base/Charts/BusterChartLegend',
|
||||
component: BusterChartLegend,
|
||||
parameters: {
|
||||
layout: 'centered'
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
onClickItem: { action: 'clicked' },
|
||||
onFocusItem: { action: 'focused' },
|
||||
showLegendHeadline: {
|
||||
control: 'select',
|
||||
description:
|
||||
'Show the legend headline will only work if a headline is provided (see example)',
|
||||
options: [false, 'total', 'average', 'min', 'max', 'current', 'median']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof BusterChartLegend>;
|
||||
|
||||
const defaultLegendItems = [
|
||||
{
|
||||
color: '#1677FF',
|
||||
inactive: false,
|
||||
type: ChartType.Line,
|
||||
formattedName: 'Revenue',
|
||||
id: 'revenue',
|
||||
serieName: 'revenue'
|
||||
},
|
||||
{
|
||||
color: '#52C41A',
|
||||
inactive: false,
|
||||
type: ChartType.Line,
|
||||
formattedName: 'Profit',
|
||||
id: 'profit',
|
||||
serieName: 'profit'
|
||||
},
|
||||
{
|
||||
color: '#F5222D',
|
||||
inactive: false,
|
||||
type: ChartType.Bar,
|
||||
formattedName: 'Orders',
|
||||
id: 'orders',
|
||||
serieName: 'orders'
|
||||
}
|
||||
];
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
legendItems: defaultLegendItems,
|
||||
show: true,
|
||||
animateLegend: true,
|
||||
containerWidth: 600,
|
||||
showLegendHeadline: false,
|
||||
onClickItem: () => {},
|
||||
onFocusItem: () => {}
|
||||
}
|
||||
};
|
||||
|
||||
export const WithHeadline: Story = {
|
||||
args: {
|
||||
legendItems: [
|
||||
{
|
||||
...defaultLegendItems[0],
|
||||
headline: {
|
||||
type: 'total',
|
||||
titleAmount: '$1,234,567'
|
||||
}
|
||||
},
|
||||
{
|
||||
...defaultLegendItems[1],
|
||||
headline: {
|
||||
type: 'total',
|
||||
titleAmount: '$567,890'
|
||||
}
|
||||
},
|
||||
{
|
||||
...defaultLegendItems[2],
|
||||
headline: {
|
||||
type: 'total',
|
||||
titleAmount: '$98,765'
|
||||
}
|
||||
}
|
||||
],
|
||||
show: true,
|
||||
animateLegend: true,
|
||||
containerWidth: 600,
|
||||
showLegendHeadline: 'total'
|
||||
}
|
||||
};
|
||||
|
||||
export const WithOverflow: Story = {
|
||||
args: {
|
||||
legendItems: [
|
||||
...defaultLegendItems,
|
||||
{
|
||||
color: '#722ED1',
|
||||
inactive: false,
|
||||
type: ChartType.Line,
|
||||
formattedName: 'Customers',
|
||||
id: 'customers',
|
||||
serieName: 'customers'
|
||||
},
|
||||
{
|
||||
color: '#13C2C2',
|
||||
inactive: false,
|
||||
type: ChartType.Line,
|
||||
formattedName: 'Average Order Value',
|
||||
id: 'aov',
|
||||
serieName: 'aov'
|
||||
},
|
||||
{
|
||||
color: '#FA8C16',
|
||||
inactive: false,
|
||||
type: ChartType.Bar,
|
||||
formattedName: 'Returns',
|
||||
id: 'returns',
|
||||
serieName: 'returns'
|
||||
}
|
||||
],
|
||||
show: true,
|
||||
animateLegend: true,
|
||||
containerWidth: 400,
|
||||
showLegendHeadline: undefined,
|
||||
onClickItem: () => {},
|
||||
onFocusItem: () => {}
|
||||
}
|
||||
};
|
||||
|
||||
export const Hidden: Story = {
|
||||
args: {
|
||||
legendItems: defaultLegendItems,
|
||||
show: false,
|
||||
animateLegend: true,
|
||||
containerWidth: 600,
|
||||
showLegendHeadline: undefined,
|
||||
onClickItem: () => {},
|
||||
onFocusItem: () => {}
|
||||
}
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { OverflowButton } from './OverflowContainer';
|
||||
import { ChartType } from '../interfaces';
|
||||
import { OverflowButton } from '../OverflowContainer';
|
||||
import { ChartType } from '../../interfaces';
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Charts/OverflowButton',
|
|
@ -1,7 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { ShimmerText, Text } from '@/components/ui';
|
||||
import { ShimmerText } from '@/components/ui/text/ShimmerText';
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export const PreparingYourRequestLoader: React.FC<{
|
||||
className?: string;
|
||||
|
@ -12,10 +13,7 @@ export const PreparingYourRequestLoader: React.FC<{
|
|||
return (
|
||||
<div className={`flex h-full w-full items-center justify-center space-x-1.5 ${className}`}>
|
||||
{error || useShimmer === false ? (
|
||||
<Text type="tertiary" className="flex items-center text-center">
|
||||
{/* {!!error && <AppMaterialIcons icon="error" className="mr-1" />} */}
|
||||
{error || text}
|
||||
</Text>
|
||||
<span className="text-text-tertiary flex items-center text-center">{error || text}</span>
|
||||
) : (
|
||||
<ShimmerText text={text} />
|
||||
)}
|
||||
|
@ -29,9 +27,7 @@ export const NoChartData: React.FC<{
|
|||
}> = ({ className = '', noDataText = 'The query ran successfully but didn’t return any data' }) => {
|
||||
return (
|
||||
<div className={`flex h-full w-full items-center justify-center ${className}`}>
|
||||
<Text type="tertiary" className={`${className}`}>
|
||||
{noDataText}
|
||||
</Text>
|
||||
<span className={cn('text-text-tertiary', className)}>{noDataText}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Text } from '@/components/ui';
|
||||
import { useMount } from 'ahooks';
|
||||
import { BusterChartProps, ChartType } from '../interfaces';
|
||||
import { type BusterChartProps, ChartType } from '../interfaces';
|
||||
|
||||
export const NoValidAxis: React.FC<{
|
||||
type: ChartType;
|
||||
|
@ -20,7 +18,7 @@ export const NoValidAxis: React.FC<{
|
|||
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Text type="tertiary">{inValidChartText}</Text>
|
||||
<span className="text-text-tertiary">{inValidChartText}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,6 @@ export const BusterMetricChart: React.FC<BusterMetricChartProps> = React.memo(
|
|||
metricSubHeader,
|
||||
metricValueAggregate,
|
||||
data,
|
||||
isDarkMode,
|
||||
animate,
|
||||
columnLabelFormats,
|
||||
metricValueLabel,
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import React from 'react';
|
||||
import { AppPopoverMenu, AppPopoverMenuProps } from '../tooltip';
|
||||
|
||||
export interface AppDropdownSelectProps extends AppPopoverMenuProps {
|
||||
items: {
|
||||
label: React.ReactNode;
|
||||
key?: string;
|
||||
index?: number;
|
||||
onClick?: () => void;
|
||||
icon?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
link?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const AppDropdownSelect: React.FC<AppDropdownSelectProps> = React.memo((props) => {
|
||||
return <AppPopoverMenu {...props} />;
|
||||
});
|
||||
|
||||
AppDropdownSelect.displayName = 'AppDropdownSelect';
|
|
@ -0,0 +1,42 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { GlobalErrorComponent } from './GlobalErrorComponent';
|
||||
|
||||
const meta: Meta<typeof GlobalErrorComponent> = {
|
||||
title: 'Base/GlobalErrorComponent',
|
||||
component: GlobalErrorComponent,
|
||||
parameters: {
|
||||
layout: 'fullscreen'
|
||||
},
|
||||
tags: ['autodocs']
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof GlobalErrorComponent>;
|
||||
|
||||
// Normal state with content
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: <div>Normal application content</div>
|
||||
}
|
||||
};
|
||||
|
||||
// Error state
|
||||
export const WithError: Story = {
|
||||
args: {
|
||||
children: <div>This content won't be visible due to error</div>
|
||||
},
|
||||
parameters: {
|
||||
error: new Error('Simulated error for story')
|
||||
},
|
||||
render: (args) => {
|
||||
const ErrorTrigger = () => {
|
||||
throw new Error('Simulated error for story');
|
||||
};
|
||||
|
||||
return (
|
||||
<GlobalErrorComponent {...args}>
|
||||
<ErrorTrigger />
|
||||
</GlobalErrorComponent>
|
||||
);
|
||||
}
|
||||
};
|
|
@ -1,9 +1,15 @@
|
|||
'use client';
|
||||
|
||||
import { Button } from 'antd';
|
||||
import { Component, ErrorInfo, ReactNode } from 'react';
|
||||
import { Title } from '@/components/ui';
|
||||
|
||||
import { Button } from '../buttons/Button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardFooter
|
||||
} from '../card/CardBase';
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
@ -31,26 +37,30 @@ export class GlobalErrorComponent extends Component<Props, State> {
|
|||
<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">
|
||||
<div className="-mt-4 max-w-md rounded-lg border border-gray-200 bg-white/90 p-10">
|
||||
<div className="mb-8 flex flex-col gap-4">
|
||||
<Title className="animate-fade-in text-center" level={1}>
|
||||
Looks like we hit an error! 😅
|
||||
</Title>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h1 className="animate-fade-in text-2xl font-semibold">
|
||||
Looks like we hit an error! 😅
|
||||
</h1>
|
||||
|
||||
<Title level={5} className="animate-slide-up m-0! text-gray-600!">
|
||||
Don't worry, it's not you - it's us!
|
||||
</Title>
|
||||
<Title level={5} className="animate-slide-up m-0! text-gray-500!">
|
||||
If this error persists, please contact Buster support!
|
||||
</Title>
|
||||
</div>
|
||||
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-600">
|
||||
Don't worry, it's not you - it'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>
|
||||
|
||||
<a href="/" className="block">
|
||||
<Button type="primary" block size="large" className="w-full">
|
||||
Take Me Home 🏠
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import CircleSpinnerLoader from './CircleSpinnerLoader';
|
||||
import { Text } from '@/components/ui';
|
||||
|
||||
export const CircleSpinnerLoaderContainer: React.FC<{
|
||||
text?: string;
|
||||
|
@ -9,7 +8,7 @@ export const CircleSpinnerLoaderContainer: React.FC<{
|
|||
return (
|
||||
<div className={`flex h-full w-full flex-col items-center justify-center ${className}`}>
|
||||
<CircleSpinnerLoader />
|
||||
{text && <Text className="mt-3">{text}</Text>}
|
||||
{text && <span className="mt-3 text-base">{text}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import React from 'react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { Toaster as ToasterSonner } from 'sonner';
|
||||
import { CircleCheck } from '@/components/ui/icons';
|
||||
import { CircleCheck, CircleXmark, CircleWarning } from '@/components/ui/icons';
|
||||
|
||||
type ToasterProps = React.ComponentProps<typeof ToasterSonner>;
|
||||
|
||||
|
@ -16,7 +16,9 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
|||
expand={true}
|
||||
visibleToasts={5}
|
||||
icons={{
|
||||
success: <CircleCheck />
|
||||
success: <CircleCheck />,
|
||||
error: <CircleXmark />,
|
||||
warning: <CircleWarning />
|
||||
}}
|
||||
swipeDirections={['right']}
|
||||
theme={theme as ToasterProps['theme']}
|
||||
|
@ -24,7 +26,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
|||
toastOptions={{
|
||||
classNames: {
|
||||
toast:
|
||||
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
||||
'group toast group-[.toaster]:bg-background! border !p-3 group-[.toaster]:text-foreground! group-[.toaster]:border-border! group-[.toaster]:shadow-hard!',
|
||||
description: 'group-[.toast]:text-muted-foreground',
|
||||
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
||||
cancelButton: 'group-[.toast]:bg-border group-[.toast]:text-foreground'
|
||||
|
|
|
@ -194,9 +194,9 @@ const TitleCell = React.memo<{ title: string; status: VerificationStatus; metric
|
|||
);
|
||||
TitleCell.displayName = 'TitleCell';
|
||||
|
||||
const OwnerCell = memo<{ name: string; image: string | null | undefined }>(({ name, image }) => (
|
||||
const OwnerCell = memo<{ name: string; image: string | undefined }>(({ name, image }) => (
|
||||
<div className="flex pl-0">
|
||||
<BusterUserAvatar image={image || undefined} name={name} size={18} />
|
||||
<BusterUserAvatar image={image} name={name} size={18} />
|
||||
</div>
|
||||
));
|
||||
OwnerCell.displayName = 'OwnerCell';
|
||||
|
|
Loading…
Reference in New Issue