diff --git a/src/components/Modal/component.stories.tsx b/src/components/Modal/component.stories.tsx index 7bc46f3..35355ab 100644 --- a/src/components/Modal/component.stories.tsx +++ b/src/components/Modal/component.stories.tsx @@ -1,12 +1,23 @@ -import React from 'react'; +import React, { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import BBBModal from './component'; import { BBBTypography } from '../Typography'; +import { BBButton } from '../Button'; +import { BBBAccordion } from '../Accordion'; +import { BBBSelect } from '../Select'; +import { BBBCheckbox } from '../Checkbox'; +import { BBBTextInput } from '../TextInput'; +import { BBBHint } from '../Hint'; +import MenuItem from '@mui/material/MenuItem'; const meta = { title: 'BBBModal', component: BBBModal, tags: ['autodocs'], + parameters: { + // Prevent the fixed overlay from covering Storybook's own UI + layout: 'centered', + }, argTypes: { isOpen: { control: 'boolean', @@ -66,15 +77,107 @@ const meta = { export default meta; type Story = StoryObj; +/** + * Wrapper component so hooks can be used inside Storybook's render function. + * The modal is controlled via a trigger button, keeping the controls panel + * accessible. Adjust args and re-open the modal to see the changes. + */ +const ModalStory: React.FC> = (args) => { + const [isOpen, setIsOpen] = useState(args.isOpen); + + // Sync with the Storybook "isOpen" control + React.useEffect(() => { + setIsOpen(args.isOpen); + }, [args.isOpen]); + + return ( + <> + setIsOpen(true)} + /> + setIsOpen(false)} + footerContent={args.footerContent ?? setIsOpen(false)} />} + ariaHideApp={false} + > + {args.children} + + + ); +}; + +const LOREM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation +ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in +voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non +proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`; + +const ModalFooter = ({ onClose }: { onClose: () => void }) => ( + <> + + + +); + +const ModalBody = () => ( +
+ Profile Settings + {LOREM} + + + + + Admin + Moderator + Viewer + + + {LOREM} + + +
+ + + +
+
+ + + + {LOREM} + + + English + Português + Español + + + +
+ {LOREM} + {}} /> +
+
+ + {LOREM} +
+); + export const Default: Story = { args: { title: 'Modal Title', - children: ( -
- Modal body content -
- ), - isOpen: true, + isOpen: false, onRequestClose: () => {}, + showDividers: false, + allowScroll: true, + noFooter: false, + stickyFooter: true, + shouldCloseOnOverlayClick: true, + shouldCloseOnEsc: true, + children: null, }, + render: (args) => , }; diff --git a/src/components/Modal/component.tsx b/src/components/Modal/component.tsx index 0741928..7b5e97c 100644 --- a/src/components/Modal/component.tsx +++ b/src/components/Modal/component.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import ReactModal from 'react-modal'; import * as Styled from './styles'; import { BBBTypography } from '../Typography'; @@ -31,6 +31,15 @@ const Modal: React.FC = ({ ...rest }) => { + const renderFooter = useCallback((isSticky: boolean) => ( + <> + {showDividers && } + + {footerContent} + + + ), [footerContent, showDividers]); + return ( = ({ {showDividers && } - - {children} - - {(!noFooter || footerContent) && ( - <> - {showDividers && ()} - - - {footerContent} - - - )} + + + {children} + + {!noFooter && !stickyFooter && renderFooter(stickyFooter)} + + {!noFooter && stickyFooter && renderFooter(stickyFooter)} ) } diff --git a/src/components/Modal/styles.ts b/src/components/Modal/styles.ts index b81d9b5..be57fa5 100644 --- a/src/components/Modal/styles.ts +++ b/src/components/Modal/styles.ts @@ -2,6 +2,7 @@ import styled from 'styled-components'; import { Styles } from 'react-modal'; import * as React from 'react'; import { spacingLarge, spacingMedium, spacingSmallMedium, borderRadiusDefault } from '../../stylesheets/sizing'; +import { colorWhite } from '../../stylesheets/pallete'; import { StyledModalBodyProps, StyledModalFooterProps } from './types'; export const modalStyles: Styles = { @@ -24,7 +25,7 @@ export const modalStyles: Styles = { right: 'auto', bottom: 'auto', borderRadius: borderRadiusDefault, - background: '#fff', + background: colorWhite, overflow: 'hidden', WebkitOverflowScrolling: 'touch', outline: 'none', @@ -53,13 +54,17 @@ export const CloseButton = styled.button` line-height: 1; `; -export const ModalBody = styled.div` +export const ModalScrollArea = styled.div` flex-grow: 1; overflow-y: ${({ $allowScroll }) => $allowScroll ? 'auto' : 'hidden'}; overflow-x: hidden; display: flex; flex-direction: column; - padding: 0 ${spacingLarge} 0; +`; + +export const ModalBodyContent = styled.div` + flex-grow: 1; + padding: 0 ${spacingLarge}; margin: ${spacingSmallMedium} 0 ${spacingSmallMedium}; `; @@ -74,6 +79,6 @@ export const ModalFooter = styled.div` ` position: sticky; bottom: 0; - background-color: #fff; + background-color: ${colorWhite}; `} `;