diff --git a/.changeset/lemon-files-complain.md b/.changeset/lemon-files-complain.md new file mode 100644 index 00000000000..2fcaf67849e --- /dev/null +++ b/.changeset/lemon-files-complain.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Remove CSS modules feature flag from ButtonGroup diff --git a/.changeset/mean-plants-cover.md b/.changeset/mean-plants-cover.md new file mode 100644 index 00000000000..f28776936e4 --- /dev/null +++ b/.changeset/mean-plants-cover.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Remove CSS modules feature flag from Details diff --git a/.changeset/two-apples-juggle.md b/.changeset/two-apples-juggle.md new file mode 100644 index 00000000000..dfa6dca0054 --- /dev/null +++ b/.changeset/two-apples-juggle.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Remove CSS modules feature flag from Radio diff --git a/.changeset/two-jokes-compete.md b/.changeset/two-jokes-compete.md new file mode 100644 index 00000000000..aa9ef5878cf --- /dev/null +++ b/.changeset/two-jokes-compete.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Remove the CSS module feature flag from Pagehead diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-colorblind-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-colorblind-linux.png new file mode 100644 index 00000000000..0d98488a13b Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-dimmed-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-dimmed-linux.png new file mode 100644 index 00000000000..c1aa5490e02 Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-high-contrast-linux.png new file mode 100644 index 00000000000..bcf4048a588 Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-linux.png new file mode 100644 index 00000000000..0d98488a13b Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-tritanopia-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-tritanopia-linux.png new file mode 100644 index 00000000000..0d98488a13b Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-colorblind-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-colorblind-linux.png new file mode 100644 index 00000000000..0a4942ddf89 Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-high-contrast-linux.png new file mode 100644 index 00000000000..2359588a9c5 Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-linux.png new file mode 100644 index 00000000000..0a4942ddf89 Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-tritanopia-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-tritanopia-linux.png new file mode 100644 index 00000000000..0a4942ddf89 Binary files /dev/null and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-SX-Prop-light-tritanopia-linux.png differ diff --git a/e2e/components/ButtonGroup.test.ts b/e2e/components/ButtonGroup.test.ts index 243f070df7f..38c79d95c69 100644 --- a/e2e/components/ButtonGroup.test.ts +++ b/e2e/components/ButtonGroup.test.ts @@ -142,4 +142,32 @@ test.describe('ButtonGroup', () => { }) } }) + + test.describe('SX Prop', () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: 'components-buttongroup-devonly--sx-prop', + globals: { + colorScheme: theme, + }, + }) + + // Default state + expect(await page.screenshot()).toMatchSnapshot(`ButtonGroup.SX Prop.${theme}.png`) + }) + + test('axe @aat', async ({page}) => { + await visit(page, { + id: 'components-buttongroup-devonly--sx-prop', + globals: { + colorScheme: theme, + }, + }) + await expect(page).toHaveNoViolations() + }) + }) + } + }) }) diff --git a/packages/react/src/ButtonGroup/ButtonGroup.dev.stories.tsx b/packages/react/src/ButtonGroup/ButtonGroup.dev.stories.tsx index c0d2f2645b7..6dc85d48472 100644 --- a/packages/react/src/ButtonGroup/ButtonGroup.dev.stories.tsx +++ b/packages/react/src/ButtonGroup/ButtonGroup.dev.stories.tsx @@ -63,3 +63,11 @@ export const LinkButtonWithIconButtons = () => ( ) + +export const SxProp = () => ( + + + + + +) diff --git a/packages/react/src/ButtonGroup/ButtonGroup.tsx b/packages/react/src/ButtonGroup/ButtonGroup.tsx index e3cc3e0632a..0869e1e1b92 100644 --- a/packages/react/src/ButtonGroup/ButtonGroup.tsx +++ b/packages/react/src/ButtonGroup/ButtonGroup.tsx @@ -1,109 +1,25 @@ -import styled from 'styled-components' -import React from 'react' -import sx from '../sx' -import type {ComponentProps} from '../utils/types' +import React, {type PropsWithChildren} from 'react' +import {type SxProp} from '../sx' import classes from './ButtonGroup.module.css' -import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' import {clsx} from 'clsx' -import {useFeatureFlag} from '../FeatureFlags' import {FocusKeys, useFocusZone} from '../hooks/useFocusZone' import {useProvidedRefOrCreate} from '../hooks' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' +import Box from '../Box' +import {defaultSxProp} from '../utils/defaultSxProp' -const StyledButtonGroup = toggleStyledComponent( - 'primer_react_css_modules_ga', - 'div', - styled.div` - display: inline-flex; - vertical-align: middle; - isolation: isolate; - - & > *:not([data-loading-wrapper]) { - /* stylelint-disable-next-line primer/spacing */ - margin-inline-end: -1px; - position: relative; - - /* reset border-radius */ - button, - a { - border-radius: 0; - } - - &:first-child { - button, - a { - border-top-left-radius: var(--borderRadius-medium); - border-bottom-left-radius: var(--borderRadius-medium); - } - } - - &:last-child { - button, - a { - border-top-right-radius: var(--borderRadius-medium); - border-bottom-right-radius: var(--borderRadius-medium); - } - } - - &:focus, - &:active, - &:hover { - z-index: 1; - } - } - - /* this is a workaround until portal based tooltips are fully removed from dotcom */ - &:has(div:last-child:empty) { - button, - a { - border-radius: var(--borderRadius-medium); - } - } - - /* if child is loading button */ - & > *[data-loading-wrapper] { - /* stylelint-disable-next-line primer/spacing */ - margin-inline-end: -1px; - position: relative; - /* reset border-radius */ - button, - a { - border-radius: 0; - } - - &:focus, - &:active, - &:hover { - z-index: 1; - } - &:first-child { - button, - a { - border-top-left-radius: var(--borderRadius-medium); - border-bottom-left-radius: var(--borderRadius-medium); - } - } - - &:last-child { - button, - a { - border-top-right-radius: var(--borderRadius-medium); - border-bottom-right-radius: var(--borderRadius-medium); - } - } - } - - ${sx}; - `, -) - -export type ButtonGroupProps = ComponentProps +export type ButtonGroupProps = { + /** The role of the group */ + role?: string + /** className passed in for styling */ + className?: string +} & PropsWithChildren & + SxProp const ButtonGroup = React.forwardRef(function ButtonGroup( - {children, className, role, ...rest}, + {children, className, role, sx, ...rest}, forwardRef, ) { - const enabled = useFeatureFlag('primer_react_css_modules_ga') const buttons = React.Children.map(children, (child, index) =>
{child}
) const buttonRef = useProvidedRefOrCreate(forwardRef as React.RefObject) @@ -114,17 +30,18 @@ const ButtonGroup = React.forwardRef(function But focusOutBehavior: 'wrap', }) + if (sx !== defaultSxProp) { + return ( + + {buttons} + + ) + } + return ( - +
{buttons} - +
) }) as PolymorphicForwardRefComponent<'div', ButtonGroupProps> diff --git a/packages/react/src/Details/Details.tsx b/packages/react/src/Details/Details.tsx index bc2a4c30909..4a001484b8e 100644 --- a/packages/react/src/Details/Details.tsx +++ b/packages/react/src/Details/Details.tsx @@ -1,33 +1,13 @@ import React, {useEffect, useState, type ComponentPropsWithoutRef, type ReactElement} from 'react' -import styled from 'styled-components' import type {SxProp} from '../sx' -import sx from '../sx' -import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' -import {useFeatureFlag} from '../FeatureFlags' import {clsx} from 'clsx' import classes from './Details.module.css' import {useMergedRefs} from '../internal/hooks/useMergedRefs' - -const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga' - -const StyledDetails = toggleStyledComponent( - CSS_MODULES_FEATURE_FLAG, - 'details', - styled.details` - & > summary { - list-style: none; - } - & > summary::-webkit-details-marker { - display: none; - } - - ${sx}; - `, -) +import {defaultSxProp} from '../utils/defaultSxProp' +import Box from '../Box' const Root = React.forwardRef( - ({className, children, ...rest}, forwardRef): ReactElement => { - const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + ({className, children, sx: sxProp = defaultSxProp, ...rest}, forwardRef): ReactElement => { const detailsRef = React.useRef(null) const ref = useMergedRefs(forwardRef, detailsRef) const [hasSummary, setHasSummary] = useState(false) @@ -60,12 +40,22 @@ const Root = React.forwardRef( } }, []) + if (sxProp !== defaultSxProp) { + return ( + + {/* Include default summary if summary is not provided */} + {!hasSummary && {'See Details'}} + {children} + + ) + } + return ( - +
{/* Include default summary if summary is not provided */} {!hasSummary && {'See Details'}} {children} - +
) }, ) diff --git a/packages/react/src/Details/__tests__/Details.test.tsx b/packages/react/src/Details/__tests__/Details.test.tsx index 28be85e0945..8be6801cae8 100644 --- a/packages/react/src/Details/__tests__/Details.test.tsx +++ b/packages/react/src/Details/__tests__/Details.test.tsx @@ -7,7 +7,7 @@ import {behavesAsComponent, checkExports} from '../../utils/testing' import axe from 'axe-core' describe('Details', () => { - behavesAsComponent({Component: Details}) + behavesAsComponent({Component: Details, options: {skipAs: true}}) checkExports('Details', { default: Details, diff --git a/packages/react/src/Pagehead/Pagehead.stories.tsx b/packages/react/src/Pagehead/Pagehead.stories.tsx index ceea390c631..7213dbf414e 100644 --- a/packages/react/src/Pagehead/Pagehead.stories.tsx +++ b/packages/react/src/Pagehead/Pagehead.stories.tsx @@ -35,22 +35,4 @@ Playground.argTypes = { options: ['div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], }, }, - forwardedAs: { - controls: false, - table: { - disable: true, - }, - }, - ref: { - controls: false, - table: { - disable: true, - }, - }, - theme: { - controls: false, - table: { - disable: true, - }, - }, } diff --git a/packages/react/src/Pagehead/Pagehead.test.tsx b/packages/react/src/Pagehead/Pagehead.test.tsx index 0c1fc8bad2b..d4844cdc75a 100644 --- a/packages/react/src/Pagehead/Pagehead.test.tsx +++ b/packages/react/src/Pagehead/Pagehead.test.tsx @@ -1,6 +1,5 @@ import React from 'react' import Pagehead from '../Pagehead' -import theme from '../theme' import {behavesAsComponent, checkExports} from '../utils/testing' import {render as HTMLRender} from '@testing-library/react' import axe from 'axe-core' @@ -13,7 +12,7 @@ describe('Pagehead', () => { }) it('should have no axe violations', async () => { - const {container} = HTMLRender(Pagehead) + const {container} = HTMLRender(Pagehead) const results = await axe.run(container) expect(results).toHaveNoViolations() }) diff --git a/packages/react/src/Pagehead/Pagehead.tsx b/packages/react/src/Pagehead/Pagehead.tsx index 3aeb74dcfaf..7f762bf1883 100644 --- a/packages/react/src/Pagehead/Pagehead.tsx +++ b/packages/react/src/Pagehead/Pagehead.tsx @@ -1,42 +1,22 @@ -import styled from 'styled-components' -import React, {type ComponentProps} from 'react' +import React from 'react' import {clsx} from 'clsx' -import {get} from '../constants' -import sx, {type SxProp} from '../sx' -import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' +import {type SxProp} from '../sx' import classes from './Pagehead.module.css' -import {useFeatureFlag} from '../FeatureFlags' +import {defaultSxProp} from '../utils/defaultSxProp' +import Box from '../Box' -const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga' - -/** - * @deprecated - */ -const StyledComponentPagehead = toggleStyledComponent( - CSS_MODULES_FEATURE_FLAG, - 'div', - styled.div` - position: relative; - padding-top: ${get('space.4')}; - padding-bottom: ${get('space.4')}; - margin-bottom: ${get('space.4')}; - border-bottom: 1px solid ${get('colors.border.default')}; - ${sx}; - `, -) - -const Pagehead = ({className, ...rest}: PageheadProps) => { - const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) - - if (enabled) { - return +const Pagehead = ({className, sx: sxProp = defaultSxProp, ...rest}: PageheadProps) => { + if (sxProp !== defaultSxProp || rest.as) { + return } - - return + return
} /** * @deprecated */ -export type PageheadProps = ComponentProps & SxProp +export type PageheadProps = SxProp & + React.ComponentPropsWithoutRef<'div'> & { + as?: React.ElementType + } export default Pagehead diff --git a/packages/react/src/Pagehead/Pagehead.types.test.tsx b/packages/react/src/Pagehead/Pagehead.types.test.tsx index df691989646..d4d46581f1c 100644 --- a/packages/react/src/Pagehead/Pagehead.types.test.tsx +++ b/packages/react/src/Pagehead/Pagehead.types.test.tsx @@ -4,7 +4,3 @@ import Pagehead from '../Pagehead' export function shouldAcceptCallWithNoProps() { return } - -export function shouldNotAcceptSystemProps() { - return -} diff --git a/packages/react/src/Radio/Radio.tsx b/packages/react/src/Radio/Radio.tsx index 33edae3fd17..be740965bc7 100644 --- a/packages/react/src/Radio/Radio.tsx +++ b/packages/react/src/Radio/Radio.tsx @@ -1,18 +1,13 @@ -import styled from 'styled-components' import type {ChangeEventHandler, InputHTMLAttributes, ReactElement} from 'react' import React, {useContext} from 'react' import type {SxProp} from '../sx' -import sx from '../sx' import type {FormValidationStatus} from '../utils/types/FormValidationStatus' import {RadioGroupContext} from '../RadioGroup/RadioGroup' -import getGlobalFocusStyles from '../internal/utils/getGlobalFocusStyles' -import {get} from '../constants' -import {sharedCheckboxAndRadioStyles} from '../internal/utils/sharedCheckboxAndRadioStyles' -import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' -import {useFeatureFlag} from '../FeatureFlags' import {clsx} from 'clsx' import classes from './Radio.module.css' import sharedClasses from '../Checkbox/shared.module.css' +import {defaultSxProp} from '../utils/defaultSxProp' +import Box from '../Box' export type RadioProps = { /** @@ -47,42 +42,6 @@ export type RadioProps = { } & InputHTMLAttributes & SxProp -const StyledRadio = toggleStyledComponent( - 'primer_react_css_modules_ga', - 'input', - styled.input` - ${sharedCheckboxAndRadioStyles}; - border-radius: var(--borderRadius-full, 100vh); - transition: - background-color, - border-color 80ms cubic-bezier(0.33, 1, 0.68, 1); /* checked -> unchecked - add 120ms delay to fully see animation-out */ - - &:checked { - border-width: var(--base-size-4, 4px); - border-color: var( - --control-checked-bgColor-rest, - ${get('colors.accent.fg')} - ); /* using bgColor here to avoid a border change in dark high contrast */ - background-color: var(--control-checked-fgColor-rest, ${get('colors.fg.onEmphasis')}); - - &:disabled { - cursor: not-allowed; - border-color: ${get('colors.fg.muted')}; - background-color: ${get('colors.fg.muted')}; - } - } - - ${getGlobalFocusStyles()}; - - @media (forced-colors: active) { - background-color: canvastext; - border-color: canvastext; - } - - ${sx} - `, -) - /** * An accessible, native radio component for selecting one option from a list. */ @@ -93,7 +52,7 @@ const Radio = React.forwardRef( disabled, name: nameProp, onChange, - sx: sxProp, + sx: sxProp = defaultSxProp, required, validationStatus, value, @@ -103,7 +62,6 @@ const Radio = React.forwardRef( ref, ): ReactElement => { const radioGroupContext = useContext(RadioGroupContext) - const enabled = useFeatureFlag('primer_react_css_modules_ga') const handleOnChange: ChangeEventHandler = e => { radioGroupContext?.onChange && radioGroupContext.onChange(e) onChange && onChange(e) @@ -117,8 +75,32 @@ const Radio = React.forwardRef( ) } + if (sxProp !== defaultSxProp) { + return ( + // eslint-disable-next-line github/a11y-role-supports-aria-props + + ) + } + return ( - ( required={required} aria-required={required ? 'true' : 'false'} aria-invalid={validationStatus === 'error' ? 'true' : 'false'} - sx={sxProp} onChange={handleOnChange} - className={clsx(className, { - [sharedClasses.Input]: enabled, - [classes.Radio]: enabled, - })} + className={clsx(className, sharedClasses.Input, classes.Radio)} {...rest} /> ) diff --git a/packages/react/src/__tests__/Radio.test.tsx b/packages/react/src/__tests__/Radio.test.tsx index 7fdad2d5e83..310329b71b8 100644 --- a/packages/react/src/__tests__/Radio.test.tsx +++ b/packages/react/src/__tests__/Radio.test.tsx @@ -14,7 +14,7 @@ describe('Radio', () => { jest.resetAllMocks() }) - behavesAsComponent({Component: Radio, toRender: () => }) + behavesAsComponent({options: {skipAs: true}, Component: Radio, toRender: () => }) checkExports('Radio', { default: Radio,