@wix/design-system
Version:
@wix/design-system
1,049 lines (994 loc) • 39.4 kB
Markdown
## Feature Examples
### Structure
- description: <p>The custom modal layout can include the following:</p><li><code>title</code> (required)</li><li><code>subtitle</code></li><li><code>content</code> (required)</li><li><code>sideActions</code></li><li><code>footnote</code></li><p></p><p>To add supplementary actions, like a "Don't show this again" checkbox, use <code>sideActions</code>. </p><p></p><p>To include additional information, add a <code>footnote</code>, e.g., “By sending an invite, you agree to the Wix Terms of Use.”</p>
- example:
```jsx
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
subtitle="Subtitle"
footnote={
<StorybookComponents.Placeholder>Footnote</StorybookComponents.Placeholder>
}
sideActions={
<StorybookComponents.Placeholder>
Side actions
</StorybookComponents.Placeholder>
}
content={
<StorybookComponents.Placeholder height="240px">
Content
</StorybookComponents.Placeholder>
}
/>;
```
### Height and width
- description: <p>By default, modals grow with their content, but they'll never extend higher than the user’s viewport (with 48 px top and bottom padding).</p><p></p><p>Control a custom modal's height with these two properties:</p><li><code>height</code> fixes height in pixels.</li><li><code>maxHeight</code> sets a custom max height in pixels.</li><p></p><p>When modal content extends below the user's viewport, <code>height</code> or <code>maxHeight</code>, content scrolls vertically with a fixed header and footer.</p><p></p><p>Control a custom modal's width with <code>width</code> property.</p><p>Minimum width of a modal is 510px., maximum width is 1254px.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
secondaryButtonOnClick={() => {}}
primaryButtonOnClick={() => {}}
title="Auto height (default)"
content={<Box height="180px" />}
/>
<CustomModalLayout
height="360px"
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
secondaryButtonOnClick={() => {}}
primaryButtonOnClick={() => {}}
title="Height is fixed at 360 px"
content={<Box height="360px" />}
/>
</StorybookComponents.Stack>;
```
### Content padding
- description: <p>Remove the default 30 px left and right content padding with the <code>removeContentPadding</code> prop.</p><p></p><p>This property can be used to show components with built-in padding like <code><Page/></code> or <code><Table/></code>.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Default content padding"
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
removeContentPadding
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="No content padding"
content={<StorybookComponents.Placeholder height="60px" />}
/>
</StorybookComponents.Stack>;
```
### Theme
- description: <p>Control the component style with <code>theme</code> prop:</p><li><code>standard</code> is the default used in all common cases. </li><li><code>premium</code> is used to promote Premium features.</li><li><code>destructive</code> is for actions that have a destructive effect on user data, like delete. For short warning messages, use the <code><MessageModalLayout/></code> in a destructive theme instead. </li>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Standard (default)"
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
theme="premium"
primaryButtonText="Upgrade"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Premium"
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
theme="destructive"
primaryButtonText="Delete"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Destructive"
content={<StorybookComponents.Placeholder height="60px" />}
/>
</StorybookComponents.Stack>;
```
### Header and footer dividers
- description: <p>Control visibility of both the header and footer dividers with the <code>showHeaderDivider</code> and <code>showFooterDivider</code> props.</p><p></p><li><code>auto</code> is the default that shows header divider only when content is scrolled and always shows the footer divider.</li><li><code>true</code> always shows the dividers.</li><li><code>false</code> always hides the dividers.</li>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Auto (default)"
height="240px"
content={<StorybookComponents.Placeholder height="300px" />}
/>
<CustomModalLayout
showFooterDivider
showHeaderDivider
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Always show dividers"
height="240px"
content={<StorybookComponents.Placeholder height="300px" />}
/>
<CustomModalLayout
showFooterDivider="false"
showHeaderDivider="false"
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Hide dividers"
height="240px"
content={<StorybookComponents.Placeholder height="300px" />}
/>
</StorybookComponents.Stack>;
```
### Help and close buttons
- description: <p>Add a help button with the <code>onHelpButtonClick</code> prop. Use it to direct users to support sources, for example, the Wix Help Center.</p><p></p><p>The close button is required for all modals.</p>
- example:
```jsx
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
onHelpButtonClick={() => {}}
title="Title"
content={<StorybookComponents.Placeholder height="60px" />}
/>;
```
### Side actions
- description: <p>Add supplementary actions in the <code>sideActions</code> container.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
sideActions={
<StorybookComponents.Placeholder>
Side actions
</StorybookComponents.Placeholder>
}
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
sideActions={<Checkbox>Don’t show this again</Checkbox>}
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
onCloseButtonClick={() => {}}
title="Title"
sideActions={
<StorybookComponents.Stack justifyContent="space-between">
<Button priority="secondary" size="small">
Cancel
</Button>
<StorybookComponents.Stack gap="8px" justifyContent="flex-end">
<Button
priority="secondary"
size="small"
prefixIcon={<Icons.DismissSmall />}
>
Reject
</Button>
<Button
priority="secondary"
size="small"
prefixIcon={<Icons.TimeSmall />}
>
Keep for later
</Button>
<Button
priority="secondary"
size="small"
prefixIcon={<Icons.ConfirmSmall />}
>
Approve
</Button>
</StorybookComponents.Stack>
</StorybookComponents.Stack>
}
content={<StorybookComponents.Placeholder height="60px" />}
/>
</StorybookComponents.Stack>;
```
### Footnote
- description: <p>Add supplementary text and links into the <code>footnote</code> container.</p><p></p><p>Footnote also has a <code>footnoteSkin</code> property, that provides a <code>light</code> skin, that can be used fore busier modal layouts.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
footnote={
<StorybookComponents.Placeholder>
Footnote
</StorybookComponents.Placeholder>
}
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
footnote={
<Text size="small">
By continuing, you agree to the <a>Wix Terms of Use</a>
</Text>
}
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
footnoteSkin="light"
footnote={
<Text size="small">
By continuing, you agree to the <a>Wix Terms of Use</a>
</Text>
}
content={<StorybookComponents.Placeholder height="60px" />}
/>
</StorybookComponents.Stack>;
```
### Title and subtitle
- description: <p>Add short text to summarize modal content with the <code>title</code> and <code>subtitle</code> props.</p><p></p><p>Both <code>title</code> and <code>subtitle</code> are nodes, so they can contain other components. For example, add inline <code><TextButton/></code> linking to relevant information in a subtitle.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title (required)"
subtitle="Subtitle (optional)"
content={<StorybookComponents.Placeholder height="60px" />}
/>
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
subtitle={
<StorybookComponents.Stack gap="6px">
<Text secondary>Subtitle</Text>
<TextButton underline="always">Inline text button</TextButton>
</StorybookComponents.Stack>
}
content={<StorybookComponents.Placeholder height="60px" />}
/>
</StorybookComponents.Stack>;
```
### Action buttons
- description: <p>Specify labels for primary and secondary actions with <code>primaryButtonText</code> and <code>secondaryButtonText</code> props.</p><p></p><p>For additional styling, like adding a prefix icon, use any available <code><Button/></code> <a href="https://www.wix-style-react.com/storybook/?path=/story/components-actions--button"></a>props via the <code>primaryButtonProps</code> and <code>secondaryButtonProps</code>.</p>
- example:
```jsx
<CustomModalLayout
primaryButtonText="Approve"
primaryButtonProps={{ prefixIcon: <Icons.ConfirmSmall /> }}
secondaryButtonText="Reject"
secondaryButtonProps={{ prefixIcon: <Icons.DismissSmall /> }}
onCloseButtonClick={() => {}}
title="Title"
content={<StorybookComponents.Placeholder height="60px" />}
/>;
```
### Disabled primary action
- description: <p>Disable a primary action using the <code>disabled</code> prop in <code>primaryButtonProps</code>.</p><p></p><p>Add an explanation in a tooltip about why this action is not allowed with <code>primaryButtonTooltipProps</code>.</p><p></p><p>Use any of the <code><Tooltip/></code> props to style the tooltip.</p>
- example:
```jsx
<CustomModalLayout
primaryButtonText="Save"
primaryButtonProps={{ disabled: true }}
primaryButtonTooltipProps={{ content: 'Tells users why they cannot Save' }}
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
content={<StorybookComponents.Placeholder height="60px" />}
/>;
```
## Developer Examples
### Vertical overflow
- description: <p>Modal headers and footers can cut content outlines when placed next to the top or bottom content areas.</p><p></p><p>Set vertical <code>overflowY</code> prop to <code>none</code> to show the full outline.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
content={<Input placeholder="Click here to see the default outlines" />}
/>
<CustomModalLayout
primaryButtonText="Save"
secondaryButtonText="Cancel"
onCloseButtonClick={() => {}}
title="Title"
overflowY="none"
content={
<Input placeholder="Click here to see the outlines with overflowY prop" />
}
/>
</StorybookComponents.Stack>;
```
### Modal with overlays
- description: <p>By default, modals do not allow content to overflow.</p><p></p><p>Use <code>popoverProps</code>, <code>appendTo</code> and <code>dynamicWidth</code> props to display overlays as intended. For more guidance, read about the <code><Popover/></code> component.</p>
- example:
```jsx
() => {
const [shown, setShown] = React.useState(false);
return (
<Card>
<Card.Header
title="Allow using coupons"
subtitle="Let visitors use coupons to get discounts & special deals."
suffix={
<Button
size="small"
priority="secondary"
onClick={() => setShown(true)}
>
Apply Coupon
</Button>
}
/>
<Modal
isOpen={shown}
onRequestClose={() => setShown(false)}
shouldCloseOnOverlayClick
screen="desktop"
>
<CustomModalLayout
primaryButtonText="Apply"
primaryButtonOnClick={() => setShown(false)}
secondaryButtonText="Cancel"
secondaryButtonOnClick={() => setShown(false)}
onCloseButtonClick={() => setShown(false)}
title="Apply a coupon"
overflowY="none"
content={
<FormField label="Select coupon">
<Dropdown
popoverProps={{
appendTo: 'scrollParent',
dynamicWidth: 'true',
}}
options={[
{ id: 0, value: 'None' },
{ id: 1, value: 'SUMMER15' },
{ id: 2, value: 'BACKTOSCHOOL' },
{ id: 3, value: 'AUGUST10' },
{ id: 4, value: 'OUTWITHTHEOLD' },
]}
placeholder="Select from the list"
/>
</FormField>
}
/>
</Modal>
</Card>
);
};
```
### Mobile
- description: <p>On mobile <code>CustomModalLayout</code> maps to <code>FullScreenModalLayout</code> for cases where complex or multi-step flows are included</p><p></p><p>If the layout is simple, short, up to one step - use <code>Drawer</code></p><p></p><p><em>To see the change, toggle 'Enable mobile support' in the top bar of Storybook.</em></p>
- example:
```jsx
() => {
const [complexOpen, setComplexOpen] = React.useState(false);
const [simpleOpen, setSimpleOpen] = React.useState(false);
const [step, setStep] = React.useState(1);
const { mobile } = React.useContext(WixDesignSystemContext);
// Complex form state
const [projectName, setProjectName] = React.useState('');
const [category, setCategory] = React.useState(null);
const [template, setTemplate] = React.useState('starter');
const [visibility, setVisibility] = React.useState('public');
const [features, setFeatures] = React.useState({ comments: false, seo: true, analytics: false });
const [notifications, setNotifications] = React.useState(true);
const [agreed, setAgreed] = React.useState(false);
// Simple form state
const [tagName, setTagName] = React.useState('');
const [tagColor, setTagColor] = React.useState('blue');
const [pinned, setPinned] = React.useState(false);
const categoryOptions = [
{ id: 'blog', value: 'blog', label: 'Blog' },
{ id: 'portfolio', value: 'portfolio', label: 'Portfolio' },
{ id: 'ecommerce', value: 'ecommerce', label: 'E-Commerce' },
{ id: 'docs', value: 'docs', label: 'Documentation' },
];
const toggleFeature = key => e =>
setFeatures(f => ({ ...f, [key]: e.target.checked }));
const stepTitles = { 1: 'Project Setup', 2: 'Configuration', 3: 'Review & Confirm' };
const SummaryRow = ({ label, value }) => (
<Box align="space-between" verticalAlign="middle" gap="SP4">
<Text size="small" style={{ flexShrink: 0 }}>{label}</Text>
<Text size="small" align="right">{value}</Text>
</Box>
);
const stepContent = {
1: (
<Box direction="vertical" gap="SP4">
<FormField label="Project name" required>
<Input placeholder="My awesome project" value={projectName} onChange={e => setProjectName(e.target.value)} />
</FormField>
<FormField label="Category">
<Dropdown placeholder="Select a category" options={categoryOptions} selectedId={category} onSelect={opt => setCategory(opt.id)} />
</FormField>
<FormField label="Start from">
<RadioGroup value={template} onChange={setTemplate}>
<RadioGroup.Radio value="blank">Blank — start from scratch</RadioGroup.Radio>
<RadioGroup.Radio value="starter">Starter template — basic structure included</RadioGroup.Radio>
<RadioGroup.Radio value="advanced">Advanced template — full setup with sample content</RadioGroup.Radio>
</RadioGroup>
</FormField>
</Box>
),
2: (
<Box direction="vertical" gap="SP4" width="100%">
<FormField label="Visibility">
<RadioGroup value={visibility} onChange={setVisibility}>
<RadioGroup.Radio value="public">Public — anyone can view</RadioGroup.Radio>
<RadioGroup.Radio value="private">Private — only you</RadioGroup.Radio>
<RadioGroup.Radio value="team">Team — invite only</RadioGroup.Radio>
</RadioGroup>
</FormField>
<Divider />
<FormField label="Enable features">
<Box direction="vertical" gap="SP2">
<Checkbox checked={features.comments} onChange={toggleFeature('comments')}>Comments</Checkbox>
<Checkbox checked={features.seo} onChange={toggleFeature('seo')}>SEO optimization</Checkbox>
<Checkbox checked={features.analytics} onChange={toggleFeature('analytics')}>Analytics dashboard</Checkbox>
</Box>
</FormField>
<Divider />
<Box align="space-between" verticalAlign="middle">
<Box direction="vertical">
<Text weight="normal">Email notifications</Text>
<Text size="tiny" skin="secondary">Get updates on project activity</Text>
</Box>
<ToggleSwitch checked={notifications} onChange={e => setNotifications(e.target.checked)} />
</Box>
</Box>
),
3: (
<Box direction="vertical" width="100%" gap="SP4">
<Box width="100%" direction="vertical" gap="SP3">
<SummaryRow label="Name" value={projectName || '—'} />
<SummaryRow label="Category" value={categoryOptions.find(o => o.id === category)?.label || '—'} />
<SummaryRow label="Template" value={template} />
<Divider />
<SummaryRow label="Visibility" value={visibility} />
<SummaryRow
label="Features"
value={Object.entries(features).filter(([, v]) => v).map(([k]) => k).join(', ') || 'None'}
/>
<SummaryRow label="Notifications" value={notifications ? 'On' : 'Off'} />
</Box>
<Checkbox checked={agreed} onChange={e => setAgreed(e.target.checked)}>
I agree to the terms and conditions
</Checkbox>
</Box>
),
};
const openComplex = () => { setStep(1); setComplexOpen(true); };
const closeComplex = () => setComplexOpen(false);
const next = () => step < 3 ? setStep(step + 1) : closeComplex();
const back = () => setStep(step - 1);
const isNextDisabled = step === 3 && !agreed;
const closeSimple = () => setSimpleOpen(false);
const simpleContent = (
<Box direction="vertical" gap="SP4">
<FormField label="Tag name" required>
<Input
placeholder="e.g. Urgent"
value={tagName}
onChange={e => setTagName(e.target.value)}
/>
</FormField>
<FormField label="Color">
<RadioGroup value={tagColor} onChange={setTagColor}>
<RadioGroup.Radio value="blue">Blue</RadioGroup.Radio>
<RadioGroup.Radio value="green">Green</RadioGroup.Radio>
<RadioGroup.Radio value="red">Red</RadioGroup.Radio>
</RadioGroup>
</FormField>
<Checkbox checked={pinned} onChange={e => setPinned(e.target.checked)}>
Pin to top
</Checkbox>
</Box>
);
return (
<Box padding="20px" gap="SP3">
<Button onClick={openComplex}>Complex form</Button>
<Button priority="secondary" onClick={() => setSimpleOpen(true)}>Simple form</Button>
{/* Complex form — FullScreenModalLayout on mobile, CustomModalLayout on desktop */}
{mobile ? (
<Modal isOpen={complexOpen} onRequestClose={closeComplex} screen="mobile">
<FullScreenModalLayout>
<FullScreenModalLayout.Header startNode={<CloseButton skin="dark" size="large" onClick={closeComplex} />}>
<Text>Step {step} of 3</Text>
</FullScreenModalLayout.Header>
<FullScreenModalLayout.Content padding>
<Box direction="vertical" gap="SP4">
<Heading size="small">{stepTitles[step]}</Heading>
{stepContent[step]}
</Box>
</FullScreenModalLayout.Content>
<FullScreenModalLayout.Footer>
<Box direction="horizontal" width="100%" gap="SP2" padding="SP4">
{step > 1 ? (
<Button priority="secondary" onClick={back} fullWidth>Back</Button>
) : (
<Button priority="secondary" onClick={closeComplex} fullWidth>Cancel</Button>
)}
<Button onClick={next} fullWidth>
{step < 3 ? 'Next' : 'Finish'}
</Button>
</Box>
</FullScreenModalLayout.Footer>
</FullScreenModalLayout>
</Modal>
) : (
<Modal isOpen={complexOpen} onRequestClose={closeComplex}>
<CustomModalLayout
width="600px"
title={stepTitles[step]}
subtitle={`Step ${step} of 3`}
content={<Box paddingBottom="12px">{stepContent[step]}</Box>}
primaryButtonText={step < 3 ? 'Next' : 'Finish'}
primaryButtonOnClick={next}
primaryButtonDisabled={isNextDisabled}
secondaryButtonText={step > 1 ? 'Back' : undefined}
secondaryButtonOnClick={step > 1 ? back : undefined}
onCloseButtonClick={closeComplex}
/>
</Modal>
)}
{/* Simple form — Drawer on mobile, CustomModalLayout on desktop */}
{mobile ? (
<Drawer open={simpleOpen} onClose={closeSimple}>
<Box direction="vertical" gap="SP4" padding="SP5">
<Heading size="small">Create tag</Heading>
{simpleContent}
<Box gap="SP2">
<Button priority="secondary" onClick={closeSimple} fullWidth>Cancel</Button>
<Button onClick={closeSimple} fullWidth>Create</Button>
</Box>
</Box>
</Drawer>
) : (
<Modal isOpen={simpleOpen} onRequestClose={closeSimple}>
<CustomModalLayout
title="Create tag"
content={<Box paddingBottom="12px">{simpleContent}</Box>}
primaryButtonText="Create"
primaryButtonOnClick={closeSimple}
secondaryButtonText="Cancel"
secondaryButtonOnClick={closeSimple}
onCloseButtonClick={closeSimple}
/>
</Modal>
)}
</Box>
);
};
```
## Common Use Case Examples
### Modal with no content padding
- description: <p>Remove default content padding when displaying a <code><Table/></code>, <code><Page/></code> or other content with built-in or custom padding.</p>
- example:
```jsx
() => {
const [shown, setShown] = React.useState(false);
const [copy, setCopy] = React.useState(false);
const data = [
{ name: 'User 1', plan: 'Gold', email: 'user1@mail.com' },
{ name: 'User 2', plan: 'Silver', email: 'user2@mail.com' },
{ name: 'User 3', plan: 'Gold', email: 'user3@mail.com' },
{ name: 'User 4', plan: 'Gold', email: 'user4@mail.com' },
{ name: 'User 5', plan: 'Bronze', email: 'user5@mail.com' },
{ name: 'User 6', plan: 'Bronze', email: 'user6@mail.com' },
{ name: 'User 7', plan: 'Gold', email: 'user7@mail.com' },
{ name: 'User 8', plan: 'Bronze', email: 'user8@mail.com' },
];
const columns = [
{ title: 'Name', render: row => row.name },
{ title: 'Plan', render: row => row.plan },
{ title: 'Email', render: row => row.email },
];
return (
<Card>
<Card.Header
title="Send coupons to your members"
subtitle="Select members who will receive a new coupon via email."
suffix={
<Button
size="small"
priority="secondary"
onClick={() => setShown(true)}
>
Send Coupons
</Button>
}
/>
<Modal
isOpen={shown}
onRequestClose={() => setShown(false)}
shouldCloseOnOverlayClick
screen="desktop"
>
<CustomModalLayout
title="Send coupons to members"
subtitle="Select members who will receive coupons via email."
onCloseButtonClick={() => setShown(false)}
primaryButtonOnClick={() => setShown(false)}
secondaryButtonOnClick={() => setShown(false)}
width="720px"
maxHeight="480px"
removeContentPadding
primaryButtonText="Send"
secondaryButtonText="Cancel"
sideActions={
<Checkbox
size="small"
checked={copy}
onChange={() => setCopy(!copy)}
>
Send copy Email to myself
</Checkbox>
}
content={
<Table data={data} columns={columns} showSelection>
<Table.Content />
</Table>
}
/>
</Modal>
</Card>
);
};
```
### Modal with multiple actions
- description: <p>If a modal has more than two actions, use <code>sideActions</code> container to group them.</p><p></p><p>Cancel button is required and should always be positioned on the left side of the action list.</p>
- example:
```jsx
() => {
const [shown, setShown] = React.useState(false);
return (
<Layout>
<Cell span={6}>
<Card>
<Box
direction="vertical"
gap="1"
paddingTop="2"
paddingLeft="4"
paddingRight="4"
paddingBottom="2"
>
<Text secondary>From name:</Text>
<Text weight="bold">User 1</Text>
<Text secondary>Reply-to email:</Text>
<Text weight="bold">User1@mail.com</Text>
</Box>
<Divider />
<Box
paddingTop="2"
paddingLeft="4"
paddingRight="4"
paddingBottom="2"
align="center"
>
<TextButton onClick={() => setShown(true)}>
Confirm Email
</TextButton>
</Box>
<Modal
isOpen={shown}
onRequestClose={() => setShown(false)}
shouldCloseOnOverlayClick
screen="desktop"
>
<CustomModalLayout
primaryButtonText="Confirm"
secondaryButtonText="Change Details"
onCloseButtonClick={() => setShown(false)}
secondaryButtonOnClick={() => setShown(false)}
primaryButtonOnClick={() => setShown(false)}
overflowY="none"
width="600px"
title="Confirm your email address"
content={
<Layout>
<Cell>
<Text>
To make changes to your sender details, please confirm
your email address.
<br />A confirmation code was sent to <b>user1@mail.com</b>
.
</Text>
</Cell>
<Cell span={6}>
<FormField label="Confirmation Code">
<Input placeholder="XX-XXX-XX" />
</FormField>
</Cell>
<Cell>
<Text>
Didn't get the code? <a>Send again</a>
</Text>
</Cell>
</Layout>
}
sideActions={
<Button
onClick={() => setShown(false)}
priority="secondary"
size="small"
>
Cancel
</Button>
}
/>
</Modal>
</Card>
</Cell>
</Layout>
);
};
```
### Modal with a page inside
- description: <p>When a <code><Page/></code> can be accessed from multiple entry points, show it in a modal to keep users in the main flow.</p><p></p><p>Make the modal as large as possible and structure content within cards. If other modals can be triggered, make them significantly smaller to communicate their hierarchy.</p>
- example:
```jsx
() => {
const [shown, setShown] = React.useState(false);
return (
<Card>
<Card.Header
title="Pricing plans"
subtitle="Let clients book this service with a plan."
suffix={
<Button
size="small"
priority="secondary"
prefixIcon={<Icons.AddSmall />}
onClick={() => setShown(true)}
>
Create New Plan
</Button>
}
/>
<Modal
isOpen={shown}
onRequestClose={() => setShown(false)}
shouldCloseOnOverlayClick
screen="desktop"
>
<CustomModalLayout
primaryButtonText="Create Plan"
secondaryButtonText="Cancel"
onCloseButtonClick={() => setShown(false)}
onHelpButtonClick={() => {}}
secondaryButtonOnClick={() => setShown(false)}
primaryButtonOnClick={() => setShown(false)}
removeContentPadding
title="Create new pricing plan"
content={
<Page>
<Page.Content>
<Box marginTop={5} display="block">
<Layout>
<Cell span="12">
<Card>
<Card.Header
title="Plan info"
subtitle="Give your plan a name and tell customers what it includes"
/>
<Card.Divider />
<Card.Content>
<Layout>
<Cell span="6">
<FormField
label="Name this plan"
required
charCount="20"
>
<Input placeholder="e.g., Silver Membership" />
</FormField>
</Cell>
<Cell span="6">
<FormField label="Add a tagline" charCount="60">
<Input placeholder="e.g., Perfect for beginners" />
</FormField>
</Cell>
<Cell span="12">
<AddItem size="tiny">
Add what this plan includes
</AddItem>
</Cell>
</Layout>
</Card.Content>
</Card>
</Cell>
<Cell span="12">
<Card>
<Card.Header
title="Connect and manage benefits"
subtitle="Set up how your benefits work in this plan."
/>
<Card.Divider />
<Box
display="block"
borderRadius={8}
paddingBottom="8px"
>
<SelectableAccordion
type="checkbox"
items={[
{
title: 'Bookings Services',
subtitle:
'Offer services as a membership or package with this plan.',
content: (
<StorybookComponents.Placeholder>
Booking Services content
</StorybookComponents.Placeholder>
),
},
{
title: 'Video Channels',
subtitle:
'Offer access to your Video Channel with this plan.',
content: (
<StorybookComponents.Placeholder>
Video Channels content
</StorybookComponents.Placeholder>
),
},
{
title: 'Blog Subscriptions',
subtitle: 'Give exclusive access to posts.',
content: (
<StorybookComponents.Placeholder>
Blog Subscriptions content
</StorybookComponents.Placeholder>
),
},
{
title: 'Forum Subscriptions',
subtitle:
'Give exclusive access to forum categories.',
content: (
<StorybookComponents.Placeholder>
Forum Subscriptions content
</StorybookComponents.Placeholder>
),
},
{
title: 'Events Memberships',
subtitle:
'Add ticket discount and ticket limit for your events.',
content: (
<StorybookComponents.Placeholder>
Events Memberships content
</StorybookComponents.Placeholder>
),
},
{
title: 'Online Programs',
subtitle:
'Allow participation in different programs with this plan.',
content: (
<StorybookComponents.Placeholder>
Online Programs content
</StorybookComponents.Placeholder>
),
},
{
title: 'Groups',
subtitle:
'Give clients access to exclusive groups.',
content: (
<StorybookComponents.Placeholder>
Groups content
</StorybookComponents.Placeholder>
),
},
]}
/>
</Box>
</Card>
</Cell>
</Layout>
</Box>
</Page.Content>
</Page>
}
/>
</Modal>
</Card>
);
};
```
### Modal with marketing layout
- description: <p>Focus users’ attention on feature promotions with modals. Use them sparingly and only after users intentionally open the promotional content. </p><p></p><p>For example, use a custom modal layout with a <code><MarketingLayout/></code> inside.</p>
- example:
```jsx
() => {
const [shown, setShown] = React.useState(false);
return (
<Card>
<Card.Header
title="Add Logo to your website"
subtitle="Customize your website to match the colors and style of your logo."
suffix={
<Button
size="small"
priority="secondary"
onClick={() => setShown(true)}
>
Connect Website
</Button>
}
/>
<Modal
isOpen={shown}
onRequestClose={() => setShown(false)}
shouldCloseOnOverlayClick
screen="desktop"
>
<CustomModalLayout
width="720px"
removeContentPadding
onCloseButtonClick={() => setShown(false)}
content={
<Card>
<MarketingLayout
size="medium"
title="Wix unlimited website Premium plan"
description="Get a customizable website, designed to match the colors and style of your logo."
actions={
<Button onClick={() => setShown(false)}>Add to Cart</Button>
}
image={
<Box height="156px">
<Image src="MarketingIllustration1.png" transparent />
</Box>
}
/>
</Card>
}
/>
</Modal>
</Card>
);
};
```