Skip to content

Commit d6fe52e

Browse files
authoredDec 6, 2024··
chore(Banner): Remove the CSS modules feature flag from Banner (#5282)
* Remove feature flag from Banner * Create metal-steaks-unite.md * Fix test * Update Banner tests to use queryAllByRole
1 parent b08f770 commit d6fe52e

File tree

3 files changed

+22
-277
lines changed

3 files changed

+22
-277
lines changed
 

‎.changeset/metal-steaks-unite.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
Remove the CSS modules feature flag from Banner

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ describe('Banner', () => {
115115
/>,
116116
)
117117

118-
expect(screen.getByRole('button', {name: 'test primary action'})).toBeInTheDocument()
118+
expect(screen.queryAllByRole('button', {name: 'test primary action', hidden: true}).length).toBe(2)
119119

120-
await user.click(screen.getByRole('button', {name: 'test primary action'}))
120+
await user.click(screen.queryAllByText('test primary action')[0])
121121
expect(onClick).toHaveBeenCalledTimes(1)
122122
})
123123

@@ -132,9 +132,9 @@ describe('Banner', () => {
132132
/>,
133133
)
134134

135-
expect(screen.getByRole('button', {name: 'test secondary action'})).toBeInTheDocument()
135+
expect(screen.queryAllByRole('button', {name: 'test secondary action', hidden: true}).length).toBe(2)
136136

137-
await user.click(screen.getByRole('button', {name: 'test secondary action'}))
137+
await user.click(screen.queryAllByText('test secondary action')[0])
138138
expect(onClick).toHaveBeenCalledTimes(1)
139139
})
140140

@@ -148,8 +148,8 @@ describe('Banner', () => {
148148
/>,
149149
)
150150

151-
expect(screen.getByRole('button', {name: 'test primary action'})).toBeInTheDocument()
152-
expect(screen.getByRole('button', {name: 'test secondary action'})).toBeInTheDocument()
151+
expect(screen.queryAllByRole('button', {name: 'test primary action', hidden: true}).length).toBe(2)
152+
expect(screen.queryAllByRole('button', {name: 'test secondary action', hidden: true}).length).toBe(2)
153153
})
154154

155155
it('should call `onDismiss` when the dismiss button is activated', async () => {

‎packages/react/src/Banner/Banner.tsx

+11-271
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import {clsx} from 'clsx'
22
import React, {forwardRef, useEffect} from 'react'
3-
import styled from 'styled-components'
43
import {AlertIcon, InfoIcon, StopIcon, CheckCircleIcon, XIcon} from '@primer/octicons-react'
54
import {Button, IconButton, type ButtonProps} from '../Button'
6-
import {get} from '../constants'
75
import {VisuallyHidden} from '../VisuallyHidden'
86
import {useMergedRefs} from '../internal/hooks/useMergedRefs'
9-
import {useFeatureFlag} from '../FeatureFlags'
107
import classes from './Banner.module.css'
11-
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
128
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
139

1410
type BannerVariant = 'critical' | 'info' | 'success' | 'upsell' | 'warning'
@@ -88,8 +84,6 @@ const labels: Record<BannerVariant, string> = {
8884
warning: 'Warning',
8985
}
9086

91-
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga'
92-
9387
export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner(
9488
{
9589
'aria-label': label,
@@ -111,7 +105,6 @@ export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner
111105
const hasActions = primaryAction || secondaryAction
112106
const bannerRef = React.useRef<HTMLElement>(null)
113107
const ref = useMergedRefs(forwardRef, bannerRef)
114-
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
115108
const supportsCustomIcon = variant === 'info' || variant === 'upsell'
116109

117110
if (__DEV__) {
@@ -137,40 +130,19 @@ export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner
137130
}
138131

139132
return (
140-
<StyledBanner
133+
<section
141134
{...rest}
142135
aria-label={label ?? labels[variant]}
143-
as="section"
144-
className={clsx(className, {
145-
[classes.Banner]: enabled,
146-
})}
136+
className={clsx(className, classes.Banner)}
147137
data-dismissible={onDismiss ? '' : undefined}
148138
data-title-hidden={hideTitle ? '' : undefined}
149139
data-variant={variant}
150140
tabIndex={-1}
151141
ref={ref}
152142
>
153-
{!enabled ? <style>{BannerContainerQuery}</style> : null}
154-
<div
155-
className={clsx({
156-
BannerIcon: !enabled,
157-
[classes.BannerIcon]: enabled,
158-
})}
159-
>
160-
{icon && supportsCustomIcon ? icon : iconForVariant[variant]}
161-
</div>
162-
<div
163-
className={clsx({
164-
BannerContainer: !enabled,
165-
[classes.BannerContainer]: enabled,
166-
})}
167-
>
168-
<div
169-
className={clsx({
170-
BannerContent: !enabled,
171-
[classes.BannerContent]: enabled,
172-
})}
173-
>
143+
<div className={classes.BannerIcon}>{icon && supportsCustomIcon ? icon : iconForVariant[variant]}</div>
144+
<div className={classes.BannerContainer}>
145+
<div className={classes.BannerContent}>
174146
{title ? (
175147
hideTitle ? (
176148
<VisuallyHidden>
@@ -189,221 +161,15 @@ export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner
189161
<IconButton
190162
aria-label="Dismiss banner"
191163
onClick={onDismiss}
192-
className={clsx({
193-
BannerDismiss: !enabled,
194-
[classes.BannerDismiss]: enabled,
195-
})}
164+
className={classes.BannerDismiss}
196165
icon={XIcon}
197166
variant="invisible"
198167
/>
199168
) : null}
200-
</StyledBanner>
169+
</section>
201170
)
202171
})
203172

204-
const StyledBanner = toggleStyledComponent(
205-
CSS_MODULES_FEATURE_FLAG,
206-
/**
207-
* For styling, it's important that the icons and the text have the same height
208-
* for alignment to occur in multi-line scenarios. Currently, we use a
209-
* line-height of `20px` so that means that the height of icons should match
210-
* that value.
211-
*/
212-
'div',
213-
styled.div`
214-
display: grid;
215-
grid-template-columns: auto minmax(0, 1fr) auto;
216-
align-items: start;
217-
background-color: var(--banner-bgColor);
218-
border: var(--borderWidth-thin, 1px) solid var(--banner-borderColor);
219-
padding: var(--base-size-8, 0.5rem);
220-
border-radius: var(--borderRadius-medium, ${get('radii.2')});
221-
222-
@supports (container-type: inline-size) {
223-
container: banner / inline-size;
224-
}
225-
226-
&[data-variant='critical'] {
227-
--banner-bgColor: ${get('colors.danger.subtle')};
228-
--banner-borderColor: ${get('colors.danger.muted')};
229-
--banner-icon-fgColor: ${get('colors.danger.fg')};
230-
}
231-
232-
&[data-variant='info'] {
233-
--banner-bgColor: ${get('colors.accent.subtle')};
234-
--banner-borderColor: ${get('colors.accent.muted')};
235-
--banner-icon-fgColor: ${get('colors.accent.fg')};
236-
}
237-
238-
&[data-variant='success'] {
239-
--banner-bgColor: ${get('colors.success.subtle')};
240-
--banner-borderColor: ${get('colors.success.muted')};
241-
--banner-icon-fgColor: ${get('colors.success.fg')};
242-
}
243-
244-
&[data-variant='upsell'] {
245-
--banner-bgColor: var(--bgColor-upsell-muted, ${get('colors.done.subtle')});
246-
--banner-borderColor: var(--borderColor-upsell-muted, ${get('colors.done.muted')});
247-
--banner-icon-fgColor: var(--fgColor-upsell-muted, ${get('colors.done.fg')});
248-
}
249-
250-
&[data-variant='warning'] {
251-
--banner-bgColor: ${get('colors.attention.subtle')};
252-
--banner-borderColor: ${get('colors.attention.muted')};
253-
--banner-icon-fgColor: ${get('colors.attention.fg')};
254-
}
255-
256-
/* BannerIcon ------------------------------------------------------------- */
257-
258-
.BannerIcon {
259-
display: grid;
260-
place-items: center;
261-
padding: var(--base-size-8, 0.5rem);
262-
}
263-
264-
.BannerIcon svg {
265-
color: var(--banner-icon-fgColor);
266-
fill: var(--banner-icon-fgColor);
267-
/* 20px is the line box height of the trailing action buttons */
268-
height: var(--base-size-20, 1.25rem);
269-
}
270-
271-
&[data-title-hidden=''] .BannerIcon svg {
272-
height: var(--base-size-16, 1rem);
273-
}
274-
275-
/* BannerContainer -------------------------------------------------------- */
276-
277-
.BannerContainer {
278-
font-size: var(--text-body-size-medium, 0.875rem);
279-
align-items: start;
280-
line-height: var(--text-body-lineHeight-medium, calc(20 / 14));
281-
row-gap: var(--base-size-4, 0.25rem);
282-
column-gap: var(--base-size-4, 0.25rem);
283-
}
284-
285-
& :where(.BannerContainer) {
286-
display: flex;
287-
flex-wrap: wrap;
288-
justify-content: space-between;
289-
}
290-
291-
&[data-dismissible]:not([data-title-hidden='']) .BannerContainer {
292-
display: grid;
293-
grid-template-columns: auto;
294-
grid-template-rows: auto;
295-
}
296-
297-
/* BannerContent ---------------------------------------------------------- */
298-
299-
.BannerContent {
300-
display: grid;
301-
row-gap: var(--base-size-4, 0.25rem);
302-
grid-column-start: 1;
303-
margin-block: var(--base-size-8, 0.5rem);
304-
}
305-
306-
&[data-title-hidden=''] .BannerContent {
307-
margin-block: var(--base-size-6, 0.375rem);
308-
}
309-
310-
@media screen and (min-width: 544px) {
311-
.BannerContent {
312-
flex: 1 1 0%;
313-
}
314-
}
315-
316-
.BannerTitle {
317-
margin: 0;
318-
font-size: inherit;
319-
font-weight: var(--base-text-weight-semibold, 600);
320-
}
321-
322-
/* BannerActions ---------------------------------------------------------- */
323-
.BannerActionsContainer {
324-
display: flex;
325-
column-gap: var(--base-size-12, 0.5rem);
326-
align-items: center;
327-
}
328-
329-
.BannerActions :where([data-primary-action='trailing']) {
330-
display: none;
331-
}
332-
333-
@media screen and (min-width: 544px) {
334-
.BannerActions :where([data-primary-action='trailing']) {
335-
display: flex;
336-
}
337-
338-
.BannerActions :where([data-primary-action='leading']) {
339-
display: none;
340-
}
341-
}
342-
343-
&[data-dismissible]:not([data-title-hidden]) .BannerActions {
344-
margin-block-end: var(--base-size-6, 0.375rem);
345-
}
346-
347-
&[data-dismissible]:not([data-title-hidden]) .BannerActionsContainer[data-primary-action='trailing'] {
348-
display: none;
349-
}
350-
351-
&[data-dismissible]:not([data-title-hidden]) .BannerActionsContainer[data-primary-action='leading'] {
352-
display: flex;
353-
}
354-
355-
/* BannerDismiss ---------------------------------------------------------- */
356-
357-
.BannerDismiss {
358-
display: grid;
359-
place-items: center;
360-
padding: var(--base-size-8, 0.5rem);
361-
margin-inline-start: var(--base-size-4, 0.25rem);
362-
}
363-
364-
.BannerDismiss svg {
365-
color: var(--banner-icon-fgColor);
366-
}
367-
`,
368-
)
369-
370-
const BannerContainerQuery = `
371-
@container banner (max-width: 500px) {
372-
.BannerContainer {
373-
display: grid;
374-
grid-template-rows: auto auto;
375-
}
376-
377-
.BannerActions {
378-
margin-block-end: var(--size-small, 0.375rem);
379-
}
380-
381-
.BannerActions [data-primary-action="trailing"] {
382-
display: none;
383-
}
384-
385-
.BannerActions [data-primary-action="leading"] {
386-
display: flex;
387-
}
388-
}
389-
390-
@container banner (min-width: 500px) {
391-
.BannerContainer {
392-
display: grid;
393-
grid-template-columns: auto auto;
394-
}
395-
396-
.BannerActions [data-primary-action="trailing"] {
397-
display: flex;
398-
min-height: var(--base-size-32, 2rem);
399-
}
400-
401-
.BannerActions [data-primary-action="leading"] {
402-
display: none;
403-
}
404-
}
405-
`
406-
407173
type HeadingElement = 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
408174

409175
export type BannerTitleProps<As extends HeadingElement> = {
@@ -413,16 +179,8 @@ export type BannerTitleProps<As extends HeadingElement> = {
413179

414180
export function BannerTitle<As extends HeadingElement>(props: BannerTitleProps<As>) {
415181
const {as: Heading = 'h2', className, children, ...rest} = props
416-
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
417182
return (
418-
<Heading
419-
{...rest}
420-
className={clsx(className, {
421-
[classes.BannerTitle]: enabled,
422-
BannerTitle: !enabled,
423-
})}
424-
data-banner-title=""
425-
>
183+
<Heading {...rest} className={clsx(className, classes.BannerTitle)} data-banner-title="">
426184
{children}
427185
</Heading>
428186
)
@@ -444,31 +202,13 @@ export type BannerActionsProps = {
444202
}
445203

446204
export function BannerActions({primaryAction, secondaryAction}: BannerActionsProps) {
447-
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
448205
return (
449-
<div
450-
className={clsx({
451-
[classes.BannerActions]: enabled,
452-
BannerActions: !enabled,
453-
})}
454-
>
455-
<div
456-
className={clsx({
457-
[classes.BannerActionsContainer]: enabled,
458-
BannerActionsContainer: !enabled,
459-
})}
460-
data-primary-action="trailing"
461-
>
206+
<div className={classes.BannerActions}>
207+
<div className={classes.BannerActionsContainer} data-primary-action="trailing">
462208
{secondaryAction ?? null}
463209
{primaryAction ?? null}
464210
</div>
465-
<div
466-
className={clsx({
467-
[classes.BannerActionsContainer]: enabled,
468-
BannerActionsContainer: !enabled,
469-
})}
470-
data-primary-action="leading"
471-
>
211+
<div className={classes.BannerActionsContainer} data-primary-action="leading">
472212
{primaryAction ?? null}
473213
{secondaryAction ?? null}
474214
</div>

0 commit comments

Comments
 (0)
Please sign in to comment.