diff --git a/packages/forms/.storybook/decorators/FormStoryWithDisplayMode.component.jsx b/packages/forms/.storybook/decorators/FormStoryWithDisplayMode.component.jsx deleted file mode 100644 index d7543abe83c..00000000000 --- a/packages/forms/.storybook/decorators/FormStoryWithDisplayMode.component.jsx +++ /dev/null @@ -1,242 +0,0 @@ -import { useState } from 'react'; -import { action } from '@storybook/addon-actions'; -import { randomUUID } from '@talend/utils'; - -import { PRESIGNED_URL_TRIGGER_ACTION } from '../../src/UIForm/fields/File/File.component'; - -function getFilteredCollection({ name, selection, certified, favorites, selected, orders }) { - const methods = { - asc: (a, b) => (a > b ? -1 : 1), - desc: (a, b) => (a < b ? -1 : 1), - }; - const collection = [ - { - id: '0', - name: 'Title with few actions', - modified: 1442880000000, - icon: 'talend-file-xls-o', - author: 'First Author', - flags: ['CERTIFIED', 'FAVORITE'], - }, - { - id: '1', - name: 'Title with lot of actions', - modified: 1537574400000, - icon: 'talend-file-xls-o', - author: 'Second Author', - }, - { - id: '2', - name: 'Title with persistant actions', - modified: 1474502400000, - author: 'Jean-Pierre DUPONT', - icon: 'talend-file-xls-o', - flags: ['FAVORITE'], - }, - { - id: '3', - name: 'Title with icon', - modified: 1506038400000, - author: 'Third Author', - icon: 'talend-file-xls-o', - flags: ['CERTIFIED'], - }, - { - id: '4', - name: 'Title in input mode', - modified: 1506038400000, - author: 'Jean-Pierre DUPONT', - icon: 'talend-file-xls-o', - }, - { - id: '5', - name: 'Title with long long long long long long long long long long long text', - modified: 1547478328552, - author: 'Jean-Pierre DUPONT with super super super long text', - icon: 'talend-file-xls-o', - flags: ['CERTIFIED', 'FAVORITE'], - }, - ]; - - let c = collection; - - if (name) { - c = c.filter(item => item.name.includes(name)); - } - if (certified) { - c = c.filter(item => item.flags && item.flags.includes('CERTIFIED')); - } - if (favorites) { - c = c.filter(item => item.flags && item.flags.includes('FAVORITE')); - } - if (selection) { - c = c.filter(item => selected.includes(item.id)); - } - - if (orders) { - if (orders.name) { - c = c.sort((a, b) => methods[orders.name](a.name, b.name)); - } - if (orders.date) { - c = c.sort((a, b) => methods[orders.date](a.modified, b.modified)); - } - } - - return c; -} - -function stringToB64(value) { - return window.btoa( - encodeURIComponent(value).replace(/%([0-9A-F]{2})/g, (_, p1) => { - return String.fromCharCode(`0x${p1}`); - }), - ); -} - -export function createCommonProps() { - return { - autocomplete: 'off', - customValidation(schema, value, properties) { - action('customValidation')(schema, value, properties); - return value.length >= 5 && 'Custom validation : The value should be less than 5 chars'; - }, - formName: 'my-form', - onChange: action('Change'), - onTrigger(event, payload) { - action('Trigger')(event, payload); - const schema = payload.schema; - const key = schema.key && schema.key[schema.key.length - 1]; - if (key && key.includes('fail')) { - return Promise.reject({ errors: { [schema.key]: 'This trigger has failed' } }); - } - - if (key && (key.includes('asyncTitleMap') || key.includes('AsyncTitleMap'))) { - return new Promise(resolve => { - setTimeout( - () => - resolve({ - titleMap: [ - { value: 'clafoutis', name: 'Clafoutis aux poires et aux fruits' }, - { value: 'conchiglioni-au-thon', name: 'Conchiglioni au thon' }, - { value: 'coquillettes-crevettes', name: 'coquillettes aux crevettes' }, - { value: 'crumble', name: 'Crumble a la danette' }, - { value: 'pomme-savane', name: 'Pomme savane' }, - { value: 'tarte-au-citron', name: 'Tarte au citron' }, - ], - }), - 3000, - ); - }); - } - - if (key === 'datasetId' && payload.trigger.onEvent === 'filter') { - return new Promise(resolve => { - setTimeout( - () => - resolve({ - collection: getFilteredCollection(payload.filters), - }), - 3000, - ); - }); - } - if (key === 'datasetId' && payload.trigger.onEvent === 'change') { - return Promise.resolve({ - properties: properties => { - const { datasetId, name } = properties; - return name && name.length - ? properties - : { - ...properties, - name: datasetId && `Resource ${datasetId} preparation`, - }; - }, - errors: errors => { - const e = { ...errors }; - delete e.name; - return e; - }, - }); - } - if ( - key && - key.startsWith('file') && - payload.trigger && - payload.trigger.action === PRESIGNED_URL_TRIGGER_ACTION && - payload.trigger.onEvent === 'change' - ) { - const { name } = event.target.files[0]; - return new Promise(resolve => { - setTimeout( - () => - resolve({ - properties: properties => ({ - ...properties, - [key]: `${randomUUID()}.${stringToB64(name)}`, - }), - }), - 3000, - ); - }); - } - - return Promise.resolve({}); - }, - onReset: action('onReset'), - onSubmit: action('Submit'), - }; -} - -export function FormStoryWithDisplayMode({ children, category, doc, ...restProps }) { - const [displayMode, setDisplayMode] = useState(); - - function toggleDisplayModeText(event) { - setDisplayMode(event.target.checked ? 'text' : undefined); - } - - return ( -
-
-
- {doc && ( - - Documentation - - )} -
-
-

Form

- {children({ ...createCommonProps(), ...restProps, displayMode })} -
-
-

Display mode

-
-
-
- -
-
-
-

UI Spec / schema

-
{JSON.stringify(restProps.data, null, 2)}
-
-
-
-
-
- ); -} diff --git a/packages/forms/.storybook/decorators/withCenteredLayout.decorator.jsx b/packages/forms/.storybook/decorators/withCenteredLayout.decorator.jsx deleted file mode 100644 index b4750567bc1..00000000000 --- a/packages/forms/.storybook/decorators/withCenteredLayout.decorator.jsx +++ /dev/null @@ -1,16 +0,0 @@ -export const withCenteredLayout = (story, { parameters: { centeredLayout } }) => { - if (!centeredLayout) { - return story(); - } - - return ( -
-
- {story()} -
-
- ); -}; diff --git a/packages/forms/.storybook/decorators/withFormStoryDisplayMode.decorator.jsx b/packages/forms/.storybook/decorators/withFormStoryDisplayMode.decorator.jsx deleted file mode 100644 index 01b78760fcf..00000000000 --- a/packages/forms/.storybook/decorators/withFormStoryDisplayMode.decorator.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import { FormStoryWithDisplayMode } from './FormStoryWithDisplayMode.component'; - -export const withFormStoryDisplayMode = (Story, { parameters: { formStoryDisplayMode }, args }) => { - if (!formStoryDisplayMode) return ; - const { doc, category } = formStoryDisplayMode; - return ( - - {props => } - - ); -}; diff --git a/packages/forms/.storybook/preview-head.html b/packages/forms/.storybook/preview-head.html deleted file mode 100644 index ae451392690..00000000000 --- a/packages/forms/.storybook/preview-head.html +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/packages/forms/.storybook/preview.js b/packages/forms/.storybook/preview.js index b6ad5a890a1..10adc47afc7 100755 --- a/packages/forms/.storybook/preview.js +++ b/packages/forms/.storybook/preview.js @@ -1,8 +1,6 @@ import { namespaces as dsNamespaces } from '@talend/locales-design-system/namespaces'; import { namespaces as componentsNamespaces } from '@talend/locales-tui-components/namespaces'; import { namespaces as formsNamespaces } from '@talend/locales-tui-forms/namespaces'; -import { withCenteredLayout } from './decorators/withCenteredLayout.decorator'; -import { withFormStoryDisplayMode } from './decorators/withFormStoryDisplayMode.decorator'; export const i18n = { namespaces: [...dsNamespaces, ...componentsNamespaces, ...formsNamespaces], @@ -17,5 +15,3 @@ export const i18n = { export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, }; - -export const decorators = [withCenteredLayout, withFormStoryDisplayMode]; diff --git a/packages/forms/src/declaration.d.ts b/packages/forms/src/declaration.d.ts index d5cf927a7cd..6db039b78b8 100644 --- a/packages/forms/src/declaration.d.ts +++ b/packages/forms/src/declaration.d.ts @@ -1 +1,2 @@ declare module '*.scss'; +declare module '*.png'; diff --git a/packages/forms/stories/Playground.stories.tsx b/packages/forms/stories/Playground.stories.tsx deleted file mode 100644 index ab6bf8e713f..00000000000 --- a/packages/forms/stories/Playground.stories.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import isEqual from 'lodash/isEqual'; - -import { FormDefinition } from '../src/types'; -import Form from '../src/FormSwitcher'; - -const DEFAULT_DATA = { jsonSchema: {}, uiSchema: [], properties: {} }; - -const actions = [ - { - title: 'Reset', - type: 'reset', - widget: 'button', - }, - { - bsStyle: 'primary', - 'data-feature': 'form.feature', - title: 'Submit', - type: 'submit', - widget: 'button', - }, -]; - -function Playground({ data }: { data: FormDefinition }) { - if (!data || !data.jsonSchema || isEqual(data, DEFAULT_DATA)) { - return ( -
-
Test UIForm
-
-
- {''} -
- Paste JSON in CONTROLS tab below 👇 -
-
-
-
- ); - } - - return ( -
- ); -} - -const meta: Meta = { - title: 'Forms/Schema/Playground', - component: Playground, - parameters: { - centeredLayout: true, - }, -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - data: DEFAULT_DATA, - }, -}; diff --git a/packages/forms/stories/JSONConcepts.stories.tsx b/packages/forms/stories/SchemaCore.stories.tsx similarity index 94% rename from packages/forms/stories/JSONConcepts.stories.tsx rename to packages/forms/stories/SchemaCore.stories.tsx index 5c7bbac2747..01cd78708e1 100644 --- a/packages/forms/stories/JSONConcepts.stories.tsx +++ b/packages/forms/stories/SchemaCore.stories.tsx @@ -1,10 +1,12 @@ import type { Meta } from '@storybook/react'; import { UIForm } from '../src'; import { concepts } from './json'; +import { argTypes } from './argTypes'; export default { title: 'Forms/Schema/Core', component: UIForm, + argTypes, parameters: { formStoryDisplayMode: { category: 'concepts', @@ -17,6 +19,7 @@ export const ConditionalRender = { data: concepts.conditionalRender, }, }; + export const CustomValidation = { args: { data: concepts.customValidation, diff --git a/packages/forms/stories/JSONFields.stories.tsx b/packages/forms/stories/SchemaFields.stories.tsx similarity index 98% rename from packages/forms/stories/JSONFields.stories.tsx rename to packages/forms/stories/SchemaFields.stories.tsx index 092464e4ec5..14319d71e4c 100644 --- a/packages/forms/stories/JSONFields.stories.tsx +++ b/packages/forms/stories/SchemaFields.stories.tsx @@ -1,10 +1,12 @@ import type { Meta } from '@storybook/react'; import { UIForm } from '../src'; import { fields } from './json'; +import { argTypes } from './argTypes'; export default { title: 'Forms/Schema/Fields', component: UIForm, + argTypes, parameters: { formStoryDisplayMode: { category: 'fields', diff --git a/packages/forms/stories/JSONFieldsets.stories.tsx b/packages/forms/stories/SchemaFieldsets.stories.tsx similarity index 96% rename from packages/forms/stories/JSONFieldsets.stories.tsx rename to packages/forms/stories/SchemaFieldsets.stories.tsx index a95aec40358..0eb5518003f 100644 --- a/packages/forms/stories/JSONFieldsets.stories.tsx +++ b/packages/forms/stories/SchemaFieldsets.stories.tsx @@ -1,10 +1,12 @@ import type { Meta } from '@storybook/react'; import { UIForm } from '../src'; import { fieldsets } from './json'; +import { argTypes } from './argTypes'; export default { title: 'Forms/Schema/Fieldsets', component: UIForm, + argTypes, parameters: { formStoryDisplayMode: { category: 'fieldsets', diff --git a/packages/forms/stories/Layout.stories.tsx b/packages/forms/stories/SchemaLayout.stories.tsx similarity index 70% rename from packages/forms/stories/Layout.stories.tsx rename to packages/forms/stories/SchemaLayout.stories.tsx index a69f0b141b6..4e23a46b61e 100644 --- a/packages/forms/stories/Layout.stories.tsx +++ b/packages/forms/stories/SchemaLayout.stories.tsx @@ -2,12 +2,14 @@ import type { Meta, StoryObj } from '@storybook/react'; import { HeaderBar, Layout, Dialog, Drawer as DrawerComponent } from '@talend/react-components'; import Form from '../src'; +import { argTypes } from './argTypes'; const simple = require('./json/concepts/simple.json'); const meta: Meta = { title: 'Forms/Schema/Layout', component: Form, + argTypes, parameters: { centeredLayout: true, }, @@ -40,20 +42,26 @@ const LayoutDrawer = ({ title, stacked = false, ...props }: LayoutDrawerProps) = type Story = StoryObj; export const Default: Story = { - render: () => ( + render: props => (

Form by default take 100% width of the container

- +
), }; +Default.args = { + data: simple, +}; export const Drawer: Story = { args: { title: 'UIForm in a drawer', data: simple, }, - render: ({ title, data }) => , + render: ({ title, ...props }) => , +}; +Drawer.args = { + data: simple, }; export const DrawerStacked: Story = { @@ -62,8 +70,8 @@ export const DrawerStacked: Story = { data: simple, stacked: true, }, - render: ({ title, data, stacked }) => ( - + render: ({ title, stacked, ...props }) => ( + ), }; @@ -71,9 +79,9 @@ export const Modal: Story = { args: { data: simple, }, - render: ({ data }) => ( + render: props => ( - + ), }; @@ -81,6 +89,7 @@ export const Modal: Story = { export const Skeleton: Story = { args: { loading: true, + data: simple, // in case the user switch to loading: false }, }; @@ -88,6 +97,7 @@ export const NoButton: Story = { args: { loading: true, actions: [], + data: simple, // in case the user switch to loading: false }, }; @@ -95,17 +105,19 @@ export const SkeletonDrawer: Story = { args: { loading: true, title: 'Form in loading in drawer', + data: simple, // in case the user switch to loading: false }, - render: ({ loading, title }) => , + render: ({ title, ...props }) => , }; export const SkeletonDrawerStacked: Story = { args: { loading: true, + data: simple, // in case the user switch to loading: false title: 'Form in loading in drawer', stacked: true, }, - render: ({ loading, title, stacked }) => ( - + render: ({ title, stacked, ...props }) => ( + ), }; diff --git a/packages/forms/stories/State.stories.tsx b/packages/forms/stories/SchemaState.stories.tsx similarity index 84% rename from packages/forms/stories/State.stories.tsx rename to packages/forms/stories/SchemaState.stories.tsx index 57db84c9a4e..80845a5f1f6 100644 --- a/packages/forms/stories/State.stories.tsx +++ b/packages/forms/stories/SchemaState.stories.tsx @@ -12,10 +12,23 @@ import { customTemplateSchema } from './UIFormStoriesSchemas/customTemplate.sche import { customWidgetSchema } from './UIFormStoriesSchemas/customWidget.schema'; import { CustomArrayTemplate } from './CustomArrayTemplate.component'; +import { argTypes } from './argTypes'; const meta: Meta = { title: 'Forms/Schema/State', component: UIForm, + argTypes: { + ...argTypes, + updating: { + table: { disable: true }, + }, + onSubmit: { + table: { disable: true }, + }, + onReset: { + table: { disable: true }, + }, + }, args: { onChange: action('Change'), onSubmit: action('onSubmit'), @@ -34,30 +47,31 @@ type Story = StoryObj; const updatingProps = updatingSchema.uiSchema.map(w => w.key); export const Updating: Story = { args: { - schema: updatingSchema, + data: updatingSchema, updating: updatingProps, }, - render: ({ schema, onChange, onSubmit, updating }) => ( + render: ({ onChange, onSubmit, updating, ...props }) => (

Updating status

Form can disable and add an animation feedback on the widgets. To do so, you need to pass a UIForm "updating" prop which is an array of the schema keys where to apply

- +
), }; export const DisplayMode: Story = { args: { - schema: displayModeSchema, + data: displayModeSchema, + displayMode: 'text', }, - render: ({ schema, onChange, onSubmit }) => ( + render: ({ onChange, onSubmit, ...props }) => ( <>

Form can be used to display data in read only

- + ), }; @@ -72,22 +86,22 @@ const errorsProps = errorsSchema.uiSchema.reduce( export const Errors: Story = { args: { - schema: errorsSchema, + data: errorsSchema, errors: errorsProps, }, - render: ({ schema, onChange, onSubmit, errors }) => ( + render: ({ onChange, onSubmit, ...props }) => (

Updating status

Form can disable and add an animation feedback on the widgets. To do so, you need to pass a UIForm "updating" prop which is an array of the schema keys where to apply

- +
), }; -function UIFormWithOnSubmitHover() { +function UIFormWithOnSubmitHover(props: any) { const [hover, setHover] = useState(false); return (
{ action('onSubmitEnter')(...args); @@ -115,17 +129,20 @@ function UIFormWithOnSubmitHover() { } export const HoverSubmit: Story = { - render: () => ( + render: props => (

Hover submit handler

Submit can detect if mouse enters or leaves by using onSubmitEnter and{' '} onSubmitLeave

- +
), }; +HoverSubmit.args = { + data: hoverSubmitSchema, +}; export const CustomActions: Story = { args: { diff --git a/packages/forms/stories/argTypes.ts b/packages/forms/stories/argTypes.ts new file mode 100644 index 00000000000..8266e46691e --- /dev/null +++ b/packages/forms/stories/argTypes.ts @@ -0,0 +1,33 @@ +export const argTypes = { + data: { + control: 'object', + }, + displayMode: { + control: 'select', + options: ['text', 'form'], + }, + initialData: { + table: { disable: true }, + }, + actions: { + table: { disable: true }, + }, + customValidation: { + table: { disable: true }, + }, + onChange: { + table: { disable: true }, + }, + onErrors: { + table: { disable: true }, + }, + onTrigger: { + table: { disable: true }, + }, + templates: { + table: { disable: true }, + }, + widgets: { + table: { disable: true }, + }, +}; diff --git a/tools/scripts-config-storybook-lib/.storybook-templates/preview.js b/tools/scripts-config-storybook-lib/.storybook-templates/preview.js index 497d7dc9675..9f13edd4020 100644 --- a/tools/scripts-config-storybook-lib/.storybook-templates/preview.js +++ b/tools/scripts-config-storybook-lib/.storybook-templates/preview.js @@ -56,7 +56,7 @@ const defaultPreview = { toolbar: { icon: 'paintbrush', items: [ - { value: 'light', left: '☀️', title: 'Light mode' }, + { value: 'light', left: '⚪', title: 'Light mode' }, { value: 'dark', left: '🌑', title: 'Dark mode' }, ], dynamicTitle: true,