Fix stories (#10851)

Fix storybook stories
<img width="1475" alt="image"
src="https://github.com/user-attachments/assets/50327d9b-f3a0-46ea-87f2-93da356ec7c9"
/>
This commit is contained in:
Charles Bochet
2025-03-13 14:31:20 +01:00
committed by GitHub
parent 2ca0dc243a
commit 885b2d62d9
12 changed files with 168 additions and 63 deletions

View File

@ -36,6 +36,6 @@ export const Default: Story = {
await canvas.findByText('Search');
await canvas.findByText('Settings');
await canvas.findByText('Linkedin');
await canvas.findByText('All companies');
await canvas.findByText('Companies');
},
};

View File

@ -2,6 +2,8 @@ import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { TaskList } from '@/activities/tasks/components/TaskList';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
@ -13,6 +15,8 @@ const meta: Meta<typeof TaskList> = {
component: TaskList,
decorators: [
ComponentDecorator,
ContextStoreDecorator,
I18nFrontDecorator,
MemoryRouterDecorator,
ObjectMetadataItemsDecorator,
SnackBarDecorator,

View File

@ -52,8 +52,11 @@ export const Readonly: Story = {
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await userEvent.type(editor, '{{ "a": {{ "b" : "d" } }');
@ -76,8 +79,11 @@ export const SaveValidJson: Story = {
onPersist: fn(),
},
play: async ({ canvasElement, args }) => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await userEvent.type(editor, '{{ "a": {{ "b" : "d" } }');
@ -93,8 +99,11 @@ export const SaveValidMultilineJson: Story = {
onPersist: fn(),
},
play: async ({ canvasElement, args }) => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await userEvent.type(
editor,
@ -115,8 +124,11 @@ export const MultilineWithDefaultValue: Story = {
defaultValue: '{\n "a": {\n "b" : "d"\n }\n}',
},
play: async ({ canvasElement }) => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await waitFor(() => {
expect((editor as HTMLElement).innerText).toBe(
@ -132,8 +144,11 @@ export const DoesNotIgnoreInvalidJson: Story = {
onPersist: fn(),
},
play: async ({ canvasElement, args }) => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await userEvent.type(editor, 'lol');
@ -180,8 +195,11 @@ export const InsertVariableInTheMiddleOnTextInput: Story = {
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
const addVariableButton = await canvas.findByRole('button', {
name: 'Add variable',
@ -222,8 +240,11 @@ export const CanUseVariableAsObjectProperty: Story = {
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
const addVariableButton = await canvas.findByRole('button', {
name: 'Add variable',
@ -250,8 +271,11 @@ export const ClearField: Story = {
play: async ({ canvasElement, args }) => {
const defaultValueStringLength = args.defaultValue!.length;
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await Promise.all([
userEvent.type(editor, `{Backspace>${defaultValueStringLength}}`),
@ -273,8 +297,11 @@ export const DoesNotBreakWhenUserInsertsNewlineInJsonString: Story = {
onPersist: fn(),
},
play: async ({ canvasElement, args }) => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await userEvent.type(editor, '"a{Enter}b"');
@ -290,8 +317,11 @@ export const AcceptsJsonEncodedNewline: Story = {
onPersist: fn(),
},
play: async ({ canvasElement, args }) => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
await userEvent.type(editor, '"a\\nb"');
@ -322,8 +352,11 @@ export const HasHistory: Story = {
const canvas = within(canvasElement);
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const editor = await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
return editor;
});
const addVariableButton = await canvas.findByRole('button', {
name: 'Add variable',

View File

@ -120,8 +120,12 @@ export const WithDeletableVariable: Story = {
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
await waitFor(() => {
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
});
const editor = canvasElement.querySelector('.ProseMirror > p');
expect(editor).toBeVisible();
const variable = await canvas.findByText('Name');
expect(variable).toBeVisible();

View File

@ -1,13 +1,11 @@
import { Meta, StoryObj } from '@storybook/react';
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';
import { useSetRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldMetadataType } from '~/generated/graphql';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
@ -17,7 +15,13 @@ import {
mockedWorkspaceMemberData,
} from '~/testing/mock-data/users';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { FieldMetadataType } from 'twenty-shared';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
const RelationWorkspaceSetterEffect = () => {
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
@ -36,31 +40,63 @@ const RelationWorkspaceSetterEffect = () => {
const RelationManyFieldInputWithContext = () => {
const setHotKeyScope = useSetHotkeyScope();
const fieldDefinition = useMemo(
() => ({
fieldMetadataId: 'relation',
label: 'People',
type: FieldMetadataType.RELATION,
iconName: 'IconLink',
metadata: {
fieldName: 'people',
relationType: RelationDefinitionType.ONE_TO_MANY,
relationObjectMetadataNamePlural: 'companies',
relationObjectMetadataNameSingular: CoreObjectNameSingular.Company,
objectMetadataNameSingular: 'company',
relationFieldMetadataId: '20202020-8c37-4163-ba06-1dada334ce3e',
},
}),
[],
);
const setRecordStoreFieldValue = useSetRecoilState(
recordStoreFamilySelector({
fieldName: 'people',
recordId: 'recordId',
}),
);
const { openFieldInput } = useOpenFieldInputEditMode();
useEffect(() => {
setRecordStoreFieldValue([]);
setHotKeyScope('hotkey-scope');
}, [setHotKeyScope]);
openFieldInput({
fieldDefinition,
recordId: 'recordId',
});
}, [
fieldDefinition,
openFieldInput,
setHotKeyScope,
setRecordStoreFieldValue,
]);
return (
<div>
<FieldContextProvider
fieldDefinition={{
fieldMetadataId: 'relation',
label: 'People',
type: FieldMetadataType.RELATION,
iconName: 'IconLink',
metadata: {
fieldName: 'people',
relationObjectMetadataNamePlural: 'companies',
relationObjectMetadataNameSingular: CoreObjectNameSingular.Company,
objectMetadataNameSingular: 'company',
relationFieldMetadataId: '20202020-8c37-4163-ba06-1dada334ce3e',
},
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: 'relation-from-many-field-record-id-people',
}}
recordId={'recordId'}
>
<RelationWorkspaceSetterEffect />
<RelationFromManyFieldInput />
</FieldContextProvider>
<FieldContextProvider
fieldDefinition={fieldDefinition}
recordId={'recordId'}
>
<RelationWorkspaceSetterEffect />
<RelationFromManyFieldInput />
</FieldContextProvider>
</RecordFieldComponentInstanceContext.Provider>
<div data-testid="data-field-input-click-outside-div" />
</div>
);

View File

@ -20,7 +20,6 @@ import {
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { getCanvasElementForDropdownTesting } from 'twenty-ui';
import {
@ -92,12 +91,8 @@ const RelationToOneFieldInputWithContext = ({
instanceId: 'relation-to-one-field-input-123-Relation',
}}
>
<SingleRecordPickerComponentInstanceContext.Provider
value={{ instanceId: 'relation-to-one-field-input-123-Relation' }}
>
<RelationWorkspaceSetterEffect />
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
</SingleRecordPickerComponentInstanceContext.Provider>
<RelationWorkspaceSetterEffect />
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
</RecordFieldComponentInstanceContext.Provider>
</FieldContextProvider>
<div data-testid="data-field-input-click-outside-div" />
@ -176,9 +171,7 @@ export const Cancel: Story = {
const emptyDiv = canvas.getByTestId('data-field-input-click-outside-div');
await waitFor(() => {
userEvent.click(emptyDiv);
expect(cancelJestFn).toHaveBeenCalledTimes(1);
});
await userEvent.click(emptyDiv);
expect(cancelJestFn).toHaveBeenCalledTimes(1);
},
};

View File

@ -39,6 +39,7 @@ export const SingleRecordPicker = ({
event.target instanceof HTMLInputElement &&
event.target.tagName === 'INPUT'
);
if (weAreNotInAnHTMLInput && isDefined(onCancel)) {
onCancel();
}

View File

@ -1,5 +1,6 @@
import { Meta, StoryObj } from '@storybook/react';
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableEmptyStateSoftDelete } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
@ -19,12 +20,16 @@ const meta: Meta = {
RecordTableDecorator,
(Story) => (
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<RecordTableComponentInstance
recordTableId="persons"
onColumnsChange={() => {}}
<RecordFiltersComponentInstanceContext.Provider
value={{ instanceId: 'record-filters-component-instance' }}
>
<Story />
</RecordTableComponentInstance>
<RecordTableComponentInstance
recordTableId="persons"
onColumnsChange={() => {}}
>
<Story />
</RecordTableComponentInstance>
</RecordFiltersComponentInstanceContext.Provider>
</SnackBarProviderScope>
),
],

View File

@ -3,6 +3,7 @@ import { ComponentDecorator } from 'twenty-ui';
import { SettingsAccountsCalendarChannelDetails } from '@/settings/accounts/components/SettingsAccountsCalendarChannelDetails';
import { CalendarChannelVisibility } from '~/generated/graphql';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
@ -12,6 +13,7 @@ const meta: Meta<typeof SettingsAccountsCalendarChannelDetails> = {
component: SettingsAccountsCalendarChannelDetails,
decorators: [
ComponentDecorator,
I18nFrontDecorator,
ObjectMetadataItemsDecorator,
SnackBarDecorator,
],

View File

@ -4,6 +4,7 @@ import { ComponentDecorator } from 'twenty-ui';
import { MessageChannelContactAutoCreationPolicy } from '@/accounts/types/MessageChannel';
import { SettingsAccountsMessageChannelDetails } from '@/settings/accounts/components/SettingsAccountsMessageChannelDetails';
import { MessageChannelVisibility } from '~/generated/graphql';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
@ -13,6 +14,7 @@ const meta: Meta<typeof SettingsAccountsMessageChannelDetails> = {
component: SettingsAccountsMessageChannelDetails,
decorators: [
ComponentDecorator,
I18nFrontDecorator,
ObjectMetadataItemsDecorator,
SnackBarDecorator,
],

View File

@ -5,7 +5,7 @@ import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerSt
import styled from '@emotion/styled';
import { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, waitFor, within } from '@storybook/test';
import { graphql, HttpResponse } from 'msw';
import { HttpResponse, graphql } from 'msw';
import { useSetRecoilState } from 'recoil';
import { ComponentDecorator, RouterDecorator } from 'twenty-ui';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
@ -63,8 +63,25 @@ const meta: Meta<typeof RightDrawerWorkflowRunViewStep> = {
msw: {
handlers: [
graphql.query('FindOneWorkflowRun', () => {
const workflowRunContext =
oneFailedWorkflowRunQueryResult.workflowRun.context;
// Rendering the whole objectMetadata information in the JSON viewer is too long for storybook
// so we remove it for the story
return HttpResponse.json({
data: oneFailedWorkflowRunQueryResult,
data: {
...oneFailedWorkflowRunQueryResult,
workflowRun: {
...oneFailedWorkflowRunQueryResult.workflowRun,
context: {
...workflowRunContext,
trigger: {
...workflowRunContext.trigger,
objectMetadata: undefined,
},
},
},
},
});
}),
...graphqlMocks.handlers,

View File

@ -1,5 +1,6 @@
import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema';
import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext';
import { flowState } from '@/workflow/states/flowState';
import { workflowIdState } from '@/workflow/states/workflowIdState';
import { WorkflowVersion } from '@/workflow/types/Workflow';
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
@ -14,6 +15,7 @@ import {
export const WorkflowStepDecorator: Decorator = (Story) => {
const setWorkflowId = useSetRecoilState(workflowIdState);
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
const setFlow = useSetRecoilState(flowState);
const workflowVersion = getWorkflowMock().versions.edges[0]
.node as WorkflowVersion;
const { populateStepsOutputSchema } = useStepsOutputSchema();
@ -22,6 +24,11 @@ export const WorkflowStepDecorator: Decorator = (Story) => {
useEffect(() => {
setWorkflowId(getWorkflowMock().id);
setWorkflowSelectedNode(getWorkflowNodeIdMock());
setFlow({
workflowVersionId: workflowVersion.id,
trigger: workflowVersion.trigger,
steps: workflowVersion.steps,
});
populateStepsOutputSchema(workflowVersion);
setReady(true);
}, [
@ -29,6 +36,7 @@ export const WorkflowStepDecorator: Decorator = (Story) => {
setWorkflowSelectedNode,
populateStepsOutputSchema,
workflowVersion,
setFlow,
]);
return (