feature(storybook): Storybook usability upgrades [C9S-140] (#2482)

This commit is contained in:
Josiah Clumont
2026-05-05 09:25:09 +12:00
committed by GitHub
parent 1ea8c1cb4e
commit 68f93fb281
79 changed files with 743 additions and 1111 deletions
+10 -8
View File
@@ -1,15 +1,21 @@
import path from 'path'; // This file has been automatically migrated to valid ESM format by Storybook.
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import path, { dirname } from 'path';
import { StorybookConfig } from '@storybook/react-webpack5'; import { StorybookConfig } from '@storybook/react-webpack5';
import { Configuration } from 'webpack'; import { Configuration } from 'webpack';
import postcss from 'postcss'; import postcss from 'postcss';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = createRequire(import.meta.url);
const config: StorybookConfig = { const config: StorybookConfig = {
stories: ['../app/**/*.stories.@(ts|tsx)'], stories: ['../app/**/*.stories.@(ts|tsx)'],
addons: [ addons: [
'@storybook/addon-links', '@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-webpack5-compiler-swc', '@storybook/addon-webpack5-compiler-swc',
'@chromatic-com/storybook', '@chromatic-com/storybook',
{ {
@@ -44,6 +50,7 @@ const config: StorybookConfig = {
], ],
}, },
}, },
'@storybook/addon-docs',
], ],
webpackFinal: (config) => { webpackFinal: (config) => {
const rules = config?.module?.rules || []; const rules = config?.module?.rules || [];
@@ -96,12 +103,7 @@ const config: StorybookConfig = {
}, },
staticDirs: ['./public'], staticDirs: ['./public'],
typescript: { typescript: {
reactDocgen: 'react-docgen-typescript', reactDocgen: 'react-docgen',
reactDocgenTypescriptOptions: {
compilerOptions: {
outDir: path.resolve(__dirname, '..', 'dist/public'),
},
},
}, },
framework: { framework: {
name: '@storybook/react-webpack5', name: '@storybook/react-webpack5',
+39 -8
View File
@@ -1,9 +1,10 @@
import { useEffect } from 'react';
import '../app/assets/css'; import '../app/assets/css';
import { pushStateLocationPlugin, UIRouter } from '@uirouter/react'; import { pushStateLocationPlugin, UIRouter } from '@uirouter/react';
import { initialize as initMSW, mswLoader } from 'msw-storybook-addon'; import { initialize as initMSW, mswLoader } from 'msw-storybook-addon';
import { handlers } from '../app/setup-tests/server-handlers'; import { handlers } from '../app/setup-tests/server-handlers';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Preview } from '@storybook/react'; import { Preview } from '@storybook/react-webpack5';
initMSW( initMSW(
{ {
@@ -26,13 +27,43 @@ const testQueryClient = new QueryClient({
}); });
const preview: Preview = { const preview: Preview = {
decorators: (Story) => ( globalTypes: {
<QueryClientProvider client={testQueryClient}> theme: {
<UIRouter plugins={[pushStateLocationPlugin]}> description: 'Portainer color theme',
<Story /> toolbar: {
</UIRouter> title: 'Theme',
</QueryClientProvider> icon: 'paintbrush',
), items: [
{ value: 'light', title: 'Light', icon: 'sun' },
{ value: 'dark', title: 'Dark', icon: 'moon' },
{ value: 'highcontrast', title: 'High Contrast', icon: 'eye' },
],
dynamicTitle: true,
},
},
},
initialGlobals: {
theme: 'light',
},
decorators: (Story, context) => {
const theme = context.globals.theme;
useEffect(() => {
if (theme === 'light') {
document.documentElement.removeAttribute('theme');
} else {
document.documentElement.setAttribute('theme', theme);
}
}, [theme]);
return (
<QueryClientProvider client={testQueryClient}>
<UIRouter plugins={[pushStateLocationPlugin]}>
<Story />
</UIRouter>
</QueryClientProvider>
);
},
loaders: [mswLoader], loaders: [mswLoader],
parameters: { parameters: {
options: { options: {
+1 -3
View File
@@ -1,8 +1,6 @@
/* eslint-disable */ /* eslint-disable */
/* tslint:disable */ /* tslint:disable */
import { v4 as uuidv4 } from 'uuid';
/** /**
* Mock Service Worker. * Mock Service Worker.
* @see https://github.com/mswjs/msw * @see https://github.com/mswjs/msw
@@ -111,7 +109,7 @@ addEventListener('fetch', function (event) {
return; return;
} }
const requestId = uuidv4(); const requestId = crypto.randomUUID();
event.respondWith(handleRequest(event, requestId, requestInterceptedAt)); event.respondWith(handleRequest(event, requestId, requestInterceptedAt));
}); });
+1 -1
View File
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { Alert } from './Alert'; import { Alert } from './Alert';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { Edition, FeatureId } from '@/react/portainer/feature-flags/enums'; import { Edition, FeatureId } from '@/react/portainer/feature-flags/enums';
import { init as initFeatureService } from '@/react/portainer/feature-flags/feature-flags.service'; import { init as initFeatureService } from '@/react/portainer/feature-flags/feature-flags.service';
+1 -1
View File
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { Badge, BadgeType, Props } from './Badge'; import { Badge, BadgeType, Props } from './Badge';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { BadgeIcon, BadgeSize, Props } from './BadgeIcon'; import { BadgeIcon, BadgeSize, Props } from './BadgeIcon';
@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { localizeDate } from '@/react/common/date-utils'; import { localizeDate } from '@/react/common/date-utils';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Anchor, Briefcase } from 'lucide-react'; import { Anchor, Briefcase } from 'lucide-react';
@@ -10,7 +10,7 @@ import { BoxSelector } from './BoxSelector';
import { BoxSelectorOption } from './types'; import { BoxSelectorOption } from './types';
const meta: Meta = { const meta: Meta = {
title: 'BoxSelector', title: 'Components/BoxSelector',
component: BoxSelector, component: BoxSelector,
}; };
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { Briefcase } from 'lucide-react'; import { Briefcase } from 'lucide-react';
@@ -12,7 +12,7 @@ import { BoxSelectorItem } from './BoxSelectorItem';
import { BoxSelectorOption } from './types'; import { BoxSelectorOption } from './types';
const meta: Meta = { const meta: Meta = {
title: 'BoxSelector/Item', title: 'Components/BoxSelector/Item',
args: { args: {
selected: false, selected: false,
description: 'description', description: 'description',
+2 -2
View File
@@ -1,11 +1,11 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Card, Props } from './Card'; import { Card, Props } from './Card';
export default { export default {
component: Card, component: Card,
title: 'Components/Card/Card', title: 'Components/Card',
} as Meta; } as Meta;
function Template({ function Template({
+1 -1
View File
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { Code } from './Code'; import { Code } from './Code';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { List } from 'lucide-react'; import { List } from 'lucide-react';
import { Link } from '@@/Link'; import { Link } from '@@/Link';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { DetailsTable } from './DetailsTable'; import { DetailsTable } from './DetailsTable';
import { DetailsRow } from './DetailsRow'; import { DetailsRow } from './DetailsRow';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Server, Cloud } from 'lucide-react'; import { Server, Cloud } from 'lucide-react';
@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { ExpandableMessageByLines } from './ExpandableMessageByLines'; import { ExpandableMessageByLines } from './ExpandableMessageByLines';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Server, Cloud } from 'lucide-react'; import { Server, Cloud } from 'lucide-react';
@@ -1,10 +1,10 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { Layers, AlertCircle, Database } from 'lucide-react'; import { Layers, AlertCircle, Database } from 'lucide-react';
import { HeaderLayout } from './HeaderLayout'; import { HeaderLayout } from './HeaderLayout';
const meta: Meta<typeof HeaderLayout> = { const meta: Meta<typeof HeaderLayout> = {
title: 'Design System/HeaderLayout', title: 'Components/HeaderLayout',
component: HeaderLayout, component: HeaderLayout,
tags: ['autodocs'], tags: ['autodocs'],
}; };
@@ -1,4 +1,4 @@
import { StoryFn, Meta } from '@storybook/react'; import { StoryFn, Meta } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { InlineLoader, Props } from './InlineLoader'; import { InlineLoader, Props } from './InlineLoader';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { InsightsBox, Props } from './InsightsBox'; import { InsightsBox, Props } from './InsightsBox';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { NavTabs, type Option } from './NavTabs'; import { NavTabs, type Option } from './NavTabs';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { UIRouter, pushStateLocationPlugin } from '@uirouter/react'; import { UIRouter, pushStateLocationPlugin } from '@uirouter/react';
import { Breadcrumbs } from './Breadcrumbs'; import { Breadcrumbs } from './Breadcrumbs';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { UserContext } from '@/react/hooks/useUser'; import { UserContext } from '@/react/hooks/useUser';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { UserContext } from '@/react/hooks/useUser'; import { UserContext } from '@/react/hooks/useUser';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PageTitle } from './PageTitle'; import { PageTitle } from './PageTitle';
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { DropdownOption } from '../DropdownMenu/DropdownMenu'; import { DropdownOption } from '../DropdownMenu/DropdownMenu';
+1 -1
View File
@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { Check } from 'lucide-react'; import { Check } from 'lucide-react';
import { StatusBadge } from './StatusBadge'; import { StatusBadge } from './StatusBadge';
@@ -0,0 +1,51 @@
import { useState } from 'react';
import { Meta } from '@storybook/react-webpack5';
import { FilterBarActiveIndicator } from './FilterBarActiveIndicator';
export default {
component: FilterBarActiveIndicator,
title: 'Design System/StatusSummaryBar/FilterBarActiveIndicator',
} as Meta;
export function Example() {
const [activeFilter, setActiveFilter] = useState<string | null>('Running');
if (!activeFilter) {
return (
<div className="flex flex-col gap-3">
<p className="text-sm text-[var(--text-muted-color)]">
Filter cleared. Pick one to bring the indicator back.
</p>
<div className="flex gap-2">
{['Running', 'Stopped', 'Outdated'].map((label) => (
<button
key={label}
type="button"
className="rounded-md border border-solid border-gray-4 bg-transparent px-3 py-1 text-sm"
onClick={() => setActiveFilter(label)}
>
{label}
</button>
))}
</div>
</div>
);
}
return (
<FilterBarActiveIndicator
label={activeFilter}
onClear={() => setActiveFilter(null)}
/>
);
}
export function LongLabel() {
return (
<FilterBarActiveIndicator
label="Outdated agents in production environments"
onClear={() => {}}
/>
);
}
@@ -0,0 +1,44 @@
import { useState } from 'react';
import { Meta } from '@storybook/react-webpack5';
import { FilterBarButton, Color } from './FilterBarButton';
export default {
component: FilterBarButton,
title: 'Design System/StatusSummaryBar/FilterBarButton',
} as Meta;
export function Example() {
const [selected, setSelected] = useState('');
const buttons: Array<{
label: string;
count: number;
color?: Color;
}> = [
{ label: 'Total', count: 18 },
{ label: 'Running', count: 12, color: 'success' },
{ label: 'Stopped', count: 4, color: 'error' },
{ label: 'Outdated', count: 2, color: 'warning' },
{ label: 'Hidden', count: 0, color: 'gray' },
];
return (
<div className="flex items-center gap-1">
{buttons.map((btn) => (
<FilterBarButton
key={btn.label}
name="status-filter"
label={btn.label}
count={btn.count}
color={btn.color}
isSelected={selected === btn.label}
onClick={() =>
setSelected((prev) => (prev === btn.label ? '' : btn.label))
}
data-cy={`filter-${btn.label.toLowerCase()}`}
/>
))}
</div>
);
}
@@ -1,5 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { StatusSummaryBar, StatusSegment } from './StatusSummaryBar'; import { StatusSummaryBar, StatusSegment } from './StatusSummaryBar';
@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Button } from '@@/buttons'; import { Button } from '@@/buttons';
@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { Button } from '@@/buttons'; import { Button } from '@@/buttons';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { TagId } from '@/portainer/tags/types'; import { TagId } from '@/portainer/tags/types';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { TeamsSelector } from './TeamsSelector'; import { TeamsSelector } from './TeamsSelector';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { ws } from 'msw'; import { ws } from 'msw';
import { useState } from 'react'; import { useState } from 'react';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { TextTip } from './TextTip'; import { TextTip } from './TextTip';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { Tooltip, Props } from './Tooltip'; import { Tooltip, Props } from './Tooltip';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { UsersSelector } from './UsersSelector'; import { UsersSelector } from './UsersSelector';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { ViewLoading } from './ViewLoading'; import { ViewLoading } from './ViewLoading';
@@ -1,5 +1,5 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import type { Meta } from '@storybook/react'; import type { Meta } from '@storybook/react-webpack5';
import { User } from 'lucide-react'; import { User } from 'lucide-react';
import { Widget } from './Widget'; import { Widget } from './Widget';
@@ -1,4 +1,4 @@
import type { Meta, StoryFn } from '@storybook/react'; import type { Meta, StoryFn } from '@storybook/react-webpack5';
import { Box, Settings, Users } from 'lucide-react'; import { Box, Settings, Users } from 'lucide-react';
import { import {
ReactStateDeclaration, ReactStateDeclaration,
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { AddButton } from './AddButton'; import { AddButton } from './AddButton';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Download } from 'lucide-react'; import { Download } from 'lucide-react';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Play, RefreshCw, Square, Trash2 } from 'lucide-react'; import { Play, RefreshCw, Square, Trash2 } from 'lucide-react';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { CopyButton, Props } from './CopyButton'; import { CopyButton, Props } from './CopyButton';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { Download } from 'lucide-react'; import { Download } from 'lucide-react';
import { LoadingButton } from './LoadingButton'; import { LoadingButton } from './LoadingButton';
@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Plus, Edit, Download, Settings } from 'lucide-react'; import { Plus, Edit, Download, Settings } from 'lucide-react';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { Clock } from 'lucide-react'; import { Clock } from 'lucide-react';
import { createColumnHelper, TableOptions } from '@tanstack/react-table'; import { createColumnHelper, TableOptions } from '@tanstack/react-table';
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { Clock } from 'lucide-react'; import { Clock } from 'lucide-react';
import { createColumnHelper, TableOptions } from '@tanstack/react-table'; import { createColumnHelper, TableOptions } from '@tanstack/react-table';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { ButtonSelector, Option } from './ButtonSelector'; import { ButtonSelector, Option } from './ButtonSelector';
@@ -1,11 +1,11 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { ColorPicker } from './ColorPicker'; import { ColorPicker } from './ColorPicker';
export default { export default {
component: ColorPicker, component: ColorPicker,
title: 'Components/Form/ColorPicker', title: 'Components/Forms/ColorPicker',
} as Meta; } as Meta;
export function Default() { export function Default() {
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { FileUploadField } from './FileUploadField'; import { FileUploadField } from './FileUploadField';
@@ -1,11 +1,11 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { FileUploadForm } from './FileUploadForm'; import { FileUploadForm } from './FileUploadForm';
export default { export default {
component: FileUploadForm, component: FileUploadForm,
title: 'Components/Form/FileUploadForm', title: 'Components/Forms/FileUploadForm',
} as Meta; } as Meta;
interface Args { interface Args {
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Input, Select } from '../Input'; import { Input, Select } from '../Input';
@@ -6,7 +6,7 @@ import { Input, Select } from '../Input';
import { FormControl } from './FormControl'; import { FormControl } from './FormControl';
export default { export default {
title: 'Components/Form/Control', title: 'Components/Forms/Control',
} as Meta; } as Meta;
interface TextFieldProps { interface TextFieldProps {
@@ -1,10 +1,10 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { FormSection } from './FormSection'; import { FormSection } from './FormSection';
export default { export default {
component: FormSection, component: FormSection,
title: 'Components/Form/FormSection', title: 'Components/Forms/FormSection',
} as Meta; } as Meta;
interface Args { interface Args {
@@ -1,11 +1,11 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { FormSectionTitle } from './FormSectionTitle'; import { FormSectionTitle } from './FormSectionTitle';
export default { export default {
component: FormSectionTitle, component: FormSectionTitle,
title: 'Components/Form/FormSectionTitle', title: 'Components/Forms/FormSectionTitle',
} as Meta; } as Meta;
function Template({ function Template({
@@ -1,10 +1,10 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Input } from './Input'; import { Input } from './Input';
export default { export default {
title: 'Components/Form/Input', title: 'Components/Forms/Input',
args: { args: {
disabled: false, disabled: false,
}, },
@@ -1,11 +1,11 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { InputLabeled } from './InputLabeled'; import { InputLabeled } from './InputLabeled';
export default { export default {
component: InputLabeled, component: InputLabeled,
title: 'Components/Form/InputLabeled', title: 'Components/Forms/InputLabeled',
} as Meta; } as Meta;
export { TextInput, NumberInput }; export { TextInput, NumberInput };
@@ -1,10 +1,10 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Select } from './Select'; import { Select } from './Select';
export default { export default {
title: 'Components/Form/Select', title: 'Components/Forms/Select',
args: { args: {
disabled: false, disabled: false,
}, },
@@ -1,11 +1,11 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { InputGroup } from '.'; import { InputGroup } from '.';
export default { export default {
component: InputGroup, component: InputGroup,
title: 'Components/Form/InputGroup', title: 'Components/Forms/InputGroup',
} as Meta; } as Meta;
export { BasicExample, Addons, Sizing }; export { BasicExample, Addons, Sizing };
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Input, Select } from '../Input'; import { Input, Select } from '../Input';
@@ -7,7 +7,7 @@ import { InputList } from './InputList';
import { DefaultType } from './types'; import { DefaultType } from './types';
const meta: Meta = { const meta: Meta = {
title: 'Components/Form/InputList', title: 'Components/Forms/InputList',
component: InputList, component: InputList,
}; };
@@ -1,11 +1,11 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Slider, Props } from './Slider'; import { Slider, Props } from './Slider';
export default { export default {
component: Slider, component: Slider,
title: 'Components/Form/Slider', title: 'Components/Forms/Slider',
} as Meta; } as Meta;
function Template({ function Template({
@@ -1,10 +1,10 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { Switch } from './Switch'; import { Switch } from './Switch';
export default { export default {
title: 'Components/Form/SwitchField/Switch', title: 'Components/Forms/SwitchField/Switch',
} as Meta; } as Meta;
export function Example() { export function Example() {
@@ -1,10 +1,10 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { SwitchField } from './SwitchField'; import { SwitchField } from './SwitchField';
export default { export default {
title: 'Components/Form/SwitchField', title: 'Components/Forms/SwitchField',
} as Meta; } as Meta;
export function Example() { export function Example() {
@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { withUserProvider } from '@/react/test-utils/withUserProvider'; import { withUserProvider } from '@/react/test-utils/withUserProvider';
@@ -7,6 +7,7 @@ import { ServiceWidget } from './ServiceWidget';
const Wrapped = withUserProvider(ServiceWidget); const Wrapped = withUserProvider(ServiceWidget);
const meta: Meta<typeof ServiceWidget> = { const meta: Meta<typeof ServiceWidget> = {
title: 'Components/Services/ServiceWidget',
component: ServiceWidget, component: ServiceWidget,
render: (args) => <Wrapped {...args} />, render: (args) => <Wrapped {...args} />,
args: { args: {
@@ -1,11 +1,11 @@
import { StoryObj, Meta } from '@storybook/react'; import { StoryObj, Meta } from '@storybook/react-webpack5';
import { StatusType } from '../../types'; import { StatusType } from '../../types';
import { DeploymentCounter } from './DeploymentCounter'; import { DeploymentCounter } from './DeploymentCounter';
const meta: Meta<typeof DeploymentCounter> = { const meta: Meta<typeof DeploymentCounter> = {
title: 'Edge/DeploymentCounter', title: 'Components/Edge/DeploymentCounter',
component: DeploymentCounter, component: DeploymentCounter,
}; };
export default meta; export default meta;
@@ -1,4 +1,4 @@
import { StoryFn } from '@storybook/react'; import { StoryFn } from '@storybook/react-webpack5';
import { import {
Environment, Environment,
@@ -11,7 +11,7 @@ import { EnvironmentItem } from './EnvironmentItem';
export default { export default {
component: EnvironmentItem, component: EnvironmentItem,
title: 'Home/EnvironmentList/EnvironmentItem', title: 'Components/Home/EnvironmentList/EnvironmentItem',
}; };
interface Args { interface Args {
@@ -1,4 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { UserViewModel } from '@/portainer/models/user'; import { UserViewModel } from '@/portainer/models/user';
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useState } from 'react'; import { useState } from 'react';
import { import {
@@ -7,7 +7,7 @@ import {
} from './CustomTemplatesVariablesDefinitionField'; } from './CustomTemplatesVariablesDefinitionField';
export default { export default {
title: 'Custom Templates/Variables Definition Field', title: 'Components/Custom Templates/Variables Definition Field',
component: CustomTemplatesVariablesDefinitionField, component: CustomTemplatesVariablesDefinitionField,
args: {}, args: {},
} as Meta<typeof CustomTemplatesVariablesDefinitionField>; } as Meta<typeof CustomTemplatesVariablesDefinitionField>;
@@ -8,7 +8,7 @@ import {
} from './CustomTemplatesVariablesField'; } from './CustomTemplatesVariablesField';
export default { export default {
title: 'Custom Templates/Variables Field', title: 'Components/Custom Templates/Variables Field',
component: CustomTemplatesVariablesField, component: CustomTemplatesVariablesField,
}; };
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { Form, Formik } from 'formik'; import { Form, Formik } from 'formik';
import { http, HttpResponse } from 'msw'; import { http, HttpResponse } from 'msw';
@@ -10,7 +10,7 @@ import { DeployMethod, GitFormModel } from './types';
export default { export default {
component: GitForm, component: GitForm,
title: 'Forms/GitForm', title: 'Components/Forms/GitForm',
parameters: { parameters: {
msw: { msw: {
handlers: [ handlers: [
@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react-webpack5';
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery'; import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
@@ -9,7 +9,7 @@ import {
const meta: Meta<typeof RegistryFormDockerhub> = { const meta: Meta<typeof RegistryFormDockerhub> = {
component: withTestQueryProvider(RegistryFormDockerhub), component: withTestQueryProvider(RegistryFormDockerhub),
title: 'Components/Form/RegistryFormDockerhub', title: 'Components/Forms/RegistryFormDockerhub',
argTypes: { argTypes: {
isLoading: { control: 'boolean' }, isLoading: { control: 'boolean' },
submitLabel: { control: 'text' }, submitLabel: { control: 'text' },
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { createMockUsers } from '@/react-tools/test-mocks'; import { createMockUsers } from '@/react-tools/test-mocks';
@@ -11,7 +11,7 @@ import { TeamMembership, TeamRole } from '../../types';
import { TeamAssociationSelector } from './TeamAssociationSelector'; import { TeamAssociationSelector } from './TeamAssociationSelector';
const meta: Meta = { const meta: Meta = {
title: 'teams/TeamAssociationSelector', title: 'Components/Teams/TeamAssociationSelector',
component: TeamAssociationSelector, component: TeamAssociationSelector,
}; };
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { UserContext } from '@/react/hooks/useUser'; import { UserContext } from '@/react/hooks/useUser';
@@ -10,7 +10,7 @@ import { TeamRole } from '@/react/portainer/users/teams/types';
import { TeamMembersList } from './TeamMembersList'; import { TeamMembersList } from './TeamMembersList';
const meta: Meta = { const meta: Meta = {
title: 'Teams/TeamAssociationSelector/TeamMembersList', title: 'Components/Teams/TeamAssociationSelector/TeamMembersList',
component: TeamMembersList, component: TeamMembersList,
}; };
@@ -1,4 +1,4 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { createMockUsers } from '@/react-tools/test-mocks'; import { createMockUsers } from '@/react-tools/test-mocks';
@@ -9,7 +9,7 @@ import { UserViewModel } from '@/portainer/models/user';
import { UsersList } from './UsersList'; import { UsersList } from './UsersList';
const meta: Meta = { const meta: Meta = {
title: 'Teams/TeamAssociationSelector/UsersList', title: 'Components/Teams/TeamAssociationSelector/UsersList',
component: UsersList, component: UsersList,
}; };
@@ -1,10 +1,10 @@
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react-webpack5';
import { CreateTeamForm } from './CreateTeamForm'; import { CreateTeamForm } from './CreateTeamForm';
import { mockExampleData } from './CreateTeamForm.mocks'; import { mockExampleData } from './CreateTeamForm.mocks';
const meta: Meta = { const meta: Meta = {
title: 'teams/CreateTeamForm', title: 'Components/Teams/CreateTeamForm',
component: CreateTeamForm, component: CreateTeamForm,
}; };
@@ -1,10 +1,10 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react-webpack5';
import { Clock, type LucideIcon } from 'lucide-react'; import { Clock, type LucideIcon } from 'lucide-react';
import { SidebarItem } from '.'; import { SidebarItem } from '.';
const meta: Meta = { const meta: Meta = {
title: 'Sidebar/SidebarItem', title: 'Components/Sidebar/SidebarItem',
component: SidebarItem, component: SidebarItem,
}; };
export default meta; export default meta;
+10 -12
View File
@@ -145,19 +145,16 @@
"@babel/preset-env": "^7.22.20", "@babel/preset-env": "^7.22.20",
"@babel/preset-react": "^7.22.15", "@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0", "@babel/preset-typescript": "^7.23.0",
"@chromatic-com/storybook": "^3.2.7", "@chromatic-com/storybook": "^5.1.2",
"@eslint-community/eslint-plugin-eslint-comments": "^4.7.1", "@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
"@eslint/compat": "^2.0.0", "@eslint/compat": "^2.0.0",
"@eslint/eslintrc": "^3.3.3", "@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@simbathesailor/use-what-changed": "^2.0.0", "@simbathesailor/use-what-changed": "^2.0.0",
"@storybook/addon-actions": "8.6.15", "@storybook/addon-links": "10.3.5",
"@storybook/addon-essentials": "8.6.15", "@storybook/addon-styling-webpack": "^3.0.2",
"@storybook/addon-links": "8.6.15", "@storybook/addon-webpack5-compiler-swc": "^4.0.3",
"@storybook/addon-styling-webpack": "^3.0.0", "@storybook/react-webpack5": "10.3.5",
"@storybook/addon-webpack5-compiler-swc": "^3.0.0",
"@storybook/react": "8.6.15",
"@storybook/react-webpack5": "8.6.15",
"@svgr/webpack": "^8.1.0", "@svgr/webpack": "^8.1.0",
"@testing-library/dom": "^9.3.4", "@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
@@ -208,7 +205,7 @@
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-regex": "^1.10.0", "eslint-plugin-regex": "^1.10.0",
"eslint-plugin-storybook": "^9", "eslint-plugin-storybook": "10.3.5",
"html-loader": "^5.1.0", "html-loader": "^5.1.0",
"html-webpack-plugin": "^5.5.3", "html-webpack-plugin": "^5.5.3",
"husky": "^8.0.0", "husky": "^8.0.0",
@@ -217,7 +214,7 @@
"lodash-webpack-plugin": "^0.11.6", "lodash-webpack-plugin": "^0.11.6",
"mini-css-extract-plugin": "^2.7.6", "mini-css-extract-plugin": "^2.7.6",
"msw": "^2.12.10", "msw": "^2.12.10",
"msw-storybook-addon": "^2.0.6", "msw-storybook-addon": "^2.0.7",
"ngtemplate-loader": "^2.1.0", "ngtemplate-loader": "^2.1.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"postcss-loader": "^7.3.3", "postcss-loader": "^7.3.3",
@@ -226,7 +223,7 @@
"react-docgen-typescript-plugin": "^1.0.5", "react-docgen-typescript-plugin": "^1.0.5",
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"speed-measure-webpack-plugin": "^1.5.0", "speed-measure-webpack-plugin": "^1.5.0",
"storybook": "8.6.15", "storybook": "10.3.5",
"storybook-css-modules-preset": "^1.1.1", "storybook-css-modules-preset": "^1.1.1",
"style-loader": "^3.3.3", "style-loader": "^3.3.3",
"swagger2openapi": "^7.0.8", "swagger2openapi": "^7.0.8",
@@ -241,7 +238,8 @@
"webpack-bundle-analyzer": "^5.2.0", "webpack-bundle-analyzer": "^5.2.0",
"webpack-cli": "^6.0.1", "webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.3", "webpack-dev-server": "^5.2.3",
"webpack-merge": "^6.0.1" "webpack-merge": "^6.0.1",
"@storybook/addon-docs": "10.3.5"
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {
+488 -981
View File
File diff suppressed because it is too large Load Diff