Skip to content

Commit

Permalink
feat(Dialog): Convert Dialog v2 to CSS Modules (#5298)
Browse files Browse the repository at this point in the history
* feat(Dialog): Convert Dialog v2 to CSS Modules

* changeset

* lint fix

* fix backdrop classname

* add unit test

* lint fix

* fix padding

* make body polymorphic

* fix width height props
  • Loading branch information
hussam-i-am authored Nov 18, 2024
1 parent d73f465 commit 3935811
Show file tree
Hide file tree
Showing 4 changed files with 561 additions and 176 deletions.
5 changes: 5 additions & 0 deletions .changeset/forty-hornets-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Convert Dialog v2 to CSS Modules behind feature flag
253 changes: 253 additions & 0 deletions packages/react/src/Dialog/Dialog.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
@keyframes dialog-backdrop-appear {
0% {
opacity: 0;
}

100% {
opacity: 1;
}
}

@keyframes Overlay--motion-scaleFade {
0% {
opacity: 0;
transform: scale(0.5);
}

100% {
opacity: 1;
transform: scale(1);
}
}

@keyframes Overlay--motion-slideUp {
from {
transform: translateY(100%);
}
}

@keyframes Overlay--motion-slideInRight {
from {
transform: translateX(-100%);
}
}

@keyframes Overlay--motion-slideInLeft {
from {
transform: translateX(100%);
}
}

.Backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
background-color: var(--overlay-backdrop-bgColor);
animation: dialog-backdrop-appear 200ms cubic-bezier(0.33, 1, 0.68, 1);
align-items: center;
justify-content: center;

&[data-position-regular='center'] {
align-items: center;
justify-content: center;
}

&[data-position-regular='left'] {
align-items: center;
justify-content: flex-start;
}

&[data-position-regular='right'] {
align-items: center;
justify-content: flex-end;
}

.DialogOverflowWrapper {
flex-grow: 1;
}

@media (max-width: 767px) {
&[data-position-narrow='center'] {
align-items: center;
justify-content: center;
}

&[data-position-narrow='bottom'] {
align-items: end;
justify-content: center;
}
}
}

.Dialog {
display: flex;
/* stylelint-disable-next-line primer/responsive-widths */
width: 640px;
min-width: 296px;
max-width: calc(100dvw - 64px);
height: auto;
max-height: calc(100dvh - 64px);
flex-direction: column;
background-color: var(--overlay-bgColor);
border-radius: var(--borderRadius-large);
border-radius: var(--borderRadius-large, var(--borderRadius-large));
box-shadow: var(--shadow-floating-small);
opacity: 1;

&:where([data-width='small']) {
width: 296px;
}

&:where([data-width='medium']) {
width: 320px;
}

&:where([data-width='large']) {
/* stylelint-disable-next-line primer/responsive-widths */
width: 480px;
}

&:where([data-height='small']) {
height: 480px;
}

&:where([data-height='large']) {
height: 640px;
}

@media screen and (prefers-reduced-motion: no-preference) {
animation: Overlay--motion-scaleFade 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running;
}

&[data-position-regular='center'] {
border-radius: var(--borderRadius-large, var(--borderRadius-large));

@media screen and (prefers-reduced-motion: no-preference) {
animation: Overlay--motion-scaleFade 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running;
}
}

&[data-position-regular='left'] {
height: 100dvh;
max-height: unset;
border-radius: var(--borderRadius-large, var(--borderRadius-large));
border-top-left-radius: 0;
border-bottom-left-radius: 0;

@media screen and (prefers-reduced-motion: no-preference) {
animation: Overlay--motion-slideInRight 0.25s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running;
}
}

&[data-position-regular='right'] {
height: 100dvh;
max-height: unset;
border-radius: var(--borderRadius-large, var(--borderRadius-large));
border-top-right-radius: 0;
border-bottom-right-radius: 0;

@media screen and (prefers-reduced-motion: no-preference) {
animation: Overlay--motion-slideInLeft 0.25s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running;
}
}

@media (max-width: 767px) {
&[data-position-narrow='center'] {
/* stylelint-disable-next-line primer/responsive-widths */
width: 640px;
height: auto;
border-radius: var(--borderRadius-large, var(--borderRadius-large));

&:where([data-width='small']) {
width: 296px;
}

&:where([data-width='medium']) {
width: 320px;
}

&:where([data-width='large']) {
/* stylelint-disable-next-line primer/responsive-widths */
width: 480px;
}

&:where([data-height='small']) {
height: 480px;
}

&:where([data-height='large']) {
height: 640px;
}
}

&[data-position-narrow='bottom'] {
width: 100dvw;
max-width: 100dvw;
height: auto;
max-height: calc(100dvh - 64px);
border-radius: var(--borderRadius-large, var(--borderRadius-large));
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;

@media screen and (prefers-reduced-motion: no-preference) {
animation: Overlay--motion-slideUp 0.25s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running;
}
}

&[data-position-narrow='fullscreen'] {
width: 100%;
max-width: 100dvw;
height: 100%;
max-height: 100dvh;
border-radius: unset !important;
flex-grow: 1;

@media screen and (prefers-reduced-motion: no-preference) {
animation: Overlay--motion-scaleFade 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running;
}
}
}
}

.Header {
z-index: 1;
padding: var(--base-size-8);
/* stylelint-disable-next-line primer/box-shadow */
box-shadow: 0 1px 0 var(--borderColor-default);
flex-shrink: 0;
}

.Title {
margin: 0; /* override default margin */
font-size: var(--text-body-size-medium);
font-weight: var(--text-title-weight-large);
}

.Subtitle {
margin: 0; /* override default margin */
margin-top: var(--base-size-4);
font-size: var(--text-body-size-small);
font-weight: var(--base-text-weight-normal);
color: var(--fgColor-muted);
}

.Body {
padding: var(--base-size-16);
overflow: auto;
flex-grow: 1;
}

.Footer {
z-index: 1;
display: flex;
padding: var(--base-size-16);
/* stylelint-disable-next-line primer/box-shadow */
box-shadow: 0 -1px 0 var(--borderColor-default);
flex-flow: wrap;
justify-content: flex-end;
gap: var(--base-size-8);
flex-shrink: 0;
}
48 changes: 48 additions & 0 deletions packages/react/src/Dialog/Dialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import MatchMediaMock from 'jest-matchmedia-mock'
import {behavesAsComponent, checkExports} from '../utils/testing'
import axe from 'axe-core'
import {Button} from '../Button'
import {FeatureFlags} from '../FeatureFlags'

let matchMedia: MatchMediaMock

Expand Down Expand Up @@ -226,6 +227,53 @@ describe('Dialog', () => {

expect(getByRole('button', {name: 'return focus to (button 2)'})).toHaveFocus()
})

it('should support `className` on the Dialog element', async () => {
const Fixture = () => {
const [isOpen, setIsOpen] = React.useState(true)
const triggerRef = React.useRef<HTMLButtonElement>(null)

return (
<>
<Button variant="primary" onClick={() => setIsOpen(true)}>
Show dialog
</Button>
{isOpen && (
<Dialog title="title" onClose={() => setIsOpen(false)} returnFocusRef={triggerRef} className="custom-class">
body
</Dialog>
)}
</>
)
}

const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Fixture />
</FeatureFlags>
)
}

const user = userEvent.setup()

let component = render(<Fixture />)
let triggerButton = component.getByRole('button', {name: 'Show dialog'})
await user.click(triggerButton)
expect(component.getByRole('dialog')).toHaveClass('custom-class')
component.unmount()

component = render(<FeatureFlagElement />)
triggerButton = component.getByRole('button', {name: 'Show dialog'})
await user.click(triggerButton)
expect(component.getByRole('dialog')).toHaveClass('custom-class')
})
})

it('automatically focuses the element that is specified as initialFocusRef', () => {
Expand Down
Loading

0 comments on commit 3935811

Please sign in to comment.