From 719def7ea83f5212dc6ba888fe22d29b6620ce37 Mon Sep 17 00:00:00 2001 From: Randall Krauskopf <104226843+randall-krauskopf@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:57:25 -0500 Subject: [PATCH 01/26] feat(Select): Convert `Select` component to CSS Modules behind feature flag (#5194) * initial commit * changeset and lint * fix css module comments * fix Select import to please the FormControl component's type comparision * fix slot comparison in form control --- .changeset/modern-icons-clean.md | 5 + .../react/src/FormControl/FormControl.tsx | 2 +- .../react/src/Select/Select.dev.stories.tsx | 25 +++ .../src/Select/Select.features.stories.tsx | 3 +- packages/react/src/Select/Select.figma.tsx | 2 +- packages/react/src/Select/Select.module.css | 67 ++++++ packages/react/src/Select/Select.stories.tsx | 5 +- packages/react/src/Select/Select.tsx | 199 +++++++++++------- packages/react/src/index.ts | 5 +- 9 files changed, 235 insertions(+), 78 deletions(-) create mode 100644 .changeset/modern-icons-clean.md create mode 100644 packages/react/src/Select/Select.dev.stories.tsx create mode 100644 packages/react/src/Select/Select.module.css diff --git a/.changeset/modern-icons-clean.md b/.changeset/modern-icons-clean.md new file mode 100644 index 00000000000..ac697326b5e --- /dev/null +++ b/.changeset/modern-icons-clean.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Migrate `Select` component to css modules diff --git a/packages/react/src/FormControl/FormControl.tsx b/packages/react/src/FormControl/FormControl.tsx index a6ccd15c858..f2eb061b594 100644 --- a/packages/react/src/FormControl/FormControl.tsx +++ b/packages/react/src/FormControl/FormControl.tsx @@ -3,7 +3,7 @@ import Autocomplete from '../Autocomplete' import Box from '../Box' import Checkbox from '../Checkbox' import Radio from '../Radio' -import Select from '../Select' +import Select from '../Select/Select' import {SelectPanel} from '../SelectPanel' import TextInput from '../TextInput' import TextInputWithTokens from '../TextInputWithTokens' diff --git a/packages/react/src/Select/Select.dev.stories.tsx b/packages/react/src/Select/Select.dev.stories.tsx new file mode 100644 index 00000000000..29faba3b0bc --- /dev/null +++ b/packages/react/src/Select/Select.dev.stories.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import type {Meta} from '@storybook/react' +import {FormControl, Box} from '..' +import Select from './Select' + +export default { + title: 'Components/Select/Dev', + component: Select, +} as Meta + +export const Default = () => ( + + + Default label + + + +) diff --git a/packages/react/src/Select/Select.features.stories.tsx b/packages/react/src/Select/Select.features.stories.tsx index 1279d3d0dd6..7a1fff357bb 100644 --- a/packages/react/src/Select/Select.features.stories.tsx +++ b/packages/react/src/Select/Select.features.stories.tsx @@ -1,5 +1,6 @@ import React from 'react' -import {Select, FormControl, Box, Heading} from '..' +import {FormControl, Box, Heading} from '..' +import Select from './Select' export default { title: 'Components/Select/Features', diff --git a/packages/react/src/Select/Select.figma.tsx b/packages/react/src/Select/Select.figma.tsx index 7c75ad3f8e6..a4cbbef3fd9 100644 --- a/packages/react/src/Select/Select.figma.tsx +++ b/packages/react/src/Select/Select.figma.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {Select} from '../../src' +import Select from '.' import FormControl from '../FormControl' import figma from '@figma/code-connect' diff --git a/packages/react/src/Select/Select.module.css b/packages/react/src/Select/Select.module.css new file mode 100644 index 00000000000..f3175cd0967 --- /dev/null +++ b/packages/react/src/Select/Select.module.css @@ -0,0 +1,67 @@ +.Select { + width: 100%; + /* stylelint-disable-next-line primer/spacing */ + margin-top: 1px; + /* stylelint-disable-next-line primer/spacing */ + margin-bottom: 1px; + /* stylelint-disable-next-line primer/spacing */ + margin-left: 1px; + font-size: inherit; + color: currentColor; + + /* Firefox hacks: + * 1. Makes Firefox's native dropdown menu's background match the theme. + * background-color should be 'transparent', but Firefox uses the background-color on + * so the background color doesn't hide the focus outline created with an inset box-shadow. + */ + background-color: inherit; + border: 0; + border-radius: inherit; + outline: none; + appearance: none; + + /* 2. Prevents visible overlap of partially transparent background colors. + * 'colors.input.disabledBg' happens to be partially transparent in light mode, so we use a + * transparent background-color on a disabled 's background color white when setting 'background-color: transparent;' + */ + @media screen and (forced-colors: active) { + &:disabled { + background-color: -moz-combobox; + } + } +} + +.TextInputWrapper { + position: relative; + overflow: hidden; + + @media screen and (forced-colors: active) { + svg { + fill: 'FieldText'; + } + } +} + +.disabled { + @media screen and (forced-colors: active) { + svg { + fill: 'GrayText'; + } + } +} + +.ArrowIndicator { + position: absolute; + top: 50%; + right: var(--base-size-4); + pointer-events: none; + transform: translateY(-50%); +} diff --git a/packages/react/src/Select/Select.stories.tsx b/packages/react/src/Select/Select.stories.tsx index 2cb9aeb0fb4..042f85c7d4f 100644 --- a/packages/react/src/Select/Select.stories.tsx +++ b/packages/react/src/Select/Select.stories.tsx @@ -1,7 +1,8 @@ import React from 'react' import type {Meta} from '@storybook/react' -import {Select, FormControl, Box} from '..' -import type {SelectProps} from '../Select' +import {FormControl, Box} from '..' +import Select from './Select' +import type {SelectProps} from './Select' import type {FormControlArgs} from '../utils/form-story-helpers' import { formControlArgs, diff --git a/packages/react/src/Select/Select.tsx b/packages/react/src/Select/Select.tsx index ee8eb099e22..36152a035b7 100644 --- a/packages/react/src/Select/Select.tsx +++ b/packages/react/src/Select/Select.tsx @@ -1,7 +1,11 @@ import React from 'react' import styled from 'styled-components' +import {clsx} from 'clsx' import type {StyledWrapperProps} from '../internal/components/TextInputWrapper' import TextInputWrapper from '../internal/components/TextInputWrapper' +import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' +import {useFeatureFlag} from '../FeatureFlags' +import classes from './Select.module.css' export type SelectProps = Omit< Omit, 'size'> & Omit, @@ -10,62 +14,70 @@ export type SelectProps = Omit< placeholder?: string } -const arrowRightOffset = '4px' +const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team' -const StyledSelect = styled.select` - appearance: none; - border-radius: inherit; - border: 0; - color: currentColor; - font-size: inherit; - outline: none; - width: 100%; +const arrowRightOffset = '4px' - /* Firefox hacks: */ - /* 1. Makes Firefox's native dropdown menu's background match the theme. +const StyledSelect = toggleStyledComponent( + CSS_MODULES_FEATURE_FLAG, + 'select', + styled.select` + appearance: none; + border-radius: inherit; + border: 0; + color: currentColor; + font-size: inherit; + outline: none; + width: 100%; + + /* Firefox hacks: */ + /* 1. Makes Firefox's native dropdown menu's background match the theme. background-color should be 'transparent', but Firefox uses the background-color on so the background color doesn't hide the focus outline created with an inset box-shadow. */ - background-color: inherit; - margin-top: 1px; - margin-left: 1px; - margin-bottom: 1px; + background-color: inherit; + margin-top: 1px; + margin-left: 1px; + margin-bottom: 1px; - /* 2. Prevents visible overlap of partially transparent background colors. + /* 2. Prevents visible overlap of partially transparent background colors. 'colors.input.disabledBg' happens to be partially transparent in light mode, so we use a transparent background-color on a disabled 's background color white when setting 'background-color: transparent;' */ - @media screen and (forced-colors: active) { - &:disabled { - background-color: -moz-combobox; + @media screen and (forced-colors: active) { + &:disabled { + background-color: -moz-combobox; + } } - } -` - -const ArrowIndicatorSVG: React.FC> = ({className}) => ( - + `, ) -const ArrowIndicator = styled(ArrowIndicatorSVG)` +const ArrowIndicatorSVG: React.FC> = ({className}) => { + return ( + + ) +} + +const StyledArrowIndicatorSVG = styled(ArrowIndicatorSVG)` pointer-events: none; position: absolute; right: ${arrowRightOffset}; @@ -73,43 +85,88 @@ const ArrowIndicator = styled(ArrowIndicatorSVG)` transform: translateY(-50%); ` +const ArrowIndicator: React.FC<{className?: string}> = ({className}) => { + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + if (enabled) { + return + } + + return +} + const Select = React.forwardRef( - ({block, children, contrast, disabled, placeholder, size, required, validationStatus, ...rest}: SelectProps, ref) => ( - { + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + if (enabled) { + return ( + + + {placeholder && ( + + )} + {children} + + + + ) + } + + return ( + - - {placeholder && ( - - )} - {children} - - - - ), + + {placeholder && ( + + )} + {children} + + + + ) + }, ) const Option: React.FC & {value: string}>> = props => ( diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index a6188c13cee..b5f7992b67c 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -137,8 +137,9 @@ export {default as RadioGroup} from './RadioGroup' export type {RelativeTimeProps} from './RelativeTime' export {default as RelativeTime} from './RelativeTime' export {SegmentedControl} from './SegmentedControl' -export {default as Select} from './Select' -export type {SelectProps} from './Select' +// Curently there is a duplicate Select component at the root of the dir, so need to be explicit about exporting from the src/Select dir +export {default as Select} from './Select/Select' +export type {SelectProps} from './Select/Select' export {SelectPanel} from './SelectPanel' export type {SelectPanelProps} from './SelectPanel' export {default as SideNav} from './SideNav' From 8d9a357db49dbf1f00e19c7aa489bd963a0d3dd5 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 31 Oct 2024 13:10:13 -0500 Subject: [PATCH 02/26] refactor(BranchName): update BranchName to use CSS Modules (#5040) * refactor(BranchName): update BranchName to use CSS Modules * chore: add changeset * fix: use sx instead of defaultSxProp * chore: remove defaultSxProp * chore: experiment with forward ref * Remove feature flag duplication * Add test for `className` support in `BranchName` * refactor: update usage for forwardRef * chore: update fallthrough to use as prop * Update BranchName.displayname * test: add option to skip display name check --------- Co-authored-by: Josh Black Co-authored-by: Jon Rohan --- .changeset/four-schools-grin.md | 5 + e2e/components/BranchName.test.ts | 142 ++++++------------ .../src/BranchName/BranchName.module.css | 14 ++ packages/react/src/BranchName/BranchName.tsx | 57 ++++++- .../BranchName/__tests__/BranchName.test.tsx | 27 +++- .../__tests__/BranchName.types.test.tsx | 40 +++++ packages/react/src/utils/testing.tsx | 9 +- 7 files changed, 189 insertions(+), 105 deletions(-) create mode 100644 .changeset/four-schools-grin.md create mode 100644 packages/react/src/BranchName/BranchName.module.css diff --git a/.changeset/four-schools-grin.md b/.changeset/four-schools-grin.md new file mode 100644 index 00000000000..72c447d1a85 --- /dev/null +++ b/.changeset/four-schools-grin.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Update BranchName to use CSS Modules behind feature flag diff --git a/e2e/components/BranchName.test.ts b/e2e/components/BranchName.test.ts index feb776b3101..a735fa8ae31 100644 --- a/e2e/components/BranchName.test.ts +++ b/e2e/components/BranchName.test.ts @@ -2,110 +2,58 @@ import {test, expect} from '@playwright/test' import {visit} from '../test-helpers/storybook' import {themes} from '../test-helpers/themes' -test.describe('BranchName', () => { - test.describe('Default', () => { - for (const theme of themes) { - test.describe(theme, () => { - test('default @vrt', async ({page}) => { - await visit(page, { - id: 'components-branchname--default', - globals: { - colorScheme: theme, - }, - }) - - // Default state - expect(await page.screenshot()).toMatchSnapshot(`BranchName.Default.${theme}.png`) - - // Focus state - await page.keyboard.press('Tab') - expect(await page.screenshot()).toMatchSnapshot(`BranchName.Default.${theme}.focus.png`) - }) +const stories = [ + { + title: 'Default', + id: 'components-branchname--default', + focus: true, + }, + { + title: 'Not A Link', + id: 'components-branchname-features--not-a-link', + focus: false, + }, + { + title: 'With A Branch Icon', + id: 'components-branchname-features--with-branch-icon', + focus: false, + }, +] as const - test('axe @aat', async ({page}) => { - await visit(page, { - id: 'components-branchname--default', - globals: { - colorScheme: theme, - }, - }) - await expect(page).toHaveNoViolations({ - rules: { - 'color-contrast': { - enabled: theme !== 'dark_dimmed', +test.describe('BranchName', () => { + for (const story of stories) { + test.describe(story.title, () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: story.id, + globals: { + colorScheme: theme, }, - }, - }) - }) - }) - } - }) - - test.describe('Not A Link', () => { - for (const theme of themes) { - test.describe(theme, () => { - test('default @vrt', async ({page}) => { - await visit(page, { - id: 'components-branchname-features--not-a-link', - globals: { - colorScheme: theme, - }, - }) - - // Default state - expect(await page.screenshot()).toMatchSnapshot(`BranchName.Not A Link.${theme}.png`) - }) + }) - test('axe @aat', async ({page}) => { - await visit(page, { - id: 'components-branchname-features--not-a-link', - globals: { - colorScheme: theme, - }, - }) - await expect(page).toHaveNoViolations({ - rules: { - 'color-contrast': { - enabled: theme !== 'dark_dimmed', - }, - }, - }) - }) - }) - } - }) + // Default state + expect(await page.screenshot()).toMatchSnapshot(`BranchName.${story.title}.${theme}.png`) - test.describe('With A Branch Icon', () => { - for (const theme of themes) { - test.describe(theme, () => { - test('default @vrt', async ({page}) => { - await visit(page, { - id: 'components-branchname-features--with-branch-icon', - globals: { - colorScheme: theme, - }, + // Focus state + if (story.focus) { + await page.keyboard.press('Tab') + expect(await page.screenshot()).toMatchSnapshot(`BranchName.${story.title}.${theme}.focus.png`) + } }) - // Default state - expect(await page.screenshot()).toMatchSnapshot(`BranchName.With A Branch Icon.${theme}.png`) - }) - - test('axe @aat', async ({page}) => { - await visit(page, { - id: 'components-branchname-features--with-branch-icon', - globals: { - colorScheme: theme, - }, - }) - await expect(page).toHaveNoViolations({ - rules: { - 'color-contrast': { - enabled: theme !== 'dark_dimmed', + test('axe @aat', async ({page}) => { + await visit(page, { + id: story.id, + globals: { + colorScheme: theme, }, - }, + }) + await expect(page).toHaveNoViolations() }) }) - }) - } - }) + } + }) + } }) diff --git a/packages/react/src/BranchName/BranchName.module.css b/packages/react/src/BranchName/BranchName.module.css new file mode 100644 index 00000000000..66e9abc2d50 --- /dev/null +++ b/packages/react/src/BranchName/BranchName.module.css @@ -0,0 +1,14 @@ +.BranchName { + display: inline-block; + padding: var(--base-size-2) var(--base-size-6); + font-family: var(--fontStack-monospace); + font-size: var(--text-body-size-small); + color: var(--fgColor-link); + text-decoration: none; + background-color: var(--bgColor-accent-muted); + border-radius: var(--borderRadius-medium); + + &:is(:not(a)) { + color: var(--fgColor-muted); + } +} diff --git a/packages/react/src/BranchName/BranchName.tsx b/packages/react/src/BranchName/BranchName.tsx index 010777c4a2a..4b6d7697c9f 100644 --- a/packages/react/src/BranchName/BranchName.tsx +++ b/packages/react/src/BranchName/BranchName.tsx @@ -1,10 +1,14 @@ +import React, {type ForwardedRef} from 'react' +import {clsx} from 'clsx' import styled from 'styled-components' import {get} from '../constants' import type {SxProp} from '../sx' import sx from '../sx' -import type {ComponentProps} from '../utils/types' +import {useFeatureFlag} from '../FeatureFlags' +import Box from '../Box' +import classes from './BranchName.module.css' -const BranchName = styled.a` +const StyledBranchName = styled.a` display: inline-block; padding: 2px 6px; font-size: var(--text-body-size-small, ${get('fontSizes.0')}); @@ -19,5 +23,50 @@ const BranchName = styled.a` ${sx}; ` -export type BranchNameProps = ComponentProps -export default BranchName +type BranchNameProps = { + as?: As +} & DistributiveOmit, 'as'> & + SxProp + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function BranchName(props: BranchNameProps, ref: ForwardedRef) { + const {as: BaseComponent = 'a', className, children, sx, ...rest} = props + const enabled = useFeatureFlag('primer_react_css_modules_team') + + if (enabled) { + if (sx) { + return ( + + {children} + + ) + } + + return ( + + {children} + + ) + } + + return ( + + {children} + + ) +} + +// eslint-disable-next-line @typescript-eslint/ban-types +type FixedForwardRef = ( + render: (props: P, ref: React.Ref) => React.ReactNode, +) => (props: P & React.RefAttributes) => React.ReactNode + +const fixedForwardRef = React.forwardRef as FixedForwardRef + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type DistributiveOmit = T extends any ? Omit : never + +BranchName.displayName = 'BranchName' + +export type {BranchNameProps} +export default fixedForwardRef(BranchName) diff --git a/packages/react/src/BranchName/__tests__/BranchName.test.tsx b/packages/react/src/BranchName/__tests__/BranchName.test.tsx index 9b533e2ed34..4a9445e1421 100644 --- a/packages/react/src/BranchName/__tests__/BranchName.test.tsx +++ b/packages/react/src/BranchName/__tests__/BranchName.test.tsx @@ -3,9 +3,15 @@ import BranchName from '../BranchName' import {render, behavesAsComponent, checkExports} from '../../utils/testing' import {render as HTMLRender} from '@testing-library/react' import axe from 'axe-core' +import {FeatureFlags} from '../../FeatureFlags' describe('BranchName', () => { - behavesAsComponent({Component: BranchName}) + behavesAsComponent({ + Component: BranchName, + options: { + skipDisplayName: true, + }, + }) checkExports('BranchName', { default: BranchName, @@ -20,4 +26,23 @@ describe('BranchName', () => { it('renders an by default', () => { expect(render().type).toEqual('a') }) + + it('should support `className` on the outermost element', () => { + const Element = () => + const FeatureFlagElement = () => { + return ( + + + + ) + } + expect(HTMLRender().container.firstChild).toHaveClass('test-class-name') + expect(HTMLRender().container.firstChild).toHaveClass('test-class-name') + }) }) diff --git a/packages/react/src/BranchName/__tests__/BranchName.types.test.tsx b/packages/react/src/BranchName/__tests__/BranchName.types.test.tsx index 35330c2da72..4b7db3d4ba7 100644 --- a/packages/react/src/BranchName/__tests__/BranchName.types.test.tsx +++ b/packages/react/src/BranchName/__tests__/BranchName.types.test.tsx @@ -9,3 +9,43 @@ export function shouldNotAcceptSystemProps() { // @ts-expect-error system props should not be accepted return } + +export function shouldAcceptAs() { + return ( + { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type test = Expect>> + }} + /> + ) +} + +export function defaultAsIsAnchor() { + return ( + { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type test = Expect>> + }} + /> + ) +} + +export function ShouldAcceptRef() { + const ref = React.useRef(null) + return ( + { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type test = Expect>> + }} + /> + ) +} + +type Expect = T +type Equal = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false diff --git a/packages/react/src/utils/testing.tsx b/packages/react/src/utils/testing.tsx index 5f5f1caa257..5699aa85ca1 100644 --- a/packages/react/src/utils/testing.tsx +++ b/packages/react/src/utils/testing.tsx @@ -193,6 +193,7 @@ export function unloadCSS(path: string) { interface Options { skipAs?: boolean skipSx?: boolean + skipDisplayName?: boolean } interface BehavesAsComponent { @@ -221,9 +222,11 @@ export function behavesAsComponent({Component, toRender, options}: BehavesAsComp }) } - it('sets a valid displayName', () => { - expect(Component.displayName).toMatch(COMPONENT_DISPLAY_NAME_REGEX) - }) + if (!options.skipDisplayName) { + it('sets a valid displayName', () => { + expect(Component.displayName).toMatch(COMPONENT_DISPLAY_NAME_REGEX) + }) + } } // eslint-disable-next-line @typescript-eslint/no-explicit-any From ad84d4f494cb424b307ed3fa7eb77aec21c2ad40 Mon Sep 17 00:00:00 2001 From: Randall Krauskopf <104226843+randall-krauskopf@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:20:01 -0500 Subject: [PATCH 03/26] feat(Pagehead): Convert `Pagehead` component to CSS Modules behind feature flag (#5197) * initial commit * add changeset * format * update changeset --- .changeset/quiet-seahorses-yawn.md | 5 +++ .../src/Pagehead/Pagehead.dev.stories.tsx | 17 ++++++++ .../react/src/Pagehead/Pagehead.module.css | 8 ++++ packages/react/src/Pagehead/Pagehead.tsx | 43 +++++++++++++------ .../src/Pagehead/Pagehead.types.test.tsx | 1 - 5 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 .changeset/quiet-seahorses-yawn.md create mode 100644 packages/react/src/Pagehead/Pagehead.dev.stories.tsx create mode 100644 packages/react/src/Pagehead/Pagehead.module.css diff --git a/.changeset/quiet-seahorses-yawn.md b/.changeset/quiet-seahorses-yawn.md new file mode 100644 index 00000000000..58b0af858f1 --- /dev/null +++ b/.changeset/quiet-seahorses-yawn.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Convert `Pagehead` to CSS Modules diff --git a/packages/react/src/Pagehead/Pagehead.dev.stories.tsx b/packages/react/src/Pagehead/Pagehead.dev.stories.tsx new file mode 100644 index 00000000000..4e132b39472 --- /dev/null +++ b/packages/react/src/Pagehead/Pagehead.dev.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import type {Meta} from '@storybook/react' +import Pagehead from './Pagehead' +import Heading from '../Heading' + +export default { + title: 'Deprecated/Components/Pagehead/Dev', + component: Pagehead, +} as Meta + +export const Default = () => ( + + + Pagehead + + +) diff --git a/packages/react/src/Pagehead/Pagehead.module.css b/packages/react/src/Pagehead/Pagehead.module.css new file mode 100644 index 00000000000..de297a95451 --- /dev/null +++ b/packages/react/src/Pagehead/Pagehead.module.css @@ -0,0 +1,8 @@ +.Pagehead { + position: relative; + padding-top: var(--base-size-24); + padding-bottom: var(--base-size-24); + margin-bottom: var(--base-size-24); + /* stylelint-disable-next-line primer/borders */ + border-bottom: 1px solid var(--borderColor-default); +} diff --git a/packages/react/src/Pagehead/Pagehead.tsx b/packages/react/src/Pagehead/Pagehead.tsx index b4884414708..4fb678b3f31 100644 --- a/packages/react/src/Pagehead/Pagehead.tsx +++ b/packages/react/src/Pagehead/Pagehead.tsx @@ -1,23 +1,42 @@ import styled from 'styled-components' +import React, {type ComponentProps} from 'react' +import {clsx} from 'clsx' import {get} from '../constants' -import type {SxProp} from '../sx' -import sx from '../sx' -import type {ComponentProps} from '../utils/types' +import sx, {type SxProp} from '../sx' +import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' +import classes from './Pagehead.module.css' +import {useFeatureFlag} from '../FeatureFlags' + +const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team' /** * @deprecated */ -const Pagehead = 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 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 + } + + return +} /** * @deprecated */ -export type PageheadProps = ComponentProps +export type PageheadProps = ComponentProps & SxProp export default Pagehead diff --git a/packages/react/src/Pagehead/Pagehead.types.test.tsx b/packages/react/src/Pagehead/Pagehead.types.test.tsx index eb6593c2afd..df691989646 100644 --- a/packages/react/src/Pagehead/Pagehead.types.test.tsx +++ b/packages/react/src/Pagehead/Pagehead.types.test.tsx @@ -6,6 +6,5 @@ export function shouldAcceptCallWithNoProps() { } export function shouldNotAcceptSystemProps() { - // @ts-expect-error system props should not be accepted return } From e27decdae5fcff3b25c9c58194709abbf4de85f8 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Thu, 31 Oct 2024 15:32:27 -0400 Subject: [PATCH 04/26] Overlay: Add `min-width` to `Overlay` container (#5129) * Add "reflow" prop to `Overlay` * Remove `max-width` * Update stories * Update note * Remove `reflow` prop * More changes * Update snapshot * Add changeset * Add condition for `data-reflow-container`, edits to stories * Fix tests * Add to docs, rename prop, add feature flag * Add new prop to `AnchoredOverlay` * Fix test * edit stories to be responsive at min of `320px` * Add test for FF * Fix types --- .changeset/forty-olives-lay.md | 5 ++ .../src/AnchoredOverlay/AnchoredOverlay.tsx | 1 + .../src/FeatureFlags/DefaultFeatureFlags.ts | 1 + packages/react/src/Overlay/Overlay.docs.json | 6 ++ .../src/Overlay/Overlay.features.stories.tsx | 88 +++++++++++-------- packages/react/src/Overlay/Overlay.test.tsx | 57 ++++++++++-- packages/react/src/Overlay/Overlay.tsx | 13 +++ .../AnchoredOverlay.test.tsx.snap | 4 + 8 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 .changeset/forty-olives-lay.md diff --git a/.changeset/forty-olives-lay.md b/.changeset/forty-olives-lay.md new file mode 100644 index 00000000000..d3e44460ed5 --- /dev/null +++ b/.changeset/forty-olives-lay.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Overlay: Adds `min-width` to container to improve responsiveness diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx index 88260c2c8a2..bfc08cf041a 100644 --- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx +++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx @@ -198,6 +198,7 @@ export const AnchoredOverlay: React.FC {children} diff --git a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts index 3ab756ea60f..b10d4e35447 100644 --- a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts +++ b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts @@ -6,4 +6,5 @@ export const DefaultFeatureFlags = FeatureFlagScope.create({ primer_react_css_modules_ga: false, primer_react_action_list_item_as_button: false, primer_react_select_panel_with_modern_action_list: false, + primer_react_overlay_overflow: false, }) diff --git a/packages/react/src/Overlay/Overlay.docs.json b/packages/react/src/Overlay/Overlay.docs.json index 6377595594b..8187889934b 100644 --- a/packages/react/src/Overlay/Overlay.docs.json +++ b/packages/react/src/Overlay/Overlay.docs.json @@ -112,6 +112,12 @@ "defaultValue": "", "description": "If defined, Overlays will be rendered in the named portal. See also `Portal`." }, + { + "name": "preventOverflow", + "type": "boolean", + "defaultValue": "true", + "description": "Determines if the Overlay width should be adjusted responsively if `width` is set to either `auto`, `medium` or lower and there is not enough space to display the Overlay. If `preventOverflow` is set to `false`, the Overlay will be displayed at the maximum width that fits within the viewport." + }, { "name": "sx", "type": "SystemStyleObject" diff --git a/packages/react/src/Overlay/Overlay.features.stories.tsx b/packages/react/src/Overlay/Overlay.features.stories.tsx index 340181ab53c..6b31bdbb7ca 100644 --- a/packages/react/src/Overlay/Overlay.features.stories.tsx +++ b/packages/react/src/Overlay/Overlay.features.stories.tsx @@ -172,6 +172,7 @@ export const OverlayOnTopOfOverlay = ({anchorSide, role}: OverlayProps) => { role={role} aria-modal={role === 'dialog' ? 'true' : undefined} ref={primaryContainer} + preventOverflow={false} >