UNPKG

@wix/design-system

Version:

@wix/design-system

831 lines (790 loc) 29.5 kB
## 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> ); }; ``` ## 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> ); }; ```