Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Modal): animation update, new variant, close button always visible #1224

Merged
merged 6 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
143 changes: 127 additions & 16 deletions packages/react-components/src/components/Modal/Modal.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArgTypes, Meta, Title } from '@storybook/blocks';
import { ArgTypes, Meta, Title, Canvas } from '@storybook/blocks';

import * as ModalStories from './Modal.stories';

Expand All @@ -8,24 +8,31 @@ import * as ModalStories from './Modal.stories';

[Component API](#ComponentAPI) | [Content Spec](#ContentSpec)

## How to use modals

### Default Modal

Default Modal window is used to gather user input without navigating away from the current page.

Use it for Data Entry:
- <b>Edit</b> details or preferences.
- <b>Invite</b> agents
- <b>Gather</b> necessary information for specific tasks

Modal includes:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Modal includes:
Modal should include:

- Form fields
- Labels and placeholders for guidance
- Error messages and validation
- Submit (Primary button kind) and cancel buttons (Secondary, Plain buttons kinds)

<Canvas of={ModalStories.Modal} sourceState="none" />

#### Example implementation

```jsx
import { GreetingQuickReply } from '@livechat/design-system-icons';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the import statements


<Modal
heading={
<ModalHeader
title="Modal Header"
iconProps={{
source: GreetingQuickReply,
kind: 'primary',
size: 'large',
}}
>
Modal description
</ModalHeader>
}
heading='Modal header'
footer={
<div>
<Button size="medium" kind="secondary">
Expand All @@ -39,7 +46,111 @@ import { GreetingQuickReply } from '@livechat/design-system-icons';
onClose={() => {}}
>
Modal content
</Modal>;
</Modal>
```

### Action Modal - Info <a id="ActionModalInfo" />

Info modal used to provide important information without requiring immediate action.

Use it for:
- <b>General notifications</b> - Inform users about updates, news, or general information that doesn’t need immediate action.
- <b>Instructions or tips</b> - Provide guidance or tips related to the current task or feature.
- <b>Announcements</b> - Share announcements, such as new features, upcoming maintenance, or scheduled downtime.
- <b>Confirmation of actions</b> - Confirm that an action has been successfully completed (e.g., "Your changes have been saved").
- <b>Educational content</b> - Offer tutorials, walkthroughs, or additional information about how to use a feature.

Info modal includes:
- An icon to indicate the type of information (e.g., info icon).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Info modal includes:
Info modal should include:

- Action Title
- Action Description
- An acknowledgment primary button (e.g., "OK", "Got it").
- Optional secondary button if further action might be needed (e.g., "Learn More").
- Close icon

<Canvas of={ModalStories.ActionModal} sourceState="none" />

#### Example implementation

```jsx
<Modal {...props}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the import statements

<ActionModalContent
icon={
<Icon
source={Error}
size="xxxlarge"
customColor="var(--content-basic-disabled)"
/>
}
heading="Action Modal Header"
actions={
<>
<Button size="large">Button</Button>
<Button size="large" kind="primary">
Button
</Button>
</>
}
>
Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet sint.
Velit officia consequat duis enim velit mollit. Exercitation veniam
consequat sunt nostrud amet.
</ActionModalContent>
</Modal>
```

### Action Modal - Confirmation

A Confirmation Modal is used to verify a user's intent before proceeding with important actions.

Use it in these scenarios:
- <b>Deleting Data</b>: Prevent accidental data loss.
- <b>Submitting a Form</b>: Confirm critical information submission.
- <b>Exiting or Logging Out</b>: Ensure unsaved changes are saved or confirm exit.
- <b>Executing a Command</b>: Double-check significant commands (e.g., start/stop services).
- <b>Irreversible Actions</b>: Confirm actions that cannot be undone.

Confirmation modal includes:
- Attention icon. (error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Confirmation modal includes:
Confirmation modal should include:

- Action Title
- Action Description
- Primary (kind=Destructive) and secondary buttons (optional).
- Close icon

The implementation is the same as [Action Modal - Info](#ActionModalInfo)

### Full space content “Promo modal”

A Promotional Modal is used to introduce new features or updates to users in an engaging way.

Use it for:
- <b>New Feature Announcements</b>: Highlight and explain new features or tools.
- <b>Product Updates</b>: Inform users about major updates or improvements.
- <b>Special Offers</b>: Promote limited-time deals, discounts, or promotions.
- <b>User Onboarding</b>: Guide new users through key features and benefits.
- <b>Engagement Boost</b>: Encourage users to try out new or underused features.

<Canvas of={ModalStories.ModalWithFullSpaceContent} sourceState="none" />

```jsx
<Modal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the import statements

labelHeading={
<ModalHeader
title="Modal Header"
avatarProps={{
type: 'image',
size: 'small',
src: 'https://cdn-labs.livechat-files.com/api/file/lc/img/100019504/df59da4b5b0cdb6030efb08787fd255d.jpg',
}}
>
{' '}
Modal description{' '}
</ModalHeader>
}
fullSpaceContent
>
<YourCustomStyledContent />
</Modal>
```

## Component API <a id="ComponentAPI" />
Expand Down
25 changes: 9 additions & 16 deletions packages/react-components/src/components/Modal/Modal.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
padding: var(--spacing-6) var(--spacing-7);
max-width: 100%;
height: fit-content;
animation: var(--transition-duration-moderate-1) fade-in-step2
var(--transition-duration-moderate-1) forwards;
animation: var(--transition-duration-fast-2) fade-in
var(--transition-duration-fast-2) forwards;
color: var(--content-basic-primary);

&--full-space {
Expand All @@ -27,7 +27,7 @@

&--visible {
display: flex;
animation: fade-in-step1 var(--transition-duration-moderate-1) forwards;
animation: fade-in var(--transition-duration-fast-2) forwards;

@media (prefers-reduced-motion) {
animation: none;
Expand Down Expand Up @@ -100,6 +100,11 @@
padding-bottom: var(--spacing-5);
width: 100%;
color: var(--content-basic-primary);

&--without-heading {
flex-direction: row-reverse;
padding-bottom: 0;
}
}

&__label-header {
Expand Down Expand Up @@ -134,24 +139,12 @@
}
}

@keyframes fade-in-step1 {
0% {
opacity: var(--transition-initial-fade-in-opacity);
}

100% {
opacity: 1;
}
}

@keyframes fade-in-step2 {
@keyframes fade-in {
0% {
opacity: var(--transition-initial-fade-in-opacity);
scale: var(--transition-initial-fade-in-scale);
}

100% {
opacity: 1;
scale: 1;
}
}
48 changes: 44 additions & 4 deletions packages/react-components/src/components/Modal/Modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from 'react';

import { GreetingQuickReply } from '@livechat/design-system-icons';
import { GreetingQuickReply, Error } from '@livechat/design-system-icons';
import { Meta, StoryFn } from '@storybook/react';

import noop from '../../utils/noop';
import { Button } from '../Button';
import { Icon } from '../Icon';

import { ActionModalContent } from './components/ActionModalContent';
import {
ModalContent,
ModalFullSpaceContent,
Expand All @@ -24,7 +26,7 @@ import {
export default {
title: 'Components/Modal',
component: ModalComponent,
subcomponents: { ModalHeader },
subcomponents: { ModalHeader, ActionModalContent, ModalBase },
parameters: {
viewMode: 'story',
previewTabs: {
Expand All @@ -51,7 +53,15 @@ const StoryTemplate: StoryFn<ModalProps> = ({
const [isOpen, setIsOpen] = React.useState(true);

return (
<>
<div
style={{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move it to const, outside the component's body. Every inline object or a functions are recreated on each render and we do not want that :)

width: '80vh',
height: '50vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Button onClick={() => setIsOpen(true)}>Open modal</Button>
{isOpen && (
<ModalComponent
Expand All @@ -62,7 +72,7 @@ const StoryTemplate: StoryFn<ModalProps> = ({
{children}
</ModalComponent>
)}
</>
</div>
);
};

Expand Down Expand Up @@ -130,6 +140,36 @@ ModalWithFullSpaceContent.args = {
footer: null,
} as ModalProps;

export const ActionModal = StoryTemplate.bind({});
ActionModal.args = {
...defaultModalProps,
children: (
<ActionModalContent
icon={
<Icon
source={Error}
size="xxxlarge"
customColor="var(--content-basic-disabled)"
/>
}
heading="Action Modal Header"
actions={
<>
<Button size="large">Button</Button>
<Button size="large" kind="primary">
Button
</Button>
</>
}
>
Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet sint.
Velit officia consequat duis enim velit mollit. Exercitation veniam
consequat sunt nostrud amet.
</ActionModalContent>
),
footer: null,
} as ModalProps;

export const ModalPortal = ({
children,
...args
Expand Down
24 changes: 15 additions & 9 deletions packages/react-components/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,21 @@ export const Modal: React.FC<ModalProps> = ({
/>
</div>
)}
{!labelHeading && heading && (
<div className={styles[`${baseClass}__header`]}>
<Heading
size="sm"
as="div"
className={styles[`${baseClass}__heading`]}
>
{heading}
</Heading>
{!labelHeading && (
<div
className={cx(styles[`${baseClass}__header`], {
[styles[`${baseClass}__header--without-heading`]]: !heading,
})}
>
{heading && (
<Heading
size="sm"
as="div"
className={styles[`${baseClass}__heading`]}
>
{heading}
</Heading>
)}
<ModalCloseButton onClick={onCloseButtonClick} />
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ export const ModalFullSpaceContent: React.FC = () => {

export const ModalContent: React.FC = () => (
<div style={{ maxWidth: 400 }}>
<Heading size="lg" as="div">
Content header
</Heading>
<Text size="sm">
Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet sint.
Velit officia consequat duis enim velit mollit. Exercitation veniam
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
$base-class: 'action-modal-content';

.#{$base-class} {
display: flex;
flex-direction: column;
align-items: center;
padding-top: var(--spacing-3);
padding-bottom: var(--spacing-10);
width: 100%;
max-width: 480px;
height: 100%;

&__icon {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--spacing-3);
min-width: 48px;
min-height: 48px;
}

&__heading {
margin-top: 0;
margin-bottom: var(--spacing-2);
}

&__content {
margin-bottom: var(--spacing-10);
text-align: center;
}

&__actions {
display: flex;
gap: var(--spacing-2);
}
}
Loading
Loading