diff --git a/packages/web-react/src/components/Modal/Modal.tsx b/packages/web-react/src/components/Modal/Modal.tsx index 6827b7d6f5..56b54c51c0 100644 --- a/packages/web-react/src/components/Modal/Modal.tsx +++ b/packages/web-react/src/components/Modal/Modal.tsx @@ -1,5 +1,6 @@ import React from 'react'; import classNames from 'classnames'; +import { AlignmentY } from '../../constants'; import { useStyleProps, useLastActiveFocus } from '../../hooks'; import { SpiritModalProps } from '../../types'; import { useModalStyleProps } from './useModalStyleProps'; @@ -7,8 +8,8 @@ import { ModalProvider } from './ModalContext'; import Dialog from '../Dialog/Dialog'; const Modal = (props: SpiritModalProps) => { - const { children, isOpen, onClose, id, ...restProps } = props; - const { classProps } = useModalStyleProps(); + const { children, alignmentY = AlignmentY.CENTER, isOpen, onClose, id, ...restProps } = props; + const { classProps } = useModalStyleProps({ modalAlignment: alignmentY }); const { styleProps, props: otherProps } = useStyleProps(restProps); const contextValue = { diff --git a/packages/web-react/src/components/Modal/ModalFooter.tsx b/packages/web-react/src/components/Modal/ModalFooter.tsx index 628e9dda5c..e789c802ec 100644 --- a/packages/web-react/src/components/Modal/ModalFooter.tsx +++ b/packages/web-react/src/components/Modal/ModalFooter.tsx @@ -1,11 +1,12 @@ import React from 'react'; import classNames from 'classnames'; +import { AlignmentX } from '../../constants'; import { useStyleProps } from '../../hooks'; import { ModalFooterProps } from '../../types'; import { useModalStyleProps } from './useModalStyleProps'; const ModalFooter = (props: ModalFooterProps) => { - const { children, alignmentX = 'right', description, ...restProps } = props; + const { children, alignmentX = AlignmentX.RIGHT, description, ...restProps } = props; const { classProps } = useModalStyleProps({ footerAlignment: alignmentX }); const { styleProps, props: otherProps } = useStyleProps(restProps); diff --git a/packages/web-react/src/components/Modal/README.md b/packages/web-react/src/components/Modal/README.md index b877e4df0d..0dbfb5ec32 100644 --- a/packages/web-react/src/components/Modal/README.md +++ b/packages/web-react/src/components/Modal/README.md @@ -25,6 +25,7 @@ Modal is a composition of several subcomponents: - [Opening the Modal](#opening-the-modal) - [Scrolling Long Content](#scrolling-long-content) - [Scrolling with ScrollView](#scrolling-with-scrollview) + - [Stacking Modals](#stacking-modals) - [Full Example](#full-example) ## Modal @@ -36,17 +37,35 @@ provides several accessibility advantages. ``` +### Vertical Alignment + +Modal can be aligned to the center (default), top, or bottom. These values come from the +[alignment dictionary][dictionary-alignment]. Using a corresponding alignment option will align the modal accordingly: + +- `top` +- `center` (default) +- `bottom` + +Example: + +```jsx + + … + +``` + ### API -| Name | Type | Default | Required | Description | -| ---------------------- | ---------------------------------------------- | ------- | -------- | ----------------------------------------------------- | -| `children` | `ReactNode` | — | ✕ | Children node | -| `closeOnBackdropClick` | `bool` | `true` | ✕ | Whether the modal will close when backdrop is clicked | -| `id` | `string` | — | ✔ | Modal ID | -| `isOpen` | `bool` | `false` | ✔ | Open state | -| `onClose` | `(event: ClickEvent or KeyboardEvent) => void` | — | ✔ | Callback on dialog closed | -| `UNSAFE_className` | `string` | — | ✕ | Modal custom class name | -| `UNSAFE_style` | `CSSProperties` | — | ✕ | Modal custom style | +| Name | Type | Default | Required | Description | +| ---------------------- | ---------------------------------------------- | -------- | -------- | ----------------------------------------------------- | +| `alignmentY` | [AlignmentY dictionary][dictionary-alignment] | `center` | ✕ | Vertical alignment of modal | +| `children` | `ReactNode` | — | ✕ | Children node | +| `closeOnBackdropClick` | `bool` | `true` | ✕ | Whether the modal will close when backdrop is clicked | +| `id` | `string` | — | ✔ | Modal ID | +| `isOpen` | `bool` | `false` | ✔ | Open state | +| `onClose` | `(event: ClickEvent or KeyboardEvent) => void` | — | ✔ | Callback on dialog closed | +| `UNSAFE_className` | `string` | — | ✕ | Modal custom class name | +| `UNSAFE_style` | `CSSProperties` | — | ✕ | Modal custom style | Also, all properties of the [`` element][mdn-dialog] are supported. @@ -215,7 +234,8 @@ Optionally, you can add a description to the footer: ### Footer Alignment ModalFooter can be aligned to the right (default), center, or left. These values come from the -[dictionary][dictionary-alignment]. Using a corresponding alignment option will align the footer actions accordingly: +[alignment dictionary][dictionary-alignment]. Using a corresponding alignment option will align the footer actions +accordingly: - `right` (default) - `center` diff --git a/packages/web-react/src/components/Modal/__tests__/useModalStyleProps.test.ts b/packages/web-react/src/components/Modal/__tests__/useModalStyleProps.test.ts index 956340b756..935fac02ec 100644 --- a/packages/web-react/src/components/Modal/__tests__/useModalStyleProps.test.ts +++ b/packages/web-react/src/components/Modal/__tests__/useModalStyleProps.test.ts @@ -5,7 +5,7 @@ describe('useModalStyleProps', () => { it('should return defaults', () => { const { result } = renderHook(() => useModalStyleProps({})); - expect(result.current.classProps.root).toBe('Modal'); + expect(result.current.classProps.root).toBe('Modal Modal--center'); expect(result.current.classProps.dialog).toBe('ModalDialog'); expect(result.current.classProps.title).toBe('ModalHeader__title'); expect(result.current.classProps.header).toBe('ModalHeader'); diff --git a/packages/web-react/src/components/Modal/demo/ModalDefault.tsx b/packages/web-react/src/components/Modal/demo/ModalDefault.tsx index b470299f1c..3e8575949e 100644 --- a/packages/web-react/src/components/Modal/demo/ModalDefault.tsx +++ b/packages/web-react/src/components/Modal/demo/ModalDefault.tsx @@ -1,12 +1,13 @@ import React, { ChangeEvent, useState } from 'react'; -import { AlignmentXDictionaryType } from '../../..'; +import { AlignmentX, AlignmentXDictionaryType, AlignmentY, AlignmentYDictionaryType } from '../../..'; import { Button, Checkbox, Modal, ModalBody, ModalDialog, ModalFooter, ModalHeader, Radio, TextField } from '../..'; const ModalDefault = () => { const [isFirstOpen, setFirstOpen] = useState(false); const [isSecondOpen, setSecondOpen] = useState(false); const [isThirdOpen, setThirdOpen] = useState(false); - const [footerAlign, setFooterAlign] = useState('right'); + const [modalAlign, setModalAlign] = useState(AlignmentY.CENTER); + const [footerAlign, setFooterAlign] = useState(AlignmentX.RIGHT); const [isExpanded, setIsExpanded] = useState(true); const toggleFirstModal = () => setFirstOpen(!isFirstOpen); @@ -17,59 +18,95 @@ const ModalDefault = () => { const handleFirstClose = () => setFirstOpen(false); const handleSecondClose = () => setSecondOpen(false); const handleThirdClose = () => setThirdOpen(false); - const handleFooterAlignChange = (e: ChangeEvent) => { - setFooterAlign(e.target.value as AlignmentXDictionaryType); + const handleModalAlignChange = (event: ChangeEvent) => { + setModalAlign(event.target.value as AlignmentYDictionaryType); + }; + const handleFooterAlignChange = (event: ChangeEvent) => { + setFooterAlign(event.target.value as AlignmentXDictionaryType); }; return ( <> - + - Modal Title + Modal Title

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus perferendis provident unde. Eveniet, iste, molestiae?

+
+
Modal alignment (from tablet up):
+ {' '} + {' '} + +
Footer alignment (from tablet up):
+ />{' '} + />{' '}
{ />
- + - + Modal with a Form @@ -110,7 +147,7 @@ const ModalDefault = () => { - + { const [isFirstOpen, setFirstOpen] = useState(false); const [isSecondOpen, setSecondOpen] = useState(false); + const [modalAlign, setModalAlign] = useState(AlignmentY.CENTER); + const [footerAlign, setFooterAlign] = useState(AlignmentX.RIGHT); const toggleFirstModal = () => setFirstOpen(!isFirstOpen); const toggleSecondModal = () => setSecondOpen(!isSecondOpen); const handleFirstClose = () => setFirstOpen(false); const handleSecondClose = () => setSecondOpen(false); + const handleModalAlignChange = (event: ChangeEvent) => { + setModalAlign(event.target.value as AlignmentYDictionaryType); + }; + const handleFooterAlignChange = (event: ChangeEvent) => { + setFooterAlign(event.target.value as AlignmentXDictionaryType); + }; return ( <> @@ -17,7 +26,7 @@ const ModalDefault = () => {
- + Modal Title @@ -26,8 +35,74 @@ const ModalDefault = () => { mollitia mollitia perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus provident unde. Eveniet, iste, molestiae?

+
+
Modal alignment:
+ {' '} + {' '} + + +
+
Footer alignment (from tablet up):
+ {' '} + {' '} + +
- +