Skip to content

Commit 6c9121e

Browse files
feat(Banner): update Banner to use CSS Modules behind feature flag (#4913)
* feat(Banner): update Banner to use CSS Modules behind feature flag * chore: add changeset * chore: remove autogenerated pnpm packageManager change * chore: fix stylelint errors * chore: add back in underline for fallback case * refactor: update autofix and eslint warning * chore: update class order for tests * chore: fix stylelint errors * Update packages/react/src/Banner/Banner.module.css Co-authored-by: Katie Langerman <18661030+langermank@users.noreply.github.com> --------- Co-authored-by: Josh Black <joshblack@users.noreply.github.com> Co-authored-by: Katie Langerman <18661030+langermank@users.noreply.github.com>
1 parent c8fe1c6 commit 6c9121e

File tree

8 files changed

+507
-143
lines changed

8 files changed

+507
-143
lines changed

.changeset/odd-rings-applaud.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': minor
3+
---
4+
5+
Update Banner to use CSS Modules behind feature flag

e2e/components/Banner.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,24 @@ test.describe('Banner', () => {
7777
id: story.id,
7878
globals: {
7979
colorScheme: theme,
80+
featureFlags: {
81+
primer_react_css_modules_team: true,
82+
},
83+
},
84+
})
85+
86+
// Default state
87+
expect(await page.screenshot()).toMatchSnapshot(`Banner.${story.title}.${theme}.png`)
88+
})
89+
90+
test('default (styled-components) @vrt', async ({page}) => {
91+
await visit(page, {
92+
id: story.id,
93+
globals: {
94+
colorScheme: theme,
95+
featureFlags: {
96+
primer_react_css_modules_team: false,
97+
},
8098
},
8199
})
82100

@@ -89,6 +107,22 @@ test.describe('Banner', () => {
89107
id: story.id,
90108
globals: {
91109
colorScheme: theme,
110+
featureFlags: {
111+
primer_react_css_modules_team: true,
112+
},
113+
},
114+
})
115+
await expect(page).toHaveNoViolations()
116+
})
117+
118+
test('axe (styled-components) @aat', async ({page}) => {
119+
await visit(page, {
120+
id: story.id,
121+
globals: {
122+
colorScheme: theme,
123+
featureFlags: {
124+
primer_react_css_modules_team: false,
125+
},
92126
},
93127
})
94128
await expect(page).toHaveNoViolations()
@@ -101,6 +135,29 @@ test.describe('Banner', () => {
101135
test(`${name} @vrt`, async ({page}) => {
102136
await visit(page, {
103137
id: story.id,
138+
globals: {
139+
featureFlags: {
140+
primer_react_css_modules_team: true,
141+
},
142+
},
143+
})
144+
const width = viewports[name]
145+
146+
await page.setViewportSize({
147+
width,
148+
height: 667,
149+
})
150+
expect(await page.screenshot()).toMatchSnapshot(`Banner.${story.title}.${name}.png`)
151+
})
152+
153+
test(`${name} (styled-components) @vrt`, async ({page}) => {
154+
await visit(page, {
155+
id: story.id,
156+
globals: {
157+
featureFlags: {
158+
primer_react_css_modules_team: false,
159+
},
160+
},
104161
})
105162
const width = viewports[name]
106163

packages/react/src/Banner/Banner.docs.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
"type": "string",
1212
"description": "Provide an optional label to override the default name for the Banner landmark region"
1313
},
14+
{
15+
"name": "className",
16+
"type": "string",
17+
"description": "Provide an optional className to add to the outermost element rendered by the Banner"
18+
},
1419
{
1520
"name": "description",
1621
"type": "React.ReactNode",
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
.Banner {
2+
display: grid;
3+
padding: var(--base-size-8);
4+
background-color: var(--banner-bgColor);
5+
border: var(--borderWidth-thin, 1px) solid var(--banner-borderColor);
6+
border-radius: var(--borderRadius-medium);
7+
grid-template-columns: auto minmax(0, 1fr) auto;
8+
align-items: start;
9+
10+
@supports (container-type: inline-size) {
11+
container: banner / inline-size;
12+
}
13+
14+
&[data-variant='critical'] {
15+
--banner-bgColor: var(--bgColor-danger-muted);
16+
--banner-borderColor: var(--borderColor-danger-muted);
17+
--banner-icon-fgColor: var(--fgColor-danger);
18+
}
19+
20+
&[data-variant='info'] {
21+
--banner-bgColor: var(--bgColor-accent-muted);
22+
--banner-borderColor: var(--borderColor-accent-muted);
23+
--banner-icon-fgColor: var(--fgColor-accent);
24+
}
25+
26+
&[data-variant='success'] {
27+
--banner-bgColor: var(--bgColor-success-muted);
28+
--banner-borderColor: var(--borderColor-success-muted);
29+
--banner-icon-fgColor: var(--fgColor-success);
30+
}
31+
32+
&[data-variant='upsell'] {
33+
--banner-bgColor: var(--bgColor-upsell-muted);
34+
--banner-borderColor: var(--borderColor-upsell-muted);
35+
--banner-icon-fgColor: var(--fgColor-upsell);
36+
}
37+
38+
&[data-variant='warning'] {
39+
--banner-bgColor: var(--bgColor-attention-muted);
40+
--banner-borderColor: var(--borderColor-attention-muted);
41+
--banner-icon-fgColor: var(--fgColor-attention);
42+
}
43+
}
44+
45+
/* BannerContainer -------------------------------------------------------- */
46+
47+
.BannerContainer {
48+
font-size: var(--text-body-size-medium);
49+
align-items: start;
50+
line-height: var(--text-body-lineHeight-medium);
51+
row-gap: var(--base-size-4);
52+
column-gap: var(--base-size-4);
53+
}
54+
55+
.Banner :where(.BannerContainer) {
56+
display: flex;
57+
flex-wrap: wrap;
58+
justify-content: space-between;
59+
}
60+
61+
.Banner[data-dismissible]:not([data-title-hidden='']) .BannerContainer {
62+
display: grid;
63+
grid-template-columns: auto;
64+
grid-template-rows: auto;
65+
}
66+
67+
/* BannerContent ---------------------------------------------------------- */
68+
69+
.BannerContent {
70+
display: grid;
71+
row-gap: var(--base-size-4);
72+
grid-column-start: 1;
73+
margin-block: var(--base-size-8);
74+
}
75+
76+
.Banner[data-title-hidden] .BannerContent {
77+
margin-block: var(--base-size-6);
78+
}
79+
80+
@media screen and (min-width: 544px) {
81+
.BannerContent {
82+
flex: 1 1 0%;
83+
}
84+
}
85+
86+
.BannerTitle {
87+
margin: 0;
88+
font-size: inherit;
89+
font-weight: var(--base-text-weight-semibold);
90+
}
91+
92+
/* BannerIcon ------------------------------------------------------------- */
93+
94+
.BannerIcon {
95+
display: grid;
96+
place-items: center;
97+
padding: var(--base-size-8);
98+
}
99+
100+
.BannerIcon svg {
101+
/* 20px is the line box height of the trailing action buttons */
102+
height: var(--base-size-20);
103+
color: var(--banner-icon-fgColor);
104+
fill: var(--banner-icon-fgColor);
105+
}
106+
107+
.Banner[data-title-hidden] .BannerIcon svg {
108+
height: var(--base-size-16);
109+
}
110+
111+
/* BannerDismiss ---------------------------------------------------------- */
112+
113+
.BannerDismiss {
114+
display: grid;
115+
place-items: center;
116+
padding: var(--base-size-8);
117+
margin-inline-start: var(--base-size-4);
118+
}
119+
120+
.BannerDismiss svg {
121+
color: var(--banner-icon-fgColor);
122+
}
123+
124+
/* BannerActions ---------------------------------------------------------- */
125+
126+
.BannerActionsContainer {
127+
display: flex;
128+
column-gap: var(--base-size-12);
129+
align-items: center;
130+
}
131+
132+
.BannerActions :where([data-primary-action='trailing']) {
133+
display: none;
134+
}
135+
136+
@media screen and (--viewportRange-regular) {
137+
.BannerActions :where([data-primary-action='trailing']) {
138+
display: flex;
139+
}
140+
141+
.BannerActions :where([data-primary-action='leading']) {
142+
display: none;
143+
}
144+
}
145+
146+
.Banner[data-dismissible] .BannerActions {
147+
margin-block-end: var(--base-size-6);
148+
}
149+
150+
/* stylelint-disable-next-line selector-max-specificity */
151+
.Banner[data-dismissible]:not([data-title-hidden]) .BannerActionsContainer[data-primary-action='trailing'] {
152+
display: none;
153+
}
154+
155+
/* stylelint-disable-next-line selector-max-specificity */
156+
.Banner[data-dismissible]:not([data-title-hidden]) .BannerActionsContainer[data-primary-action='leading'] {
157+
display: flex;
158+
}
159+
160+
/* Layout ------------------------------------------------------------------- */
161+
162+
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
163+
@container banner (max-width: 500px) {
164+
.BannerContainer {
165+
display: grid;
166+
grid-template-rows: auto auto;
167+
}
168+
169+
.BannerActions {
170+
margin-block-end: var(--base-size-6);
171+
}
172+
173+
.BannerActions [data-primary-action='trailing'] {
174+
display: none;
175+
}
176+
177+
.BannerActions [data-primary-action='leading'] {
178+
display: flex;
179+
}
180+
}
181+
182+
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
183+
@container banner (min-width: 500px) {
184+
.BannerContainer {
185+
display: grid;
186+
grid-template-columns: auto auto;
187+
}
188+
189+
.BannerActions [data-primary-action='trailing'] {
190+
display: flex;
191+
}
192+
193+
.BannerActions [data-primary-action='leading'] {
194+
display: none;
195+
}
196+
}

packages/react/src/Banner/Banner.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ describe('Banner', () => {
2929
expect(screen.getByRole('heading', {name: 'test'})).toBeInTheDocument()
3030
})
3131

32+
it('should support a custom `className` on the outermost element', () => {
33+
const {container} = render(<Banner title="test" className="test" />)
34+
expect(container.firstChild).toHaveClass('test')
35+
})
36+
3237
it('should label the landmark element with the corresponding variant label text', () => {
3338
render(<Banner title="test" />)
3439
expect(screen.getByRole('region')).toEqual(screen.getByLabelText('Information'))

0 commit comments

Comments
 (0)