Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const stories = [
// both slow and redundant with individual component stories.
NODE_ENV === 'test' ? '' : './stories/playground/**/*.story.@(jsx|tsx)',
NODE_ENV === 'test' ? '' : './stories/**/*.mdx',
'./stories/design-system/**/*.story.@(ts|tsx)',
'../packages/block-editor/src/**/stories/*.story.@(js|jsx|tsx|mdx)',
'../packages/components/src/**/stories/*.story.@(jsx|tsx)',
'../packages/components/src/**/stories/*.mdx',
Expand Down
5 changes: 5 additions & 0 deletions storybook/package-styles/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ const CONFIG = [
ltr: [ designTokens ],
rtl: [ designTokens ],
},
{
componentIdMatcher: /^design-system-patterns-/,
ltr: [ componentsLtr, dataviewsLtr ],
rtl: [ componentsRtl, dataviewsRtl ],
},
];

export default CONFIG;
39 changes: 39 additions & 0 deletions storybook/stories/design-system/patterns/save-and-submit.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Canvas, Meta } from '@storybook/addon-docs/blocks';
import * as SaveSubmitStories from './save-and-submit.story';

<Meta of={ SaveSubmitStories } />

# Save & Submit

Guidelines for form submission and saving settings.

## Feedback

The following feedback patterns help users interpret save and submit results.

### Saving

- While saving the button state switches to a busy or loading state.
- A [Snackbar](https://wordpress.github.io/gutenberg/?path=/docs/components-snackbar--docs) confirms a successful save.

<Canvas of={ SaveSubmitStories.SaveFormLayoutCard } />

### No unsaved changes

When all changes are saved the Save button is disabled and reveals a “No unsaved changes” tooltip on hover or focus.

### Invalid fields

If the save or submit fails due to user error (an invalid field or a required field is omitted) then the page scrolls with animation to the first invalid field, which shows a validation error message.

### Failed save

If the save process fails for any reason the user is notified via `ConfirmDialog`.

### Auto-save

- Auto-saving is restricted to `ToggleControl` fields.
- Follows the same feedback conventions as other save methods.
- It is preferable to use a toggle instead of a checkbox for turning features on and off. The exception would be if the checkbox is part of a form with other fields that also need to be updated at the same time.
- Keep labels short. Use the description under the toggle or in the card title to provide more context.
- Lead labels with a verb like “Use, Allow, Show,” or “Enable”.
69 changes: 69 additions & 0 deletions storybook/stories/design-system/patterns/save-and-submit.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Button, Card, CardBody } from '@wordpress/components';
import { DataForm } from '@wordpress/dataviews';
import { useState } from '@wordpress/element';

const LayoutCardSaveExample = () => {
const [ data, setData ] = useState( { title: '' } );

return (
<div style={ { maxWidth: '480px' } }>
<Card
size={ {
blockStart: 'medium',
blockEnd: 'medium',
inlineStart: 'medium',
inlineEnd: 'medium',
} }
>
<CardBody>
<DataForm
data={ data }
fields={ [
{
id: 'title',
label: 'Title',
type: 'text',
},
] }
form={ {
layout: { type: 'regular' },
fields: [ 'title' ],
} }
onChange={ ( edits ) =>
setData( ( previous ) => ( {
...previous,
...edits,
} ) )
}
/>
<Button
variant="primary"
isBusy
style={ { marginTop: '16px' } }
>
Save
</Button>
</CardBody>
</Card>
</div>
);
};

const meta: Meta< typeof LayoutCardSaveExample > = {
title: 'Design System/Patterns/Save & Submit',
component: LayoutCardSaveExample,
parameters: {
controls: { disable: true },
},
tags: [ '!dev' /* Hide individual story pages from sidebar */ ],
};

export default meta;

type Story = StoryObj< typeof LayoutCardSaveExample >;

export const SaveFormLayoutCard: Story = {
name: 'Save form layout card',
render: () => <LayoutCardSaveExample />,
};
Loading