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

feat(Dialog): Convert Dialog v2 to CSS Modules #5298

Merged
merged 12 commits into from
Nov 18, 2024
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
Loading