Docs/storybook improvements (#877)

* docs: use PageDecorator

* docs: use decorators in TableHeader stories

* docs: use theming parameter in App stories

* docs: enable auto-generated docs for stories

Closes #702
This commit is contained in:
Thaïs
2023-07-24 20:06:37 +02:00
committed by GitHub
parent 07180af8c0
commit c16d420d17
23 changed files with 339 additions and 342 deletions

View File

@ -1,63 +1,73 @@
const path = require('path'); const path = require('path');
module.exports = { module.exports = {
webpackFinal: config => { webpackFinal: (config) => {
config.module.rules.push({ config.module.rules.push({
test: /\.tsx?$/, test: /\.tsx?$/,
exclude: /node_modules/, exclude: /node_modules/,
use: [{ use: [
loader: require.resolve('babel-loader'), {
options: { loader: require.resolve('babel-loader'),
presets: [require('@babel/preset-typescript').default, [require('@babel/preset-react').default, { options: {
runtime: 'automatic' presets: [
}], require('@babel/preset-env').default] require('@babel/preset-typescript').default,
} [
}] require('@babel/preset-react').default,
{
runtime: 'automatic',
},
],
require('@babel/preset-env').default,
],
},
},
],
}); });
config.resolve.extensions.push('.ts', '.tsx'); config.resolve.extensions.push('.ts', '.tsx');
config.module.rules.push({ config.module.rules.push({
test: /\.mjs$/, test: /\.mjs$/,
include: /node_modules/, include: /node_modules/,
type: 'javascript/auto' type: 'javascript/auto',
}); });
config.module.rules.push({ config.module.rules.push({
test: /\.svg$/, test: /\.svg$/,
use: [ use: [
{ {
loader: '@svgr/webpack' loader: '@svgr/webpack',
}, },
{ {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: 'static/media/[path][name].[ext]' name: 'static/media/[path][name].[ext]',
} },
} },
], ],
type: 'javascript/auto', type: 'javascript/auto',
issuer: { issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/] and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
} },
}); });
config.resolve.extensions.push('.mjs'); config.resolve.extensions.push('.mjs');
config.resolve.alias = { config.resolve.alias = {
...config.resolve.alias, ...config.resolve.alias,
'~': path.resolve(__dirname, "../src"), '~': path.resolve(__dirname, '../src'),
'@': path.resolve(__dirname, "../src/modules"), '@': path.resolve(__dirname, '../src/modules'),
}; };
return config; return config;
}, },
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [ addons: [
"@storybook/addon-links", '@storybook/addon-links',
"@storybook/addon-essentials", '@storybook/addon-essentials',
"@storybook/addon-interactions", '@storybook/addon-interactions',
"@storybook/addon-coverage", '@storybook/addon-coverage',
"@storybook/addon-styling", '@storybook/addon-styling',
"storybook-addon-pseudo-states", 'storybook-addon-pseudo-states',
"storybook-addon-cookie", 'storybook-addon-cookie',
], ],
docs: { autodocs: true },
framework: { framework: {
name: '@storybook/react-webpack5', name: '@storybook/react-webpack5',
options: {} options: {},
}, },
}; };

View File

@ -1,21 +0,0 @@
import { Meta } from '@storybook/react';
import { App } from '~/App';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { Story } from './App.stories';
import { renderWithDarkMode } from './shared';
const meta: Meta<typeof App> = {
title: 'App/App/DarkMode',
component: App,
};
export default meta;
export const DarkMode: Story = {
render: () => renderWithDarkMode(true),
parameters: {
msw: graphqlMocks,
},
};

View File

@ -1,21 +1,52 @@
import { MemoryRouter } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { useRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { isAuthenticatingState } from '@/auth/states/isAuthenticatingState';
import { App } from '~/App'; import { App } from '~/App';
import { FullHeightStorybookLayout } from '~/testing/FullHeightStorybookLayout';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedUsersData } from '~/testing/mock-data/users';
import { render } from './shared'; const MockedAuth: React.FC<React.PropsWithChildren> = ({ children }) => {
const [, setCurrentUser] = useRecoilState(currentUserState);
const [, setIsAuthenticating] = useRecoilState(isAuthenticatingState);
setCurrentUser(mockedUsersData[0]);
setIsAuthenticating(false);
return <>{children}</>;
};
const meta: Meta<typeof App> = { const meta: Meta<typeof App> = {
title: 'App/App', title: 'App/App',
component: App, component: App,
decorators: [
(Story) => (
<MemoryRouter>
<FullHeightStorybookLayout>
<MockedAuth>
<Story />
</MockedAuth>
</FullHeightStorybookLayout>
</MemoryRouter>
),
],
parameters: {
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export type Story = StoryObj<typeof App>; export type Story = StoryObj<typeof App>;
export const Default: Story = { export const Default: Story = {};
render,
export const DarkMode: Story = {
parameters: { parameters: {
msw: graphqlMocks, theming: {
themeOverride: 'dark',
},
}, },
}; };

View File

@ -1,42 +0,0 @@
import { MemoryRouter } from 'react-router-dom';
import { ThemeProvider } from '@emotion/react';
import { useRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { isAuthenticatingState } from '@/auth/states/isAuthenticatingState';
import { darkTheme } from '@/ui/themes/themes';
import { App } from '~/App';
import { FullHeightStorybookLayout } from '~/testing/FullHeightStorybookLayout';
import { mockedUsersData } from '~/testing/mock-data/users';
export const render = () => renderWithDarkMode(false);
const MockedAuth: React.FC<React.PropsWithChildren> = ({ children }) => {
const [, setCurrentUser] = useRecoilState(currentUserState);
const [, setIsAuthenticating] = useRecoilState(isAuthenticatingState);
setCurrentUser(mockedUsersData[0]);
setIsAuthenticating(false);
return <>{children}</>;
};
export const renderWithDarkMode = (forceDarkMode?: boolean) => {
const AppInStoryBook = (
<FullHeightStorybookLayout>
<MockedAuth>
<App />
</MockedAuth>
</FullHeightStorybookLayout>
);
return (
<MemoryRouter>
{forceDarkMode ? (
<ThemeProvider theme={darkTheme}>{AppInStoryBook}</ThemeProvider>
) : (
AppInStoryBook
)}
</MemoryRouter>
);
};

View File

@ -13,6 +13,7 @@ const meta: Meta<typeof RoundedIconButton> = {
title: 'UI/Button/RoundedIconButton', title: 'UI/Button/RoundedIconButton',
component: RoundedIconButton, component: RoundedIconButton,
decorators: [ComponentDecorator], decorators: [ComponentDecorator],
argTypes: { icon: { control: false } },
args: { onClick: clickJestFn, icon: <IconArrowRight size={15} /> }, args: { onClick: clickJestFn, icon: <IconArrowRight size={15} /> },
}; };

View File

@ -2,37 +2,45 @@ import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import { IconList } from '@/ui/icon/index'; import { IconList } from '@/ui/icon/index';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { companiesFilters } from '~/pages/companies/companies-filters';
import { availableSorts } from '~/pages/companies/companies-sorts'; import { availableSorts } from '~/pages/companies/companies-sorts';
import { getRenderWrapperForEntityTableComponent } from '~/testing/renderWrappers'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { HooksEntityTable } from '../../../components/HooksEntityTable';
import { TableContext } from '../../../states/TableContext';
import { TableHeader } from '../TableHeader'; import { TableHeader } from '../TableHeader';
const meta: Meta<typeof TableHeader> = { const meta: Meta<typeof TableHeader> = {
title: 'UI/Table/TableHeader', title: 'UI/Table/TableHeader',
component: TableHeader, component: TableHeader,
decorators: [
(Story) => (
<RecoilScope SpecificContext={TableContext}>
{/* TODO: add company mocked loader <CompanyEntityTableData */}
<HooksEntityTable
availableFilters={companiesFilters}
numberOfColumns={5}
/>
<Story />
</RecoilScope>
),
ComponentDecorator,
],
argTypes: { viewIcon: { control: false } },
args: {
viewName: 'ViewName',
viewIcon: <IconList />,
availableSorts,
},
}; };
export default meta; export default meta;
type Story = StoryObj<typeof TableHeader>; type Story = StoryObj<typeof TableHeader>;
export const Empty: Story = { export const Empty: Story = {};
render: getRenderWrapperForEntityTableComponent(
<TableHeader
viewName="ViewName"
viewIcon={<IconList />}
availableSorts={availableSorts}
/>,
),
};
export const WithSortsAndFilters: Story = { export const WithSortsAndFilters: Story = {
render: getRenderWrapperForEntityTableComponent(
<TableHeader
viewName="ViewName"
viewIcon={<IconList />}
availableSorts={availableSorts}
/>,
),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
const outsideClick = await canvas.findByText('ViewName'); const outsideClick = await canvas.findByText('ViewName');

View File

@ -3,24 +3,22 @@ import type { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/testing-library'; import { within } from '@storybook/testing-library';
import { graphql } from 'msw'; import { graphql } from 'msw';
import { getRenderWrapperForPage } from '~/testing/renderWrappers'; import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
import { GET_CURRENT_USER } from '../../../modules/users/queries'; import { GET_CURRENT_USER } from '../../../modules/users/queries';
import { mockedOnboardingUsersData } from '../../../testing/mock-data/users';
import { CreateProfile } from '../CreateProfile'; import { CreateProfile } from '../CreateProfile';
const meta: Meta<typeof CreateProfile> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Auth/CreateProfile', title: 'Pages/Auth/CreateProfile',
component: CreateProfile, component: CreateProfile,
}; decorators: [PageDecorator],
args: { currentPath: '/create/profile' },
export default meta;
export type Story = StoryObj<typeof CreateProfile>;
export const Default: Story = {
render: getRenderWrapperForPage(<CreateProfile />, '/create/profile'),
parameters: { parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: [ msw: [
graphql.query( graphql.query(
getOperationName(GET_CURRENT_USER) ?? '', getOperationName(GET_CURRENT_USER) ?? '',
@ -34,6 +32,13 @@ export const Default: Story = {
), ),
], ],
}, },
};
export default meta;
export type Story = StoryObj<typeof CreateProfile>;
export const Default: Story = {
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
await canvas.findByText('Create profile'); await canvas.findByText('Create profile');

View File

@ -3,24 +3,22 @@ import type { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/testing-library'; import { within } from '@storybook/testing-library';
import { graphql } from 'msw'; import { graphql } from 'msw';
import { getRenderWrapperForPage } from '~/testing/renderWrappers'; import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
import { GET_CURRENT_USER } from '../../../modules/users/queries'; import { GET_CURRENT_USER } from '../../../modules/users/queries';
import { mockedOnboardingUsersData } from '../../../testing/mock-data/users';
import { CreateWorkspace } from '../CreateWorkspace'; import { CreateWorkspace } from '../CreateWorkspace';
const meta: Meta<typeof CreateWorkspace> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Auth/CreateWorkspace', title: 'Pages/Auth/CreateWorkspace',
component: CreateWorkspace, component: CreateWorkspace,
}; decorators: [PageDecorator],
args: { currentPath: '/create/workspace' },
export default meta;
export type Story = StoryObj<typeof CreateWorkspace>;
export const Default: Story = {
render: getRenderWrapperForPage(<CreateWorkspace />, '/create/workspace'),
parameters: { parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: [ msw: [
graphql.query( graphql.query(
getOperationName(GET_CURRENT_USER) ?? '', getOperationName(GET_CURRENT_USER) ?? '',
@ -34,6 +32,13 @@ export const Default: Story = {
), ),
], ],
}, },
};
export default meta;
export type Story = StoryObj<typeof CreateWorkspace>;
export const Default: Story = {
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
await canvas.findByText('Create your workspace'); await canvas.findByText('Create your workspace');

View File

@ -1,14 +1,26 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { fireEvent, within } from '@storybook/testing-library'; import { fireEvent, within } from '@storybook/testing-library';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { SignInUp } from '../SignInUp'; import { SignInUp } from '../SignInUp';
const meta: Meta<typeof SignInUp> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Auth/SignInUp', title: 'Pages/Auth/SignInUp',
component: SignInUp, component: SignInUp,
decorators: [PageDecorator],
args: { currentPath: '/sign-in' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
cookie: {
tokenPair: '{}',
},
},
}; };
export default meta; export default meta;
@ -16,13 +28,6 @@ export default meta;
export type Story = StoryObj<typeof SignInUp>; export type Story = StoryObj<typeof SignInUp>;
export const Default: Story = { export const Default: Story = {
render: getRenderWrapperForPage(<SignInUp />, '/sign-in'),
parameters: {
msw: graphqlMocks,
cookie: {
tokenPair: '{}',
},
},
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
const continueWithEmailButton = await canvas.findByText( const continueWithEmailButton = await canvas.findByText(

View File

@ -3,23 +3,31 @@ import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import assert from 'assert'; import assert from 'assert';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { sleep } from '~/testing/sleep'; import { sleep } from '~/testing/sleep';
import { Companies } from '../Companies'; import { Companies } from '../Companies';
import { Story } from './Companies.stories'; import { Story } from './Companies.stories';
const meta: Meta<typeof Companies> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Companies/FilterBy', title: 'Pages/Companies/FilterBy',
component: Companies, component: Companies,
decorators: [PageDecorator],
args: { currentPath: '/companies' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export const FilterByName: Story = { export const FilterByName: Story = {
render: getRenderWrapperForPage(<Companies />, '/companies'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -50,13 +58,9 @@ export const FilterByName: Story = {
expect(await canvas.findByText('Name:')).toBeInTheDocument(); expect(await canvas.findByText('Name:')).toBeInTheDocument();
expect(await canvas.findByText('Contains Air')).toBeInTheDocument(); expect(await canvas.findByText('Contains Air')).toBeInTheDocument();
}, },
parameters: {
msw: graphqlMocks,
},
}; };
export const FilterByAccountOwner: Story = { export const FilterByAccountOwner: Story = {
render: getRenderWrapperForPage(<Companies />, '/companies'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -99,7 +103,4 @@ export const FilterByAccountOwner: Story = {
expect(await canvas.findByText('Account owner:')).toBeInTheDocument(); expect(await canvas.findByText('Account owner:')).toBeInTheDocument();
expect(await canvas.findByText('Is Charles Test')).toBeInTheDocument(); expect(await canvas.findByText('Is Charles Test')).toBeInTheDocument();
}, },
parameters: {
msw: graphqlMocks,
},
}; };

View File

@ -2,22 +2,30 @@ import { expect } from '@storybook/jest';
import type { Meta } from '@storybook/react'; import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { Companies } from '../Companies'; import { Companies } from '../Companies';
import { Story } from './Companies.stories'; import { Story } from './Companies.stories';
const meta: Meta<typeof Companies> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Companies/SortBy', title: 'Pages/Companies/SortBy',
component: Companies, component: Companies,
decorators: [PageDecorator],
args: { currentPath: '/companies' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export const SortByName: Story = { export const SortByName: Story = {
render: getRenderWrapperForPage(<Companies />, '/companies'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -36,7 +44,4 @@ export const SortByName: Story = {
await expect(canvas.queryAllByTestId('remove-icon-name')).toStrictEqual([]); await expect(canvas.queryAllByTestId('remove-icon-name')).toStrictEqual([]);
}, },
parameters: {
msw: graphqlMocks,
},
}; };

View File

@ -1,22 +1,26 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { Companies } from '../Companies'; import { Companies } from '../Companies';
const meta: Meta<typeof Companies> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Companies', title: 'Pages/Companies',
component: Companies, component: Companies,
decorators: [PageDecorator],
args: { currentPath: '/companies' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export type Story = StoryObj<typeof Companies>; export type Story = StoryObj<typeof Companies>;
export const Default: Story = { export const Default: Story = {};
render: getRenderWrapperForPage(<Companies />, '/companies'),
parameters: {
msw: graphqlMocks,
},
};

View File

@ -10,57 +10,53 @@ import {
} from '@/activities/queries'; } from '@/activities/queries';
import { CREATE_COMMENT_THREAD_WITH_COMMENT } from '@/activities/queries/create'; import { CREATE_COMMENT_THREAD_WITH_COMMENT } from '@/activities/queries/create';
import { GET_COMPANY } from '@/companies/queries'; import { GET_COMPANY } from '@/companies/queries';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedCommentThreads } from '~/testing/mock-data/comment-threads'; import { mockedCommentThreads } from '~/testing/mock-data/comment-threads';
import { mockedCompaniesData } from '~/testing/mock-data/companies'; import { mockedCompaniesData } from '~/testing/mock-data/companies';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { CompanyShow } from '../CompanyShow'; import { CompanyShow } from '../CompanyShow';
const meta: Meta<typeof CompanyShow> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Companies/Company', title: 'Pages/Companies/Company',
component: CompanyShow, component: CompanyShow,
decorators: [PageDecorator],
args: { currentPath: '/companies/89bb825c-171e-4bcc-9cf7-43448d6fb278' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: [
...graphqlMocks,
graphql.query(
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
(req, res, ctx) => {
return res(
ctx.data({
findManyCommentThreads: mockedCommentThreads,
}),
);
},
),
graphql.query(getOperationName(GET_COMPANY) ?? '', (req, res, ctx) => {
return res(
ctx.data({
findUniqueCompany: mockedCompaniesData[0],
}),
);
}),
],
},
}; };
export default meta; export default meta;
export type Story = StoryObj<typeof CompanyShow>; export type Story = StoryObj<typeof CompanyShow>;
const companyShowCommonGraphqlMocks = [ export const Default: Story = {};
graphql.query(
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
(req, res, ctx) => {
return res(
ctx.data({
findManyCommentThreads: mockedCommentThreads,
}),
);
},
),
graphql.query(getOperationName(GET_COMPANY) ?? '', (req, res, ctx) => {
return res(
ctx.data({
findUniqueCompany: mockedCompaniesData[0],
}),
);
}),
];
export const Default: Story = {
render: getRenderWrapperForPage(
<CompanyShow />,
'/companies/89bb825c-171e-4bcc-9cf7-43448d6fb278',
),
parameters: {
msw: [...graphqlMocks, ...companyShowCommonGraphqlMocks],
},
};
export const EditNote: Story = { export const EditNote: Story = {
render: getRenderWrapperForPage(
<CompanyShow />,
'/companies/89bb825c-171e-4bcc-9cf7-43448d6fb278',
),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
const firstNoteTitle = await canvas.findByText('My very first note'); const firstNoteTitle = await canvas.findByText('My very first note');
@ -84,8 +80,7 @@ export const EditNote: Story = {
}, },
parameters: { parameters: {
msw: [ msw: [
...graphqlMocks, ...meta.parameters?.msw,
...companyShowCommonGraphqlMocks,
graphql.mutation( graphql.mutation(
getOperationName(CREATE_COMMENT_THREAD_WITH_COMMENT) ?? '', getOperationName(CREATE_COMMENT_THREAD_WITH_COMMENT) ?? '',
(req, res, ctx) => { (req, res, ctx) => {

View File

@ -1,14 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/testing-library'; import { within } from '@storybook/testing-library';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { Opportunities } from '../Opportunities'; import { Opportunities } from '../Opportunities';
const meta: Meta<typeof Opportunities> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Opportunities/Default', title: 'Pages/Opportunities/Default',
component: Opportunities, component: Opportunities,
decorators: [PageDecorator],
args: { currentPath: '/opportunities' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
@ -16,10 +25,6 @@ export default meta;
export type Story = StoryObj<typeof Opportunities>; export type Story = StoryObj<typeof Opportunities>;
export const Default: Story = { export const Default: Story = {
render: getRenderWrapperForPage(<Opportunities />, '/opportunities'),
parameters: {
msw: graphqlMocks,
},
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
await canvas.findByText('All opportunities'); await canvas.findByText('All opportunities');

View File

@ -3,23 +3,31 @@ import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import assert from 'assert'; import assert from 'assert';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { sleep } from '~/testing/sleep'; import { sleep } from '~/testing/sleep';
import { People } from '../People'; import { People } from '../People';
import { Story } from './People.stories'; import { Story } from './People.stories';
const meta: Meta<typeof People> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/FilterBy', title: 'Pages/People/FilterBy',
component: People, component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export const Email: Story = { export const Email: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -49,13 +57,9 @@ export const Email: Story = {
expect(await canvas.findByText('Email:')).toBeInTheDocument(); expect(await canvas.findByText('Email:')).toBeInTheDocument();
expect(await canvas.findByText('Contains al')).toBeInTheDocument(); expect(await canvas.findByText('Contains al')).toBeInTheDocument();
}, },
parameters: {
msw: graphqlMocks,
},
}; };
export const CompanyName: Story = { export const CompanyName: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -98,7 +102,4 @@ export const CompanyName: Story = {
expect(await canvas.findByText('Company:')).toBeInTheDocument(); expect(await canvas.findByText('Company:')).toBeInTheDocument();
expect(await canvas.findByText('Is Qonto')).toBeInTheDocument(); expect(await canvas.findByText('Is Qonto')).toBeInTheDocument();
}, },
parameters: {
msw: graphqlMocks,
},
}; };

View File

@ -7,26 +7,34 @@ import { graphql } from 'msw';
import { UPDATE_ONE_PERSON } from '@/people/queries'; import { UPDATE_ONE_PERSON } from '@/people/queries';
import { SEARCH_COMPANY_QUERY } from '@/search/queries/search'; import { SEARCH_COMPANY_QUERY } from '@/search/queries/search';
import { Company } from '~/generated/graphql'; import { Company } from '~/generated/graphql';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { fetchOneFromData } from '~/testing/mock-data'; import { fetchOneFromData } from '~/testing/mock-data';
import { mockedCompaniesData } from '~/testing/mock-data/companies'; import { mockedCompaniesData } from '~/testing/mock-data/companies';
import { mockedPeopleData } from '~/testing/mock-data/people'; import { mockedPeopleData } from '~/testing/mock-data/people';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { sleep } from '~/testing/sleep'; import { sleep } from '~/testing/sleep';
import { People } from '../People'; import { People } from '../People';
import { Story } from './People.stories'; import { Story } from './People.stories';
const meta: Meta<typeof People> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/Input', title: 'Pages/People/Input',
component: People, component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export const InteractWithManyRows: Story = { export const InteractWithManyRows: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -64,13 +72,9 @@ export const InteractWithManyRows: Story = {
canvas.queryByTestId('editable-cell-edit-mode-container'), canvas.queryByTestId('editable-cell-edit-mode-container'),
).toBeInTheDocument(); ).toBeInTheDocument();
}, },
parameters: {
msw: graphqlMocks,
},
}; };
export const CheckCheckboxes: Story = { export const CheckCheckboxes: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -95,9 +99,6 @@ export const CheckCheckboxes: Story = {
expect(secondCheckbox.checked).toBe(false); expect(secondCheckbox.checked).toBe(false);
}, },
parameters: {
msw: graphqlMocks,
},
}; };
const editRelationMocks = ( const editRelationMocks = (
@ -185,7 +186,6 @@ const editRelationMocks = (
]; ];
export const EditRelation: Story = { export const EditRelation: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement, step }) => { play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -229,7 +229,6 @@ export const EditRelation: Story = {
}); });
}, },
parameters: { parameters: {
actions: {},
msw: editRelationMocks('Qonto', ['Airbnb', 'Aircall'], { msw: editRelationMocks('Qonto', ['Airbnb', 'Aircall'], {
name: 'Airbnb', name: 'Airbnb',
domainName: 'airbnb.com', domainName: 'airbnb.com',
@ -238,7 +237,6 @@ export const EditRelation: Story = {
}; };
export const SelectRelationWithKeys: Story = { export const SelectRelationWithKeys: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -274,7 +272,6 @@ export const SelectRelationWithKeys: Story = {
expect(allAirbns.length).toBe(1); expect(allAirbns.length).toBe(1);
}, },
parameters: { parameters: {
actions: {},
msw: editRelationMocks('Qonto', ['Airbnb', 'Aircall'], { msw: editRelationMocks('Qonto', ['Airbnb', 'Aircall'], {
name: 'Aircall', name: 'Aircall',
domainName: 'aircall.io', domainName: 'aircall.io',

View File

@ -2,23 +2,31 @@ import { expect } from '@storybook/jest';
import type { Meta } from '@storybook/react'; import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { sleep } from '~/testing/sleep'; import { sleep } from '~/testing/sleep';
import { People } from '../People'; import { People } from '../People';
import { Story } from './People.stories'; import { Story } from './People.stories';
const meta: Meta<typeof People> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/SortBy', title: 'Pages/People/SortBy',
component: People, component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export const Email: Story = { export const Email: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -32,13 +40,9 @@ export const Email: Story = {
expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument(); expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument();
}, },
parameters: {
msw: graphqlMocks,
},
}; };
export const Cancel: Story = { export const Cancel: Story = {
render: getRenderWrapperForPage(<People />, '/people'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
@ -59,7 +63,4 @@ export const Cancel: Story = {
[], [],
); );
}, },
parameters: {
msw: graphqlMocks,
},
}; };

View File

@ -1,22 +1,26 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { People } from '../People'; import { People } from '../People';
const meta: Meta<typeof People> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People', title: 'Pages/People',
component: People, component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export type Story = StoryObj<typeof People>; export type Story = StoryObj<typeof People>;
export const Default: Story = { export const Default: Story = {};
render: getRenderWrapperForPage(<People />, '/people'),
parameters: {
msw: graphqlMocks,
},
};

View File

@ -1,30 +1,35 @@
import { Route, Routes } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import {} from '@storybook/react';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedPeopleData } from '~/testing/mock-data/people'; import { mockedPeopleData } from '~/testing/mock-data/people';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { PersonShow } from '../PersonShow'; import { PersonShow } from '../PersonShow';
const meta: Meta<typeof PersonShow> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/Person', title: 'Pages/People/Person',
component: PersonShow, component: PersonShow,
decorators: [
(Story) => (
<Routes>
<Route path="/person/:personId" element={<Story />} />
</Routes>
),
PageDecorator,
],
args: { currentPath: `/person/${mockedPeopleData[0].id}` },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export type Story = StoryObj<typeof PersonShow>; export type Story = StoryObj<typeof PersonShow>;
export const Default: Story = { export const Default: Story = {};
render: getRenderWrapperForPage(
<Routes>
<Route path="/person/:personId" element={<PersonShow />} />
</Routes>,
`/person/${mockedPeopleData[0].id}`,
),
parameters: {
msw: graphqlMocks,
},
};

View File

@ -1,35 +1,35 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/testing-library'; import { within } from '@storybook/testing-library';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { SettingsProfile } from '../SettingsProfile'; import { SettingsProfile } from '../SettingsProfile';
const meta: Meta<typeof SettingsProfile> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Settings/SettingsProfile', title: 'Pages/Settings/SettingsProfile',
component: SettingsProfile, component: SettingsProfile,
decorators: [PageDecorator],
args: { currentPath: '/settings/profile' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
export type Story = StoryObj<typeof SettingsProfile>; export type Story = StoryObj<typeof SettingsProfile>;
export const Default: Story = { export const Default: Story = {};
render: getRenderWrapperForPage(<SettingsProfile />, '/settings/profile'),
parameters: {
msw: graphqlMocks,
},
};
export const LogOut: Story = { export const LogOut: Story = {
render: getRenderWrapperForPage(<SettingsProfile />, '/settings/profile'),
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
const logoutButton = await canvas.findByText('Logout'); const logoutButton = await canvas.findByText('Logout');
await logoutButton.click(); await logoutButton.click();
}, },
parameters: {
msw: graphqlMocks,
},
}; };

View File

@ -1,14 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/testing-library'; import { within } from '@storybook/testing-library';
import {
PageDecorator,
type PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { SettingsWorkspaceMembers } from '../SettingsWorkspaceMembers'; import { SettingsWorkspaceMembers } from '../SettingsWorkspaceMembers';
const meta: Meta<typeof SettingsWorkspaceMembers> = { const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Settings/SettingsWorkspaceMembers', title: 'Pages/Settings/SettingsWorkspaceMembers',
component: SettingsWorkspaceMembers, component: SettingsWorkspaceMembers,
decorators: [PageDecorator],
args: { currentPath: '/settings/workspace-members' },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
},
}; };
export default meta; export default meta;
@ -16,13 +25,6 @@ export default meta;
export type Story = StoryObj<typeof SettingsWorkspaceMembers>; export type Story = StoryObj<typeof SettingsWorkspaceMembers>;
export const Default: Story = { export const Default: Story = {
render: getRenderWrapperForPage(
<SettingsWorkspaceMembers />,
'/settings/workspace-members',
),
parameters: {
msw: graphqlMocks,
},
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
await canvas.findByText('Copy link'); await canvas.findByText('Copy link');

View File

@ -0,0 +1,30 @@
import { HotkeysProvider } from 'react-hotkeys-hook';
import { MemoryRouter } from 'react-router-dom';
import { Decorator } from '@storybook/react';
import { ClientConfigProvider } from '../../modules/client-config/components/ClientConfigProvider';
import { INITIAL_HOTKEYS_SCOPES } from '../../modules/ui/hotkey/constants';
import { DefaultLayout } from '../../modules/ui/layout/components/DefaultLayout';
import { UserProvider } from '../../modules/users/components/UserProvider';
import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout';
export type PageDecoratorArgs = { currentPath: string };
export const PageDecorator: Decorator<{ currentPath: string }> = (
Story,
{ args },
) => (
<UserProvider>
<ClientConfigProvider>
<HotkeysProvider initiallyActiveScopes={INITIAL_HOTKEYS_SCOPES}>
<MemoryRouter initialEntries={[args.currentPath]}>
<FullHeightStorybookLayout>
<DefaultLayout>
<Story />
</DefaultLayout>
</FullHeightStorybookLayout>
</MemoryRouter>
</HotkeysProvider>
</ClientConfigProvider>
</UserProvider>
);

View File

@ -1,55 +0,0 @@
import React from 'react';
import { HotkeysProvider } from 'react-hotkeys-hook';
import { MemoryRouter } from 'react-router-dom';
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
import { INITIAL_HOTKEYS_SCOPES } from '@/ui/hotkey/constants';
import { DefaultLayout } from '@/ui/layout/components/DefaultLayout';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { HooksEntityTable } from '@/ui/table/components/HooksEntityTable';
import { TableContext } from '@/ui/table/states/TableContext';
import { UserProvider } from '@/users/components/UserProvider';
import { companiesFilters } from '~/pages/companies/companies-filters';
import { ComponentStorybookLayout } from './ComponentStorybookLayout';
import { FullHeightStorybookLayout } from './FullHeightStorybookLayout';
export function getRenderWrapperForPage(
children: React.ReactElement,
currentPath: string,
) {
return function render() {
return (
<UserProvider>
<ClientConfigProvider>
<HotkeysProvider initiallyActiveScopes={INITIAL_HOTKEYS_SCOPES}>
<MemoryRouter initialEntries={[currentPath]}>
<FullHeightStorybookLayout>
<DefaultLayout>{children}</DefaultLayout>
</FullHeightStorybookLayout>
</MemoryRouter>
</HotkeysProvider>
</ClientConfigProvider>
</UserProvider>
);
};
}
export function getRenderWrapperForEntityTableComponent(
children: React.ReactElement,
) {
return function Render() {
return (
<RecoilScope SpecificContext={TableContext}>
{/*
TODO: add company mocked loader
<CompanyEntityTableData */}
<HooksEntityTable
availableFilters={companiesFilters}
numberOfColumns={5}
/>
<ComponentStorybookLayout>{children}</ComponentStorybookLayout>
</RecoilScope>
);
};
}