From e89edbc6c8892e324eeb1af381a70f2ac0e75a3c Mon Sep 17 00:00:00 2001 From: Jon Rohan Date: Thu, 14 Nov 2024 22:01:18 -0800 Subject: [PATCH] feat(Breadcrumbs): Convert Breadcrumbs to css module behind feature flag (#5150) * Convert Breadcrumbs to css module behind feature flag * Create fair-wolves-attack.md * Update @primer/react version to minor * Change `ComponentPropsWithoutRef` to `ComponentPropsWithRef` * Lint fix * Use old styled.a for item * Update snapshot and type * Refactor BreadcrumbsItem component with generics * Remove ref * Unused import * Refactor BreadcrumbsItem to support polymorphic components and add tests for "as" prop * Remove test for 'as' prop in BreadcrumbsItem * Remove snapshot test for 'as' prop in BreadcrumbsItem --- .changeset/fair-wolves-attack.md | 5 + .../src/Breadcrumbs/Breadcrumbs.module.css | 60 +++++++ .../react/src/Breadcrumbs/Breadcrumbs.tsx | 154 ++++++++++++------ .../BreadcrumbsItem.test.tsx.snap | 1 - 4 files changed, 167 insertions(+), 53 deletions(-) create mode 100644 .changeset/fair-wolves-attack.md create mode 100644 packages/react/src/Breadcrumbs/Breadcrumbs.module.css diff --git a/.changeset/fair-wolves-attack.md b/.changeset/fair-wolves-attack.md new file mode 100644 index 00000000000..c618fad7d05 --- /dev/null +++ b/.changeset/fair-wolves-attack.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Convert Breadcrumbs to css module behind feature flag diff --git a/packages/react/src/Breadcrumbs/Breadcrumbs.module.css b/packages/react/src/Breadcrumbs/Breadcrumbs.module.css new file mode 100644 index 00000000000..96c61e7072c --- /dev/null +++ b/packages/react/src/Breadcrumbs/Breadcrumbs.module.css @@ -0,0 +1,60 @@ +.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; + font-size: var(--text-body-size-medium); + color: var(--fgColor-link); + text-decoration: none; + + &:hover, + &:focus { + text-decoration: underline; + } +} + +.ItemSelected { + color: var(--fgColor-default); + pointer-events: none; + + &:focus { + text-decoration: none; + } +} diff --git a/packages/react/src/Breadcrumbs/Breadcrumbs.tsx b/packages/react/src/Breadcrumbs/Breadcrumbs.tsx index 7b1753aa853..32b72149679 100644 --- a/packages/react/src/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/react/src/Breadcrumbs/Breadcrumbs.tsx @@ -7,38 +7,51 @@ 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 {useFeatureFlag} from '../FeatureFlags' +import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' const SELECTED_CLASS = 'selected' +const CSS_MODULES_FLAG = 'primer_react_css_modules_team' -const Wrapper = styled.li` - display: inline-block; - white-space: nowrap; - list-style: none; - &::after { - font-size: ${get('fontSizes.1')}; - content: ''; +const Wrapper = toggleStyledComponent( + CSS_MODULES_FLAG, + '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` - display: flex; - justify-content: space-between; - ${sx}; -` +const BreadcrumbsBase = toggleStyledComponent( + CSS_MODULES_FLAG, + 'nav', + styled.nav` + display: flex; + justify-content: space-between; + ${sx}; + `, +) export type BreadcrumbsProps = React.PropsWithChildren< { @@ -46,13 +59,31 @@ export type BreadcrumbsProps = React.PropsWithChildren< } & SxProp > +const BreadcrumbsList = ({children}: React.PropsWithChildren) => { + const enabled = useFeatureFlag(CSS_MODULES_FLAG) + if (enabled) { + return
    {children}
+ } + + return ( + + {children} + + ) +} + function Breadcrumbs({className, children, sx: sxProp}: React.PropsWithChildren) { - const wrappedChildren = React.Children.map(children, child => {child}) + const enabled = useFeatureFlag(CSS_MODULES_FLAG) + const wrappedChildren = React.Children.map(children, child => ( + {child} + )) return ( - - - {wrappedChildren} - + + {wrappedChildren} ) } @@ -60,29 +91,48 @@ function Breadcrumbs({className, children, sx: sxProp}: React.PropsWithChildren< type StyledBreadcrumbsItemProps = { to?: To selected?: boolean -} & SxProp - -const BreadcrumbsItem = styled.a.attrs(props => ({ - className: clsx(props.selected && SELECTED_CLASS, props.className), - 'aria-current': props.selected ? 'page' : null, -}))` - 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 { + className?: string +} & SxProp & + React.ComponentPropsWithoutRef<'a'> + +const StyledBreadcrumbsItem = toggleStyledComponent( + CSS_MODULES_FLAG, + 'a', + styled.a` + color: ${get('colors.accent.fg')}; + display: inline-block; + font-size: ${get('fontSizes.1')}; text-decoration: none; - } - ${sx}; -` + &:hover, + &:focus { + text-decoration: underline; + } + &.selected { + color: ${get('colors.fg.default')}; + pointer-events: none; + } + &.selected:focus { + text-decoration: none; + } + ${sx}; + `, +) + +const BreadcrumbsItem = React.forwardRef(({selected, className, ...rest}, ref) => { + const enabled = useFeatureFlag(CSS_MODULES_FLAG) + return ( + + ) +}) as PolymorphicForwardRefComponent<'a', StyledBreadcrumbsItemProps> Breadcrumbs.displayName = 'Breadcrumbs' diff --git a/packages/react/src/Breadcrumbs/__tests__/__snapshots__/BreadcrumbsItem.test.tsx.snap b/packages/react/src/Breadcrumbs/__tests__/__snapshots__/BreadcrumbsItem.test.tsx.snap index 9b4c8a741f8..250ad1236ce 100644 --- a/packages/react/src/Breadcrumbs/__tests__/__snapshots__/BreadcrumbsItem.test.tsx.snap +++ b/packages/react/src/Breadcrumbs/__tests__/__snapshots__/BreadcrumbsItem.test.tsx.snap @@ -28,6 +28,5 @@ exports[`Breadcrumbs.Item respects the "selected" prop 1`] = ` `;