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

refactor(components): refactor modal for web (app and pd) #17193

Merged
merged 12 commits into from
Jan 8, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
DIRECTION_ROW,
Flex,
Icon,
LegacyStyledText,
Link,
Modal,
PrimaryButton,
SPACING,
Modal,
LegacyStyledText,
TYPOGRAPHY,
} from '@opentrons/components'
import { getTopPortalEl } from '/app/App/portal'
Expand Down
70 changes: 27 additions & 43 deletions components/src/modals/Modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
import type * as React from 'react'
import { PrimaryButton, StyledText } from '../atoms'
import { SPACING } from '../ui-style-constants'
import { Flex } from '../primitives'
import { JUSTIFY_END } from '../styles'
import { Modal as ModalComponent } from './Modal'

import { LegacyStyledText } from '../atoms'
import { SPACING, TYPOGRAPHY } from '../ui-style-constants'
import { PrimaryBtn } from '../primitives'
import { COLORS } from '../helix-design-system'
import { Modal } from './Modal'
import type { Meta, StoryObj } from '@storybook/react'

import type { Story, Meta } from '@storybook/react'

export default {
const meta: Meta<typeof ModalComponent> = {
title: 'Library/Molecules/modals/Modal',
component: Modal,
} as Meta

const Template: Story<React.ComponentProps<typeof Modal>> = args => (
<Modal {...args} />
)
component: ModalComponent,
}
export default meta
type Story = StoryObj<typeof ModalComponent>
const bodyText = 'Modal body goes here'

const Children = (
<>
<LegacyStyledText
fontWeight={TYPOGRAPHY.fontWeightSemiBold}
fontSize={TYPOGRAPHY.fontSizeP}
paddingTop={SPACING.spacing4}
>
{'Modal body goes here'}
</LegacyStyledText>
<StyledText desktopStyle="bodyDefaultRegular">{bodyText}</StyledText>
)

<PrimaryBtn
backgroundColor={COLORS.blue50}
marginTop="28rem"
textTransform={TYPOGRAPHY.textTransformNone}
>
<LegacyStyledText
fontWeight={TYPOGRAPHY.fontWeightRegular}
fontSize={TYPOGRAPHY.fontSizeP}
>
{'btn text'}
</LegacyStyledText>
</PrimaryBtn>
</>
const Footer = (
<Flex justifyContent={JUSTIFY_END} padding={SPACING.spacing24}>
<PrimaryButton onClick={() => {}}>{'btn text'}</PrimaryButton>
</Flex>
)

export const Primary = Template.bind({})
Primary.args = {
type: 'info',
onClose: () => {},
closeOnOutsideClick: false,
title: 'Modal Title',
children: Children,
export const Modal: Story = {
args: {
type: 'info',
onClose: () => {},
closeOnOutsideClick: false,
title: 'Modal Title',
children: Children,
footer: Footer,
},
}
1 change: 0 additions & 1 deletion components/src/modals/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const Modal = (props: ModalProps): JSX.Element => {
name: 'ot-alert',
color: iconColor(type),
size: '1.25rem',
marginRight: SPACING.spacing8,
}

const modalHeader = (
Expand Down
122 changes: 63 additions & 59 deletions components/src/modals/ModalHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { css } from 'styled-components'
import styled, { css } from 'styled-components'

import { Icon } from '../icons'
import { Box, Btn, Flex } from '../primitives'
import { LegacyStyledText } from '../atoms'
import { ALIGN_CENTER, JUSTIFY_CENTER, JUSTIFY_SPACE_BETWEEN } from '../styles'
import { SPACING, TYPOGRAPHY } from '../ui-style-constants'
import { StyledText } from '../atoms'
import {
ALIGN_CENTER,
DISPLAY_FLEX,
JUSTIFY_CENTER,
JUSTIFY_SPACE_BETWEEN,
} from '../styles'
import { SPACING } from '../ui-style-constants'
import { COLORS } from '../helix-design-system'

import type { MouseEventHandler, ReactNode } from 'react'
Expand All @@ -21,22 +26,6 @@ export interface ModalHeaderProps {
closeButton?: ReactNode
}

const closeIconStyles = css`
display: flex;
justify-content: ${JUSTIFY_CENTER};
align-items: ${ALIGN_CENTER};
border-radius: 0.875rem;
width: 1.625rem;
height: 1.625rem;
&:hover {
background-color: ${COLORS.grey30};
}

&:active {
background-color: ${COLORS.grey35};
}
`

export const ModalHeader = (props: ModalHeaderProps): JSX.Element => {
const {
icon,
Expand All @@ -45,57 +34,72 @@ export const ModalHeader = (props: ModalHeaderProps): JSX.Element => {
titleElement1,
titleElement2,
backgroundColor,
color,
color = COLORS.black90,
closeButton,
} = props
return (
<>
<Flex
alignItems={ALIGN_CENTER}
justifyContent={JUSTIFY_SPACE_BETWEEN}
paddingX={SPACING.spacing24}
paddingY={SPACING.spacing16}
<StyledModalHeader
backgroundColor={backgroundColor}
data-testid="Modal_header"
role="heading"
>
<Flex alignItems={ALIGN_CENTER} gridGap={SPACING.spacing8}>
<Flex alignItems={ALIGN_CENTER} gridGap={SPACING.spacing16}>
{icon != null && <Icon {...icon} data-testid="Modal_header_icon" />}
{titleElement1}
{titleElement2}
{/* TODO (nd: 08/07/2024) Convert to StyledText once designs are resolved */}
<LegacyStyledText
as="h3"
fontWeight={TYPOGRAPHY.fontWeightSemiBold}
color={color}
>
<StyledText color={color} desktopStyle="bodyLargeSemiBold">
{title}
</LegacyStyledText>
</StyledText>
</Flex>
{closeButton != null
? closeButton
: onClose != null && (
<Btn
onClick={onClose}
css={closeIconStyles}
data-testid={`ModalHeader_icon_close${
typeof title === 'string' ? `_${title}` : ''
}`}
>
<Icon
name="close"
width={SPACING.spacing24}
height={SPACING.spacing24}
color={color}
/>
</Btn>
)}
</Flex>
<Box
borderBottom={`1px solid ${COLORS.grey30}`}
marginY="0"
width="100%"
data-testid="divider"
/>
{closeButton != null ||
(onClose != null && (
<Btn
onClick={onClose}
css={closeIconStyles}
data-testid={`ModalHeader_icon_close${
typeof title === 'string' ? `_${title}` : ''
}`}
>
<Icon
name="close"
width={SPACING.spacing24}
height={SPACING.spacing24}
color={color}
/>
</Btn>
))}
</StyledModalHeader>
<StyledDivider data-testid="divider" />
</>
)
}

const StyledModalHeader = styled(Flex)`
padding: ${SPACING.spacing16} ${SPACING.spacing24};
justify-content: ${JUSTIFY_SPACE_BETWEEN};
align-items: ${ALIGN_CENTER};
background-color: ${props => props.backgroundColor};
`

const StyledDivider = styled(Box)`
border-bottom: 1px solid ${COLORS.grey30};
margin: 0;
width: 100%;
`

const closeIconStyles = css`
display: ${DISPLAY_FLEX};
justify-content: ${JUSTIFY_CENTER};
align-items: ${ALIGN_CENTER};
border-radius: 0.875rem;
width: 1.625rem;
height: 1.625rem;
&:hover {
background-color: ${COLORS.grey30};
}

&:active {
background-color: ${COLORS.grey35};
}
`
4 changes: 3 additions & 1 deletion components/src/modals/ModalShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ALIGN_CENTER,
ALIGN_END,
CURSOR_DEFAULT,
DISPLAY_FLEX,
JUSTIFY_CENTER,
JUSTIFY_END,
OVERFLOW_AUTO,
Expand Down Expand Up @@ -110,7 +111,7 @@ const ContentArea = styled.div<{
position: Position
noPadding: boolean
}>`
display: flex;
display: ${DISPLAY_FLEX};
position: ${POSITION_ABSOLUTE};
align-items: ${({ position }) =>
position === 'center' ? ALIGN_CENTER : ALIGN_END};
Expand All @@ -137,6 +138,7 @@ const ModalArea = styled.div<
box-shadow: ${BORDERS.smallDropShadow};
height: ${({ isFullPage }) => (isFullPage ? '100%' : 'auto')};
background-color: ${COLORS.white};

@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
border-radius: ${BORDERS.borderRadius16};
}
Expand Down
7 changes: 1 addition & 6 deletions components/src/modals/__tests__/ModalHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'

import { renderWithProviders } from '../../testing/utils'
import { ModalHeader } from '../ModalHeader'
import { COLORS } from '../../helix-design-system'
import { SPACING } from '../../ui-style-constants'
import { ALIGN_CENTER, JUSTIFY_CENTER } from '../../styles'
import { ModalHeader } from '../ModalHeader'

import type { ComponentProps } from 'react'

Expand Down Expand Up @@ -40,7 +39,6 @@ describe('ModalHeader', () => {
name: 'ot-alert',
color: COLORS.black90,
size: '1.25rem',
marginRight: SPACING.spacing8,
}
render(props)
expect(screen.getByTestId('Modal_header_icon')).toHaveStyle(
Expand All @@ -52,9 +50,6 @@ describe('ModalHeader', () => {
expect(screen.getByTestId('Modal_header_icon')).toHaveStyle(
`height: 1.25rem`
)
expect(screen.getByTestId('Modal_header_icon')).toHaveStyle(
`margin-right: ${SPACING.spacing8}`
)
})

it('should call a mock function when clicking close icon', () => {
Expand Down
Loading