Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into update_stylelint
Browse files Browse the repository at this point in the history
  • Loading branch information
jonrohan committed Oct 8, 2024
1 parent 62acfe1 commit 77fc108
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/mighty-buckets-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Convert `Breadcrumbs` and `Breadcrumbs.Item` to CSS modules behind the primer_react_css_modules_team feature flag
48 changes: 48 additions & 0 deletions e2e/components/Breadcrumbs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,32 @@ test.describe('Breadcrumbs', () => {
id: 'components-breadcrumbs--default',
globals: {
colorScheme: theme,
featureFlags: {
primer_react_css_modules_team: true,
},
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`Breadcrumbs.Default.${theme}.png`)

// Hover state
await page.getByRole('link', {name: 'Home'}).hover()
expect(await page.screenshot()).toMatchSnapshot(`Breadcrumbs.Default.${theme}.hover.png`)

// Focus state
await page.keyboard.press('Tab')
expect(await page.screenshot()).toMatchSnapshot(`Breadcrumbs.Default.${theme}.focus.png`)
})

test('default @vrt (styled components)', async ({page}) => {
await visit(page, {
id: 'components-breadcrumbs--default',
globals: {
colorScheme: theme,
featureFlags: {
primer_react_css_modules_team: false,
},
},
})

Expand All @@ -31,6 +57,28 @@ test.describe('Breadcrumbs', () => {
id: 'components-breadcrumbs--default',
globals: {
colorScheme: theme,
featureFlags: {
primer_react_css_modules_team: true,
},
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},
},
})
})

test('axe @aat (styled components)', async ({page}) => {
await visit(page, {
id: 'components-breadcrumbs--default',
globals: {
colorScheme: theme,
featureFlags: {
primer_react_css_modules_team: false,
},
},
})
await expect(page).toHaveNoViolations({
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/Banner/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ const StyledBanner = toggleStyledComponent(
* line-height of `20px` so that means that the height of icons should match
* that value.
*/
'div',
styled.div`
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
Expand Down
57 changes: 57 additions & 0 deletions packages/react/src/Breadcrumbs/Breadcrumbs.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.BreadcrumbsBase {
display: flex;
justify-content: space-between;
}

.BreadcrumbsList {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}

.ItemWrapper {
display: inline-block;
font-size: var(--text-body-size-medium);
white-space: nowrap;
list-style: none;

&::after {
display: inline-block;
height: 0.8em;
/* stylelint-disable-next-line primer/spacing */
margin: 0 0.5em;
font-size: var(--text-body-size-medium);
content: '';
/* stylelint-disable-next-line primer/borders, primer/colors */
border-right: 0.1em solid var(--fgColor-muted);
transform: rotate(15deg) translateY(0.0625em);
}

&:first-child {
margin-left: 0;
}

&:last-child {
&::after {
content: none;
}
}
}

.Item {
display: inline-block;

&:hover,
&:focus {
text-decoration: underline;
}
}

.ItemSelected {
color: var(--fgColor-default);
pointer-events: none;

&:focus {
text-decoration: none;
}
}
147 changes: 98 additions & 49 deletions packages/react/src/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,82 @@ import {get} from '../constants'
import type {SxProp} from '../sx'
import sx from '../sx'
import type {ComponentProps} from '../utils/types'
import classes from './Breadcrumbs.module.css'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {FeatureFlags, useFeatureFlag} from '../FeatureFlags'
import Link from '../Link'

const SELECTED_CLASS = 'selected'

const Wrapper = styled.li`
display: inline-block;
white-space: nowrap;
list-style: none;
&::after {
font-size: ${get('fontSizes.1')};
content: '';
const Wrapper = toggleStyledComponent(
'primer_react_css_modules_team',
'li',
styled.li`
display: inline-block;
height: 0.8em;
margin: 0 0.5em;
border-right: 0.1em solid;
border-color: ${get('colors.fg.muted')};
transform: rotate(15deg) translateY(0.0625em);
}
&:first-child {
margin-left: 0;
}
&:last-child {
white-space: nowrap;
list-style: none;
&::after {
content: none;
font-size: ${get('fontSizes.1')};
content: '';
display: inline-block;
height: 0.8em;
margin: 0 0.5em;
border-right: 0.1em solid;
border-color: ${get('colors.fg.muted')};
transform: rotate(15deg) translateY(0.0625em);
}
}
`
&:first-child {
margin-left: 0;
}
&:last-child {
&::after {
content: none;
}
}
`,
)

const BreadcrumbsBase = styled.nav<SxProp>`
display: flex;
justify-content: space-between;
${sx};
`
const BreadcrumbsBase = toggleStyledComponent(
'primer_react_css_modules_team',
'nav',
styled.nav<SxProp>`
display: flex;
justify-content: space-between;
${sx};
`,
)

export type BreadcrumbsProps = React.PropsWithChildren<
{
className?: string
} & SxProp
>

const BreadcrumbsList = ({children}: React.PropsWithChildren) => {
const enabled = useFeatureFlag('primer_react_css_modules_team')
if (enabled) {
return <ol className={classes.BreadcrumbsList}>{children}</ol>
}

return (
<Box as="ol" my={0} pl={0}>
{children}
</Box>
)
}

function Breadcrumbs({className, children, sx: sxProp}: React.PropsWithChildren<BreadcrumbsProps>) {
const wrappedChildren = React.Children.map(children, child => <Wrapper>{child}</Wrapper>)
const enabled = useFeatureFlag('primer_react_css_modules_team')
const wrappedChildren = React.Children.map(children, child => (
<Wrapper className={clsx({[classes.ItemWrapper]: enabled})}>{child}</Wrapper>
))
return (
<BreadcrumbsBase className={className} aria-label="Breadcrumbs" sx={sxProp}>
<Box as="ol" my={0} pl={0}>
{wrappedChildren}
</Box>
<BreadcrumbsBase
className={clsx(className, {[classes.BreadcrumbsBase]: enabled})}
aria-label="Breadcrumbs"
sx={sxProp}
>
<BreadcrumbsList>{wrappedChildren}</BreadcrumbsList>
</BreadcrumbsBase>
)
}
Expand All @@ -62,27 +92,46 @@ type StyledBreadcrumbsItemProps = {
selected?: boolean
} & SxProp

const BreadcrumbsItem = styled.a.attrs<StyledBreadcrumbsItemProps>(props => ({
className: clsx(props.selected && SELECTED_CLASS, props.className),
'aria-current': props.selected ? 'page' : null,
}))<StyledBreadcrumbsItemProps>`
color: ${get('colors.accent.fg')};
display: inline-block;
font-size: ${get('fontSizes.1')};
text-decoration: none;
&:hover,
&:focus {
text-decoration: underline;
}
&.selected {
color: ${get('colors.fg.default')};
pointer-events: none;
}
&.selected:focus {
const StyledBreadcrumbsItem = toggleStyledComponent(
'primer_react_css_modules_team',
'a',
styled.a.attrs<StyledBreadcrumbsItemProps>(props => ({
className: clsx(props.selected && SELECTED_CLASS, props.className),
'aria-current': props.selected ? 'page' : null,
}))<StyledBreadcrumbsItemProps>`
color: ${get('colors.accent.fg')};
display: inline-block;
font-size: ${get('fontSizes.1')};
text-decoration: none;
&:hover,
&:focus {
text-decoration: underline;
}
&.selected {
color: ${get('colors.fg.default')};
pointer-events: none;
}
&.selected:focus {
text-decoration: none;
}
${sx};
`,
)
const BreadcrumbsItem = ({
selected,
...props
}: StyledBreadcrumbsItemProps & React.ComponentPropsWithoutRef<typeof Link>) => {
const enabled = useFeatureFlag('primer_react_css_modules_team')
if (enabled) {
return (
// Remove this when the feature flag is removed from Link
<FeatureFlags flags={{primer_react_css_modules_ga: true}}>
<Link className={clsx(classes.Item, {[classes.ItemSelected]: selected})} {...props} />
</FeatureFlags>
)
}
${sx};
`
return <StyledBreadcrumbsItem selected={selected} {...props} />
}

Breadcrumbs.displayName = 'Breadcrumbs'

Expand Down
1 change: 1 addition & 0 deletions packages/react/src/ButtonGroup/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {useFeatureFlag} from '../FeatureFlags'

const StyledButtonGroup = toggleStyledComponent(
'primer_react_css_modules_staff',
'div',
styled.div`
display: inline-flex;
vertical-align: middle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,32 @@ import {toggleStyledComponent} from '../toggleStyledComponent'
import styled from 'styled-components'

describe('toggleStyledComponent', () => {
test('renders the component as `defaultAs` when flag is enabled and no `as` prop is provided', () => {
const TestComponent = toggleStyledComponent('testFeatureFlag', 'span', ({as: BaseComponent = 'div'}) => (
<BaseComponent />
))
const {container} = render(
<FeatureFlags flags={{testFeatureFlag: true}}>
<TestComponent />
</FeatureFlags>,
)
expect(container.firstChild).toBeInstanceOf(HTMLSpanElement)
})

test('renders the component as `defaultAs` when flag is disabled and no `as` prop is provided', () => {
const TestComponent = toggleStyledComponent('testFeatureFlag', 'span', ({as: BaseComponent = 'div'}) => (
<BaseComponent />
))
const {container} = render(
<FeatureFlags flags={{testFeatureFlag: false}}>
<TestComponent />
</FeatureFlags>,
)
expect(container.firstChild).toBeInstanceOf(HTMLSpanElement)
})

test('renders the `as` prop when flag is enabled', () => {
const TestComponent = toggleStyledComponent('testFeatureFlag', () => <div />)
const TestComponent = toggleStyledComponent('testFeatureFlag', 'div', () => <div />)
const {container} = render(
<FeatureFlags flags={{testFeatureFlag: true}}>
<TestComponent as="button" />
Expand All @@ -16,7 +40,7 @@ describe('toggleStyledComponent', () => {
})

test('renders a div as fallback when flag is enabled and no `as` prop is provided', () => {
const TestComponent = toggleStyledComponent('testFeatureFlag', () => <div />)
const TestComponent = toggleStyledComponent('testFeatureFlag', 'div', () => <div />)
const {container} = render(
<FeatureFlags flags={{testFeatureFlag: true}}>
<TestComponent />
Expand All @@ -26,7 +50,7 @@ describe('toggleStyledComponent', () => {
})

test('renders Box with `as` if `sx` is provided and flag is enabled', () => {
const TestComponent = toggleStyledComponent('testFeatureFlag', () => styled.div``)
const TestComponent = toggleStyledComponent('testFeatureFlag', 'div', () => styled.div``)
const {container} = render(
<FeatureFlags flags={{testFeatureFlag: true}}>
<TestComponent as="button" sx={{color: 'red'}} />
Expand All @@ -38,7 +62,7 @@ describe('toggleStyledComponent', () => {
})

test('renders styled component when flag is disabled', () => {
const StyledComponent = toggleStyledComponent('testFeatureFlag', styled.div.attrs({['data-styled']: true})``)
const StyledComponent = toggleStyledComponent('testFeatureFlag', 'div', styled.div.attrs({['data-styled']: true})``)
const {container} = render(
<FeatureFlags flags={{testFeatureFlag: false}}>
<StyledComponent />
Expand Down
Loading

0 comments on commit 77fc108

Please sign in to comment.