From fafe315eff56ce14c1a1fd79eea7353460e0c575 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Tue, 23 Sep 2025 15:22:09 -0700 Subject: [PATCH 01/27] remove sx from docs --- .../react/src/ActionList/ActionList.docs.json | 56 ------------------- packages/react/src/ActionList/Divider.tsx | 3 +- 2 files changed, 1 insertion(+), 58 deletions(-) diff --git a/packages/react/src/ActionList/ActionList.docs.json b/packages/react/src/ActionList/ActionList.docs.json index 6afbe71c766..ccaf7865466 100644 --- a/packages/react/src/ActionList/ActionList.docs.json +++ b/packages/react/src/ActionList/ActionList.docs.json @@ -36,11 +36,6 @@ "type": "AriaRole", "defaultValue": "", "description": "ARIA role describing the function of the list. `listbox` and `menu` are a common values." - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ], "subcomponents": [ @@ -113,11 +108,6 @@ "required": false, "description": "id to attach to the root element of the Item", "defaultValue": "" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ] }, @@ -144,11 +134,6 @@ "required": false, "description": "", "defaultValue": "" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ] }, @@ -183,11 +168,6 @@ "required": false, "description": "Text describing why the item is inactive. This may be used when an item's usual functionality\nis unavailable due to a system error such as a database outage.", "defaultValue": "" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ], "passthrough": { @@ -204,11 +184,6 @@ "defaultValue": "", "required": true, "description": "Icon (or similar) positioned before item text." - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ] }, @@ -221,11 +196,6 @@ "defaultValue": "", "required": true, "description": "Visual positioned after item text." - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ] }, @@ -282,11 +252,6 @@ "defaultValue": "'inline'", "description": "`inline` descriptions are positioned beside primary text. `block` descriptions are positioned below primary text." }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "className", "type": "string | undefined", @@ -337,11 +302,6 @@ "defaultValue": "h3", "required": false, "description": "The level of the heading and it is only required (enforce by runtime warning) for lists. (i.e. not required for ActionMenu or listbox roles)" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ] }, @@ -385,22 +345,6 @@ "type": "AriaRole", "defaultValue": "", "description": "ARIA role describing the function of the list inside the group. `listbox` and `menu` are a common values." - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - } - ] - }, - { - "filePath": "/Users/mperrotti/work-dir/react/packages/react/src/ActionList/Divider.tsx", - "name": "ActionList.Divider", - "props": [ - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ] } diff --git a/packages/react/src/ActionList/Divider.tsx b/packages/react/src/ActionList/Divider.tsx index 5caa365a49c..e6622594278 100644 --- a/packages/react/src/ActionList/Divider.tsx +++ b/packages/react/src/ActionList/Divider.tsx @@ -4,7 +4,7 @@ import {clsx} from 'clsx' import classes from './ActionList.module.css' import {BoxWithFallback} from '../internal/components/BoxWithFallback' -export type ActionListDividerProps = SxProp & { +export type ActionListDividerProps = { className?: string } @@ -17,7 +17,6 @@ export const Divider: React.FC> className={clsx(className, classes.Divider)} as="li" aria-hidden="true" - sx={sx} data-component="ActionList.Divider" /> ) From b232717f01615513237a2e2bfa98dbbd6cc48719 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Tue, 23 Sep 2025 15:22:31 -0700 Subject: [PATCH 02/27] remove sx from ActionList/Divider --- packages/react/src/ActionList/Divider.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react/src/ActionList/Divider.tsx b/packages/react/src/ActionList/Divider.tsx index e6622594278..4576332fd2d 100644 --- a/packages/react/src/ActionList/Divider.tsx +++ b/packages/react/src/ActionList/Divider.tsx @@ -1,5 +1,4 @@ import type React from 'react' -import type {SxProp} from '../sx' import {clsx} from 'clsx' import classes from './ActionList.module.css' import {BoxWithFallback} from '../internal/components/BoxWithFallback' @@ -11,7 +10,7 @@ export type ActionListDividerProps = { /** * Visually separates `Item`s or `Group`s in an `ActionList`. */ -export const Divider: React.FC> = ({sx, className}) => { +export const Divider: React.FC> = ({className}) => { return ( Date: Tue, 23 Sep 2025 15:23:16 -0700 Subject: [PATCH 03/27] remove sx from ActionList/Description --- packages/react/src/ActionList/Description.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react/src/ActionList/Description.tsx b/packages/react/src/ActionList/Description.tsx index c4624c6f061..cb1e7bd425d 100644 --- a/packages/react/src/ActionList/Description.tsx +++ b/packages/react/src/ActionList/Description.tsx @@ -1,6 +1,5 @@ import React from 'react' import Truncate from '../Truncate' -import type {SxProp} from '../sx' import {ItemContext} from './shared' import classes from './ActionList.module.css' import {BoxWithFallback} from '../internal/components/BoxWithFallback' @@ -20,11 +19,10 @@ export type ActionListDescriptionProps = { * Whether the inline description should truncate the text on overflow. */ truncate?: boolean -} & SxProp +} export const Description: React.FC> = ({ variant = 'inline', - sx, className, truncate, ...props @@ -47,7 +45,6 @@ export const Description: React.FC Date: Tue, 23 Sep 2025 15:30:25 -0700 Subject: [PATCH 04/27] remove sx from ActionList/Group --- packages/react/src/ActionList/Group.tsx | 32 ++++++++----------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/react/src/ActionList/Group.tsx b/packages/react/src/ActionList/Group.tsx index 5fbb20ed254..10fec9a3934 100644 --- a/packages/react/src/ActionList/Group.tsx +++ b/packages/react/src/ActionList/Group.tsx @@ -1,6 +1,5 @@ import React from 'react' import {useId} from '../hooks/useId' -import type {SxProp} from '../sx' import {ListContext, type ActionListProps} from './shared' import type {ActionListHeadingProps} from './Heading' import {useSlots} from '../hooks/useSlots' @@ -8,14 +7,13 @@ import {invariant} from '../utils/invariant' import {clsx} from 'clsx' import classes from './ActionList.module.css' import groupClasses from './Group.module.css' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' type HeadingProps = { as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' className?: string children: React.ReactNode id?: string -} & SxProp +} const Heading: React.FC> = ({ as: Component = 'h3', @@ -25,10 +23,9 @@ const Heading: React.FC> ...rest }) => { return ( - // Box is temporary to support lingering sx usage - + {children} - + ) } @@ -58,12 +55,11 @@ export type ActionListGroupProps = React.HTMLAttributes & { * Secondary text which provides additional information about a `Group`. */ auxiliaryText?: string -} & SxProp & { - /** - * Whether multiple Items or a single Item can be selected in the Group. Overrides value on ActionList root. - */ - selectionVariant?: ActionListProps['selectionVariant'] | false - } + /** + * Whether multiple Items or a single Item can be selected in the Group. Overrides value on ActionList root. + */ + selectionVariant?: ActionListProps['selectionVariant'] | false +} type ContextProps = Pick & {groupHeadingId: string | undefined} export const GroupContext = React.createContext({ @@ -101,12 +97,7 @@ export const Group: React.FC> = ({ } return ( - +
  • {title && !slots.groupHeading ? ( // Escape hatch: supports old API in a non breaking way @@ -126,13 +117,12 @@ export const Group: React.FC> = ({ {slots.groupHeading ? childrenWithoutSlots : props.children} - +
  • ) } export type ActionListGroupHeadingProps = Pick & Omit & - SxProp & React.HTMLAttributes & { as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' headingWrapElement?: 'div' | 'li' @@ -156,7 +146,6 @@ export const GroupHeading: React.FC { @@ -208,7 +197,6 @@ export const GroupHeading: React.FC {_internalBackwardCompatibleTitle ?? children} From 243765e741160973b9e97ff7f95b42fe56f1d05a Mon Sep 17 00:00:00 2001 From: llastflowers Date: Tue, 23 Sep 2025 15:31:27 -0700 Subject: [PATCH 05/27] remove sx from ActionList/Item --- packages/react/src/ActionList/Item.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx index 49944d74fe2..b109c4b0b59 100644 --- a/packages/react/src/ActionList/Item.tsx +++ b/packages/react/src/ActionList/Item.tsx @@ -54,7 +54,6 @@ export const Item = React.forwardRef( selected = undefined, active = false, onSelect: onSelectUser, - sx: sxProp, id, role, loading, @@ -245,7 +244,6 @@ export const Item = React.forwardRef( Date: Tue, 23 Sep 2025 15:33:34 -0700 Subject: [PATCH 06/27] remove boxwithfallback from ActionList/Item and sx from ActionList/LinkItem --- packages/react/src/ActionList/Item.tsx | 6 ++---- packages/react/src/ActionList/LinkItem.tsx | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx index b109c4b0b59..7167e2501fc 100644 --- a/packages/react/src/ActionList/Item.tsx +++ b/packages/react/src/ActionList/Item.tsx @@ -16,7 +16,6 @@ import {invariant} from '../utils/invariant' import VisuallyHidden from '../_VisuallyHidden' import classes from './ActionList.module.css' import {clsx} from 'clsx' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' type ActionListSubItemProps = { children?: React.ReactNode @@ -241,9 +240,8 @@ export const Item = React.forwardRef( trailingVisualId, }} > - ( {!inactive && !loading && !menuContext && Boolean(slots.trailingAction) && slots.trailingAction} {slots.subItem} - + ) }, diff --git a/packages/react/src/ActionList/LinkItem.tsx b/packages/react/src/ActionList/LinkItem.tsx index 06b3cbcd035..cb18077eaf0 100644 --- a/packages/react/src/ActionList/LinkItem.tsx +++ b/packages/react/src/ActionList/LinkItem.tsx @@ -21,12 +21,12 @@ type LinkProps = { // LinkItem does not support selected, loading, variants, etc. export type ActionListLinkItemProps = Pick< ActionListItemProps, - 'active' | 'children' | 'sx' | 'inactiveText' | 'variant' | 'size' + 'active' | 'children' | 'inactiveText' | 'variant' | 'size' > & LinkProps export const LinkItem = React.forwardRef( - ({sx, active, inactiveText, variant, size, as: Component, className, ...props}, forwardedRef) => { + ({active, inactiveText, variant, size, as: Component, className, ...props}, forwardedRef) => { return ( { const clickHandler = (event: React.MouseEvent) => { onClick && onClick(event) From aa52014387f48130154f63233cd654e1f80d84ab Mon Sep 17 00:00:00 2001 From: llastflowers Date: Tue, 23 Sep 2025 15:34:21 -0700 Subject: [PATCH 07/27] remove boxwithfallback from ActionList/List --- packages/react/src/ActionList/List.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react/src/ActionList/List.tsx b/packages/react/src/ActionList/List.tsx index f8448ba0055..706ae570ecb 100644 --- a/packages/react/src/ActionList/List.tsx +++ b/packages/react/src/ActionList/List.tsx @@ -9,7 +9,6 @@ import {useProvidedRefOrCreate} from '../hooks' import {FocusKeys, useFocusZone} from '../hooks/useFocusZone' import {clsx} from 'clsx' import classes from './ActionList.module.css' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' export const List = React.forwardRef( ( @@ -58,8 +57,7 @@ export const List = React.forwardRef( }} > {slots.heading} - ( {...props} > {childrenWithoutSlots} - + ) }, From 543f7aa32ef550599f4ebee53b462903286b9997 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Tue, 23 Sep 2025 15:36:32 -0700 Subject: [PATCH 08/27] remove sx from ActionList/Heading --- packages/react/src/ActionList/Heading.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react/src/ActionList/Heading.tsx b/packages/react/src/ActionList/Heading.tsx index 55f858f6db2..a59e9be08ca 100644 --- a/packages/react/src/ActionList/Heading.tsx +++ b/packages/react/src/ActionList/Heading.tsx @@ -1,5 +1,4 @@ import React, {forwardRef} from 'react' -import type {SxProp} from '../sx' import {useRefObjectAsForwardedRef} from '../hooks' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import {default as HeadingComponent} from '../Heading' @@ -17,7 +16,7 @@ export type ActionListHeadingProps = { size?: HeadingVariants visuallyHidden?: boolean className?: string -} & SxProp +} export const Heading = forwardRef(({as, size, children, visuallyHidden = false, className, ...props}, forwardedRef) => { const innerRef = React.useRef(null) From 150e0fce915d7515da241f1789cb188d5cc49141 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Wed, 24 Sep 2025 10:27:33 -0700 Subject: [PATCH 09/27] remove sx from ActionList/Visuals and update styled-react exports --- packages/react/src/ActionList/Visuals.tsx | 6 ++---- packages/styled-react/src/index.tsx | 12 +++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/react/src/ActionList/Visuals.tsx b/packages/react/src/ActionList/Visuals.tsx index a7eb14db29a..e93f4ebca44 100644 --- a/packages/react/src/ActionList/Visuals.tsx +++ b/packages/react/src/ActionList/Visuals.tsx @@ -1,17 +1,15 @@ import React from 'react' import {AlertIcon} from '@primer/octicons-react' import Spinner from '../Spinner' -import type {SxProp} from '../sx' import {ItemContext} from './shared' import {Tooltip, type TooltipProps} from '../TooltipV2' import {clsx} from 'clsx' import classes from './ActionList.module.css' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' -export type VisualProps = SxProp & React.HTMLAttributes +export type VisualProps = React.HTMLAttributes export const VisualContainer: React.FC> = ({className, ...props}) => { - return + return } export type ActionListLeadingVisualProps = VisualProps diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index ccbb097bd1b..9aa55df9475 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -1,4 +1,6 @@ import { + ActionList as PrimerActionList, + type ActionListProps as PrimerActionListProps, type BetterSystemStyleObject, Box, type BoxProps, @@ -68,6 +70,14 @@ type StyledProps = SxProp & PositionProps & ShadowProps +type ActionListProps = PrimerActionListProps & SxProp + +const ActionList: ForwardRefComponent<'div', ActionListProps> = styled(PrimerActionList).withConfig({ + shouldForwardProp: prop => prop !== 'sx', +})` + ${sx} +` + type SpinnerProps = PrimerSpinnerProps & SxProp function Spinner(props: SpinnerProps) { @@ -257,6 +267,7 @@ export {Header, type HeaderProps} from './components/Header' export {Flash} from './components/Flash' export { + ActionList, Checkbox, CounterLabel, LinkButton, @@ -274,7 +285,6 @@ export { } export { - ActionList, ActionMenu, Autocomplete, Avatar, From 75096c735b7521bcca955d87a0d40cc29cfb34fa Mon Sep 17 00:00:00 2001 From: "Brittany L. Houtz" <55068883+llastflowers@users.noreply.github.com> Date: Fri, 26 Sep 2025 10:53:13 -0700 Subject: [PATCH 10/27] Create lovely-plums-buy.md --- .changeset/lovely-plums-buy.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/lovely-plums-buy.md diff --git a/.changeset/lovely-plums-buy.md b/.changeset/lovely-plums-buy.md new file mode 100644 index 00000000000..d97dc239e2d --- /dev/null +++ b/.changeset/lovely-plums-buy.md @@ -0,0 +1,5 @@ +--- +"@primer/react": major +--- + +Remove support for styled-components/sx from ActionList component and sub-components From d1719e8127713a10d86fa962074d0031e769164c Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Sep 2025 06:53:41 -0500 Subject: [PATCH 11/27] refactor(styled-react): update structure to reduce merge conflicts (#6861) Co-authored-by: Marie Lucca --- .../__snapshots__/exports.test.ts.snap | 1 + .../styled-react/src/components/Checkbox.tsx | 10 + .../src/components/CounterLabel.tsx | 10 + .../src/components/PageHeader.tsx | 29 +- .../src/components/RadioGroup.tsx | 45 +++ .../src/components/RelativeTime.tsx | 10 + .../src/components/SegmentedControl.tsx | 37 ++ .../styled-react/src/components/Spinner.tsx | 9 + .../src/components/StateLabel.tsx | 12 + .../src/components/StateLabelProps.tsx | 10 + .../styled-react/src/components/SubNav.tsx | 26 ++ .../styled-react/src/components/Timeline.tsx | 44 ++ .../src/components/ToggleSwitch.tsx | 12 + .../styled-react/src/components/Truncate.tsx | 13 + .../src/components/UnderlineNav.tsx | 30 ++ packages/styled-react/src/index.tsx | 382 +++--------------- packages/styled-react/src/styled-props.ts | 27 ++ 17 files changed, 384 insertions(+), 323 deletions(-) create mode 100644 packages/styled-react/src/components/Checkbox.tsx create mode 100644 packages/styled-react/src/components/CounterLabel.tsx create mode 100644 packages/styled-react/src/components/RadioGroup.tsx create mode 100644 packages/styled-react/src/components/RelativeTime.tsx create mode 100644 packages/styled-react/src/components/SegmentedControl.tsx create mode 100644 packages/styled-react/src/components/Spinner.tsx create mode 100644 packages/styled-react/src/components/StateLabel.tsx create mode 100644 packages/styled-react/src/components/StateLabelProps.tsx create mode 100644 packages/styled-react/src/components/SubNav.tsx create mode 100644 packages/styled-react/src/components/Timeline.tsx create mode 100644 packages/styled-react/src/components/ToggleSwitch.tsx create mode 100644 packages/styled-react/src/components/Truncate.tsx create mode 100644 packages/styled-react/src/components/UnderlineNav.tsx create mode 100644 packages/styled-react/src/styled-props.ts diff --git a/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap index 3cef707a5e0..fbb4eb59ca8 100644 --- a/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap @@ -28,6 +28,7 @@ exports[`@primer/styled-react exports 1`] = ` "Overlay", "PageHeader", "PageLayout", + "ProgressBar", "RadioGroup", "RelativeTime", "SegmentedControl", diff --git a/packages/styled-react/src/components/Checkbox.tsx b/packages/styled-react/src/components/Checkbox.tsx new file mode 100644 index 00000000000..f8ee4411c81 --- /dev/null +++ b/packages/styled-react/src/components/Checkbox.tsx @@ -0,0 +1,10 @@ +import {Checkbox as PrimerCheckbox, type CheckboxProps as PrimerCheckboxProps} from '@primer/react' +import {Box} from './Box' +import {forwardRef} from 'react' +import {type SxProp} from '../sx' + +export type CheckboxProps = PrimerCheckboxProps & SxProp + +export const Checkbox = forwardRef(function Checkbox(props, ref) { + return +}) diff --git a/packages/styled-react/src/components/CounterLabel.tsx b/packages/styled-react/src/components/CounterLabel.tsx new file mode 100644 index 00000000000..ec07906fa56 --- /dev/null +++ b/packages/styled-react/src/components/CounterLabel.tsx @@ -0,0 +1,10 @@ +import {CounterLabel as PrimerCounterLabel, type CounterLabelProps as PrimerCounterLabelProps} from '@primer/react' +import {Box} from './Box' +import {forwardRef} from 'react' +import {type SxProp} from '../sx' + +export type CounterLabelProps = PrimerCounterLabelProps & SxProp + +export const CounterLabel = forwardRef(function CounterLabel(props, ref) { + return +}) diff --git a/packages/styled-react/src/components/PageHeader.tsx b/packages/styled-react/src/components/PageHeader.tsx index 62b85dd7d49..db23410c7c7 100644 --- a/packages/styled-react/src/components/PageHeader.tsx +++ b/packages/styled-react/src/components/PageHeader.tsx @@ -75,18 +75,35 @@ const PageHeaderTitleArea: ForwardRefComponent<'div', PageHeaderTitleAreaProps> ${sx} ` -const PageHeader = Object.assign(PageHeaderImpl, { +type PageHeaderComponent = ForwardRefComponent<'div', PageHeaderProps> & { + Actions: typeof PageHeaderActions + ContextArea: typeof PrimerPageHeader.ContextArea + ParentLink: typeof PrimerPageHeader.ParentLink + ContextBar: typeof PrimerPageHeader.ContextBar + TitleArea: typeof PageHeaderTitleArea + ContextAreaActions: typeof PrimerPageHeader.ContextAreaActions + LeadingAction: typeof PrimerPageHeader.LeadingAction + Breadcrumbs: typeof PrimerPageHeader.Breadcrumbs + LeadingVisual: typeof PrimerPageHeader.LeadingVisual + Title: typeof PageHeaderTitle + TrailingVisual: typeof PrimerPageHeader.TrailingVisual + Description: typeof PrimerPageHeader.Description + TrailingAction: typeof PrimerPageHeader.TrailingAction +} + +const PageHeader: PageHeaderComponent = Object.assign(PageHeaderImpl, { Actions: PageHeaderActions, - Title: PageHeaderTitle, - TitleArea: PageHeaderTitleArea, ContextArea: PrimerPageHeader.ContextArea, - ContextAreaActions: PrimerPageHeader.ContextAreaActions, - TrailingVisual: PrimerPageHeader.TrailingVisual, - Description: PrimerPageHeader.Description, + ParentLink: PrimerPageHeader.ParentLink, ContextBar: PrimerPageHeader.ContextBar, + TitleArea: PageHeaderTitleArea, + ContextAreaActions: PrimerPageHeader.ContextAreaActions, LeadingAction: PrimerPageHeader.LeadingAction, Breadcrumbs: PrimerPageHeader.Breadcrumbs, LeadingVisual: PrimerPageHeader.LeadingVisual, + Title: PageHeaderTitle, + TrailingVisual: PrimerPageHeader.TrailingVisual, + Description: PrimerPageHeader.Description, TrailingAction: PrimerPageHeader.TrailingAction, }) diff --git a/packages/styled-react/src/components/RadioGroup.tsx b/packages/styled-react/src/components/RadioGroup.tsx new file mode 100644 index 00000000000..2e5a9ba5af7 --- /dev/null +++ b/packages/styled-react/src/components/RadioGroup.tsx @@ -0,0 +1,45 @@ +import {Box, RadioGroup as PrimerRadioGroup, type RadioGroupProps as PrimerRadioGroupProps} from '@primer/react' +import React, {type PropsWithChildren} from 'react' +import type {SxProp} from '../sx' + +export type RadioGroupProps = PropsWithChildren & SxProp + +const RadioGroupImpl = (props: RadioGroupProps) => { + return +} + +// Define local types based on the internal component props +type CheckboxOrRadioGroupLabelProps = PropsWithChildren< + { + className?: string + visuallyHidden?: boolean + } & SxProp +> +const CheckboxOrRadioGroupLabel = (props: CheckboxOrRadioGroupLabelProps) => { + return +} + +type CheckboxOrRadioGroupCaptionProps = PropsWithChildren< + { + className?: string + } & SxProp +> +const CheckboxOrRadioGroupCaption = (props: CheckboxOrRadioGroupCaptionProps) => { + return +} + +type CheckboxOrRadioGroupValidationProps = PropsWithChildren< + { + className?: string + variant: 'error' | 'success' + } & SxProp +> +const CheckboxOrRadioGroupValidation = (props: CheckboxOrRadioGroupValidationProps) => { + return +} + +export const RadioGroup = Object.assign(RadioGroupImpl, { + Label: CheckboxOrRadioGroupLabel, + Caption: CheckboxOrRadioGroupCaption, + Validation: CheckboxOrRadioGroupValidation, +}) diff --git a/packages/styled-react/src/components/RelativeTime.tsx b/packages/styled-react/src/components/RelativeTime.tsx new file mode 100644 index 00000000000..e9c19782561 --- /dev/null +++ b/packages/styled-react/src/components/RelativeTime.tsx @@ -0,0 +1,10 @@ +import {Box, RelativeTime as PrimerRelativeTime, type RelativeTimeProps as PrimerRelativeTimeProps} from '@primer/react' +import React from 'react' +import type {SxProp} from '../sx' + +export type RelativeTimeProps = PrimerRelativeTimeProps & SxProp + +export function RelativeTime(props: RelativeTimeProps) { + // @ts-expect-error the types for Box are not correctly inferred here + return +} diff --git a/packages/styled-react/src/components/SegmentedControl.tsx b/packages/styled-react/src/components/SegmentedControl.tsx new file mode 100644 index 00000000000..86ea27bed35 --- /dev/null +++ b/packages/styled-react/src/components/SegmentedControl.tsx @@ -0,0 +1,37 @@ +import { + type SegmentedControlProps as PrimerSegmentedControlProps, + SegmentedControl as PrimerSegmentedControl, + type SegmentedControlButtonProps as PrimerSegmentedControlButtonProps, + type SegmentedControlIconButtonProps as PrimerSegmentedControlIconButtonProps, +} from '@primer/react' +import type {PropsWithChildren} from 'react' +import type {SxProp} from '../sx' +import {Box} from './Box' + +type SegmentedControlProps = PropsWithChildren & SxProp +type SegmentedControlButtonProps = PropsWithChildren & SxProp +type SegmentedControlIconButtonProps = PropsWithChildren & SxProp + +const SegmentedControlButton = (props: SegmentedControlButtonProps) => { + return +} + +const SegmentedControlIconButton = (props: SegmentedControlIconButtonProps) => { + return +} + +const SegmentedControlImpl = (props: SegmentedControlProps) => { + return +} + +const SegmentedControl = Object.assign(SegmentedControlImpl, { + Button: SegmentedControlButton, + IconButton: SegmentedControlIconButton, +}) + +export { + SegmentedControl, + type SegmentedControlProps, + type SegmentedControlButtonProps, + type SegmentedControlIconButtonProps, +} diff --git a/packages/styled-react/src/components/Spinner.tsx b/packages/styled-react/src/components/Spinner.tsx new file mode 100644 index 00000000000..e6c007c68f1 --- /dev/null +++ b/packages/styled-react/src/components/Spinner.tsx @@ -0,0 +1,9 @@ +import {Box, Spinner as PrimerSpinner, type SpinnerProps as PrimerSpinnerProps} from '@primer/react' +import React from 'react' +import type {SxProp} from '../sx' + +export type SpinnerProps = PrimerSpinnerProps & SxProp + +export function Spinner(props: SpinnerProps) { + return +} diff --git a/packages/styled-react/src/components/StateLabel.tsx b/packages/styled-react/src/components/StateLabel.tsx new file mode 100644 index 00000000000..75e28432400 --- /dev/null +++ b/packages/styled-react/src/components/StateLabel.tsx @@ -0,0 +1,12 @@ +import {StateLabel as PrimerStateLabel, type StateLabelProps as PrimerStateLabelProps} from '@primer/react' +import {forwardRef} from 'react' +import {Box} from './Box' +import type {SxProp} from '../sx' + +type StateLabelProps = PrimerStateLabelProps & SxProp + +const StateLabel = forwardRef(function StateLabel(props, ref) { + return +}) + +export {StateLabel, type StateLabelProps} diff --git a/packages/styled-react/src/components/StateLabelProps.tsx b/packages/styled-react/src/components/StateLabelProps.tsx new file mode 100644 index 00000000000..2d08f8c79d4 --- /dev/null +++ b/packages/styled-react/src/components/StateLabelProps.tsx @@ -0,0 +1,10 @@ +import {StateLabel as PrimerStateLabel, type StateLabelProps as PrimerStateLabelProps} from '@primer/react' +import {Box} from './Box' +import {forwardRef} from 'react' +import {type SxProp} from '../sx' + +export type StateLabelProps = PrimerStateLabelProps & SxProp + +export const StateLabel = forwardRef(function StateLabel(props, ref) { + return +}) diff --git a/packages/styled-react/src/components/SubNav.tsx b/packages/styled-react/src/components/SubNav.tsx new file mode 100644 index 00000000000..fb61f2038c7 --- /dev/null +++ b/packages/styled-react/src/components/SubNav.tsx @@ -0,0 +1,26 @@ +import { + SubNav as PrimerSubNav, + type SubNavProps as PrimerSubNavProps, + type SubNavLinkProps as PrimerSubNavLinkProps, +} from '@primer/react' +import {forwardRef} from 'react' +import {Box} from './Box' +import type {SxProp} from '../sx' + +type SubNavProps = PrimerSubNavProps & SxProp + +const SubNavImpl = forwardRef(function SubNav(props, ref) { + return +}) + +type SubNavLinkProps = PrimerSubNavLinkProps & SxProp + +const SubNavLink = forwardRef(function SubNavLink(props, ref) { + return +}) + +const SubNav = Object.assign(SubNavImpl, { + Link: SubNavLink, +}) + +export {SubNav, type SubNavProps, type SubNavLinkProps} diff --git a/packages/styled-react/src/components/Timeline.tsx b/packages/styled-react/src/components/Timeline.tsx new file mode 100644 index 00000000000..cdd42e60662 --- /dev/null +++ b/packages/styled-react/src/components/Timeline.tsx @@ -0,0 +1,44 @@ +import { + Timeline as PrimerTimeline, + type TimelineProps as PrimerTimelineProps, + type TimelineItemProps as PrimerTimelineItemProps, + type TimelineBadgeProps as PrimerTimelineBadgeProps, + type TimelineBodyProps as PrimerTimelineBodyProps, + type TimelineBreakProps as PrimerTimelineBreakProps, +} from '@primer/react' +import {Box} from './Box' +import {forwardRef} from 'react' +import {type SxProp} from '../sx' + +export type TimelineProps = PrimerTimelineProps & SxProp +export type TimelineItemProps = PrimerTimelineItemProps & SxProp +export type TimelineBadgeProps = PrimerTimelineBadgeProps & SxProp +export type TimelineBodyProps = PrimerTimelineBodyProps & SxProp +export type TimelineBreakProps = PrimerTimelineBreakProps & SxProp + +const TimelineImpl = forwardRef(function Timeline(props, ref) { + return +}) + +const TimelineItem = forwardRef(function TimelineItem(props, ref) { + return +}) + +function TimelineBadge(props: TimelineBadgeProps) { + return +} + +const TimelineBody = forwardRef(function TimelineBody(props, ref) { + return +}) + +const TimelineBreak = forwardRef(function TimelineBreak(props, ref) { + return +}) + +export const Timeline = Object.assign(TimelineImpl, { + Item: TimelineItem, + Badge: TimelineBadge, + Body: TimelineBody, + Break: TimelineBreak, +}) diff --git a/packages/styled-react/src/components/ToggleSwitch.tsx b/packages/styled-react/src/components/ToggleSwitch.tsx new file mode 100644 index 00000000000..53974f63d85 --- /dev/null +++ b/packages/styled-react/src/components/ToggleSwitch.tsx @@ -0,0 +1,12 @@ +import {ToggleSwitch as PrimerToggleSwitch, type ToggleSwitchProps as PrimerToggleSwitchProps} from '@primer/react' +import {forwardRef} from 'react' +import {Box} from './Box' +import type {StyledProps} from '../styled-props' + +type ToggleSwitchProps = PrimerToggleSwitchProps & Omit + +const ToggleSwitch = forwardRef(function ToggleSwitch(props, ref) { + return +}) + +export {ToggleSwitch, type ToggleSwitchProps} diff --git a/packages/styled-react/src/components/Truncate.tsx b/packages/styled-react/src/components/Truncate.tsx new file mode 100644 index 00000000000..da6376a9197 --- /dev/null +++ b/packages/styled-react/src/components/Truncate.tsx @@ -0,0 +1,13 @@ +import {Truncate as PrimerTruncate, type TruncateProps as PrimerTruncateProps} from '@primer/react' +import {sx, type SxProp} from '../sx' +import styled from 'styled-components' +import type {PropsWithChildren} from 'react' +import type {ForwardRefComponent} from '../polymorphic' + +export type TruncateProps = PropsWithChildren & SxProp + +export const Truncate: ForwardRefComponent<'div', TruncateProps> = styled(PrimerTruncate).withConfig({ + shouldForwardProp: prop => prop !== 'sx', +})` + ${sx} +` diff --git a/packages/styled-react/src/components/UnderlineNav.tsx b/packages/styled-react/src/components/UnderlineNav.tsx new file mode 100644 index 00000000000..e4bec07908b --- /dev/null +++ b/packages/styled-react/src/components/UnderlineNav.tsx @@ -0,0 +1,30 @@ +import { + UnderlineNav as PrimerUnderlineNav, + type UnderlineNavProps as PrimerUnderlineNavProps, + type UnderlineNavItemProps as PrimerUnderlineNavItemProps, +} from '@primer/react' +import {Box} from './Box' +import type {ForwardRefComponent} from '../polymorphic' +import {forwardRef} from 'react' +import styled from 'styled-components' +import {sx, type SxProp} from '../sx' + +export type UnderlineNavProps = PrimerUnderlineNavProps & SxProp + +const UnderlineNavImpl = forwardRef(function UnderlineNav(props, ref) { + return +}) + +export type UnderlineNavItemProps = PrimerUnderlineNavItemProps & SxProp + +const UnderlineNavItem: ForwardRefComponent<'a', UnderlineNavItemProps> = styled( + PrimerUnderlineNav.Item, +).withConfig({ + shouldForwardProp: prop => prop !== 'sx', +})` + ${sx} +` + +export const UnderlineNav = Object.assign(UnderlineNavImpl, { + Item: UnderlineNavItem, +}) diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index 9aa55df9475..8be52072298 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -1,326 +1,74 @@ -import { - ActionList as PrimerActionList, - type ActionListProps as PrimerActionListProps, - type BetterSystemStyleObject, - Box, - type BoxProps, - type SxProp, - Spinner as PrimerSpinner, - type SpinnerProps as PrimerSpinnerProps, - RadioGroup as PrimerRadioGroup, - type RadioGroupProps as PrimerRadioGroupProps, - Checkbox as PrimerCheckbox, - type CheckboxProps as PrimerCheckboxProps, - CounterLabel as PrimerCounterLabel, - type CounterLabelProps as PrimerCounterLabelProps, - StateLabel as PrimerStateLabel, - type StateLabelProps as PrimerStateLabelProps, - SubNav as PrimerSubNav, - type RelativeTimeProps as PrimerRelativeTimeProps, - RelativeTime as PrimerRelativeTime, - type SubNavProps as PrimerSubNavProps, - type SubNavLinkProps as PrimerSubNavLinkProps, - Timeline as PrimerTimeline, - type TimelineProps as PrimerTimelineProps, - type TimelineItemProps as PrimerTimelineItemProps, - type TimelineBadgeProps as PrimerTimelineBadgeProps, - type TimelineBodyProps as PrimerTimelineBodyProps, - type TimelineBreakProps as PrimerTimelineBreakProps, - ToggleSwitch as PrimerToggleSwitch, - type ToggleSwitchProps as PrimerToggleSwitchProps, - Truncate as PrimerTruncate, - type TruncateProps as PrimerTruncateProps, - type SegmentedControlProps as PrimerSegmentedControlProps, - SegmentedControl as PrimerSegmentedControl, - type SegmentedControlButtonProps as PrimerSegmentedControlButtonProps, - type SegmentedControlIconButtonProps as PrimerSegmentedControlIconButtonProps, - UnderlineNav as PrimerUnderlineNav, - type UnderlineNavProps as PrimerUnderlineNavProps, - type UnderlineNavItemProps as PrimerUnderlineNavItemProps, - sx, -} from '@primer/react' -import React, {forwardRef, type PropsWithChildren} from 'react' -import type {ForwardRefComponent} from './polymorphic' - -import type { - BackgroundProps, - BorderProps, - ColorProps, - FlexboxProps, - GridProps, - LayoutProps, - PositionProps, - ShadowProps, - SpaceProps, - TypographyProps, -} from 'styled-system' -import styled from 'styled-components' - -import {LinkButton, type LinkButtonProps} from './components/LinkButton' - -type StyledProps = SxProp & - SpaceProps & - ColorProps & - TypographyProps & - LayoutProps & - FlexboxProps & - GridProps & - BackgroundProps & - BorderProps & - PositionProps & - ShadowProps - -type ActionListProps = PrimerActionListProps & SxProp - -const ActionList: ForwardRefComponent<'div', ActionListProps> = styled(PrimerActionList).withConfig({ - shouldForwardProp: prop => prop !== 'sx', -})` - ${sx} -` - -type SpinnerProps = PrimerSpinnerProps & SxProp - -function Spinner(props: SpinnerProps) { - return -} - -type RelativeTimeProps = PrimerRelativeTimeProps & SxProp - -function RelativeTime(props: RelativeTimeProps) { - // @ts-expect-error the types for Box are not correctly inferred here - return -} - -type RadioGroupProps = PropsWithChildren & SxProp - -const RadioGroupImpl = (props: RadioGroupProps) => { - return -} - -// Define local types based on the internal component props -type CheckboxOrRadioGroupLabelProps = PropsWithChildren< - { - className?: string - visuallyHidden?: boolean - } & SxProp -> -const CheckboxOrRadioGroupLabel = (props: CheckboxOrRadioGroupLabelProps) => { - return -} - -type CheckboxOrRadioGroupCaptionProps = PropsWithChildren< - { - className?: string - } & SxProp -> -const CheckboxOrRadioGroupCaption = (props: CheckboxOrRadioGroupCaptionProps) => { - return -} - -type CheckboxOrRadioGroupValidationProps = PropsWithChildren< - { - className?: string - variant: 'error' | 'success' - } & SxProp -> -const CheckboxOrRadioGroupValidation = (props: CheckboxOrRadioGroupValidationProps) => { - return -} - -const RadioGroup = Object.assign(RadioGroupImpl, { - Label: CheckboxOrRadioGroupLabel, - Caption: CheckboxOrRadioGroupCaption, - Validation: CheckboxOrRadioGroupValidation, -}) - -type SegmentedControlProps = PropsWithChildren & SxProp -type SegmentedControlButtonProps = PropsWithChildren & SxProp -type SegmentedControlIconButtonProps = PropsWithChildren & SxProp - -const SegmentedControlButton = (props: SegmentedControlButtonProps) => { - return -} - -const SegmentedControlIconButton = (props: SegmentedControlIconButtonProps) => { - return -} - -const SegmentedControlImpl = (props: SegmentedControlProps) => { - return -} - -const SegmentedControl = Object.assign(SegmentedControlImpl, { - Button: SegmentedControlButton, - IconButton: SegmentedControlIconButton, -}) - -type CheckboxProps = PrimerCheckboxProps & SxProp - -const Checkbox = forwardRef(function Checkbox(props, ref) { - return -}) - -type CounterLabelProps = PrimerCounterLabelProps & SxProp - -const CounterLabel = forwardRef(function CounterLabel(props, ref) { - return -}) - -type StateLabelProps = PrimerStateLabelProps & SxProp - -const StateLabel = forwardRef(function StateLabel(props, ref) { - return -}) - -type SubNavProps = PrimerSubNavProps & SxProp - -const SubNavImpl = forwardRef(function SubNav(props, ref) { - return -}) - -type SubNavLinkProps = PrimerSubNavLinkProps & SxProp - -const SubNavLink = forwardRef(function SubNavLink(props, ref) { - return -}) - -const SubNav = Object.assign(SubNavImpl, { - Link: SubNavLink, -}) - -type TimelineProps = PrimerTimelineProps & SxProp -type TimelineItemProps = PrimerTimelineItemProps & SxProp -type TimelineBadgeProps = PrimerTimelineBadgeProps & SxProp -type TimelineBodyProps = PrimerTimelineBodyProps & SxProp -type TimelineBreakProps = PrimerTimelineBreakProps & SxProp - -const TimelineImpl = forwardRef(function Timeline(props, ref) { - return -}) - -const TimelineItem = forwardRef(function TimelineItem(props, ref) { - return -}) - -function TimelineBadge(props: TimelineBadgeProps) { - return -} - -const TimelineBody = forwardRef(function TimelineBody(props, ref) { - return -}) - -const TimelineBreak = forwardRef(function TimelineBreak(props, ref) { - return -}) - -const Timeline = Object.assign(TimelineImpl, { - Item: TimelineItem, - Badge: TimelineBadge, - Body: TimelineBody, - Break: TimelineBreak, -}) - -type ToggleSwitchProps = PrimerToggleSwitchProps & Omit - -const ToggleSwitch = forwardRef(function ToggleSwitch(props, ref) { - return -}) - +export {ActionList} from '@primer/react' +export {ActionMenu} from '@primer/react' +export {Autocomplete} from '@primer/react' +export {Avatar} from '@primer/react' +export {Breadcrumbs} from '@primer/react' +export {Box, type BoxProps} from './components/Box' +export {Button} from '@primer/react' +export {CheckboxGroup} from '@primer/react' +export {CircleBadge} from '@primer/react' +export {Details} from '@primer/react' +export {Dialog} from '@primer/react' +export {FormControl} from '@primer/react' +export {Heading} from '@primer/react' +export {IconButton} from '@primer/react' +export {Label} from '@primer/react' +export {Link} from '@primer/react' +export {NavList} from '@primer/react' +export {Overlay} from '@primer/react' +export {PageLayout} from '@primer/react' +export {ProgressBar} from '@primer/react' +export {Select} from '@primer/react' +export {Text} from '@primer/react' +export {Textarea} from '@primer/react' +export {TextInput} from '@primer/react' +export {Token} from '@primer/react' +export {Tooltip} from '@primer/react' +export {type TextInputProps} from '@primer/react' +export {type TokenProps} from '@primer/react' + +// theming depends on styled-components +export {ThemeProvider} from '@primer/react' +export {merge} from '@primer/react' +export {theme} from '@primer/react' +export {themeGet} from '@primer/react' +export {useColorSchemeVar} from '@primer/react' +export {useTheme} from '@primer/react' + +export {Checkbox, type CheckboxProps} from './components/Checkbox' +export {CounterLabel, type CounterLabelProps} from './components/CounterLabel' +export {Flash} from './components/Flash' +export {Header, type HeaderProps} from './components/Header' +export {LinkButton, type LinkButtonProps} from './components/LinkButton' export { PageHeader, type PageHeaderProps, type PageHeaderActionsProps, type PageHeaderTitleProps, } from './components/PageHeader' - -type TruncateProps = PropsWithChildren & SxProp - -const Truncate: ForwardRefComponent<'div', TruncateProps> = styled(PrimerTruncate).withConfig({ - shouldForwardProp: prop => prop !== 'sx', -})` - ${sx} -` - -type UnderlineNavProps = PrimerUnderlineNavProps & SxProp - -const UnderlineNavImpl = forwardRef(function UnderlineNav(props, ref) { - return -}) - -type UnderlineNavItemProps = PrimerUnderlineNavItemProps & SxProp - -const UnderlineNavItem: ForwardRefComponent<'a', UnderlineNavItemProps> = styled( - PrimerUnderlineNav.Item, -).withConfig({ - shouldForwardProp: prop => prop !== 'sx', -})` - ${sx} -` - -const UnderlineNav = Object.assign(UnderlineNavImpl, { - Item: UnderlineNavItem, -}) - -export {Header, type HeaderProps} from './components/Header' - -export {Flash} from './components/Flash' - +export {RadioGroup, type RadioGroupProps} from './components/RadioGroup' +export {RelativeTime, type RelativeTimeProps} from './components/RelativeTime' export { - ActionList, - Checkbox, - CounterLabel, - LinkButton, - type LinkButtonProps, - RadioGroup, - RelativeTime, SegmentedControl, - Spinner, - StateLabel, - SubNav, - Timeline, - ToggleSwitch, - Truncate, - UnderlineNav, -} - + type SegmentedControlProps, + type SegmentedControlButtonProps, + type SegmentedControlIconButtonProps, +} from './components/SegmentedControl' +export {Spinner, type SpinnerProps} from './components/Spinner' +export {StateLabel, type StateLabelProps} from './components/StateLabel' +export {SubNav, type SubNavProps, type SubNavLinkProps} from './components/SubNav' export { - ActionMenu, - Autocomplete, - Avatar, - Breadcrumbs, - Button, - CheckboxGroup, - CircleBadge, - Details, - Dialog, - FormControl, - Heading, - IconButton, - Label, - Link, - NavList, - Overlay, - PageLayout, - Select, - Text, - type TextProps, - Textarea, - TextInput, - type TextInputProps, - Token, - type TokenProps, - Tooltip, - // styled-components components or types - Box, - sx, - - // theming depends on styled-components - ThemeProvider, - merge, - theme, - themeGet, - useColorSchemeVar, - useTheme, -} from '@primer/react' -export type {BoxProps, SxProp, BetterSystemStyleObject} + Timeline, + type TimelineProps, + type TimelineItemProps, + type TimelineBadgeProps, + type TimelineBodyProps, + type TimelineBreakProps, +} from './components/Timeline' +export {ToggleSwitch, type ToggleSwitchProps} from './components/ToggleSwitch' +export {Truncate, type TruncateProps} from './components/Truncate' +export {UnderlineNav, type UnderlineNavProps, type UnderlineNavItemProps} from './components/UnderlineNav' + +export {sx, type SxProp} from './sx' + +export type {BetterSystemStyleObject} from './styled-props' diff --git a/packages/styled-react/src/styled-props.ts b/packages/styled-react/src/styled-props.ts new file mode 100644 index 00000000000..2806edcf0d4 --- /dev/null +++ b/packages/styled-react/src/styled-props.ts @@ -0,0 +1,27 @@ +import type { + BackgroundProps, + BorderProps, + ColorProps, + FlexboxProps, + GridProps, + LayoutProps, + PositionProps, + ShadowProps, + SpaceProps, + TypographyProps, +} from 'styled-system' +import type {SxProp} from './sx' + +export type StyledProps = SxProp & + SpaceProps & + ColorProps & + TypographyProps & + LayoutProps & + FlexboxProps & + GridProps & + BackgroundProps & + BorderProps & + PositionProps & + ShadowProps + +export type {BetterSystemStyleObject} from '@primer/react' From 7dda4a416dc11e1c347d33557717b02147d8f0a6 Mon Sep 17 00:00:00 2001 From: "primer[bot]" <119360173+primer[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:06:06 -0400 Subject: [PATCH 12/27] Release tracking (rc) (#6892) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/pre.json | 32 +++++++++++++++ examples/codesandbox/package.json | 2 +- examples/nextjs/package.json | 2 +- examples/theming/package.json | 2 +- packages/react/CHANGELOG.md | 66 ++++++++++++++++++++++++++++++ packages/react/package.json | 2 +- packages/styled-react/CHANGELOG.md | 19 +++++++++ packages/styled-react/package.json | 6 +-- 8 files changed, 124 insertions(+), 7 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index fa447ffe3d3..29a6d5e10b7 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -12,10 +12,14 @@ "@primer/styled-react": "0.1.0" }, "changesets": [ + "beige-plums-obey", + "beige-shrimps-sleep", + "better-coins-dream", "big-oranges-marry", "bright-dolls-dance", "bright-parents-flow", "busy-bananas-smash", + "chilly-dingos-build", "chubby-colts-nail", "cold-teams-buy", "cruel-papers-film", @@ -24,20 +28,30 @@ "deprecate-textinputwithtokens", "dirty-chairs-invent", "eighty-queens-tap", + "fair-views-laugh", "few-mails-sing", + "fifty-walls-yawn", "five-squids-sin", + "fresh-points-cover", "gentle-rockets-go", + "gentle-sheep-feel", + "giant-impalas-occur", "gold-dingos-sneeze", "great-hats-serve", "hot-bears-cry", "hot-melons-jump", + "hot-mirrors-feel", "icy-pianos-say", "lemon-jokes-camp", + "light-colts-burn", "little-peas-mix", "lovely-vans-burn", "metal-badgers-joke", "metal-cups-peel", + "metal-deer-refuse", "metal-lamps-appear", + "mighty-lizards-lick", + "nasty-seals-carry", "new-dragons-melt", "new-shrimps-appear", "nine-cobras-talk", @@ -45,25 +59,43 @@ "pretty-women-sing", "proud-chairs-study", "rich-walls-fold", + "rotten-carpets-raise", + "rude-cycles-scream", "salty-geese-own", "seven-coats-hang", "seven-forks-join", + "sharp-beans-run", + "sharp-papayas-destroy", + "shiny-deer-lose", + "short-cloths-deny", + "shy-flies-marry", + "silent-wasps-warn", "silly-parks-give", "slick-months-cry", + "smooth-singers-thank", "soft-islands-warn", "spotty-colts-hear", + "stale-avocados-enjoy", "stale-wasps-smell", "strange-knives-study", "strange-peas-kiss", "strong-falcons-design", "strong-lions-tan", + "tame-ducks-ring", + "tasty-cheetahs-pay", + "ten-jobs-attack", "thin-oranges-lick", "three-hounds-refuse", + "three-vans-join", "tricky-eggs-cross", + "twelve-sloths-flow", "twenty-baboons-pull", "unlucky-hotels-shake", + "upset-flowers-yawn", "wet-crews-drum", + "wet-mammals-feel", "wet-terms-argue", + "wicked-starfishes-shout", "witty-chicken-leave", "young-dots-heal", "young-timers-accept" diff --git a/examples/codesandbox/package.json b/examples/codesandbox/package.json index 72d87534498..c72b18fef1e 100644 --- a/examples/codesandbox/package.json +++ b/examples/codesandbox/package.json @@ -17,7 +17,7 @@ "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.3", - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "styled-components": "5.x", "typescript": "^5.9.2", "vite": "^7.1.5" diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 9270d7027d8..f87a3f877a3 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -10,7 +10,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "next": "^15.2.3", "react": "18.3.1", "react-dom": "18.3.1", diff --git a/examples/theming/package.json b/examples/theming/package.json index 736f21bada8..09a278253f7 100644 --- a/examples/theming/package.json +++ b/examples/theming/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@primer/octicons-react": "^19.14.0", - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "clsx": "^2.1.1", "next": "^15.2.3", "react": "18.3.1", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index d9837b20d88..98280f07b3a 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,71 @@ # @primer/react +## 38.0.0-rc.4 + +### Major Changes + +- [#6857](https://github.com/primer/react/pull/6857) [`b9b558e`](https://github.com/primer/react/commit/b9b558efe1033150b05adc88d28234db5dc82f95) Thanks [@pksjce](https://github.com/pksjce)! - Remove PointerBox from @primer-react + +- [#6708](https://github.com/primer/react/pull/6708) [`b7b8a36`](https://github.com/primer/react/commit/b7b8a36d14b3c5b6f9c289423691fe9285eae786) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Remove sx prop support from the Spinner component. + +- [#6833](https://github.com/primer/react/pull/6833) [`5ad1e93`](https://github.com/primer/react/commit/5ad1e939c1ef494f7680b0753a4d7c6296e088d6) Thanks [@joshblack](https://github.com/joshblack)! - Remove sx support from Radio + +- [#6841](https://github.com/primer/react/pull/6841) [`3e02e2c`](https://github.com/primer/react/commit/3e02e2c235a3633b646b8f2a493f3a23e17187bf) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for `sx` from `deprecated/Pagehead` + +- [#6692](https://github.com/primer/react/pull/6692) [`bb8d648`](https://github.com/primer/react/commit/bb8d648a42ec84e9baefae00fa87ede2be449e64) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Remove sx Props and BoxWithFallBack from Timeline component. + +- [#6668](https://github.com/primer/react/pull/6668) [`acb6233`](https://github.com/primer/react/commit/acb6233fa0cf715d660ae222ebca9853115cb707) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Update UnderlineNav component to no longer support sx and remove Box usage from it. + +- [#6835](https://github.com/primer/react/pull/6835) [`961c1c4`](https://github.com/primer/react/commit/961c1c48d942b9d8d81289a5d09d78f4d985f5da) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for `sx` from `ButtonGroup` + +- [#6834](https://github.com/primer/react/pull/6834) [`fa70b5e`](https://github.com/primer/react/commit/fa70b5eaae4c68ccac2bbfa44546ee85594a054a) Thanks [@joshblack](https://github.com/joshblack)! - Remove the `sx` prop from `Flash` + +- [#6876](https://github.com/primer/react/pull/6876) [`2102252`](https://github.com/primer/react/commit/2102252ef34899bb37c52ca4df193ba5cca41a94) Thanks [@llastflowers](https://github.com/llastflowers)! - Update FilteredActionList and FilteredActionListLoaders components to no longer support sx + +- [#6840](https://github.com/primer/react/pull/6840) [`aba8050`](https://github.com/primer/react/commit/aba8050ee9d324fa53a7b81d30b4f8dc7186c70a) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for `sx` prop from `deprecated/FilteredSearch` + +- [#6685](https://github.com/primer/react/pull/6685) [`00261c9`](https://github.com/primer/react/commit/00261c934fe88a74ef310c3134fe202226156b4b) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Remove the sx prop from RadioGroup and Truncate. + +- [#6878](https://github.com/primer/react/pull/6878) [`7081dd3`](https://github.com/primer/react/commit/7081dd3b45f7dae08f7a39038be95dbf20dd8a27) Thanks [@llastflowers](https://github.com/llastflowers)! - Update BaseStyles component to no longer support sx + +- [#6679](https://github.com/primer/react/pull/6679) [`6158135`](https://github.com/primer/react/commit/6158135e42be9486bb7de35fbedf96e412b8a1d7) Thanks [@llastflowers](https://github.com/llastflowers)! - Update CounterLabel component to no longer support sx + +- [#6827](https://github.com/primer/react/pull/6827) [`6843040`](https://github.com/primer/react/commit/6843040ad9b9810cb14f6186e53b01e83bed60f8) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for the sx prop from RelativeTime + +- [#6813](https://github.com/primer/react/pull/6813) [`a1a4ad0`](https://github.com/primer/react/commit/a1a4ad00b87b489f6341aa818a9b8b8662fe2a82) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for `sx` prop for `InlineMessage` + +- [#6866](https://github.com/primer/react/pull/6866) [`3237a4e`](https://github.com/primer/react/commit/3237a4e549779a2b3144b1f48974e533d28cca2a) Thanks [@francinelucca](https://github.com/francinelucca)! - chore(LinkButton): remove sx from LinkButton + +- [#6655](https://github.com/primer/react/pull/6655) [`092185f`](https://github.com/primer/react/commit/092185f7b9d6c70296e629e03790676502b701e5) Thanks [@llastflowers](https://github.com/llastflowers)! - Update CheckboxGroup component to no longer support sx + +- [#6654](https://github.com/primer/react/pull/6654) [`513cc3f`](https://github.com/primer/react/commit/513cc3feed5723632e54f07cae9d4397e3783fdc) Thanks [@llastflowers](https://github.com/llastflowers)! - Update Checkbox component to no longer support sx + +- [#6837](https://github.com/primer/react/pull/6837) [`134f96e`](https://github.com/primer/react/commit/134f96e81476f829f2b0f0b44fa73cc1604983d5) Thanks [@TylerJDev](https://github.com/TylerJDev)! - Update ProgressBar to no longer support sx + +- [#6839](https://github.com/primer/react/pull/6839) [`32febac`](https://github.com/primer/react/commit/32febac10a887cce418abf6f4419cfb100a8f292) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for `sx` from `Header` + +- [#6871](https://github.com/primer/react/pull/6871) [`44b3d73`](https://github.com/primer/react/commit/44b3d732157048059681f3fdcb86c5d77a59aec5) Thanks [@joshblack](https://github.com/joshblack)! - Remove support for `sx` from `PageHeader` + +- [#6863](https://github.com/primer/react/pull/6863) [`9614c0e`](https://github.com/primer/react/commit/9614c0eae7d77bdf879df474c44d613b673e6d4e) Thanks [@pksjce](https://github.com/pksjce)! - Remove Banner from experimental + +### Minor Changes + +- [#6456](https://github.com/primer/react/pull/6456) [`fbd3ac7`](https://github.com/primer/react/commit/fbd3ac75a67b89635dcd28879a1b4a93cef70289) Thanks [@TylerJDev](https://github.com/TylerJDev)! - TreeView: Add trailing actions prop `secondaryActions` + +- [#6855](https://github.com/primer/react/pull/6855) [`356a129`](https://github.com/primer/react/commit/356a129d2a1a0c02d9ca59280e7711e501c0d20d) Thanks [@cheshire137](https://github.com/cheshire137)! - Add buttonLabelOn and buttonLabelOff to ToggleSwitch + +### Patch Changes + +- [#6829](https://github.com/primer/react/pull/6829) [`b568765`](https://github.com/primer/react/commit/b568765159a22fefbb2e521947a6c99109cb6f19) Thanks [@francinelucca](https://github.com/francinelucca)! - chore: fix ActionBar gap issue + +- [#6879](https://github.com/primer/react/pull/6879) [`c8fc6b8`](https://github.com/primer/react/commit/c8fc6b81d61834ab143a578b379d7dcc49e17d62) Thanks [@francinelucca](https://github.com/francinelucca)! - chore(FilteredActionList): fix className override + +- [#6896](https://github.com/primer/react/pull/6896) [`5c29b01`](https://github.com/primer/react/commit/5c29b010db51e85c387963aee45ee97f12aae7ef) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Fix typing issues in PageHeader exports. + +- [#6881](https://github.com/primer/react/pull/6881) [`8d52362`](https://github.com/primer/react/commit/8d523624366dda9cbb5c200560ffcf9b91f57655) Thanks [@langermank](https://github.com/langermank)! - Implement forced colors for progress bar + +- [#6854](https://github.com/primer/react/pull/6854) [`dd8eeed`](https://github.com/primer/react/commit/dd8eeeddb39a7afbdd38d3df8f0568f176e5d4de) Thanks [@pksjce](https://github.com/pksjce)! - Breadcrumbs: Fix esc button not being able to focus on the menubuttonRef + ## 38.0.0-rc.3 ### Major Changes diff --git a/packages/react/package.json b/packages/react/package.json index a9232579168..5c3f1e4008c 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,7 +1,7 @@ { "name": "@primer/react", "type": "module", - "version": "38.0.0-rc.3", + "version": "38.0.0-rc.4", "description": "An implementation of GitHub's Primer Design System using React", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/packages/styled-react/CHANGELOG.md b/packages/styled-react/CHANGELOG.md index a6b5801491a..0eec5ee9355 100644 --- a/packages/styled-react/CHANGELOG.md +++ b/packages/styled-react/CHANGELOG.md @@ -1,5 +1,24 @@ # @primer/styled-react +## 1.0.0-rc.4 + +### Minor Changes + +- [#6848](https://github.com/primer/react/pull/6848) [`156903c`](https://github.com/primer/react/commit/156903c27504b7a24d71db94a48df934d629c4d8) Thanks [@jonrohan](https://github.com/jonrohan)! - feat(styled-react): Add an automatic generation of a components.json file to be used in the `primer-react/use-styled-react-import` eslint plugin + +- [#6866](https://github.com/primer/react/pull/6866) [`3237a4e`](https://github.com/primer/react/commit/3237a4e549779a2b3144b1f48974e533d28cca2a) Thanks [@francinelucca](https://github.com/francinelucca)! - chore(LinkButton): remove sx from LinkButton + +### Patch Changes + +- [#6862](https://github.com/primer/react/pull/6862) [`697d6e0`](https://github.com/primer/react/commit/697d6e01eca0be0405445b3b406ef30da48545df) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Export TooltipProps, TextInputProps, TokenProps. + +- [#6896](https://github.com/primer/react/pull/6896) [`5c29b01`](https://github.com/primer/react/commit/5c29b010db51e85c387963aee45ee97f12aae7ef) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Fix typing issues in PageHeader exports. + +- [#6901](https://github.com/primer/react/pull/6901) [`e6e2f8b`](https://github.com/primer/react/commit/e6e2f8b0479fe726184337112feab29867c42296) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Export TextProps. + +- Updated dependencies [[`b9b558e`](https://github.com/primer/react/commit/b9b558efe1033150b05adc88d28234db5dc82f95), [`b7b8a36`](https://github.com/primer/react/commit/b7b8a36d14b3c5b6f9c289423691fe9285eae786), [`fbd3ac7`](https://github.com/primer/react/commit/fbd3ac75a67b89635dcd28879a1b4a93cef70289), [`b568765`](https://github.com/primer/react/commit/b568765159a22fefbb2e521947a6c99109cb6f19), [`5ad1e93`](https://github.com/primer/react/commit/5ad1e939c1ef494f7680b0753a4d7c6296e088d6), [`3e02e2c`](https://github.com/primer/react/commit/3e02e2c235a3633b646b8f2a493f3a23e17187bf), [`bb8d648`](https://github.com/primer/react/commit/bb8d648a42ec84e9baefae00fa87ede2be449e64), [`c8fc6b8`](https://github.com/primer/react/commit/c8fc6b81d61834ab143a578b379d7dcc49e17d62), [`acb6233`](https://github.com/primer/react/commit/acb6233fa0cf715d660ae222ebca9853115cb707), [`961c1c4`](https://github.com/primer/react/commit/961c1c48d942b9d8d81289a5d09d78f4d985f5da), [`fa70b5e`](https://github.com/primer/react/commit/fa70b5eaae4c68ccac2bbfa44546ee85594a054a), [`2102252`](https://github.com/primer/react/commit/2102252ef34899bb37c52ca4df193ba5cca41a94), [`5c29b01`](https://github.com/primer/react/commit/5c29b010db51e85c387963aee45ee97f12aae7ef), [`aba8050`](https://github.com/primer/react/commit/aba8050ee9d324fa53a7b81d30b4f8dc7186c70a), [`00261c9`](https://github.com/primer/react/commit/00261c934fe88a74ef310c3134fe202226156b4b), [`7081dd3`](https://github.com/primer/react/commit/7081dd3b45f7dae08f7a39038be95dbf20dd8a27), [`6158135`](https://github.com/primer/react/commit/6158135e42be9486bb7de35fbedf96e412b8a1d7), [`6843040`](https://github.com/primer/react/commit/6843040ad9b9810cb14f6186e53b01e83bed60f8), [`a1a4ad0`](https://github.com/primer/react/commit/a1a4ad00b87b489f6341aa818a9b8b8662fe2a82), [`8d52362`](https://github.com/primer/react/commit/8d523624366dda9cbb5c200560ffcf9b91f57655), [`3237a4e`](https://github.com/primer/react/commit/3237a4e549779a2b3144b1f48974e533d28cca2a), [`092185f`](https://github.com/primer/react/commit/092185f7b9d6c70296e629e03790676502b701e5), [`513cc3f`](https://github.com/primer/react/commit/513cc3feed5723632e54f07cae9d4397e3783fdc), [`dd8eeed`](https://github.com/primer/react/commit/dd8eeeddb39a7afbdd38d3df8f0568f176e5d4de), [`134f96e`](https://github.com/primer/react/commit/134f96e81476f829f2b0f0b44fa73cc1604983d5), [`32febac`](https://github.com/primer/react/commit/32febac10a887cce418abf6f4419cfb100a8f292), [`356a129`](https://github.com/primer/react/commit/356a129d2a1a0c02d9ca59280e7711e501c0d20d), [`44b3d73`](https://github.com/primer/react/commit/44b3d732157048059681f3fdcb86c5d77a59aec5), [`9614c0e`](https://github.com/primer/react/commit/9614c0eae7d77bdf879df474c44d613b673e6d4e)]: + - @primer/react@38.0.0-rc.4 + ## 1.0.0-rc.3 ### Patch Changes diff --git a/packages/styled-react/package.json b/packages/styled-react/package.json index 15b74a008bd..b379adf1e63 100644 --- a/packages/styled-react/package.json +++ b/packages/styled-react/package.json @@ -1,6 +1,6 @@ { "name": "@primer/styled-react", - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "module", "exports": { ".": { @@ -30,7 +30,7 @@ "devDependencies": { "@babel/preset-react": "^7.27.1", "@babel/preset-typescript": "^7.27.1", - "@primer/react": "^38.0.0-rc.3", + "@primer/react": "^38.0.0-rc.4", "@rollup/plugin-babel": "^6.0.4", "@types/react": "18.3.11", "@types/react-dom": "18.3.1", @@ -46,7 +46,7 @@ "typescript": "^5.9.2" }, "peerDependencies": { - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "@types/react": "18.x || 19.x", "@types/react-dom": "18.x || 19.x", "@types/react-is": "18.x || 19.x", From cfb36a4d1dd4cc747621d79725e02a48b056136e Mon Sep 17 00:00:00 2001 From: Katie Langerman <18661030+langermank@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:40:01 -0700 Subject: [PATCH 13/27] Bump `primer/primitives` v11.2.0 (#6909) Co-authored-by: Jon Rohan --- package-lock.json | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1bf95e6e2bf..040495c7167 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "react-dom": "^18.3.1" }, "devDependencies": { - "@primer/react": "38.0.0-rc.2", + "@primer/react": "38.0.0-rc.3", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.3", @@ -88,7 +88,7 @@ "name": "example-nextjs", "version": "0.0.0", "dependencies": { - "@primer/react": "38.0.0-rc.2", + "@primer/react": "38.0.0-rc.3", "next": "^15.2.3", "react": "18.3.1", "react-dom": "18.3.1", @@ -104,7 +104,7 @@ "version": "0.0.0", "dependencies": { "@primer/octicons-react": "^19.14.0", - "@primer/react": "38.0.0-rc.2", + "@primer/react": "38.0.0-rc.3", "clsx": "^2.1.1", "next": "^15.2.3", "react": "18.3.1", @@ -25576,13 +25576,13 @@ }, "packages/mcp": { "name": "@primer/mcp", - "version": "0.0.4-rc.0", + "version": "0.1.0-rc.1", "dependencies": { "@babel/runtime": "^7.28.4", "@modelcontextprotocol/sdk": "^1.12.0", "@primer/octicons": "^19.15.5", "@primer/primitives": "10.x || 11.x", - "@primer/react": "^38.0.0-rc.0", + "@primer/react": "^38.0.0-rc.3", "cheerio": "^1.0.0", "turndown": "^7.2.0", "zod": "^3.23.8" @@ -25839,7 +25839,7 @@ }, "packages/react": { "name": "@primer/react", - "version": "38.0.0-rc.2", + "version": "38.0.0-rc.3", "license": "MIT", "dependencies": { "@github/mini-throttle": "^2.1.1", @@ -25850,7 +25850,7 @@ "@primer/behaviors": "^1.8.2", "@primer/live-region-element": "^0.7.1", "@primer/octicons-react": "^19.13.0", - "@primer/primitives": "10.x || 11.x", + "@primer/primitives": "11.2.0", "@styled-system/css": "^5.1.5", "@styled-system/props": "^5.1.5", "@styled-system/theme-get": "^5.1.2", @@ -26000,7 +26000,9 @@ "license": "BSD-3-Clause" }, "packages/react/node_modules/@primer/primitives": { - "version": "11.1.0", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-11.2.0.tgz", + "integrity": "sha512-JDX0kFjPqQ2gEv7ryDzS1tk9ZM8cylCet1VzImaK9Mv8/jj+/I5Ox7Z1UdM8N35y0aUetJzB3HqodtMnQ2oPBg==", "license": "MIT" }, "packages/react/node_modules/@types/react-test-renderer": { @@ -26413,11 +26415,11 @@ }, "packages/styled-react": { "name": "@primer/styled-react", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "devDependencies": { "@babel/preset-react": "^7.27.1", "@babel/preset-typescript": "^7.27.1", - "@primer/react": "^38.0.0-rc.2", + "@primer/react": "^38.0.0-rc.3", "@rollup/plugin-babel": "^6.0.4", "@types/react": "18.3.11", "@types/react-dom": "18.3.1", @@ -26433,7 +26435,7 @@ "typescript": "^5.9.2" }, "peerDependencies": { - "@primer/react": "38.0.0-rc.2", + "@primer/react": "38.0.0-rc.3", "@types/react": "18.x || 19.x", "@types/react-dom": "18.x || 19.x", "@types/react-is": "18.x || 19.x", From 0708eb2f967487d5aa77ebee79f07d50bc811942 Mon Sep 17 00:00:00 2001 From: Lukas Oppermann Date: Fri, 26 Sep 2025 21:40:04 +0200 Subject: [PATCH 14/27] Replace bgColor for StateLabel draft with new draft token (#6905) Co-authored-by: Jon Rohan --- .changeset/sweet-islands-sleep.md | 5 +++++ packages/react/src/StateLabel/StateLabel.module.css | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 .changeset/sweet-islands-sleep.md diff --git a/.changeset/sweet-islands-sleep.md b/.changeset/sweet-islands-sleep.md new file mode 100644 index 00000000000..25ca2791643 --- /dev/null +++ b/.changeset/sweet-islands-sleep.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Replace StateLabel color with new draft token diff --git a/packages/react/src/StateLabel/StateLabel.module.css b/packages/react/src/StateLabel/StateLabel.module.css index 2df846dc6f9..19f3ae958de 100644 --- a/packages/react/src/StateLabel/StateLabel.module.css +++ b/packages/react/src/StateLabel/StateLabel.module.css @@ -64,15 +64,19 @@ } .StateLabel:where([data-status='draft']) { - background-color: var(--bgColor-neutral-emphasis); + /* stylelint-disable-next-line primer/colors */ + background-color: var(--bgColor-draft-emphasis, var(--bgColor-neutral-emphasis)); color: var(--fgColor-onEmphasis); - box-shadow: var(--boxShadow-thin, inset 0 0 0 1px) var(--borderColor-neutral-emphasis, transparent); + box-shadow: var(--boxShadow-thin, inset 0 0 0 1px) + var(--borderColor-draft-emphasis, var(--borderColor-neutral-emphasis, transparent)); } .StateLabel:where([data-status='issueDraft']) { - background-color: var(--bgColor-neutral-emphasis); + /* stylelint-disable-next-line primer/colors */ + background-color: var(--bgColor-draft-emphasis, var(--bgColor-neutral-emphasis)); color: var(--fgColor-onEmphasis); - box-shadow: var(--boxShadow-thin, inset 0 0 0 1px) var(--borderColor-neutral-emphasis, transparent); + box-shadow: var(--boxShadow-thin, inset 0 0 0 1px) + var(--borderColor-draft-emphasis, var(--borderColor-neutral-emphasis, transparent)); } .StateLabel:where([data-status='unavailable']) { From 28af9d54e50c4cee2049769c000ecceabb6fdf78 Mon Sep 17 00:00:00 2001 From: Jon Rohan Date: Fri, 26 Sep 2025 12:41:16 -0700 Subject: [PATCH 15/27] feat(TabNav): remove support for sx prop (#6864) Co-authored-by: Marie Lucca Co-authored-by: Marie Lucca <40550942+francinelucca@users.noreply.github.com> --- .changeset/lucky-wasps-nail.md | 6 ++++ packages/react/src/TabNav/TabNav.tsx | 21 ++++++------ .../primer-react-deprecated.browser.test.tsx | 5 ++- .../styled-react/src/components/TabNav.tsx | 32 +++++++++++++++++++ packages/styled-react/src/deprecated.tsx | 3 +- 5 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 .changeset/lucky-wasps-nail.md create mode 100644 packages/styled-react/src/components/TabNav.tsx diff --git a/.changeset/lucky-wasps-nail.md b/.changeset/lucky-wasps-nail.md new file mode 100644 index 00000000000..37174b1f61f --- /dev/null +++ b/.changeset/lucky-wasps-nail.md @@ -0,0 +1,6 @@ +--- +"@primer/react": major +"@primer/styled-react": patch +--- + +Remove support for `sx` from the `TabNav` component diff --git a/packages/react/src/TabNav/TabNav.tsx b/packages/react/src/TabNav/TabNav.tsx index 46d38c3c5d6..876e130160f 100644 --- a/packages/react/src/TabNav/TabNav.tsx +++ b/packages/react/src/TabNav/TabNav.tsx @@ -2,16 +2,14 @@ import {clsx} from 'clsx' import type {To} from 'history' import React, {useRef, useState} from 'react' import {FocusKeys, useFocusZone} from '../hooks/useFocusZone' -import type {SxProp} from '../sx' -import type {ComponentProps} from '../utils/types' +import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import styles from './TabNav.module.css' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' /** * @deprecated */ -export type TabNavProps = ComponentProps +export type TabNavProps = React.HTMLProps /** * @deprecated @@ -53,13 +51,13 @@ function TabNav({children, 'aria-label': ariaLabel, ...rest}: TabNavProps) { ) return ( - }> +
    }> - +
    ) } @@ -73,18 +71,17 @@ export type TabNavLinkProps = React.DetailedHTMLProps(function TabNavLink( - {selected, className, as = 'a', ...rest}: TabNavLinkProps, +const TabNavLink = React.forwardRef(function TabNavLink( + {selected, className, as: Component = 'a', ...rest}: TabNavLinkProps, ref, ) { return ( - (function {...rest} /> ) -}) +}) as PolymorphicForwardRefComponent<'a', TabNavLinkProps> TabNavLink.displayName = 'TabNav.Link' diff --git a/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx b/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx index c8fbd7fbc54..dd0a9696a04 100644 --- a/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx +++ b/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx @@ -1,6 +1,7 @@ import {render, screen} from '@testing-library/react' import {describe, expect, test} from 'vitest' import {Dialog, Octicon, TabNav, Tooltip} from '../deprecated' +import {Button} from '../index' describe('@primer/react/deprecated', () => { test('Dialog supports `sx` prop', () => { @@ -19,8 +20,10 @@ describe('@primer/react/deprecated', () => { }) test('TabNav.Link supports `sx` prop', () => { - render() + render() expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') + expect(window.getComputedStyle(screen.getByRole('tab')).backgroundColor).toBe('rgb(255, 0, 0)') + expect(screen.getByRole('tab').tagName).toBe('BUTTON') }) test('Tooltip supports `sx` prop', () => { diff --git a/packages/styled-react/src/components/TabNav.tsx b/packages/styled-react/src/components/TabNav.tsx new file mode 100644 index 00000000000..94e39f50eb3 --- /dev/null +++ b/packages/styled-react/src/components/TabNav.tsx @@ -0,0 +1,32 @@ +import {TabNav as PrimerTabNav} from '@primer/react/deprecated' +import type {TabNavProps as PrimerTabNavProps, TabNavLinkProps as PrimerTabNavLinkProps} from '@primer/react/deprecated' +import {sx, type SxProp} from '../sx' +import styled from 'styled-components' +import {type ForwardRefComponent} from '../polymorphic' + +type TabNavProps = PrimerTabNavProps & SxProp +type TabNavLinkProps = PrimerTabNavLinkProps & SxProp + +const StyledTabNav = styled(PrimerTabNav).withConfig({ + shouldForwardProp: prop => (prop as keyof TabNavProps) !== 'sx', +})` + ${sx} +` + +// @ts-ignore forwardedAs is valid here but I don't know how to fix the typescript error +const TabNavImpl = ({as, ...props}: TabNavProps) => + +const StyledTabNavLink: ForwardRefComponent<'a', TabNavLinkProps> = styled(PrimerTabNav.Link).withConfig({ + shouldForwardProp: prop => (prop as keyof TabNavLinkProps) !== 'sx', +})` + ${sx} +` + +// @ts-ignore forwardedAs is valid here but I don't know how to fix the typescript error +const TabNavLink = ({as, ...props}: TabNavLinkProps) => + +const TabNav = Object.assign(TabNavImpl, { + Link: TabNavLink, +}) + +export {TabNav, type TabNavProps, type TabNavLinkProps} diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index bc0a2c05744..fb375d8b666 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -1 +1,2 @@ -export {Dialog, Octicon, TabNav, Tooltip, type TooltipProps} from '@primer/react/deprecated' +export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/TabNav' +export {Dialog, Octicon, Tooltip, type TooltipProps} from '@primer/react/deprecated' From f7546a5dbd22454c2fe16e2664338fc46cd31604 Mon Sep 17 00:00:00 2001 From: Jon Rohan Date: Fri, 26 Sep 2025 12:44:28 -0700 Subject: [PATCH 16/27] Add 'prereleased' type to release event triggers (#6912) --- .github/workflows/lock-release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lock-release.yml b/.github/workflows/lock-release.yml index 9de1283379f..9a588d0590c 100644 --- a/.github/workflows/lock-release.yml +++ b/.github/workflows/lock-release.yml @@ -2,7 +2,9 @@ name: Toggle Release Lock on: release: - types: [released] + types: + - released + - prereleased workflow_dispatch: inputs: action: @@ -78,7 +80,7 @@ jobs: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - name: Update all PRs that are toggled merge when ready run: | - PR_NUMBERS=$(gh pr list -L 100 -R primer/react --state open --json number,baseRefName,autoMergeRequest,mergeStateStatus,reviewDecision -q '.[] | select(.autoMergeRequest != null) | select(.baseRefName == "main") | select(.mergeStateStatus == "BEHIND") | select(.reviewDecision == "APPROVED") | .number') + PR_NUMBERS=$(gh pr list -L 100 -R primer/react --state open --json number,baseRefName,autoMergeRequest,reviewDecision -q '.[] | select(.autoMergeRequest != null) | select(.baseRefName == "main") | select(.reviewDecision == "APPROVED") | .number') if [ -n "$PR_NUMBERS" ]; then echo "Updating $PR_NUMBERS" echo "$PR_NUMBERS" | xargs -I {} gh pr update-branch -R primer/react {} From e366130c8e0441d9d092409c9f7cc13bed815c56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:39:15 -0400 Subject: [PATCH 17/27] chore(deps-dev): bump tar-fs from 3.1.0 to 3.1.1 (#6907) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marie Lucca <40550942+francinelucca@users.noreply.github.com> --- package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 040495c7167..14572a7e84e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23266,7 +23266,9 @@ } }, "node_modules/tar-fs": { - "version": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", "dev": true, "license": "MIT", "dependencies": { From 46d9d1c2575a97f4753ec9a393c899597378f06f Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 26 Sep 2025 15:54:33 -0500 Subject: [PATCH 18/27] feat(Dialog): remove support for sx (#6812) Co-authored-by: Mike Perrotti Co-authored-by: Jon Rohan Co-authored-by: Marie Lucca --- .changeset/sweet-results-smell.md | 5 + e2e/components/Dialog.test.ts | 12 -- .../react/src/Dialog/Dialog.dev.stories.tsx | 107 ------------------ .../src/Dialog/Dialog.features.stories.tsx | 4 +- packages/react/src/Dialog/Dialog.module.css | 12 ++ packages/react/src/Dialog/Dialog.tsx | 55 ++++----- .../__tests__/primer-react.browser.test.tsx | 27 +++++ .../styled-react/src/components/Dialog.tsx | 41 +++++++ packages/styled-react/src/experimental.tsx | 3 +- packages/styled-react/src/index.tsx | 2 +- 10 files changed, 114 insertions(+), 154 deletions(-) create mode 100644 .changeset/sweet-results-smell.md create mode 100644 packages/styled-react/src/components/Dialog.tsx diff --git a/.changeset/sweet-results-smell.md b/.changeset/sweet-results-smell.md new file mode 100644 index 00000000000..63155480051 --- /dev/null +++ b/.changeset/sweet-results-smell.md @@ -0,0 +1,5 @@ +--- +'@primer/react': major +--- + +Remove support for `sx` from the Dialog component and sub-components diff --git a/e2e/components/Dialog.test.ts b/e2e/components/Dialog.test.ts index 84365d50f31..f622e770b35 100644 --- a/e2e/components/Dialog.test.ts +++ b/e2e/components/Dialog.test.ts @@ -27,18 +27,6 @@ const stories = [ title: 'Position sidesheet', id: 'components-dialog-features--side-sheet', }, - { - title: 'Dev: With Css', - id: 'components-dialog-dev--with-css', - }, - { - title: 'Dev: With Sx', - id: 'components-dialog-dev--with-sx', - }, - { - title: 'Dev: With Sx And Css', - id: 'components-dialog-dev--with-sx-and-css', - }, ] as const test.describe('Dialog', () => { diff --git a/packages/react/src/Dialog/Dialog.dev.stories.tsx b/packages/react/src/Dialog/Dialog.dev.stories.tsx index abf3f391772..2cb2a5134e1 100644 --- a/packages/react/src/Dialog/Dialog.dev.stories.tsx +++ b/packages/react/src/Dialog/Dialog.dev.stories.tsx @@ -87,113 +87,6 @@ export const WithCss = ({width, height, subtitle}: DialogStoryProps) => { ) } -function SxHeader({title, subtitle, dialogLabelId}: React.PropsWithChildren) { - if (typeof title === 'string' && typeof subtitle === 'string') { - return ( - -

    {title.toUpperCase()}

    -
    - ) - } - return null -} -function SxBody({children}: React.PropsWithChildren) { - return {children} -} -function SxFooter({footerButtons}: React.PropsWithChildren) { - return ( - - {footerButtons ? : null} - - ) -} -export const WithSx = ({width, height, subtitle}: DialogStoryProps) => { - const [isOpen, setIsOpen] = useState(true) - const onDialogClose = useCallback(() => setIsOpen(false), []) - return ( - <> - - {isOpen && ( - - {lipsum} - - )} - - ) -} - -function SxAndCssHeader({ - title, - subtitle, - dialogLabelId, -}: React.PropsWithChildren) { - if (typeof title === 'string' && typeof subtitle === 'string') { - return ( - -

    {title.toUpperCase()}

    -
    - ) - } - return null -} -function SxAndCssBody({children}: React.PropsWithChildren) { - return ( - - {children} - - ) -} -function SxAndCssFooter({footerButtons}: React.PropsWithChildren) { - return ( - - {footerButtons ? : null} - - ) -} -export const WithSxAndCss = ({width, height, subtitle}: DialogStoryProps) => { - const [isOpen, setIsOpen] = useState(true) - const onDialogClose = useCallback(() => setIsOpen(false), []) - return ( - <> - - {isOpen && ( - - {lipsum} - - )} - - ) -} - export const WithScrollBody = () => { const [isOpen, setIsOpen] = useState(false) const buttonRef = useRef(null) diff --git a/packages/react/src/Dialog/Dialog.features.stories.tsx b/packages/react/src/Dialog/Dialog.features.stories.tsx index 01a38d8e1f2..5f67b70948d 100644 --- a/packages/react/src/Dialog/Dialog.features.stories.tsx +++ b/packages/react/src/Dialog/Dialog.features.stories.tsx @@ -79,11 +79,11 @@ function CustomHeader({ return null } function CustomBody({children}: React.PropsWithChildren) { - return {children} + return {children} } function CustomFooter({footerButtons}: React.PropsWithChildren) { return ( - + {footerButtons ? : null} ) diff --git a/packages/react/src/Dialog/Dialog.module.css b/packages/react/src/Dialog/Dialog.module.css index fb773a6b1b4..d750854c158 100644 --- a/packages/react/src/Dialog/Dialog.module.css +++ b/packages/react/src/Dialog/Dialog.module.css @@ -270,6 +270,18 @@ Add a border between the body and footer if: flex-shrink: 0; } +.HeaderInner { + display: flex; +} + +.HeaderContent { + display: flex; + padding-inline: var(--base-size-8); + padding-block: var(--base-size-6); + flex-direction: column; + flex-grow: 1; +} + .Title { margin: 0; /* override default margin */ font-size: var(--text-body-size-medium); diff --git a/packages/react/src/Dialog/Dialog.tsx b/packages/react/src/Dialog/Dialog.tsx index 14f6151ede9..9bfacac1238 100644 --- a/packages/react/src/Dialog/Dialog.tsx +++ b/packages/react/src/Dialog/Dialog.tsx @@ -1,10 +1,8 @@ import React, {useCallback, useEffect, useRef, useState, type SyntheticEvent} from 'react' import type {ButtonProps} from '../Button' import {Button, IconButton} from '../Button' -import Box from '../Box' import {useOnEscapePress, useProvidedRefOrCreate} from '../hooks' import {useFocusTrap} from '../hooks/useFocusTrap' -import type {SxProp} from '../sx' import {XIcon} from '@primer/octicons-react' import {useFocusZone} from '../hooks/useFocusZone' import {FocusKeys} from '@primer/behaviors' @@ -17,7 +15,6 @@ import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../uti import classes from './Dialog.module.css' import {clsx} from 'clsx' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' /* Dialog Version 2 */ @@ -52,7 +49,7 @@ export type DialogButtonProps = Omit & { /** * Props to customize the rendering of the Dialog. */ -export interface DialogProps extends SxProp { +export interface DialogProps { /** * Title of the Dialog. Also serves as the aria-label for this Dialog. */ @@ -198,13 +195,13 @@ const DefaultHeader: React.FC> = ({ }, [onClose]) return ( - - +
    +
    {title ?? 'Dialog'} {subtitle && {subtitle}} - +
    - +
    ) } @@ -245,7 +242,6 @@ const _Dialog = React.forwardRef - - {header} @@ -339,50 +332,50 @@ const _Dialog = React.forwardRef {footer} - - + + ) }) _Dialog.displayName = 'Dialog' -type StyledHeaderProps = React.ComponentProps<'div'> & SxProp +type StyledHeaderProps = React.ComponentProps<'div'> -const Header = React.forwardRef(function Header({className, ...rest}, forwardRef) { - return +const Header = React.forwardRef(function Header({className, ...rest}, forwardRef) { + return
    }) Header.displayName = 'Dialog.Header' -type StyledTitleProps = React.ComponentProps<'h1'> & SxProp +type StyledTitleProps = React.ComponentProps<'h1'> -const Title = React.forwardRef(function Title({className, ...rest}, forwardRef) { - return +const Title = React.forwardRef(function Title({className, ...rest}, forwardRef) { + return

    }) Title.displayName = 'Dialog.Title' -type StyledSubtitleProps = React.ComponentProps<'h2'> & SxProp +type StyledSubtitleProps = React.ComponentProps<'h2'> -const Subtitle = React.forwardRef(function Subtitle( +const Subtitle = React.forwardRef(function Subtitle( {className, ...rest}, forwardRef, ) { - return + return

    }) Subtitle.displayName = 'Dialog.Subtitle' -type StyledBodyProps = React.ComponentProps<'div'> & SxProp +type StyledBodyProps = React.ComponentProps<'div'> -const Body = React.forwardRef(function Body({className, ...rest}, forwardRef) { - return +const Body = React.forwardRef(function Body({className, ...rest}, forwardRef) { + return
    }) as PolymorphicForwardRefComponent<'div', StyledBodyProps> Body.displayName = 'Dialog.Body' -type StyledFooterProps = React.ComponentProps<'div'> & SxProp +type StyledFooterProps = React.ComponentProps<'div'> -const Footer = React.forwardRef(function Footer({className, ...rest}, forwardRef) { - return +const Footer = React.forwardRef(function Footer({className, ...rest}, forwardRef) { + return
    }) Footer.displayName = 'Dialog.Footer' diff --git a/packages/styled-react/src/__tests__/primer-react.browser.test.tsx b/packages/styled-react/src/__tests__/primer-react.browser.test.tsx index 89eeeb65764..f86970ef32f 100644 --- a/packages/styled-react/src/__tests__/primer-react.browser.test.tsx +++ b/packages/styled-react/src/__tests__/primer-react.browser.test.tsx @@ -155,6 +155,33 @@ describe('@primer/react', () => { expect(window.getComputedStyle(screen.getByRole('dialog')).backgroundColor).toBe('rgb(255, 0, 0)') }) + test('Dialog.Header supports `sx` prop', () => { + render( + {}} + renderHeader={() => } + />, + ) + expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') + }) + + test('Dialog.Body supports `sx` prop', () => { + render( + {}} renderBody={() => } />, + ) + expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') + }) + + test('Dialog.Footer supports `sx` prop', () => { + render( + {}} + renderFooter={() => } + />, + ) + expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') + }) + test('Flash supports `sx` prop', () => { render() expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') diff --git a/packages/styled-react/src/components/Dialog.tsx b/packages/styled-react/src/components/Dialog.tsx new file mode 100644 index 00000000000..827be448318 --- /dev/null +++ b/packages/styled-react/src/components/Dialog.tsx @@ -0,0 +1,41 @@ +import {Dialog as PrimerDialog} from '@primer/react' +import type {DialogProps as PrimerDialogProps} from '@primer/react' +import {Box} from './Box' +import type {SxProp} from '../sx' +import {forwardRef, type ComponentPropsWithoutRef, type PropsWithChildren} from 'react' + +type DialogProps = PropsWithChildren & SxProp + +const DialogImpl = forwardRef(function Dialog(props, ref) { + // @ts-expect-error - PrimerDialog is not recognized as a valid component type + return +}) + +type DialogHeaderProps = ComponentPropsWithoutRef<'div'> & SxProp + +const DialogHeader = forwardRef(function DialogHeader(props, ref) { + return +}) + +type StyledBodyProps = React.ComponentProps<'div'> & SxProp + +const DialogBody = forwardRef(function DialogBody(props, ref) { + // @ts-expect-error - PrimerDialog.Body is not recognized as a valid component type + return +}) + +type StyledFooterProps = React.ComponentProps<'div'> & SxProp + +const DialogFooter = forwardRef(function DialogFooter(props, ref) { + return +}) + +const Dialog = Object.assign(DialogImpl, { + Buttons: PrimerDialog.Buttons, + Header: DialogHeader, + Body: DialogBody, + Footer: DialogFooter, +}) + +export {Dialog} +export type {DialogProps} diff --git a/packages/styled-react/src/experimental.tsx b/packages/styled-react/src/experimental.tsx index 7b1764bbcb9..84f0becab80 100644 --- a/packages/styled-react/src/experimental.tsx +++ b/packages/styled-react/src/experimental.tsx @@ -1,7 +1,8 @@ +export {Dialog, type DialogProps} from './components/Dialog' export { PageHeader, type PageHeaderProps, type PageHeaderActionsProps, type PageHeaderTitleProps, } from './components/PageHeader' -export {Dialog, Table, Tooltip, UnderlinePanels} from '@primer/react/experimental' +export {Table, Tooltip, UnderlinePanels} from '@primer/react/experimental' diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index 8be52072298..13a6dcbddae 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -8,7 +8,6 @@ export {Button} from '@primer/react' export {CheckboxGroup} from '@primer/react' export {CircleBadge} from '@primer/react' export {Details} from '@primer/react' -export {Dialog} from '@primer/react' export {FormControl} from '@primer/react' export {Heading} from '@primer/react' export {IconButton} from '@primer/react' @@ -37,6 +36,7 @@ export {useTheme} from '@primer/react' export {Checkbox, type CheckboxProps} from './components/Checkbox' export {CounterLabel, type CounterLabelProps} from './components/CounterLabel' +export {Dialog, type DialogProps} from './components/Dialog' export {Flash} from './components/Flash' export {Header, type HeaderProps} from './components/Header' export {LinkButton, type LinkButtonProps} from './components/LinkButton' From 80635ab731fd18a524231442de6cdc131ef50b8b Mon Sep 17 00:00:00 2001 From: Mike Perrotti Date: Fri, 26 Sep 2025 16:54:42 -0400 Subject: [PATCH 19/27] Remove Box usage and `sx` prop from PageLayout (#6872) --- .changeset/gold-geckos-send.md | 5 + .../PageLayout.dev.stories.module.css | 9 ++ .../src/PageLayout/PageLayout.dev.stories.tsx | 11 +- .../src/PageLayout/PageLayout.module.css | 20 +++ .../src/PageLayout/PageLayout.stories.tsx | 1 - packages/react/src/PageLayout/PageLayout.tsx | 141 +++++++----------- .../src/components/PageLayout.tsx | 57 +++++++ packages/styled-react/src/index.tsx | 2 +- 8 files changed, 155 insertions(+), 91 deletions(-) create mode 100644 .changeset/gold-geckos-send.md create mode 100644 packages/react/src/PageLayout/PageLayout.dev.stories.module.css create mode 100644 packages/styled-react/src/components/PageLayout.tsx diff --git a/.changeset/gold-geckos-send.md b/.changeset/gold-geckos-send.md new file mode 100644 index 00000000000..5e3441f4a4a --- /dev/null +++ b/.changeset/gold-geckos-send.md @@ -0,0 +1,5 @@ +--- +'@primer/react': major +--- + +Removes sx prop from PageLayout and subcomponents diff --git a/packages/react/src/PageLayout/PageLayout.dev.stories.module.css b/packages/react/src/PageLayout/PageLayout.dev.stories.module.css new file mode 100644 index 00000000000..35584590235 --- /dev/null +++ b/packages/react/src/PageLayout/PageLayout.dev.stories.module.css @@ -0,0 +1,9 @@ +.DebugPageLayout { + /* `red` is just used for debugging purposes. VRTs fail if changed. */ + /* stylelint-disable-next-line color-named */ + border: var(--borderWidth-thin) solid red; +} + +.SuccessColor { + color: var(--fgColor-success); +} diff --git a/packages/react/src/PageLayout/PageLayout.dev.stories.tsx b/packages/react/src/PageLayout/PageLayout.dev.stories.tsx index 1eca843ae20..5be8f52ffad 100644 --- a/packages/react/src/PageLayout/PageLayout.dev.stories.tsx +++ b/packages/react/src/PageLayout/PageLayout.dev.stories.tsx @@ -1,6 +1,7 @@ import type {Meta, StoryFn} from '@storybook/react-vite' import {Placeholder} from '../Placeholder' import {PageLayout} from './PageLayout' +import classes from './PageLayout.dev.stories.module.css' const meta: Meta = { title: 'Components/PageLayout/Dev', @@ -346,11 +347,11 @@ export const Default: StoryFn = args => ( padding={args.padding} rowGap={args.rowGap} columnGap={args.columnGap} - sx={{border: '1px solid red'}} + className={classes.DebugPageLayout} > ( ( ( padding={args.padding} rowGap={args.rowGap} columnGap={args.columnGap} - sx={args.sx} > {args['Render header?'] ? ( className?: string style?: React.CSSProperties -} & SxProp +} // eslint-disable-next-line @typescript-eslint/no-unused-vars const containerWidths = { @@ -73,7 +70,6 @@ const Root: React.FC> = ({ rowGap = 'normal', columnGap = 'normal', children, - sx, className, style, _slotsConfig: slotsConfig, @@ -93,14 +89,13 @@ const Root: React.FC> = ({ return ( -
    @@ -108,7 +103,7 @@ const Root: React.FC> = ({
    {rest}
    {slots.footer}
    -
    +
    ) } @@ -123,11 +118,10 @@ type DividerProps = { className?: string style?: React.CSSProperties position?: keyof typeof panePositions -} & SxProp +} const HorizontalDivider: React.FC> = ({ variant = 'none', - sx, className, position, style, @@ -136,8 +130,7 @@ const HorizontalDivider: React.FC> = ({ const responsiveVariant = useResponsiveValue(variant, 'none') return ( - { const [isDragging, setIsDragging] = React.useState(false) const [isKeyboardDrag, setIsKeyboardDrag] = React.useState(false) @@ -268,8 +260,7 @@ const VerticalDivider: React.FC {draggable ? ( // Drag handle - <> - { - if (event.button === 0) { - setIsDragging(true) - onDragStart?.() - } - }} - onKeyDown={event => { - if ( - event.key === 'ArrowLeft' || - event.key === 'ArrowRight' || - event.key === 'ArrowUp' || - event.key === 'ArrowDown' - ) { - setIsKeyboardDrag(true) - onDragStart?.() - } - }} - onDoubleClick={onDoubleClick} - /> - +
    { + if (event.button === 0) { + setIsDragging(true) + onDragStart?.() + } + }} + onKeyDown={(event: React.KeyboardEvent) => { + if ( + event.key === 'ArrowLeft' || + event.key === 'ArrowRight' || + event.key === 'ArrowUp' || + event.key === 'ArrowDown' + ) { + setIsKeyboardDrag(true) + onDragStart?.() + } + }} + onDoubleClick={onDoubleClick} + /> ) : null} - +
    ) } @@ -355,7 +336,7 @@ export type PageLayoutHeaderProps = { hidden?: boolean | ResponsiveValue className?: string style?: React.CSSProperties -} & SxProp +} const Header: React.FC> = ({ 'aria-label': label, @@ -366,7 +347,6 @@ const Header: React.FC> = ({ hidden = false, children, style, - sx, className, }) => { // Combine divider and dividerWhenNarrow for backwards compatibility @@ -380,12 +360,10 @@ const Header: React.FC> = ({ const {rowGap} = React.useContext(PageLayoutContext) return ( - + ) } @@ -443,7 +421,7 @@ export type PageLayoutContentProps = { hidden?: boolean | ResponsiveValue className?: string style?: React.CSSProperties -} & SxProp +} // TODO: Account for pane width when centering content // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -462,19 +440,17 @@ const Content: React.FC> = ({ padding = 'none', hidden = false, children, - sx, className, style, }) => { const isHidden = useResponsiveValue(hidden, false) + const Component = as return ( - @@ -489,7 +465,7 @@ const Content: React.FC> = ({ > {children}
    -
    + ) } @@ -563,7 +539,7 @@ export type PageLayoutPaneProps = { id?: string className?: string style?: React.CSSProperties -} & SxProp +} // eslint-disable-next-line @typescript-eslint/no-unused-vars const panePositions = { @@ -601,7 +577,6 @@ const Pane = React.forwardRef - +

    ) }, ) @@ -803,7 +779,7 @@ export type PageLayoutFooterProps = { hidden?: boolean | ResponsiveValue className?: string style?: React.CSSProperties -} & SxProp +} const Footer: React.FC> = ({ 'aria-label': label, @@ -813,7 +789,6 @@ const Footer: React.FC> = ({ dividerWhenNarrow = 'inherit', hidden = false, children, - sx, className, style, }) => { @@ -828,13 +803,11 @@ const Footer: React.FC> = ({ const {rowGap} = React.useContext(PageLayoutContext) return ( - + ) } diff --git a/packages/styled-react/src/components/PageLayout.tsx b/packages/styled-react/src/components/PageLayout.tsx new file mode 100644 index 00000000000..de4670c6528 --- /dev/null +++ b/packages/styled-react/src/components/PageLayout.tsx @@ -0,0 +1,57 @@ +import React, {type PropsWithChildren} from 'react' +import styled from 'styled-components' +import type { + PageLayoutProps as PrimerPageLayoutProps, + PageLayoutContentProps as PrimerPageLayoutContentProps, + PageLayoutHeaderProps as PrimerPageLayoutHeaderProps, + PageLayoutPaneProps as PrimerPageLayoutPaneProps, + PageLayoutFooterProps as PrimerPageLayoutFooterProps, +} from '@primer/react' +import {PageLayout as PrimerPageLayout} from '@primer/react' +import {sx, type SxProp} from '../sx' + +const Wrapper = styled.div` + ${sx} +` + +type PageLayoutProps = PropsWithChildren & SxProp + +const PageLayoutImpl = React.forwardRef((props, ref) => { + // @ts-expect-error - PrimerPageLayout is not recognized as a valid component type + return +}) + +type PageLayoutContentProps = PropsWithChildren & SxProp + +const PageLayoutContent = React.forwardRef((props, ref) => { + return +}) + +type PageLayoutHeaderProps = PropsWithChildren & SxProp + +const PageLayoutHeader = React.forwardRef((props, ref) => { + // @ts-expect-error - PrimerPageLayout.Header is not recognized as a valid component type + return +}) + +type PageLayoutPaneProps = PropsWithChildren & SxProp + +const PageLayoutPane = React.forwardRef((props, ref) => { + return +}) + +type PageLayoutFooterProps = PropsWithChildren & SxProp + +const PageLayoutFooter = React.forwardRef((props, ref) => { + // @ts-expect-error - PrimerPageLayout.Footer is not recognized as a valid component type + return +}) + +const PageLayout = Object.assign(PageLayoutImpl, { + Content: PageLayoutContent, + Header: PageLayoutHeader, + Pane: PageLayoutPane, + Footer: PageLayoutFooter, +}) + +export {PageLayout, type PageLayoutProps} diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index 13a6dcbddae..c0171145350 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -15,7 +15,6 @@ export {Label} from '@primer/react' export {Link} from '@primer/react' export {NavList} from '@primer/react' export {Overlay} from '@primer/react' -export {PageLayout} from '@primer/react' export {ProgressBar} from '@primer/react' export {Select} from '@primer/react' export {Text} from '@primer/react' @@ -40,6 +39,7 @@ export {Dialog, type DialogProps} from './components/Dialog' export {Flash} from './components/Flash' export {Header, type HeaderProps} from './components/Header' export {LinkButton, type LinkButtonProps} from './components/LinkButton' +export {PageLayout, type PageLayoutProps} from './components/PageLayout' export { PageHeader, type PageHeaderProps, From a7d59f22362d00a63e63c5c2f3c7715dffb2e645 Mon Sep 17 00:00:00 2001 From: "Brittany L. Houtz" <55068883+llastflowers@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:01:10 -0700 Subject: [PATCH 20/27] Update FormControl ValidationIcon position (#6908) --- .changeset/pretty-emus-cover.md | 5 +++++ .../react/src/internal/components/InputValidation.module.css | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/pretty-emus-cover.md diff --git a/.changeset/pretty-emus-cover.md b/.changeset/pretty-emus-cover.md new file mode 100644 index 00000000000..d92a66b9daf --- /dev/null +++ b/.changeset/pretty-emus-cover.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +update FormControl ValidationIcon position diff --git a/packages/react/src/internal/components/InputValidation.module.css b/packages/react/src/internal/components/InputValidation.module.css index 97428fbb395..48e10f1361b 100644 --- a/packages/react/src/internal/components/InputValidation.module.css +++ b/packages/react/src/internal/components/InputValidation.module.css @@ -20,8 +20,8 @@ } .ValidationIcon { - align-items: center; display: flex; + margin-top: var(--base-size-2); margin-inline-end: var(--base-size-4); min-height: var(--inputValidation-iconSize); } From 72f7f821ff5f3f16c26cbe937d8a3b95fcc4585b Mon Sep 17 00:00:00 2001 From: Mike Perrotti Date: Fri, 26 Sep 2025 17:04:07 -0400 Subject: [PATCH 21/27] Remove Box usage and `sx` prop from Link and misc other areas (stories, etc) (#6825) --- .changeset/light-schools-wish.md | 5 ++ .../src/Hidden/Hidden.examples.stories.tsx | 2 +- packages/react/src/Link/Link.docs.json | 5 -- packages/react/src/Link/Link.tsx | 51 ++++++++----------- .../react/src/Link/__tests__/Link.test.tsx | 11 ---- .../Overlay.features.stories.module.css | 11 ++++ .../src/Overlay/Overlay.features.stories.tsx | 10 +--- packages/react/src/Popover/Popover.figma.tsx | 8 +-- packages/react/src/live-region/Announce.tsx | 4 +- packages/react/src/live-region/AriaAlert.tsx | 16 ++---- packages/react/src/live-region/AriaStatus.tsx | 18 ++----- .../react/src/utils/modern-polymorphic.ts | 35 +++++++++++++ packages/react/src/utils/polymorphic2.ts | 23 --------- packages/styled-react/src/components/Link.tsx | 12 +++++ packages/styled-react/src/index.tsx | 2 +- 15 files changed, 98 insertions(+), 115 deletions(-) create mode 100644 .changeset/light-schools-wish.md create mode 100644 packages/react/src/utils/modern-polymorphic.ts delete mode 100644 packages/react/src/utils/polymorphic2.ts create mode 100644 packages/styled-react/src/components/Link.tsx diff --git a/.changeset/light-schools-wish.md b/.changeset/light-schools-wish.md new file mode 100644 index 00000000000..7368a2e3133 --- /dev/null +++ b/.changeset/light-schools-wish.md @@ -0,0 +1,5 @@ +--- +'@primer/react': major +--- + +Removes `Box` component usage and `sx` prop from the `Link` component, Storybook stories, and a .figma.tsx file diff --git a/packages/react/src/Hidden/Hidden.examples.stories.tsx b/packages/react/src/Hidden/Hidden.examples.stories.tsx index 2ee71b07234..a9cbda74ad9 100644 --- a/packages/react/src/Hidden/Hidden.examples.stories.tsx +++ b/packages/react/src/Hidden/Hidden.examples.stories.tsx @@ -61,7 +61,7 @@ export const PullRequestPage = () => ( Open - + broccolinisoup {' '} wants to merge 3 commits into diff --git a/packages/react/src/Link/Link.docs.json b/packages/react/src/Link/Link.docs.json index a340feb72bc..8887776364e 100644 --- a/packages/react/src/Link/Link.docs.json +++ b/packages/react/src/Link/Link.docs.json @@ -58,11 +58,6 @@ "name": "as", "type": "React.ElementType", "defaultValue": "\"a\"" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ], "subcomponents": [] diff --git a/packages/react/src/Link/Link.tsx b/packages/react/src/Link/Link.tsx index bba00e98cb6..59d218173c1 100644 --- a/packages/react/src/Link/Link.tsx +++ b/packages/react/src/Link/Link.tsx @@ -1,13 +1,12 @@ import {clsx} from 'clsx' -import React, {forwardRef, useEffect} from 'react' +import React, {useEffect, type ForwardedRef, type ElementRef} from 'react' import {useRefObjectAsForwardedRef} from '../hooks' -import type {SxProp} from '../sx' import classes from './Link.module.css' -import Box from '../Box' import type {ComponentProps} from '../utils/types' -import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' +import {type PolymorphicProps, fixedForwardRef} from '../utils/modern-polymorphic' -type StyledLinkProps = { +type StyledLinkProps = { + as?: As /** @deprecated use CSS modules to style hover color */ hoverColor?: string muted?: boolean @@ -15,11 +14,15 @@ type StyledLinkProps = { underline?: boolean // Link inside a text block inline?: boolean -} & SxProp +} -const Link = forwardRef(({as: Component = 'a', className, inline, underline, hoverColor, ...props}, forwardedRef) => { - const innerRef = React.useRef(null) - useRefObjectAsForwardedRef(forwardedRef, innerRef) +export const UnwrappedLink = ( + props: PolymorphicProps, + ref: ForwardedRef, +) => { + const {as: Component = 'a', className, inline, underline, hoverColor, ...restProps} = props + const innerRef = React.useRef>(null) + useRefObjectAsForwardedRef(ref, innerRef) if (__DEV__) { /** @@ -46,37 +49,23 @@ const Link = forwardRef(({as: Component = 'a', className, inline, underline, hov }, [innerRef]) } - if (props.sx) { - return ( - - ) - } - return ( ) -}) as PolymorphicForwardRefComponent<'a', StyledLinkProps> +} + +const LinkComponent = fixedForwardRef(UnwrappedLink) -Link.displayName = 'Link' +const Link = Object.assign(LinkComponent, {displayName: 'Link'}) export type LinkProps = ComponentProps export default Link diff --git a/packages/react/src/Link/__tests__/Link.test.tsx b/packages/react/src/Link/__tests__/Link.test.tsx index e43fe5737c4..142ab485496 100644 --- a/packages/react/src/Link/__tests__/Link.test.tsx +++ b/packages/react/src/Link/__tests__/Link.test.tsx @@ -18,11 +18,6 @@ describe('Link', () => { expect(container.firstChild).toHaveStyle('--fgColor-accent: #0969da') }) - it('respects the "sx" prop', () => { - const {container} = render() - expect(container.firstChild).toHaveStyle('font-style: italic') - }) - it('applies button styles when rendering a button element', () => { const {container} = render() expect((container.firstChild as Element).tagName).toBe('BUTTON') @@ -33,12 +28,6 @@ describe('Link', () => { expect(container.firstChild).toHaveAttribute('data-muted', 'true') }) - it('respects the "sx" prop when "muted" prop is also passed', () => { - const {container} = render() - expect(container.firstChild).toHaveAttribute('data-muted', 'true') - expect(container.firstChild).toHaveStyle('color: rgb(89, 99, 110)') - }) - it('logs a warning when trying to render invalid "as" prop', () => { const consoleSpy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {}) diff --git a/packages/react/src/Overlay/Overlay.features.stories.module.css b/packages/react/src/Overlay/Overlay.features.stories.module.css index b8fedfc4532..590996551c4 100644 --- a/packages/react/src/Overlay/Overlay.features.stories.module.css +++ b/packages/react/src/Overlay/Overlay.features.stories.module.css @@ -136,3 +136,14 @@ right: var(--base-size-4); top: var(--base-size-4); } + +.IssueLink { + display: block; + border: var(--borderWidth-thin) solid var(--borderColor-default); + padding: var(--base-size-8); + transition: background 0.2s; +} + +.IssueLink:hover { + background-color: var(--bgColor-muted); +} diff --git a/packages/react/src/Overlay/Overlay.features.stories.tsx b/packages/react/src/Overlay/Overlay.features.stories.tsx index 4b89797ea61..925d6659e57 100644 --- a/packages/react/src/Overlay/Overlay.features.stories.tsx +++ b/packages/react/src/Overlay/Overlay.features.stories.tsx @@ -429,15 +429,7 @@ export const MemexIssueOverlay = ({role, open}: Args) => { event.preventDefault() setOverlayOpen(true) }} - sx={{ - display: 'block', - border: '1px solid', - borderColor: 'border.default', - p: 2, - ':hover': { - backgroundColor: 'canvas.subtle', - }, - }} + className={classes.IssueLink} > {title} diff --git a/packages/react/src/Popover/Popover.figma.tsx b/packages/react/src/Popover/Popover.figma.tsx index 4eb8d307373..5fae163a466 100644 --- a/packages/react/src/Popover/Popover.figma.tsx +++ b/packages/react/src/Popover/Popover.figma.tsx @@ -24,13 +24,7 @@ figma.connect(Popover, 'https://www.figma.com/design/GCvY3Qv8czRgZgvl1dG6lp/Prim example: ({caret, heading, body, action}) => ( - - {heading} - + {heading} {body} {action} diff --git a/packages/react/src/live-region/Announce.tsx b/packages/react/src/live-region/Announce.tsx index 15d697bb690..6e439d10a9b 100644 --- a/packages/react/src/live-region/Announce.tsx +++ b/packages/react/src/live-region/Announce.tsx @@ -3,11 +3,11 @@ import type React from 'react' import {useEffect, useRef, useState, type ElementRef} from 'react' import {useEffectOnce} from '../internal/hooks/useEffectOnce' import {useEffectCallback} from '../internal/hooks/useEffectCallback' -import type {PolymorphicProps} from '../utils/polymorphic2' +import type {PolymorphicProps} from '../utils/modern-polymorphic' export type AnnounceProps = PolymorphicProps< - 'div', As, + 'div', { /** * Specify if the content of the element should be announced when this diff --git a/packages/react/src/live-region/AriaAlert.tsx b/packages/react/src/live-region/AriaAlert.tsx index 7f9e48fa47f..78286a06abc 100644 --- a/packages/react/src/live-region/AriaAlert.tsx +++ b/packages/react/src/live-region/AriaAlert.tsx @@ -1,11 +1,11 @@ import type React from 'react' import {type ElementType} from 'react' import {Announce} from './Announce' -import type {PolymorphicProps} from '../utils/polymorphic2' +import type {PolymorphicProps} from '../utils/modern-polymorphic' export type AriaAlertProps = PolymorphicProps< - 'div', As, + 'div', { /** * Specify if the content of the element should be announced when this @@ -22,14 +22,6 @@ export type AriaAlertProps = PolymorphicProps< } > -export function AriaAlert({ - announceOnShow = true, - children, - ...rest -}: AriaAlertProps) { - return ( - - {children} - - ) +export function AriaAlert(props: AriaAlertProps) { + return } diff --git a/packages/react/src/live-region/AriaStatus.tsx b/packages/react/src/live-region/AriaStatus.tsx index a9a4ff13aac..6d237bc3445 100644 --- a/packages/react/src/live-region/AriaStatus.tsx +++ b/packages/react/src/live-region/AriaStatus.tsx @@ -1,11 +1,11 @@ import type React from 'react' import {type ElementType} from 'react' import {Announce} from './Announce' -import type {PolymorphicProps} from '../utils/polymorphic2' +import type {PolymorphicProps} from '../utils/modern-polymorphic' -export type AriaStatusProps = PolymorphicProps< - 'div', +export type AriaStatusProps = PolymorphicProps< As, + 'div', { /** * Specify if the content of the element should be announced when this @@ -27,14 +27,6 @@ export type AriaStatusProps = PolymorphicProps< } > -export function AriaStatus({ - announceOnShow = false, - children, - ...rest -}: AriaStatusProps) { - return ( - - {children} - - ) +export function AriaStatus(props: AriaStatusProps) { + return } diff --git a/packages/react/src/utils/modern-polymorphic.ts b/packages/react/src/utils/modern-polymorphic.ts new file mode 100644 index 00000000000..5f83844ac10 --- /dev/null +++ b/packages/react/src/utils/modern-polymorphic.ts @@ -0,0 +1,35 @@ +// Mostly taken from https://github.com/total-typescript/react-typescript-tutorial/blob/main/src/08-advanced-patterns/72-as-prop-with-forward-ref.solution.tsx + +import {forwardRef} from 'react' +import type {ComponentPropsWithRef, ElementType} from 'react' + +/** + * Distributive Omit utility type that works correctly with union types + */ +type DistributiveOmit = T extends unknown ? Omit : never + +/** + * Fixed version of forwardRef that provides better type inference for polymorphic components + */ +// TODO: figure out how to change this type so we can set displayName +// like this: `ComponentName.displayName = 'DisplayName' instead of using workarounds +type FixedForwardRef = = Record>( + render: (props: P, ref: React.Ref) => React.ReactNode, +) => (props: P & React.RefAttributes) => React.ReactNode + +/** + * Cast forwardRef to the fixed version with better type inference + */ +export const fixedForwardRef = forwardRef as FixedForwardRef + +/** + * Simplified polymorphic props type that handles the common pattern of + * `DistributiveOmit, 'as'>` + */ +export type PolymorphicProps< + TAs extends ElementType, + TDefaultElement extends ElementType = 'div', + Props extends Record = Record, +> = DistributiveOmit & Props, 'as'> & { + as?: TAs +} diff --git a/packages/react/src/utils/polymorphic2.ts b/packages/react/src/utils/polymorphic2.ts deleted file mode 100644 index b48264baa27..00000000000 --- a/packages/react/src/utils/polymorphic2.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * This file is an alternative to polymorphic.ts that hopes to support - * polyrmophic components in React. It explicitly hopes to make it easy to - * type the props of components and allow for explicitly setting the type of a - * component - */ - -import type {ElementType} from 'react' - -type AsProp = { - /** - * Customize the element type of the container element for the component - */ - as?: As -} - -type PolymorphicProps = Props & - AsProp & - (As extends React.ElementType - ? Omit, keyof Props> - : Omit, keyof Props>) - -export type {PolymorphicProps} diff --git a/packages/styled-react/src/components/Link.tsx b/packages/styled-react/src/components/Link.tsx new file mode 100644 index 00000000000..c87a2629684 --- /dev/null +++ b/packages/styled-react/src/components/Link.tsx @@ -0,0 +1,12 @@ +import {Link as PrimerLink, type LinkProps as PrimerLinkProps} from '@primer/react' +import styled from 'styled-components' +import {sx, type SxProp} from '../sx' + +type LinkProps = PrimerLinkProps & SxProp + +const Link = styled(PrimerLink).withConfig({ + shouldForwardProp: prop => prop !== 'sx', +})` + ${sx} +` +export {Link, type LinkProps} diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index c0171145350..c21bf22b9b0 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -12,7 +12,6 @@ export {FormControl} from '@primer/react' export {Heading} from '@primer/react' export {IconButton} from '@primer/react' export {Label} from '@primer/react' -export {Link} from '@primer/react' export {NavList} from '@primer/react' export {Overlay} from '@primer/react' export {ProgressBar} from '@primer/react' @@ -38,6 +37,7 @@ export {CounterLabel, type CounterLabelProps} from './components/CounterLabel' export {Dialog, type DialogProps} from './components/Dialog' export {Flash} from './components/Flash' export {Header, type HeaderProps} from './components/Header' +export {Link, type LinkProps} from './components/Link' export {LinkButton, type LinkButtonProps} from './components/LinkButton' export {PageLayout, type PageLayoutProps} from './components/PageLayout' export { From 234baa050732a9bfb4ff92e65697a85c92bdeffa Mon Sep 17 00:00:00 2001 From: Mike Perrotti Date: Fri, 26 Sep 2025 17:17:35 -0400 Subject: [PATCH 22/27] Remove Box usage from components (#6805) --- .changeset/yummy-years-greet.md | 5 ++++ packages/react/src/Button/ButtonBase.tsx | 19 +++++++++---- packages/react/src/Label/Label.tsx | 2 ++ .../src/LabelGroup/LabelGroup.module.css | 14 ++++++++++ packages/react/src/LabelGroup/LabelGroup.tsx | 7 +++-- packages/react/src/NavList/NavList.module.css | 8 ++++++ .../src/Pagination/Pagination.module.css | 4 +++ .../src/Pagination/Pagination.stories.tsx | 6 ---- packages/react/src/Pagination/Pagination.tsx | 28 ++++++------------- packages/react/src/Placeholder.module.css | 13 +++++++++ packages/react/src/Placeholder.tsx | 23 +++++++-------- .../ScrollableRegion.module.css | 8 ++++++ .../src/ScrollableRegion/ScrollableRegion.tsx | 17 ++++------- .../__snapshots__/TextInput.test.tsx.snap | 10 +++---- .../components/ConditionalWrapper.tsx | 12 ++++---- .../ValidationAnimationContainer.tsx | 5 ++-- 16 files changed, 107 insertions(+), 74 deletions(-) create mode 100644 .changeset/yummy-years-greet.md create mode 100644 packages/react/src/LabelGroup/LabelGroup.module.css create mode 100644 packages/react/src/NavList/NavList.module.css create mode 100644 packages/react/src/Placeholder.module.css create mode 100644 packages/react/src/ScrollableRegion/ScrollableRegion.module.css diff --git a/.changeset/yummy-years-greet.md b/.changeset/yummy-years-greet.md new file mode 100644 index 00000000000..3e2db59c156 --- /dev/null +++ b/.changeset/yummy-years-greet.md @@ -0,0 +1,5 @@ +--- +'@primer/react': major +--- + +Removes usage of Box component from other components. diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index e60b9b9460d..d5e3ea3b689 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -1,6 +1,8 @@ import React, {forwardRef} from 'react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' -import Box from '../Box' +import styled from 'styled-components' +import sx from '../sx' +import type {SxProp} from '../sx' import type {ButtonProps} from './types' import {getAlignContentSize} from './styles' import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' @@ -15,6 +17,12 @@ import {clsx} from 'clsx' import classes from './ButtonBase.module.css' import {isElement} from 'react-is' +// TODO: remove this when we remove the `sx` prop from buttons +// Styled span component for button content that can handle sx prop +const BoxTemporaryWorkaround = styled.span` + ${sx}; +` + const renderModuleVisual = ( Visual: React.ElementType | React.ReactElement, loading: boolean, @@ -92,7 +100,7 @@ const ButtonBase = forwardRef( className={block ? classes.ConditionalWrapper : undefined} data-loading-wrapper > - - + { /* If there is a trailing action, render it unless the button is in a loading state and there is no leading or trailing visual to replace with a loading spinner. */ @@ -196,7 +203,7 @@ const ButtonBase = forwardRef( } )} - + {loading && ( {loadingAnnouncement} diff --git a/packages/react/src/Label/Label.tsx b/packages/react/src/Label/Label.tsx index 993c5ef85e0..a35224568b5 100644 --- a/packages/react/src/Label/Label.tsx +++ b/packages/react/src/Label/Label.tsx @@ -1,3 +1,5 @@ +// TODO: merge https://github.com/primer/react/pull/6631 which removes `Box` usage + import {clsx} from 'clsx' import Box from '../Box' import classes from './Label.module.css' diff --git a/packages/react/src/LabelGroup/LabelGroup.module.css b/packages/react/src/LabelGroup/LabelGroup.module.css new file mode 100644 index 00000000000..4009aae37e8 --- /dev/null +++ b/packages/react/src/LabelGroup/LabelGroup.module.css @@ -0,0 +1,14 @@ +.OverlayContainer { + align-items: flex-start; + display: flex; +} + +.OverlayInner { + display: flex; + flex-wrap: wrap; + gap: var(--base-size-4); +} + +.CloseButton { + flex-shrink: 0; +} diff --git a/packages/react/src/LabelGroup/LabelGroup.tsx b/packages/react/src/LabelGroup/LabelGroup.tsx index 54b3d184c42..a5babffb2e4 100644 --- a/packages/react/src/LabelGroup/LabelGroup.tsx +++ b/packages/react/src/LabelGroup/LabelGroup.tsx @@ -7,6 +7,7 @@ import VisuallyHidden from '../_VisuallyHidden' import {AnchoredOverlay} from '../AnchoredOverlay' import {Button, IconButton} from '../Button' import {useTheme} from '../ThemeProvider' +import classes from './LabelGroup.module.css' export type LabelGroupProps = { /** Customize the element type of the rendered container */ @@ -132,14 +133,14 @@ const OverlayToggle: React.FC< )} focusZoneSettings={{disabled: true}} > -
    -
    {children}
    +
    +
    {children}
    diff --git a/packages/react/src/NavList/NavList.module.css b/packages/react/src/NavList/NavList.module.css new file mode 100644 index 00000000000..9908361e112 --- /dev/null +++ b/packages/react/src/NavList/NavList.module.css @@ -0,0 +1,8 @@ +.GroupHeading > a { + color: var(--fgColor-default); + text-decoration: inherit; +} + +.GroupHeading > a:hover { + text-decoration: underline; +} diff --git a/packages/react/src/Pagination/Pagination.module.css b/packages/react/src/Pagination/Pagination.module.css index 8f5857fc50b..1c6e2e46d56 100644 --- a/packages/react/src/Pagination/Pagination.module.css +++ b/packages/react/src/Pagination/Pagination.module.css @@ -120,6 +120,10 @@ text-align: center; } +.TablePaginationSteps { + display: inline-block; +} + @media screen and (--viewportRange-narrow) { .TablePaginationSteps[data-hidden-viewport-ranges*='narrow'] > *:not(:first-child):not(:last-child) { display: none; diff --git a/packages/react/src/Pagination/Pagination.stories.tsx b/packages/react/src/Pagination/Pagination.stories.tsx index ccf24d794fe..64a2047e6d4 100644 --- a/packages/react/src/Pagination/Pagination.stories.tsx +++ b/packages/react/src/Pagination/Pagination.stories.tsx @@ -78,10 +78,4 @@ Playground.argTypes = { disable: true, }, }, - theme: { - control: false, - table: { - disable: true, - }, - }, } diff --git a/packages/react/src/Pagination/Pagination.tsx b/packages/react/src/Pagination/Pagination.tsx index 17f3f352aa5..556864aa2ca 100644 --- a/packages/react/src/Pagination/Pagination.tsx +++ b/packages/react/src/Pagination/Pagination.tsx @@ -1,11 +1,9 @@ import React from 'react' -import Box from '../Box' import {buildComponentData, buildPaginationModel, type PageDataProps} from './model' import type {ResponsiveValue} from '../hooks/useResponsiveValue' import {viewportRanges} from '../hooks/useResponsiveValue' import {clsx} from 'clsx' import classes from './Pagination.module.css' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' const getViewportRangesToHidePages = (showPages: PaginationProps['showPages']) => { if (showPages && typeof showPages !== 'boolean') { @@ -33,7 +31,6 @@ export type PageProps = { } & Omit type UsePaginationPagesParameters = { - theme?: Record // set to theme type once /src/theme.js is converted pageCount: number currentPage: number onPageChange: (e: React.MouseEvent, n: number) => void @@ -45,7 +42,6 @@ type UsePaginationPagesParameters = { } function usePaginationPages({ - theme, pageCount, currentPage, onPageChange, @@ -67,22 +63,22 @@ function usePaginationPages({ if (renderPage && props.as !== 'span') { return renderPage({key, children: content, number: page.num, className: classes.Page, ...props}) } + const Component = props.as || 'a' return ( // @ts-ignore giving me grief about children and "as" props - + {content} - + ) }) - }, [model, hrefBuilder, pageChange, renderPage, theme]) + }, [model, hrefBuilder, pageChange, renderPage]) return children } export type PaginationProps = { className?: string - theme?: Record pageCount: number currentPage: number onPageChange?: (e: React.MouseEvent, n: number) => void @@ -95,7 +91,6 @@ export type PaginationProps = { function Pagination({ className, - theme: _theme, pageCount, currentPage, onPageChange = noop, @@ -107,7 +102,6 @@ function Pagination({ ...rest }: PaginationProps) { const pageElements = usePaginationPages({ - theme: _theme, pageCount, currentPage, onPageChange, @@ -119,20 +113,14 @@ function Pagination({ }) return ( - - +
    {pageElements} - - +
    + ) } diff --git a/packages/react/src/Placeholder.module.css b/packages/react/src/Placeholder.module.css new file mode 100644 index 00000000000..6aa4f63fa21 --- /dev/null +++ b/packages/react/src/Placeholder.module.css @@ -0,0 +1,13 @@ +.Placeholder { + /* these are set in the component */ + --placeholder-width: ; + --placeholder-height: ; + + width: var(--placeholder-width, 100%); + height: var(--placeholder-height); + display: grid; + place-items: center; + background-color: var(--bgColor-inset); + border-radius: var(--borderRadius-medium); + border: var(--borderWidth-thin) solid var(--borderColor-muted); +} diff --git a/packages/react/src/Placeholder.tsx b/packages/react/src/Placeholder.tsx index fee4f290e8d..e5ab7b36d29 100644 --- a/packages/react/src/Placeholder.tsx +++ b/packages/react/src/Placeholder.tsx @@ -1,5 +1,5 @@ -import {Box} from '.' import type React from 'react' +import classes from './Placeholder.module.css' /** Private component used to render placeholders in storybook and documentation examples */ export const Placeholder: React.FC< @@ -11,20 +11,17 @@ export const Placeholder: React.FC< }> > = ({width, height, id, label}) => { return ( - {label} - +
    ) } diff --git a/packages/react/src/ScrollableRegion/ScrollableRegion.module.css b/packages/react/src/ScrollableRegion/ScrollableRegion.module.css new file mode 100644 index 00000000000..cf38fe50fae --- /dev/null +++ b/packages/react/src/ScrollableRegion/ScrollableRegion.module.css @@ -0,0 +1,8 @@ +.ScrollableRegion { + /* When setting overflow, we also set `position: relative` to avoid + * `position: absolute` items breaking out of the container and causing + * scrollbars on the page. This can occur with common classes like `sr-only` + * and can cause difficult to track down layout issues */ + position: relative; + overflow: auto; +} diff --git a/packages/react/src/ScrollableRegion/ScrollableRegion.tsx b/packages/react/src/ScrollableRegion/ScrollableRegion.tsx index e1c2ca12d52..b177d76f399 100644 --- a/packages/react/src/ScrollableRegion/ScrollableRegion.tsx +++ b/packages/react/src/ScrollableRegion/ScrollableRegion.tsx @@ -1,6 +1,7 @@ import React from 'react' -import Box from '../Box' +import {clsx} from 'clsx' import {useOverflow} from '../hooks/useOverflow' +import classes from './ScrollableRegion.module.css' type Labelled = | { @@ -14,19 +15,11 @@ type Labelled = type ScrollableRegionProps = React.ComponentPropsWithoutRef<'div'> & Labelled -const defaultStyles = { - // When setting overflow, we also set `position: relative` to avoid - // `position: absolute` items breaking out of the container and causing - // scrollbars on the page. This can occur with common classes like `sr-only` - // and can cause difficult to track down layout issues - position: 'relative', - overflow: 'auto', -} - function ScrollableRegion({ 'aria-label': label, 'aria-labelledby': labelledby, children, + className, ...rest }: ScrollableRegionProps) { const ref = React.useRef(null) @@ -41,9 +34,9 @@ function ScrollableRegion({ : {} return ( - +
    {children} - +
    ) } diff --git a/packages/react/src/TextInput/__snapshots__/TextInput.test.tsx.snap b/packages/react/src/TextInput/__snapshots__/TextInput.test.tsx.snap index b7639b324c8..04f3f456271 100644 --- a/packages/react/src/TextInput/__snapshots__/TextInput.test.tsx.snap +++ b/packages/react/src/TextInput/__snapshots__/TextInput.test.tsx.snap @@ -294,7 +294,7 @@ exports[`TextInput > renders trailingAction icon button 1`] = ` - ) -}) as PolymorphicForwardRefComponent - -const DivItemContainerNoBox = React.forwardRef(({children, ...props}, forwardedRef) => { - return ( -
    } {...props}> - {children} -
    - ) -}) as PolymorphicForwardRefComponent - -export const Item = React.forwardRef( - ( - { - variant = 'default', - size = 'medium', - disabled = false, - inactiveText, - selected = undefined, - active = false, - onSelect: onSelectUser, - id, - role, - loading, - _PrivateItemWrapper, - className, - groupId: _groupId, - renderItem: _renderItem, - handleAddItem: _handleAddItem, - ...props - }, - forwardedRef, - ): JSX.Element => { - const baseSlots = { - leadingVisual: LeadingVisual, - trailingVisual: TrailingVisual, - trailingAction: TrailingAction, - subItem: SubItem, - } - - const [partialSlots, childrenWithoutSlots] = useSlots(props.children, {...baseSlots, description: Description}) - - const slots = {description: undefined, ...partialSlots} - - const {container, afterSelect, selectionAttribute, defaultTrailingVisual} = - React.useContext(ActionListContainerContext) - - // Be sure to avoid rendering the container unless there is a default - const wrappedDefaultTrailingVisual = defaultTrailingVisual ? ( - {defaultTrailingVisual} - ) : null - const trailingVisual = slots.trailingVisual ?? wrappedDefaultTrailingVisual - - const {role: listRole, selectionVariant: listSelectionVariant} = React.useContext(ListContext) - const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext) - const inactive = Boolean(inactiveText) - // TODO change `menuContext` check to ```listRole !== undefined && ['menu', 'listbox'].includes(listRole)``` - // once we have a better way to handle existing usage in dotcom that incorrectly use ActionList.TrailingAction - const menuContext = container === 'ActionMenu' || container === 'SelectPanel' || container === 'FilteredActionList' - // TODO: when we change `menuContext` to check `listRole` instead of `container` - const showInactiveIndicator = inactive && !(listRole !== undefined && ['menu', 'listbox'].includes(listRole)) - - const onSelect = React.useCallback( - ( - event: React.MouseEvent | React.KeyboardEvent, - // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type - afterSelect?: Function, - ) => { - if (typeof onSelectUser === 'function') onSelectUser(event) - if (event.defaultPrevented) return - if (typeof afterSelect === 'function') afterSelect(event) - }, - [onSelectUser], +const ButtonItemContainerNoBox = React.forwardRef>( + ({children, style, ...props}, forwardedRef) => { + return ( + ) + }, +) - const selectionVariant: ActionListProps['selectionVariant'] = groupSelectionVariant - ? groupSelectionVariant - : listSelectionVariant - - /** Infer item role based on the container */ - let inferredItemRole: ActionListItemProps['role'] - if (container === 'ActionMenu') { - if (selectionVariant === 'single') inferredItemRole = 'menuitemradio' - else if (selectionVariant === 'multiple') inferredItemRole = 'menuitemcheckbox' - else inferredItemRole = 'menuitem' - } else if (listRole === 'listbox') { - if (selectionVariant !== undefined && !role) inferredItemRole = 'option' - } - - const itemRole = role || inferredItemRole - - if (slots.trailingAction) { - invariant( - !menuContext, - `ActionList.TrailingAction can not be used within a list with an ARIA role of "menu" or "listbox".`, - ) - } - - /** Infer the proper selection attribute based on the item's role */ - let inferredSelectionAttribute: 'aria-selected' | 'aria-checked' | undefined - if (itemRole === 'menuitemradio' || itemRole === 'menuitemcheckbox') inferredSelectionAttribute = 'aria-checked' - else if (itemRole === 'option') inferredSelectionAttribute = 'aria-selected' - - const itemSelectionAttribute = selectionAttribute || inferredSelectionAttribute - // Ensures ActionList.Item retains list item semantics if a valid ARIA role is applied, or if item is inactive - const listItemSemantics = - role === 'option' || role === 'menuitem' || role === 'menuitemradio' || role === 'menuitemcheckbox' - - const listRoleTypes = ['listbox', 'menu', 'list'] - const listSemantics = (listRole && listRoleTypes.includes(listRole)) || inactive || listItemSemantics - const buttonSemantics = !listSemantics && !_PrivateItemWrapper - - const clickHandler = React.useCallback( - (event: React.MouseEvent) => { - if (disabled || inactive || loading) return - onSelect(event, afterSelect) - }, - [onSelect, disabled, inactive, afterSelect, loading], +const DivItemContainerNoBox = React.forwardRef>( + ({children, ...props}, forwardedRef) => { + return ( +
    } {...props}> + {children} +
    ) + }, +) + +const UnwrappedItem = ( + { + variant = 'default', + size = 'medium', + disabled = false, + inactiveText, + selected = undefined, + active = false, + onSelect: onSelectUser, + sx: sxProp, + id, + role, + loading, + _PrivateItemWrapper, + className, + groupId: _groupId, + renderItem: _renderItem, + handleAddItem: _handleAddItem, + ...props + }: ActionListItemProps, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + forwardedRef: React.Ref, +): JSX.Element => { + const baseSlots = { + leadingVisual: LeadingVisual, + trailingVisual: TrailingVisual, + trailingAction: TrailingAction, + subItem: SubItem, + } + + const [partialSlots, childrenWithoutSlots] = useSlots(props.children, {...baseSlots, description: Description}) + + const slots = {description: undefined, ...partialSlots} + + const {container, afterSelect, selectionAttribute, defaultTrailingVisual} = + React.useContext(ActionListContainerContext) + + // Be sure to avoid rendering the container unless there is a default + const wrappedDefaultTrailingVisual = defaultTrailingVisual ? ( + {defaultTrailingVisual} + ) : null + const trailingVisual = slots.trailingVisual ?? wrappedDefaultTrailingVisual + + const {role: listRole, selectionVariant: listSelectionVariant} = React.useContext(ListContext) + const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext) + const inactive = Boolean(inactiveText) + // TODO change `menuContext` check to ```listRole !== undefined && ['menu', 'listbox'].includes(listRole)``` + // once we have a better way to handle existing usage in dotcom that incorrectly use ActionList.TrailingAction + const menuContext = container === 'ActionMenu' || container === 'SelectPanel' || container === 'FilteredActionList' + // TODO: when we change `menuContext` to check `listRole` instead of `container` + const showInactiveIndicator = inactive && !(listRole !== undefined && ['menu', 'listbox'].includes(listRole)) + + const onSelect = React.useCallback( + ( + event: React.MouseEvent | React.KeyboardEvent, + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type + afterSelect?: Function, + ) => { + if (typeof onSelectUser === 'function') onSelectUser(event) + if (event.defaultPrevented) return + if (typeof afterSelect === 'function') afterSelect(event) + }, + [onSelectUser], + ) - const keyPressHandler = React.useCallback( - (event: React.KeyboardEvent) => { - if (disabled || inactive || loading) return - if ([' ', 'Enter'].includes(event.key)) { - if (event.key === ' ') { - event.preventDefault() // prevent scrolling on Space - // immediately reset defaultPrevented once its job is done - // so as to not disturb the functions that use that event after this - event.defaultPrevented = false - } - onSelect(event, afterSelect) - } - }, - [onSelect, disabled, loading, inactive, afterSelect], + const selectionVariant: ActionListProps['selectionVariant'] = groupSelectionVariant + ? groupSelectionVariant + : listSelectionVariant + + /** Infer item role based on the container */ + let inferredItemRole: ActionListItemProps['role'] + if (container === 'ActionMenu') { + if (selectionVariant === 'single') inferredItemRole = 'menuitemradio' + else if (selectionVariant === 'multiple') inferredItemRole = 'menuitemcheckbox' + else inferredItemRole = 'menuitem' + } else if (listRole === 'listbox') { + if (selectionVariant !== undefined && !role) inferredItemRole = 'option' + } + + const itemRole = role || inferredItemRole + + if (slots.trailingAction) { + invariant( + !menuContext, + `ActionList.TrailingAction can not be used within a list with an ARIA role of "menu" or "listbox".`, ) + } + + /** Infer the proper selection attribute based on the item's role */ + let inferredSelectionAttribute: 'aria-selected' | 'aria-checked' | undefined + if (itemRole === 'menuitemradio' || itemRole === 'menuitemcheckbox') inferredSelectionAttribute = 'aria-checked' + else if (itemRole === 'option') inferredSelectionAttribute = 'aria-selected' + + const itemSelectionAttribute = selectionAttribute || inferredSelectionAttribute + // Ensures ActionList.Item retains list item semantics if a valid ARIA role is applied, or if item is inactive + const listItemSemantics = + role === 'option' || role === 'menuitem' || role === 'menuitemradio' || role === 'menuitemcheckbox' + + const listRoleTypes = ['listbox', 'menu', 'list'] + const listSemantics = (listRole && listRoleTypes.includes(listRole)) || inactive || listItemSemantics + const buttonSemantics = !listSemantics && !_PrivateItemWrapper + + const clickHandler = React.useCallback( + (event: React.MouseEvent) => { + if (disabled || inactive || loading) return + onSelect(event, afterSelect) + }, + [onSelect, disabled, inactive, afterSelect, loading], + ) - const itemId = useId(id) - const labelId = `${itemId}--label` - const inlineDescriptionId = `${itemId}--inline-description` - const blockDescriptionId = `${itemId}--block-description` - const trailingVisualId = `${itemId}--trailing-visual` - const inactiveWarningId = inactive && !showInactiveIndicator ? `${itemId}--warning-message` : undefined - - const DefaultItemWrapper = listSemantics ? DivItemContainerNoBox : ButtonItemContainerNoBox - - const ItemWrapper = _PrivateItemWrapper || DefaultItemWrapper - - // only apply aria-selected and aria-checked to selectable items - const selectableRoles = ['menuitemradio', 'menuitemcheckbox', 'option'] - const includeSelectionAttribute = itemSelectionAttribute && itemRole && selectableRoles.includes(itemRole) - - let focusable - - if (showInactiveIndicator) { - focusable = true - } - - // Extract the variant prop value from the description slot component - const descriptionVariant = slots.description?.props.variant ?? 'inline' - - const menuItemProps = { - onClick: clickHandler, - onKeyPress: !buttonSemantics ? keyPressHandler : undefined, - 'aria-disabled': disabled ? true : undefined, - 'data-inactive': inactive ? true : undefined, - 'data-loading': loading && !inactive ? true : undefined, - tabIndex: focusable ? undefined : 0, - 'aria-labelledby': `${labelId} ${slots.trailingVisual ? trailingVisualId : ''} ${ - slots.description && descriptionVariant === 'inline' ? inlineDescriptionId : '' - }`, - 'aria-describedby': - [ - slots.description && descriptionVariant === 'block' ? blockDescriptionId : undefined, - inactiveWarningId ?? undefined, - ] - .filter(String) - .join(' ') - .trim() || undefined, - ...(includeSelectionAttribute && {[itemSelectionAttribute]: selected}), - role: itemRole, - id: itemId, - } - - const containerProps = _PrivateItemWrapper - ? {role: itemRole ? 'none' : undefined, ...props} - : // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - (listSemantics && {...menuItemProps, ...props, ref: forwardedRef}) || {} - - const wrapperProps = _PrivateItemWrapper - ? menuItemProps - : !listSemantics && { - ...menuItemProps, - ...props, - ref: forwardedRef, + const keyPressHandler = React.useCallback( + (event: React.KeyboardEvent) => { + if (disabled || inactive || loading) return + if ([' ', 'Enter'].includes(event.key)) { + if (event.key === ' ') { + event.preventDefault() // prevent scrolling on Space + // immediately reset defaultPrevented once its job is done + // so as to not disturb the functions that use that event after this + event.defaultPrevented = false } + onSelect(event, afterSelect) + } + }, + [onSelect, disabled, loading, inactive, afterSelect], + ) - return ( - + -
  • - - - + + + + {slots.leadingVisual} + + + + + {childrenWithoutSlots} + {/* Loading message needs to be in here so it is read with the label */} + {/* If the item is inactive, we do not simultaneously announce that it is loading */} + {loading === true && !inactive && Loading} + + {slots.description} + - {slots.leadingVisual} + {trailingVisual} - - - - {childrenWithoutSlots} - {/* Loading message needs to be in here so it is read with the label */} - {/* If the item is inactive, we do not simultaneously announce that it is loading */} - {loading === true && !inactive && Loading} + + { + // If the item is inactive, but it's not in an overlay (e.g. ActionMenu, SelectPanel), + // render the inactive warning message directly in the item. + !showInactiveIndicator && inactiveText ? ( + + {inactiveText} - {slots.description} - - - {trailingVisual} - - - { - // If the item is inactive, but it's not in an overlay (e.g. ActionMenu, SelectPanel), - // render the inactive warning message directly in the item. - !showInactiveIndicator && inactiveText ? ( - - {inactiveText} - - ) : null - } - - - {!inactive && !loading && !menuContext && Boolean(slots.trailingAction) && slots.trailingAction} - {slots.subItem} -
  • -
    - ) - }, -) as PolymorphicForwardRefComponent<'li', ActionListItemProps> + ) : null + } +
    + + {!inactive && !loading && !menuContext && Boolean(slots.trailingAction) && slots.trailingAction} + {slots.subItem} +
    + + ) +} + +const Item = fixedForwardRef(UnwrappedItem) + +Object.assign(Item, {displayName: 'ActionList.Item'}) -Item.displayName = 'ActionList.Item' +export {Item} diff --git a/packages/react/src/ActionList/List.tsx b/packages/react/src/ActionList/List.tsx index 706ae570ecb..97ead828fea 100644 --- a/packages/react/src/ActionList/List.tsx +++ b/packages/react/src/ActionList/List.tsx @@ -1,5 +1,5 @@ import React from 'react' -import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' +import {fixedForwardRef} from '../utils/modern-polymorphic' import {ActionListContainerContext} from './ActionListContainerContext' import {useSlots} from '../hooks/useSlots' import {Heading} from './Heading' @@ -9,68 +9,82 @@ import {useProvidedRefOrCreate} from '../hooks' import {FocusKeys, useFocusZone} from '../hooks/useFocusZone' import {clsx} from 'clsx' import classes from './ActionList.module.css' +import {BoxWithFallback} from '../internal/components/BoxWithFallback' -export const List = React.forwardRef( - ( - {variant = 'inset', selectionVariant, showDividers = false, role, disableFocusZone = false, className, ...props}, - forwardedRef, - ): JSX.Element => { - const [slots, childrenWithoutSlots] = useSlots(props.children, { - heading: Heading, - }) +const UnwrappedList = ( + props: ActionListProps, + forwardedRef: React.Ref, +): JSX.Element => { + const { + as: Component = 'ul', + variant = 'inset', + selectionVariant, + showDividers = false, + role, + disableFocusZone = false, + className, + ...restProps + } = props + const [slots, childrenWithoutSlots] = useSlots(restProps.children, { + heading: Heading, + }) - const headingId = useId() + const headingId = useId() - /** if list is inside a Menu, it will get a role from the Menu */ - const { - listRole: listRoleFromContainer, - listLabelledBy, - selectionVariant: containerSelectionVariant, // TODO: Remove after DropdownMenu2 deprecation - enableFocusZone: enableFocusZoneFromContainer, - container, - } = React.useContext(ActionListContainerContext) + /** if list is inside a Menu, it will get a role from the Menu */ + const { + listRole: listRoleFromContainer, + listLabelledBy, + selectionVariant: containerSelectionVariant, // TODO: Remove after DropdownMenu2 deprecation + enableFocusZone: enableFocusZoneFromContainer, + container, + } = React.useContext(ActionListContainerContext) - const ariaLabelledBy = slots.heading ? (slots.heading.props.id ?? headingId) : listLabelledBy - const listRole = role || listRoleFromContainer - const listRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) + const ariaLabelledBy = slots.heading ? (slots.heading.props.id ?? headingId) : listLabelledBy + const listRole = role || listRoleFromContainer + const listRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) - let enableFocusZone = false - if (enableFocusZoneFromContainer !== undefined) enableFocusZone = enableFocusZoneFromContainer - else if (listRole && !disableFocusZone) enableFocusZone = ['menu', 'menubar', 'listbox'].includes(listRole) + let enableFocusZone = false + if (enableFocusZoneFromContainer !== undefined) enableFocusZone = enableFocusZoneFromContainer + else if (listRole && !disableFocusZone) enableFocusZone = ['menu', 'menubar', 'listbox'].includes(listRole) - useFocusZone({ - disabled: !enableFocusZone, - containerRef: listRef, - bindKeys: FocusKeys.ArrowVertical | FocusKeys.HomeAndEnd | FocusKeys.PageUpDown, - focusOutBehavior: - listRole === 'menu' || container === 'SelectPanel' || container === 'FilteredActionList' ? 'wrap' : undefined, - }) + useFocusZone({ + disabled: !enableFocusZone, + containerRef: listRef, + bindKeys: FocusKeys.ArrowVertical | FocusKeys.HomeAndEnd | FocusKeys.PageUpDown, + focusOutBehavior: + listRole === 'menu' || container === 'SelectPanel' || container === 'FilteredActionList' ? 'wrap' : undefined, + }) - return ( - + {slots.heading} + - {slots.heading} -
      - {childrenWithoutSlots} -
    -
    - ) - }, -) as PolymorphicForwardRefComponent<'ul', ActionListProps> + {childrenWithoutSlots} +
    + + ) +} -List.displayName = 'ActionList' +const List = fixedForwardRef(UnwrappedList) + +Object.assign(List, {displayName: 'ActionList'}) + +export {List} diff --git a/packages/react/src/ActionList/shared.ts b/packages/react/src/ActionList/shared.ts index a4563ee4f7b..fe3d937c5f8 100644 --- a/packages/react/src/ActionList/shared.ts +++ b/packages/react/src/ActionList/shared.ts @@ -1,8 +1,15 @@ import React from 'react' import type {SxProp} from '../sx' import type {AriaRole} from '../utils/types' +import type {PolymorphicProps} from '../utils/modern-polymorphic' -export type ActionListItemProps = { +// need to explicitly omit `onSelect` from native HTML
  • attributes to avoid collision +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ExcludeSelectEventHandler = T extends any ? Omit : never + +export type ActionListItemProps = ExcludeSelectEventHandler< + PolymorphicProps +> & { /** * Primary content for an Item */ @@ -57,6 +64,10 @@ export type ActionListItemProps = { groupId?: string renderItem?: (item: React.FC>) => React.ReactNode handleAddItem?: (item: React.FC>) => void + /** + * @deprecated `as` prop has no effect on `ActionList.Item`, only `ActionList.LinkItem` + */ + as?: As } & SxProp type MenuItemProps = { @@ -70,7 +81,7 @@ type MenuItemProps = { className?: string } -export type ItemContext = Pick & { +export type ItemContext = Pick, 'variant' | 'disabled' | 'size'> & { inlineDescriptionId?: string blockDescriptionId?: string trailingVisualId?: string @@ -80,8 +91,8 @@ export type ItemContext = Pick({}) export const getVariantStyles = ( - variant: ActionListItemProps['variant'], - disabled: ActionListItemProps['disabled'], + variant: ActionListItemProps['variant'], + disabled: ActionListItemProps['disabled'], inactive?: boolean, ) => { if (disabled) { @@ -120,32 +131,39 @@ export const getVariantStyles = ( export const TEXT_ROW_HEIGHT = '20px' // custom value off the scale -export type ActionListProps = React.PropsWithChildren<{ - /** - * `inset` children are offset (vertically and horizontally) from `List`’s edges, `full` children are flush (vertically and horizontally) with `List` edges - */ - variant?: 'inset' | 'horizontal-inset' | 'full' - /** - * Whether multiple Items or a single Item can be selected. - */ - selectionVariant?: 'single' | 'radio' | 'multiple' - /** - * Display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`. - */ - showDividers?: boolean - /** - * The ARIA role describing the function of `List` component. `listbox` or `menu` are a common values. - */ - role?: AriaRole - /** - * Disables the focus zone for the list if applicable. Focus zone is enabled by default for `menu` and `listbox` roles, or components such as `ActionMenu` and `SelectPanel`. - */ - disableFocusZone?: boolean - className?: string -}> & +export type ActionListProps = PolymorphicProps< + As, + 'ul', + React.PropsWithChildren<{ + /** + * `inset` children are offset (vertically and horizontally) from `List`’s edges, `full` children are flush (vertically and horizontally) with `List` edges + */ + variant?: 'inset' | 'horizontal-inset' | 'full' + /** + * Whether multiple Items or a single Item can be selected. + */ + selectionVariant?: 'single' | 'radio' | 'multiple' + /** + * Display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`. + */ + showDividers?: boolean + /** + * The ARIA role describing the function of `List` component. `listbox` or `menu` are a common values. + */ + role?: AriaRole + /** + * Disables the focus zone for the list if applicable. Focus zone is enabled by default for `menu` and `listbox` roles, or components such as `ActionMenu` and `SelectPanel`. + */ + disableFocusZone?: boolean + className?: string + }> +> & SxProp -type ContextProps = Pick & { +type ContextProps = Pick< + ActionListProps, + 'variant' | 'selectionVariant' | 'showDividers' | 'role' +> & { headingId?: string } diff --git a/packages/react/src/Autocomplete/AutocompleteMenu.tsx b/packages/react/src/Autocomplete/AutocompleteMenu.tsx index bc00c27eb64..d2f19b6d6f7 100644 --- a/packages/react/src/Autocomplete/AutocompleteMenu.tsx +++ b/packages/react/src/Autocomplete/AutocompleteMenu.tsx @@ -6,7 +6,7 @@ import type {ScrollIntoViewOptions} from '@primer/behaviors' import type {ActionListItemProps} from '../ActionList' import {ActionList} from '../ActionList' import {useFocusZone} from '../hooks/useFocusZone' -import type {ComponentProps, MandateProps} from '../utils/types' +import type {ComponentProps, MandateProps, AriaRole} from '../utils/types' import Spinner from '../Spinner' import {useId} from '../hooks/useId' import {AutocompleteContext} from './AutocompleteContext' @@ -365,19 +365,25 @@ function AutocompleteMenu(props: AutocompleteMe text, leadingVisual: LeadingVisual, trailingVisual: TrailingVisual, - // @ts-expect-error this is defined in the items above but is - // missing in TS key, + role, ...itemProps } = item return ( - onAction(item)} {...itemProps} id={id} data-id={id}> + onAction(item)} + {...itemProps} + id={id} + data-id={id} + role={role as AriaRole} + > {LeadingVisual && ( {isElement(LeadingVisual) ? LeadingVisual : } )} - {children ?? text} + {(children ?? text) as React.ReactNode} {TrailingVisual && ( {isElement(TrailingVisual) ? TrailingVisual : } diff --git a/packages/react/src/NavList/NavList.docs.json b/packages/react/src/NavList/NavList.docs.json index e3315ab91ab..d1800f44558 100644 --- a/packages/react/src/NavList/NavList.docs.json +++ b/packages/react/src/NavList/NavList.docs.json @@ -26,11 +26,6 @@ "name": "as", "type": "React.ElementType", "defaultValue": "\"nav\"" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ], "subcomponents": [ @@ -68,11 +63,6 @@ "name": "as", "type": "React.ElementType", "defaultValue": "\"a\"" - }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true } ], "passthrough": { @@ -83,11 +73,6 @@ { "name": "NavList.LeadingVisual", "props": [ - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "ref", "type": "React.RefObject" @@ -97,11 +82,6 @@ { "name": "NavList.TrailingVisual", "props": [ - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "ref", "type": "React.RefObject" @@ -111,11 +91,6 @@ { "name": "NavList.SubNav", "props": [ - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "ref", "type": "React.RefObject" @@ -137,11 +112,6 @@ "type": "string", "description": "The text that gets rendered as the group's heading. Alternatively, you can pass the `NavList.GroupHeading` component as a child of `NavList.Group`.\nIf both `title` and `NavList.GroupHeading` are passed, `NavList.GroupHeading` will be rendered as the heading." }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "ref", "type": "React.RefObject" @@ -172,11 +142,6 @@ "description": "", "defaultValue": "" }, - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "ref", "type": "React.RefObject" @@ -186,11 +151,6 @@ { "name": "NavList.Divider", "props": [ - { - "name": "sx", - "type": "SystemStyleObject", - "deprecated": true - }, { "name": "ref", "type": "React.RefObject" @@ -264,4 +224,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/packages/react/src/NavList/NavList.tsx b/packages/react/src/NavList/NavList.tsx index 360558fded8..282bdae1e75 100644 --- a/packages/react/src/NavList/NavList.tsx +++ b/packages/react/src/NavList/NavList.tsx @@ -1,6 +1,7 @@ import {ChevronDownIcon, PlusIcon, type Icon} from '@primer/octicons-react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import React, {isValidElement} from 'react' +import {clsx} from 'clsx' import type { ActionListTrailingActionProps, ActionListDividerProps, @@ -11,27 +12,22 @@ import type { import {ActionList} from '../ActionList' import {SubItem} from '../ActionList/Item' import {ActionListContainerContext} from '../ActionList/ActionListContainerContext' -import Box from '../Box' -import type {SxProp} from '../sx' -import {merge} from '../sx' -import {defaultSxProp} from '../utils/defaultSxProp' import {useId} from '../hooks/useId' import useIsomorphicLayoutEffect from '../utils/useIsomorphicLayoutEffect' import classes from '../ActionList/ActionList.module.css' +import navListClasses from './NavList.module.css' import {flushSync} from 'react-dom' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' // ---------------------------------------------------------------------------- // NavList export type NavListProps = { children: React.ReactNode -} & SxProp & - React.ComponentProps<'nav'> +} & React.ComponentProps<'nav'> const Root = React.forwardRef(({children, ...props}, ref) => { return ( - + ) }) @@ -54,10 +50,10 @@ export type NavListItemProps = { href?: string 'aria-current'?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false' | boolean inactiveText?: string -} & SxProp +} const Item = React.forwardRef( - ({'aria-current': ariaCurrent, children, defaultOpen, sx: sxProp = defaultSxProp, ...props}, ref) => { + ({'aria-current': ariaCurrent, children, defaultOpen, ...props}, ref) => { const {depth} = React.useContext(SubNavContext) // Get SubNav from children @@ -79,7 +75,6 @@ const Item = React.forwardRef( subNav={subNav} depth={depth} defaultOpen={defaultOpen} - sx={sxProp} style={{'--subitem-depth': depth} as React.CSSProperties} > {childrenWithoutSubNavOrTrailingAction} @@ -112,7 +107,7 @@ type ItemWithSubNavProps = { depth: number defaultOpen?: boolean style: React.CSSProperties -} & SxProp +} const ItemWithSubNavContext = React.createContext<{buttonId: string; subNavId: string; isOpen: boolean}>({ buttonId: '', @@ -120,14 +115,7 @@ const ItemWithSubNavContext = React.createContext<{buttonId: string; subNavId: s isOpen: false, }) -function ItemWithSubNav({ - children, - subNav, - depth: _depth, - defaultOpen, - style, - sx: sxProp = defaultSxProp, -}: ItemWithSubNavProps) { +function ItemWithSubNav({children, subNav, depth: _depth, defaultOpen, style}: ItemWithSubNavProps) { const buttonId = useId() const subNavId = useId() const [isOpen, setIsOpen] = React.useState((defaultOpen || null) ?? false) @@ -147,28 +135,6 @@ function ItemWithSubNav({ } }, [subNav, buttonId]) - if (sxProp !== defaultSxProp) { - return ( - - setIsOpen(open => !open)} - style={style} - sx={sxProp} - > - {children} - {/* What happens if the user provides a TrailingVisual? */} - - - - {React.cloneElement(subNav as React.ReactElement, {ref: subNavRef})} - - - ) - } return ( ({depth: 0}) // NOTE: SubNav must be a direct child of an Item -const SubNav = React.forwardRef(({children, sx: sxProp = defaultSxProp}: NavListSubNavProps, forwardedRef) => { +const SubNav = React.forwardRef(({children}, forwardedRef) => { const {buttonId, subNavId} = React.useContext(ItemWithSubNavContext) const {depth} = React.useContext(SubNavContext) if (!buttonId || !subNavId) { @@ -215,22 +181,6 @@ const SubNav = React.forwardRef(({children, sx: sxProp = defaultSxProp}: NavList return null } - if (sxProp !== defaultSxProp) { - return ( - - - {children} - - - ) - } return (
      @@ -283,18 +233,9 @@ TrailingAction.displayName = 'NavList.TrailingAction' export type NavListGroupProps = React.HTMLAttributes & { children: React.ReactNode title?: string -} & SxProp +} -const defaultSx = {} -const Group: React.FC = ({title, children, sx: sxProp = defaultSx, ...props}) => { - if (sxProp !== defaultSx) { - return ( - - {title ? {title} : null} - {children} - - ) - } +const Group: React.FC = ({title, children, ...props}) => { return ( <> @@ -419,20 +360,11 @@ export type NavListGroupHeadingProps = ActionListGroupHeadingProps * This is an alternative to the `title` prop on `NavList.Group`. * It was primarily added to allow links in group headings. */ -const GroupHeading: React.FC = ({as = 'h3', sx: sxProp = defaultSxProp, ...rest}) => { +const GroupHeading: React.FC = ({as = 'h3', className, ...rest}) => { return ( ( - { - '> a {': { - color: 'var(--fgColor-default)', - textDecoration: 'inherit', - ':hover': {textDecoration: 'underline'}, - }, - }, - sxProp, - )} + className={clsx(navListClasses.GroupHeading, className)} data-component="NavList.GroupHeading" headingWrapElement="li" {...rest} diff --git a/packages/react/src/SegmentedControl/SegmentedControl.tsx b/packages/react/src/SegmentedControl/SegmentedControl.tsx index f0e3ce38fd0..a706f41a0ef 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControl.tsx @@ -132,7 +132,7 @@ const Root: React.FC> = ({ { + onSelect={event => { isUncontrolled && setSelectedIndexInternalState(index) onChange && onChange(index) child.props.onClick && child.props.onClick(event as React.MouseEvent) diff --git a/packages/react/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/react/src/__tests__/__snapshots__/exports.test.ts.snap index 574c3bf2a19..d2483dd002d 100644 --- a/packages/react/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/react/src/__tests__/__snapshots__/exports.test.ts.snap @@ -95,6 +95,7 @@ exports[`@primer/react > should not update exports without a semver change 1`] = "merge", "NavList", "type NavListDividerProps", + "type NavListGroupHeadingProps", "type NavListGroupProps", "type NavListItemProps", "type NavListLeadingVisualProps", diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index c64013bba6c..65a4f2492b0 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -117,6 +117,7 @@ export type { NavListLeadingVisualProps, NavListTrailingVisualProps, NavListDividerProps, + NavListGroupHeadingProps, } from './NavList' export {default as Overlay} from './Overlay' export type {OverlayProps} from './Overlay' diff --git a/packages/react/src/utils/modern-polymorphic.ts b/packages/react/src/utils/modern-polymorphic.ts index 5f83844ac10..d29e9e14f24 100644 --- a/packages/react/src/utils/modern-polymorphic.ts +++ b/packages/react/src/utils/modern-polymorphic.ts @@ -13,7 +13,7 @@ type DistributiveOmit = T extends unknown ? Omi */ // TODO: figure out how to change this type so we can set displayName // like this: `ComponentName.displayName = 'DisplayName' instead of using workarounds -type FixedForwardRef = = Record>( +type FixedForwardRef = ( render: (props: P, ref: React.Ref) => React.ReactNode, ) => (props: P & React.RefAttributes) => React.ReactNode @@ -29,7 +29,7 @@ export const fixedForwardRef = forwardRef as FixedForwardRef export type PolymorphicProps< TAs extends ElementType, TDefaultElement extends ElementType = 'div', - Props extends Record = Record, + Props = object, > = DistributiveOmit & Props, 'as'> & { as?: TAs } diff --git a/packages/styled-react/src/__tests__/primer-react.browser.test.tsx b/packages/styled-react/src/__tests__/primer-react.browser.test.tsx index f86970ef32f..ef1f7b70823 100644 --- a/packages/styled-react/src/__tests__/primer-react.browser.test.tsx +++ b/packages/styled-react/src/__tests__/primer-react.browser.test.tsx @@ -235,7 +235,7 @@ describe('@primer/react', () => { expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') }) - test.todo('NavList.Item supports `sx` prop', () => { + test('NavList.Item supports `sx` prop', () => { render( @@ -243,12 +243,36 @@ describe('@primer/react', () => { , ) - expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') + + const itemAnchorEl = screen.getByTestId('component') + const itemLiEl = itemAnchorEl.closest('li') + expect(itemLiEl).not.toBeNull() + expect(window.getComputedStyle(itemLiEl!).backgroundColor).toBe('rgb(255, 0, 0)') }) test('NavList.Group supports `sx` prop', () => { - const {container} = render(test) - expect(window.getComputedStyle(container.firstElementChild!).backgroundColor).toBe('rgb(255, 0, 0)') + render( + + + item + + , + ) + expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') + }) + + test('NavList.GroupHeading supports `sx` prop', () => { + render( + + + + test + + item + + , + ) + expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') }) test('NavList.LeadingVisual supports `sx` prop', () => { @@ -375,12 +399,12 @@ describe('@primer/react', () => { expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') }) - test('SubNav supports `sx` prop', () => { + test.skip('SubNav supports `sx` prop', () => { render() expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') }) - test('SubNav.Link supports `sx` prop', () => { + test.skip('SubNav.Link supports `sx` prop', () => { render() expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)') }) diff --git a/packages/styled-react/src/components/NavList.tsx b/packages/styled-react/src/components/NavList.tsx new file mode 100644 index 00000000000..2519af4870e --- /dev/null +++ b/packages/styled-react/src/components/NavList.tsx @@ -0,0 +1,56 @@ +import {NavList as PrimerNavList, Box} from '@primer/react' +import type { + NavListProps as PrimerNavListProps, + NavListItemProps as PrimerNavListItemProps, + NavListGroupProps as PrimerNavListGroupProps, +} from '@primer/react' +import {forwardRef, type ComponentProps, type PropsWithChildren} from 'react' +import {type SxProp} from '../sx' + +type NavListProps = PropsWithChildren & SxProp + +const NavListImpl = forwardRef(function NavList(props, ref) { + return +}) + +type NavListItemProps = PropsWithChildren & SxProp + +const NavListItem = forwardRef(function NavListItem(props, ref) { + // @ts-expect-error - PrimerNavList.Item is not recognized as a valid component type + return +}) + +type NavListGroupProps = PropsWithChildren & SxProp + +const NavListGroup = forwardRef(function NavListGroup(props, ref) { + // @ts-expect-error - PrimerNavList.Group is not recognized as a valid component type + return +}) + +const NavList = Object.assign(NavListImpl, { + // Wrapped components that need sx support added back in + Item: NavListItem, + Group: NavListGroup, + + // Re-exporting others directly + // TODO: try to remove typecasts to work around "non-portable types" TS error + SubNav: PrimerNavList.SubNav as React.FC>>, + Divider: PrimerNavList.Divider as React.FC>>, + LeadingVisual: PrimerNavList.LeadingVisual as React.FC< + React.PropsWithChildren> + >, + TrailingVisual: PrimerNavList.TrailingVisual as React.FC< + React.PropsWithChildren> + >, + TrailingAction: PrimerNavList.TrailingAction as React.FC< + React.PropsWithChildren> + >, + GroupHeading: PrimerNavList.GroupHeading as React.FC< + React.PropsWithChildren> + >, + GroupExpand: PrimerNavList.GroupExpand as React.FC< + React.PropsWithChildren> + >, +}) + +export {NavList, type NavListProps} diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index c21bf22b9b0..2dbcfe783eb 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -12,7 +12,6 @@ export {FormControl} from '@primer/react' export {Heading} from '@primer/react' export {IconButton} from '@primer/react' export {Label} from '@primer/react' -export {NavList} from '@primer/react' export {Overlay} from '@primer/react' export {ProgressBar} from '@primer/react' export {Select} from '@primer/react' @@ -39,6 +38,7 @@ export {Flash} from './components/Flash' export {Header, type HeaderProps} from './components/Header' export {Link, type LinkProps} from './components/Link' export {LinkButton, type LinkButtonProps} from './components/LinkButton' +export {NavList, type NavListProps} from './components/NavList' export {PageLayout, type PageLayoutProps} from './components/PageLayout' export { PageHeader, From 41fea0993d24263665e9c715a334d49836de3e01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:57:01 +0200 Subject: [PATCH 24/27] chore(deps-dev): bump @vitest/eslint-plugin from 1.3.12 to 1.3.13 in the vitest group (#6916) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 24 ++++++++++++------------ package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 14572a7e84e..3a6b3f394d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@primer/stylelint-config": "13.3.0", "@size-limit/preset-big-lib": "11.2.0", "@vitest/browser": "^3.2.4", - "@vitest/eslint-plugin": "^1.3.12", + "@vitest/eslint-plugin": "^1.3.13", "eslint": "^9.35.0", "eslint-import-resolver-typescript": "3.7.0", "eslint-plugin-clsx": "^0.0.10", @@ -75,7 +75,7 @@ "react-dom": "^18.3.1" }, "devDependencies": { - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.3", @@ -88,7 +88,7 @@ "name": "example-nextjs", "version": "0.0.0", "dependencies": { - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "next": "^15.2.3", "react": "18.3.1", "react-dom": "18.3.1", @@ -104,7 +104,7 @@ "version": "0.0.0", "dependencies": { "@primer/octicons-react": "^19.14.0", - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "clsx": "^2.1.1", "next": "^15.2.3", "react": "18.3.1", @@ -8742,9 +8742,9 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.3.12", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.12.tgz", - "integrity": "sha512-cSEyUYGj8j8SLqKrzN7BlfsJ3wG67eRT25819PXuyoSBogLXiyagdKx4MHWHV1zv+EEuyMXsEKkBEKzXpxyBrg==", + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.13.tgz", + "integrity": "sha512-QfzXd1+lCY3dIqPHOZlagA2bJYoWC5yAU3adv8Gks0rHAL6FpyXKYBiyMCuU6mRrbKUMphGqwDQobinOvYgJig==", "dev": true, "license": "MIT", "dependencies": { @@ -25841,7 +25841,7 @@ }, "packages/react": { "name": "@primer/react", - "version": "38.0.0-rc.3", + "version": "38.0.0-rc.4", "license": "MIT", "dependencies": { "@github/mini-throttle": "^2.1.1", @@ -25852,7 +25852,7 @@ "@primer/behaviors": "^1.8.2", "@primer/live-region-element": "^0.7.1", "@primer/octicons-react": "^19.13.0", - "@primer/primitives": "11.2.0", + "@primer/primitives": "10.x || 11.x", "@styled-system/css": "^5.1.5", "@styled-system/props": "^5.1.5", "@styled-system/theme-get": "^5.1.2", @@ -26417,11 +26417,11 @@ }, "packages/styled-react": { "name": "@primer/styled-react", - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "devDependencies": { "@babel/preset-react": "^7.27.1", "@babel/preset-typescript": "^7.27.1", - "@primer/react": "^38.0.0-rc.3", + "@primer/react": "^38.0.0-rc.4", "@rollup/plugin-babel": "^6.0.4", "@types/react": "18.3.11", "@types/react-dom": "18.3.1", @@ -26437,7 +26437,7 @@ "typescript": "^5.9.2" }, "peerDependencies": { - "@primer/react": "38.0.0-rc.3", + "@primer/react": "38.0.0-rc.4", "@types/react": "18.x || 19.x", "@types/react-dom": "18.x || 19.x", "@types/react-is": "18.x || 19.x", diff --git a/package.json b/package.json index a950950c6d0..0a77520c34f 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@primer/stylelint-config": "13.3.0", "@size-limit/preset-big-lib": "11.2.0", "@vitest/browser": "^3.2.4", - "@vitest/eslint-plugin": "^1.3.12", + "@vitest/eslint-plugin": "^1.3.13", "eslint": "^9.35.0", "eslint-import-resolver-typescript": "3.7.0", "eslint-plugin-clsx": "^0.0.10", From 82719ab4112f12599f356756cba73aa241d902f7 Mon Sep 17 00:00:00 2001 From: Marie Lucca <40550942+francinelucca@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:37:24 -0400 Subject: [PATCH 25/27] chore: add test qrapg QL query workflow (#6920) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/graphql-test.yml | 80 ++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 .github/workflows/graphql-test.yml diff --git a/.github/workflows/graphql-test.yml b/.github/workflows/graphql-test.yml new file mode 100644 index 00000000000..f1969b8a870 --- /dev/null +++ b/.github/workflows/graphql-test.yml @@ -0,0 +1,80 @@ +name: Test GraphQL Queries +on: + workflow_dispatch: + +jobs: + test-query: + runs-on: ubuntu-latest + steps: + - name: Test GraphQL Query + run: | + echo "Testing GraphQL query against GitHub API..." + + # Test query to get repository information + QUERY='query { + repository(owner: "primer", name: "react") { + name + description + stargazerCount + forkCount + defaultBranchRef { + name + } + languages(first: 5) { + nodes { + name + color + } + } + } + }' + + # Make the GraphQL API call with detailed error logging + echo "Making GraphQL API call..." + HTTP_STATUS=$(curl -w "%{http_code}" -s -X POST \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{\"query\": \"$(echo $QUERY | tr '\n' ' ' | sed 's/"/\\"/g')\"}" \ + -o response.json \ + https://api.github.com/graphql) + + RESPONSE=$(cat response.json) + + echo "HTTP Status Code: $HTTP_STATUS" + echo "Full GraphQL Response:" + echo "$RESPONSE" | jq '.' || echo "Raw response (invalid JSON): $RESPONSE" + + # Check HTTP status first + if [ "$HTTP_STATUS" != "200" ]; then + echo "❌ HTTP request failed with status code: $HTTP_STATUS" + echo "Full response body:" + echo "$RESPONSE" + exit 1 + fi + + # Check if the response contains GraphQL errors + if echo "$RESPONSE" | jq -e '.errors' > /dev/null 2>&1; then + echo "❌ GraphQL query returned errors:" + echo "$RESPONSE" | jq '.errors' + echo "Full error details:" + echo "$RESPONSE" | jq '.errors[] | {message: .message, type: .type, path: .path, locations: .locations}' || true + exit 1 + fi + + # Check if the query was successful and has expected data + if echo "$RESPONSE" | jq -e '.data.repository.name' > /dev/null 2>&1; then + echo "✅ GraphQL query executed successfully!" + echo "Repository name: $(echo "$RESPONSE" | jq -r '.data.repository.name')" + echo "Stars: $(echo "$RESPONSE" | jq -r '.data.repository.stargazerCount')" + echo "Default branch: $(echo "$RESPONSE" | jq -r '.data.repository.defaultBranchRef.name')" + else + echo "❌ GraphQL query failed - no valid data returned" + echo "Expected data.repository.name but got:" + echo "$RESPONSE" | jq '.data // "No data field found"' + exit 1 + fi + + # Cleanup + rm -f response.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 50914818893b0dfb704f572462f56ca654246ad6 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 29 Sep 2025 11:26:57 -0700 Subject: [PATCH 26/27] re-remove sx and BoxWithFallback from ActionList/Item.tsx --- packages/react/src/ActionList/Item.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx index 65408b623b4..de12e491d8c 100644 --- a/packages/react/src/ActionList/Item.tsx +++ b/packages/react/src/ActionList/Item.tsx @@ -15,7 +15,6 @@ import {invariant} from '../utils/invariant' import VisuallyHidden from '../_VisuallyHidden' import classes from './ActionList.module.css' import {clsx} from 'clsx' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' import {fixedForwardRef} from '../utils/modern-polymorphic' type ActionListSubItemProps = { @@ -57,7 +56,6 @@ const UnwrappedItem = ( selected = undefined, active = false, onSelect: onSelectUser, - sx: sxProp, id, role, loading, @@ -246,10 +244,8 @@ const UnwrappedItem = ( trailingVisualId, }} > - ( {!inactive && !loading && !menuContext && Boolean(slots.trailingAction) && slots.trailingAction} {slots.subItem} - + ) } From fa08f12abbb7e55d0cf64df751559a52aedbb043 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 29 Sep 2025 11:28:03 -0700 Subject: [PATCH 27/27] re-remove sx and BoxWithFallback from ActionList/List.tsx --- packages/react/src/ActionList/List.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react/src/ActionList/List.tsx b/packages/react/src/ActionList/List.tsx index 97ead828fea..c5c3860f6fb 100644 --- a/packages/react/src/ActionList/List.tsx +++ b/packages/react/src/ActionList/List.tsx @@ -9,7 +9,6 @@ import {useProvidedRefOrCreate} from '../hooks' import {FocusKeys, useFocusZone} from '../hooks/useFocusZone' import {clsx} from 'clsx' import classes from './ActionList.module.css' -import {BoxWithFallback} from '../internal/components/BoxWithFallback' const UnwrappedList = ( props: ActionListProps, @@ -67,8 +66,7 @@ const UnwrappedList = ( }} > {slots.heading} - ( {...restProps} > {childrenWithoutSlots} - + ) }