From c0531e7e6a709de428df0a2d9fe16d8a8ea02bf3 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 01:35:37 -0400 Subject: [PATCH 01/49] chore: remove remaining styled-system components: --- .../FilteredActionList.module.css | 6 + .../FilteredActionList/FilteredActionList.tsx | 11 +- .../src/LabelGroup/LabelGroup.module.css | 38 ++++ packages/react/src/LabelGroup/LabelGroup.tsx | 70 ++----- .../PageLayout.examples.stories.tsx | 195 ------------------ .../react/src/UnderlineNav/UnderlineNav.tsx | 11 +- .../deprecated/ActionList/Divider.module.css | 8 + .../src/deprecated/ActionList/Divider.tsx | 12 +- .../react/src/deprecated/ActionList/Group.tsx | 10 +- .../deprecated/ActionList/Header.module.css | 24 +++ .../src/deprecated/ActionList/Header.tsx | 44 +--- .../UnderlineNav/UnderlineNav.module.css | 86 ++++++++ .../deprecated/UnderlineNav/UnderlineNav.tsx | 102 ++------- .../ValidationAnimationContainer.module.css | 19 ++ .../ValidationAnimationContainer.tsx | 24 +-- .../internal/utils/getGlobalFocusStyles.ts | 29 --- 16 files changed, 239 insertions(+), 450 deletions(-) create mode 100644 packages/react/src/deprecated/ActionList/Divider.module.css create mode 100644 packages/react/src/deprecated/ActionList/Header.module.css create mode 100644 packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css create mode 100644 packages/react/src/internal/components/ValidationAnimationContainer.module.css delete mode 100644 packages/react/src/internal/utils/getGlobalFocusStyles.ts diff --git a/packages/react/src/FilteredActionList/FilteredActionList.module.css b/packages/react/src/FilteredActionList/FilteredActionList.module.css index c09649da9a2..4613a4e96a8 100644 --- a/packages/react/src/FilteredActionList/FilteredActionList.module.css +++ b/packages/react/src/FilteredActionList/FilteredActionList.module.css @@ -4,6 +4,12 @@ overflow: hidden; } +.Header { + /* stylelint-disable-next-line primer/box-shadow */ + box-shadow: 0 1px 0 var(--borderColor-default); + z-index: 1; +} + .Container { display: flex; height: 100%; diff --git a/packages/react/src/FilteredActionList/FilteredActionList.tsx b/packages/react/src/FilteredActionList/FilteredActionList.tsx index cc01dc685ab..61ccb33538e 100644 --- a/packages/react/src/FilteredActionList/FilteredActionList.tsx +++ b/packages/react/src/FilteredActionList/FilteredActionList.tsx @@ -3,10 +3,8 @@ import {scrollIntoView, FocusKeys} from '@primer/behaviors' import type {KeyboardEventHandler} from 'react' import type React from 'react' import {useCallback, useEffect, useRef, useState} from 'react' -import styled from 'styled-components' import type {TextInputProps} from '../TextInput' import TextInput from '../TextInput' -import {get} from '../constants' import {ActionList} from '../ActionList' import type {GroupedListProps, ListPropsBase, ItemInput, RenderItemFn} from './' import {useFocusZone} from '../hooks/useFocusZone' @@ -49,11 +47,6 @@ export interface FilteredActionListProps extends Partial void } -const StyledHeader = styled.div` - box-shadow: 0 1px 0 ${get('colors.border.default')}; - z-index: 1; -` - export function FilteredActionList({ loading = false, placeholderText, @@ -346,7 +339,7 @@ export function FilteredActionList({ return (
- +
- +
Items will be filtered as you type {onSelectAllChange !== undefined && (
diff --git a/packages/react/src/LabelGroup/LabelGroup.module.css b/packages/react/src/LabelGroup/LabelGroup.module.css index 4009aae37e8..4f3d1f25bc4 100644 --- a/packages/react/src/LabelGroup/LabelGroup.module.css +++ b/packages/react/src/LabelGroup/LabelGroup.module.css @@ -1,3 +1,41 @@ +.Container { + display: flex; + flex-wrap: nowrap; + gap: var(--base-size-4); + /* stylelint-disable-next-line primer/typography */ + line-height: 1; + max-width: 100%; + overflow: hidden; + + &:where([data-overflow='inline']) { + flex-wrap: wrap; + } + + &:where([data-list]){ + padding-inline-start: 0; + margin-block-start: 0; + margin-block-end: 0; + list-style-type: none; + } +} + +.ItemWrapper { + display: flex; + align-items: center; + /* This min-height matches the height of the expand/collapse button. + Without it, the labels/tokens will do a slight layout shift when expanded. + This is because the height of the first row will match the token sizes, + but the height of the second row will be the height of the collapse button. + */ + min-height: 28px; +} + +.ItemWrapper--hidden { + order: 9999; + pointer-events: none; + visibility: hidden; +} + .OverlayContainer { align-items: flex-start; display: flex; diff --git a/packages/react/src/LabelGroup/LabelGroup.tsx b/packages/react/src/LabelGroup/LabelGroup.tsx index a5babffb2e4..e4a3458360b 100644 --- a/packages/react/src/LabelGroup/LabelGroup.tsx +++ b/packages/react/src/LabelGroup/LabelGroup.tsx @@ -1,12 +1,11 @@ import React from 'react' -import styled from 'styled-components' import {XIcon} from '@primer/octicons-react' import {getFocusableChild} from '@primer/behaviors/utils' -import {get} from '../constants' import VisuallyHidden from '../_VisuallyHidden' import {AnchoredOverlay} from '../AnchoredOverlay' import {Button, IconButton} from '../Button' import {useTheme} from '../ThemeProvider' +import {clsx} from 'clsx' import classes from './LabelGroup.module.css' export type LabelGroupProps = { @@ -18,45 +17,6 @@ export type LabelGroupProps = { visibleChildCount?: 'auto' | number className?: string } - -const StyledLabelGroupContainer = styled.div` - display: flex; - flex-wrap: nowrap; - gap: ${get('space.1')}; - line-height: 1; - max-width: 100%; - overflow: hidden; - - &[data-overflow='inline'] { - flex-wrap: wrap; - } - - &[data-list] { - padding-inline-start: 0; - margin-block-start: 0; - margin-block-end: 0; - list-style-type: none; - } -` - -const ItemWrapper = styled.div` - display: flex; - align-items: center; - - /* This min-height matches the height of the expand/collapse button. - Without it, the labels/tokens will do a slight layout shift when expanded. - This is because the height of the first row will match the token sizes, - but the height of the second row will be the height of the collapse button. - */ - min-height: 28px; - - &.ItemWrapper--hidden { - order: 9999; - pointer-events: none; - visibility: hidden; - } -` - // Calculates the width of the overlay to cover the labels/tokens and the expand button. const getOverlayWidth = ( buttonClientRect: DOMRect, @@ -151,7 +111,7 @@ const LabelGroup: React.FC> = ({ children, visibleChildCount, overflowStyle = 'overlay', - as = 'ul', + as: Component = 'ul', className, }) => { const containerRef = React.useRef(null) @@ -173,7 +133,7 @@ const LabelGroup: React.FC> = ({ const {theme} = useTheme() - const overlayPaddingPx = parseInt(get('space.2')(theme), 10) + const overlayPaddingPx = 8 // equivalent to var(--base-size-8) const hiddenItemIds = Object.keys(visibilityMap).filter(key => !visibilityMap[key]) @@ -322,28 +282,30 @@ const LabelGroup: React.FC> = ({ } }, [overflowStyle, isOverflowShown]) - const isList = as === 'ul' || as === 'ol' + const isList = Component === 'ul' || Component === 'ol' const ToggleWrapper = isList ? 'li' : React.Fragment + const ItemWrapperComponent = isList ? 'li' : 'span' + // If truncation is enabled, we need to render based on truncation logic. return visibleChildCount ? ( - {React.Children.map(children, (child, index) => ( - {child} - + ))} {overflowStyle === 'inline' ? ( @@ -371,15 +333,15 @@ const LabelGroup: React.FC> = ({ )} - + ) : ( - + {isList ? React.Children.map(children, (child, index) => { return
  • {child}
  • }) : children} -
    + ) } diff --git a/packages/react/src/PageLayout/PageLayout.examples.stories.tsx b/packages/react/src/PageLayout/PageLayout.examples.stories.tsx index 4605bb722e6..675119d4ac5 100644 --- a/packages/react/src/PageLayout/PageLayout.examples.stories.tsx +++ b/packages/react/src/PageLayout/PageLayout.examples.stories.tsx @@ -677,201 +677,6 @@ export const FiltersBottomSheetTwoLevels: StoryFn = () => { FiltersBottomSheetTwoLevels.storyName = 'Filters w/ 2 levels (btm sheet on narrow)' -// -// TODO: uncomment this story if we decide we want to allow this pattern for separate sets of filters -// -// export const ResponsiveNavCombo2: Story = () => { -// const [currentHash, setCurrentHash] = React.useState(window.location.hash) -// const [isOpen, setIsOpen] = React.useState(false) - -// const categories = [ -// { -// hash: '#fruits', -// name: 'Fruits', -// }, -// { -// hash: '#vegetables', -// name: 'Vegetables', -// }, -// { -// hash: '#animals', -// name: 'Animals', -// }, -// ] -// const selectedCategory = currentHash ? categories.find(option => currentHash.includes(option.hash)) : categories[0] - -// const buttonRef = React.useRef(null) - -// const onDialogClose = React.useCallback(() => setIsOpen(false), []) -// const getFilteredItems = (category: keyof typeof filterableItems) => -// filterableItems[category].filter(item => -// currentHash.includes('filter') ? currentHash.includes(`filter=${item.color}`) : true, -// ) - -// // Fake routing to mimic the behavior of a single page application -// React.useEffect(() => { -// const handleHashChange = () => { -// setCurrentHash(window.location.hash) -// } -// window.addEventListener('hashchange', handleHashChange) -// return () => { -// window.removeEventListener('hashchange', handleHashChange) -// } -// }, []) - -// return ( -// <> -// {/* -// Filters only work when you open the canvas in a new tab without the Storybook chrome. -// */} -// -// -// -// -// -// -// ) -// } - // ResponsiveNavCombo2.storyName = 'Responsive nav combo - action menu + btm sheet' ///////////////////////////////////////////////////////////////// diff --git a/packages/react/src/UnderlineNav/UnderlineNav.tsx b/packages/react/src/UnderlineNav/UnderlineNav.tsx index 0c8a417773d..ba9fdc6c540 100644 --- a/packages/react/src/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNav.tsx @@ -8,7 +8,6 @@ import type {ChildWidthArray, ResponsiveProps, ChildSize} from './types' import VisuallyHidden from '../_VisuallyHidden' import {moreBtnStyles, getDividerStyle, menuStyles, menuItemStyles, baseMenuStyles, baseMenuMinWidth} from './styles' import {UnderlineItemList, UnderlineWrapper, LoadingCounter, GAP} from '../internal/components/UnderlineTabbedInterface' -import styled from 'styled-components' import {Button} from '../Button' import {TriangleDownIcon} from '@primer/octicons-react' import {useOnEscapePress} from '../hooks/useOnEscapePress' @@ -41,12 +40,6 @@ export const MORE_BTN_WIDTH = 86 // The height is needed to make sure we don't have a layout shift when the more button is the only item in the nav. const MORE_BTN_HEIGHT = 45 -export const MoreMenuListItem = styled.li` - display: flex; - align-items: center; - height: ${MORE_BTN_HEIGHT}px; -` - const overflowEffect = ( navWidth: number, moreMenuWidth: number, @@ -310,7 +303,7 @@ export const UnderlineNav = forwardRef( {listItems} {menuItems.length > 0 && ( - +
  • {!onlyMenuVisible &&
    }
  • )}
    diff --git a/packages/react/src/deprecated/ActionList/Divider.module.css b/packages/react/src/deprecated/ActionList/Divider.module.css new file mode 100644 index 00000000000..e0050fc4ea3 --- /dev/null +++ b/packages/react/src/deprecated/ActionList/Divider.module.css @@ -0,0 +1,8 @@ +.Divider { + height: var(--borderWidth-thin); + /* stylelint-disable-next-line primer/colors */ + background: var(--borderColor-muted); + /* stylelint-disable-next-line primer/spacing */ + margin-top: calc(var(--base-size-8) - 1px); + margin-bottom: var(--base-size-8); +} diff --git a/packages/react/src/deprecated/ActionList/Divider.tsx b/packages/react/src/deprecated/ActionList/Divider.tsx index 48b5660736c..68ae04c8df7 100644 --- a/packages/react/src/deprecated/ActionList/Divider.tsx +++ b/packages/react/src/deprecated/ActionList/Divider.tsx @@ -1,18 +1,10 @@ -import styled from 'styled-components' -import {get} from '../../constants' - -export const StyledDivider = styled.div` - height: 1px; - background: ${get('colors.border.muted')}; - margin-top: calc(${get('space.2')} - 1px); - margin-bottom: ${get('space.2')}; -` +import classes from './Divider.module.css' /** * Visually separates `Item`s or `Group`s in an `ActionList`. */ export function Divider(): JSX.Element { - return + return
    } /** diff --git a/packages/react/src/deprecated/ActionList/Group.tsx b/packages/react/src/deprecated/ActionList/Group.tsx index a0b27711d0b..fb9a93b60a6 100644 --- a/packages/react/src/deprecated/ActionList/Group.tsx +++ b/packages/react/src/deprecated/ActionList/Group.tsx @@ -1,7 +1,5 @@ import type React from 'react' -import styled from 'styled-components' import type {SxProp} from '../../sx' -import sx from '../../sx' import type {HeaderProps} from './Header' import {Header} from './Header' @@ -30,18 +28,14 @@ export interface GroupProps extends React.ComponentPropsWithoutRef<'div'>, SxPro showItemDividers?: boolean } -const StyledGroup = styled.div` - ${sx} -` - /** * Collects related `Items` in an `ActionList`. */ export function Group({header, items, ...props}: GroupProps): JSX.Element { return ( - +
    {header &&
    } {items} - +
    ) } diff --git a/packages/react/src/deprecated/ActionList/Header.module.css b/packages/react/src/deprecated/ActionList/Header.module.css new file mode 100644 index 00000000000..2041cbbf3c1 --- /dev/null +++ b/packages/react/src/deprecated/ActionList/Header.module.css @@ -0,0 +1,24 @@ +.Header { + /* 6px vertical padding + 20px line height = 32px total height + * + * TODO: When rem-based spacing on a 4px scale lands, replace + * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'. + */ + /* stylelint-disable-next-line primer/spacing */ + padding: 6px var(--base-size-16); + font-size: var(--text-body-size-small); + font-weight: var(--text-title-weight-large); + color: var(--fgColor-muted); + + &:where([data-filled]){ + background: var(--bgColor-muted); + margin: var(--base-size-8) 0; + border-top: var(--borderWidth-thin) solid var(--borderColor-default); + border-bottom: var(--borderWidth-thin) solid var(--borderColor-default); + + &:first-child { + margin-top: 0; + } + } +} + diff --git a/packages/react/src/deprecated/ActionList/Header.tsx b/packages/react/src/deprecated/ActionList/Header.tsx index 8c6efbdd6aa..0f7ef469b13 100644 --- a/packages/react/src/deprecated/ActionList/Header.tsx +++ b/packages/react/src/deprecated/ActionList/Header.tsx @@ -1,8 +1,7 @@ import type React from 'react' -import styled, {css} from 'styled-components' -import {get} from '../../constants' +import {clsx} from 'clsx' import type {SxProp} from '../../sx' -import sx from '../../sx' +import classes from './Header.module.css' /** * Contract for props passed to the `Header` component. @@ -27,35 +26,6 @@ export interface HeaderProps extends React.ComponentPropsWithoutRef<'div'>, SxPr auxiliaryText?: string } -export const StyledHeader = styled.div<{variant: HeaderProps['variant']} & SxProp>` - { - /* 6px vertical padding + 20px line height = 32px total height - * - * TODO: When rem-based spacing on a 4px scale lands, replace - * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'. - */ - } - padding: 6px ${get('space.3')}; - font-size: ${get('fontSizes.0')}; - font-weight: ${get('fontWeights.bold')}; - color: ${get('colors.fg.muted')}; - - ${({variant}) => - variant === 'filled' && - css` - background: ${get('colors.canvas.subtle')}; - margin: ${get('space.2')} 0; - border-top: 1px solid ${get('colors.neutral.muted')}; - border-bottom: 1px solid ${get('colors.neutral.muted')}; - - &:first-child { - margin-top: 0; - } - `} - - ${sx} -` - /** * Displays the name and description of a `Group`. */ @@ -64,12 +34,18 @@ export function Header({ title, auxiliaryText, children: _children, + className, ...props }: HeaderProps): JSX.Element { return ( - +
    {title} {auxiliaryText && {auxiliaryText}} - +
    ) } diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css new file mode 100644 index 00000000000..37929259dfd --- /dev/null +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -0,0 +1,86 @@ +.UnderlineNav { + display: flex; + justify-content: space-between; + border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted); +} + +.UnderlineNav--right { + justify-content: flex-end; +} + +.UnderlineNav--right .UnderlineNavItem { + margin-right: 0; + margin-left: var(--base-size-16); +} + +.UnderlineNav--right .UnderlineNavActions { + flex: 1 1 auto; +} + +.UnderlineNav--full { + display: block; +} + +.UnderlineNavBody { + display: flex; + /* stylelint-disable-next-line primer/spacing */ + margin-bottom: -1px; +} + +.UnderlineNavActions { + align-self: center; +} + +.UnderlineNavLink { + padding: var(--base-size-16) var(--base-size-8); + margin-right: var(--base-size-16); + font-size: var(--text-body-size-medium); + line-height: var(--text-body-lineHeight-medium); + color: var(--fgColor-default); + text-align: center; + /* stylelint-disable-next-line primer/borders */ + border-bottom: 2px solid transparent; + text-decoration: none; + + /* fallback :focus state */ + &:focus:not(:disabled) { + box-shadow: none; + outline: 2px solid var(--fgColor-accent); + outline-offset: -8px; + + /* remove fallback :focus if :focus-visible is supported */ + &:not(:focus-visible) { + outline: solid 1px transparent; + } + } + + /* default focus state */ + &:focus-visible:not(:disabled) { + box-shadow: none; + outline: 2px solid var(--fgColor-accent); + outline-offset: -8px; + } + +} + +.UnderlineNavLink:hover, +.UnderlineNavLink:focus { + color: var(--fgColor-default); + text-decoration: none; + border-bottom-color: var(--borderColor-muted); + transition: border-bottom-color 0.2s ease; +} + +.UnderlineNavLink:hover .UnderlineNavOcticon, +.UnderlineNavLink:focus .UnderlineNavOcticon { + color: var(--fgColor-muted); +} + +.UnderlineNavLink:where([data-selected]) { + color: var(--fgColor-default); + border-bottom-color: var(--borderColor-accent-emphasis); +} + +.UnderlineNavLink:where([data-selected]) .UnderlineNavOcticon { + color: var(--fgColor-default); +} diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx index 2ecc4372336..f2e6080a51e 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx @@ -1,63 +1,29 @@ import {clsx} from 'clsx' import type {To} from 'history' -import type React from 'react' -import styled from 'styled-components' -import {get} from '../../constants' +import React from 'react' import type {ComponentProps} from '../../utils/types' -import getGlobalFocusStyles from '../../internal/utils/getGlobalFocusStyles' - -const ITEM_CLASS = 'PRC-UnderlineNav-item' -const SELECTED_CLASS = 'PRC-selected' - -const UnderlineNavBase = styled.nav` - display: flex; - justify-content: space-between; - border-bottom: 1px solid ${get('colors.border.muted')}; - &.PRC-UnderlineNav--right { - justify-content: flex-end; - - .PRC-UnderlineNav-item { - margin-right: 0; - margin-left: ${get('space.3')}; - } - - .PRC-UnderlineNav-actions { - flex: 1 1 auto; - } - } - &.PRC-UnderlineNav--full { - display: block; - } - - .PRC-UnderlineNav-body { - display: flex; - margin-bottom: -1px; - } - - .PRC-UnderlineNav-actions { - align-self: center; - } -` +import classes from './UnderlineNav.module.css' export type UnderlineNavProps = { actions?: React.ReactNode align?: 'right' full?: boolean label?: string -} & ComponentProps +} & React.ComponentProps<'nav'> -function UnderlineNav({actions, className, align, children, full, label, theme, ...rest}: UnderlineNavProps) { - const classes = clsx( +function UnderlineNav({actions, className, align, children, full, label, ...rest}: UnderlineNavProps) { + const navClasses = clsx( className, + classes.UnderlineNav, 'PRC-UnderlineNav', - align && `PRC-UnderlineNav--${align}`, - full && 'PRC-UnderlineNav--full', + align && classes['UnderlineNav--right'], + full && classes['UnderlineNav--full'], ) return ( - -
    {children}
    - {actions &&
    {actions}
    } -
    + ) } @@ -66,45 +32,19 @@ type StyledUnderlineNavLinkProps = { selected?: boolean } -const UnderlineNavLink = styled.a.attrs(props => ({ - className: clsx(ITEM_CLASS, props.selected && SELECTED_CLASS, props.className), -}))` - padding: ${get('space.3')} ${get('space.2')}; - margin-right: ${get('space.3')}; - font-size: ${get('fontSizes.1')}; - line-height: ${get('lineHeights.default')}; - color: ${get('colors.fg.default')}; - text-align: center; - border-bottom: 2px solid transparent; - text-decoration: none; - - &:hover, - &:focus { - color: ${get('colors.fg.default')}; - text-decoration: none; - border-bottom-color: ${get('colors.neutral.muted')}; - transition: border-bottom-color 0.2s ease; - - .PRC-UnderlineNav-octicon { - color: ${get('colors.fg.muted')}; - } - } - - &.PRC-selected { - color: ${get('colors.fg.default')}; - border-bottom-color: ${get('colors.primer.border.active')}; - - .PRC-UnderlineNav-octicon { - color: ${get('colors.fg.default')}; - } - } +type UnderlineNavLinkProps = React.ComponentProps<'a'> & StyledUnderlineNavLinkProps - ${getGlobalFocusStyles('-8px')}; -` +const UnderlineNavLink = React.forwardRef(function UnderlineNavLink( + {className, selected, ...props}, + forwardRef, +) { + const linkClasses = clsx(className, classes.UnderlineNavLink) + return +}) UnderlineNavLink.displayName = 'UnderlineNav.Link' -export type UnderlineNavLinkProps = ComponentProps +export type {UnderlineNavLinkProps} /** * @deprecated UnderlineNav is deprecated and will be replaced by the draft `UnderlineNav` in the next major release. See https://primer.style/react/drafts/UnderlineNav2 for more details. */ diff --git a/packages/react/src/internal/components/ValidationAnimationContainer.module.css b/packages/react/src/internal/components/ValidationAnimationContainer.module.css new file mode 100644 index 00000000000..747a7e24082 --- /dev/null +++ b/packages/react/src/internal/components/ValidationAnimationContainer.module.css @@ -0,0 +1,19 @@ +.Animation:where([data-show]) { + animation: 170ms fadeIn cubic-bezier(0.44, 0.74, 0.36, 1); + + @media (prefers-reduced-motion) { + animation: none; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-100%); + } + + to { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/packages/react/src/internal/components/ValidationAnimationContainer.tsx b/packages/react/src/internal/components/ValidationAnimationContainer.tsx index 00e3f2cfa67..cb502eec431 100644 --- a/packages/react/src/internal/components/ValidationAnimationContainer.tsx +++ b/packages/react/src/internal/components/ValidationAnimationContainer.tsx @@ -1,29 +1,11 @@ import type {HTMLProps} from 'react' import type React from 'react' import {useEffect, useState} from 'react' -import styled, {keyframes, css} from 'styled-components' +import classes from './ValidationAnimationContainer.module.css' interface Props extends HTMLProps { show?: boolean } - -const fadeIn = keyframes` - 0% { - opacity: 0; - transform: translateY(-100%); - } - 100% { - opacity: 1; - transform: translateY(0); - } - ` -// using easeOutQuint easing fn https://easings.net/#easeOutQuint -const AnimatedElement = styled.div` - animation: ${props => props.show && css`170ms ${fadeIn} cubic-bezier(0.44, 0.74, 0.36, 1);`}; - @media (prefers-reduced-motion) { - animation: none; - } -` const ValidationAnimationContainer: React.FC> = ({show, children}) => { const [shouldRender, setRender] = useState(show) @@ -37,9 +19,9 @@ const ValidationAnimationContainer: React.FC> = ( return shouldRender ? (
    - +
    {children} - +
    ) : null } diff --git a/packages/react/src/internal/utils/getGlobalFocusStyles.ts b/packages/react/src/internal/utils/getGlobalFocusStyles.ts deleted file mode 100644 index 1c22a9d448e..00000000000 --- a/packages/react/src/internal/utils/getGlobalFocusStyles.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type {CSSProperties} from 'react' -import {css} from 'styled-components' -import {get} from '../../constants' - -const globalFocusStyle = css` - box-shadow: none; - outline: 2px solid ${get('colors.accent.fg')}; -` - -const getGlobalFocusStyles = (outlineOffset?: CSSProperties['outlineOffset']) => css` - /* fallback :focus state */ - &:focus:not(:disabled) { - ${globalFocusStyle}; - outline-offset: ${typeof outlineOffset === 'undefined' ? '2px' : outlineOffset}; - - // remove fallback :focus if :focus-visible is supported - &:not(:focus-visible) { - outline: solid 1px transparent; - } - } - - /* default focus state */ - &:focus-visible:not(:disabled) { - ${globalFocusStyle}; - outline-offset: ${typeof outlineOffset === 'undefined' ? '2px' : outlineOffset}; - } -` - -export default getGlobalFocusStyles From 375cc73639afecf2ae66b3c42c9027a806e84bd8 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 11:31:16 -0400 Subject: [PATCH 02/49] minor fixes --- packages/react/src/deprecated/ActionList/Divider.tsx | 2 +- packages/react/src/deprecated/ActionList/Header.tsx | 1 + packages/react/src/deprecated/ActionList/Item.tsx | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Divider.tsx b/packages/react/src/deprecated/ActionList/Divider.tsx index 68ae04c8df7..8119f96eb9c 100644 --- a/packages/react/src/deprecated/ActionList/Divider.tsx +++ b/packages/react/src/deprecated/ActionList/Divider.tsx @@ -4,7 +4,7 @@ import classes from './Divider.module.css' * Visually separates `Item`s or `Group`s in an `ActionList`. */ export function Divider(): JSX.Element { - return
    + return
    } /** diff --git a/packages/react/src/deprecated/ActionList/Header.tsx b/packages/react/src/deprecated/ActionList/Header.tsx index 0f7ef469b13..837d7fd0f47 100644 --- a/packages/react/src/deprecated/ActionList/Header.tsx +++ b/packages/react/src/deprecated/ActionList/Header.tsx @@ -42,6 +42,7 @@ export function Header({ role="heading" className={clsx(className, classes.Header)} data-filled={variant === 'filled' ? '' : undefined} + data-component="ActionList.Header" {...props} > {title} diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index e5768ecb7fa..834f61e7a38 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -7,8 +7,6 @@ import sx from '../../sx' import Truncate from '../../Truncate' import type {ItemInput} from './List' import styled from 'styled-components' -import {StyledHeader} from './Header' -import {StyledDivider} from './Divider' import {useTheme} from '../../ThemeProvider' import { activeDescendantActivatedDirectly, @@ -205,7 +203,7 @@ const StyledItem = styled.div< } // Item dividers - :not(:first-of-type):not(${StyledDivider} + &):not(${StyledHeader} + &) { + :not(:first-of-type):not([data-component='ActionList.Divider'] + &):not([data-component='ActionList.Header'] + &) { margin-top: ${({showDivider}) => (showDivider ? `1px` : '0')}; ${DividedContent}::before { From 0ae93e7dd398ab3b180cb494383868e5993838c4 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 11:44:28 -0400 Subject: [PATCH 03/49] fixes --- packages/react/src/LabelGroup/LabelGroup.tsx | 3 --- packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx | 1 - 2 files changed, 4 deletions(-) diff --git a/packages/react/src/LabelGroup/LabelGroup.tsx b/packages/react/src/LabelGroup/LabelGroup.tsx index e4a3458360b..dac8b77a9fb 100644 --- a/packages/react/src/LabelGroup/LabelGroup.tsx +++ b/packages/react/src/LabelGroup/LabelGroup.tsx @@ -4,7 +4,6 @@ import {getFocusableChild} from '@primer/behaviors/utils' import VisuallyHidden from '../_VisuallyHidden' import {AnchoredOverlay} from '../AnchoredOverlay' import {Button, IconButton} from '../Button' -import {useTheme} from '../ThemeProvider' import {clsx} from 'clsx' import classes from './LabelGroup.module.css' @@ -131,8 +130,6 @@ const LabelGroup: React.FC> = ({ toJSON: () => undefined, }) - const {theme} = useTheme() - const overlayPaddingPx = 8 // equivalent to var(--base-size-8) const hiddenItemIds = Object.keys(visibilityMap).filter(key => !visibilityMap[key]) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx index f2e6080a51e..9f9140af5f0 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx @@ -1,7 +1,6 @@ import {clsx} from 'clsx' import type {To} from 'history' import React from 'react' -import type {ComponentProps} from '../../utils/types' import classes from './UnderlineNav.module.css' export type UnderlineNavProps = { From 5420180fa481d11bd10dd17fe0fee58a17b8de64 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 11:51:29 -0400 Subject: [PATCH 04/49] re-add file --- .../internal/utils/getGlobalFocusStyles.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/react/src/internal/utils/getGlobalFocusStyles.ts diff --git a/packages/react/src/internal/utils/getGlobalFocusStyles.ts b/packages/react/src/internal/utils/getGlobalFocusStyles.ts new file mode 100644 index 00000000000..1c22a9d448e --- /dev/null +++ b/packages/react/src/internal/utils/getGlobalFocusStyles.ts @@ -0,0 +1,29 @@ +import type {CSSProperties} from 'react' +import {css} from 'styled-components' +import {get} from '../../constants' + +const globalFocusStyle = css` + box-shadow: none; + outline: 2px solid ${get('colors.accent.fg')}; +` + +const getGlobalFocusStyles = (outlineOffset?: CSSProperties['outlineOffset']) => css` + /* fallback :focus state */ + &:focus:not(:disabled) { + ${globalFocusStyle}; + outline-offset: ${typeof outlineOffset === 'undefined' ? '2px' : outlineOffset}; + + // remove fallback :focus if :focus-visible is supported + &:not(:focus-visible) { + outline: solid 1px transparent; + } + } + + /* default focus state */ + &:focus-visible:not(:disabled) { + ${globalFocusStyle}; + outline-offset: ${typeof outlineOffset === 'undefined' ? '2px' : outlineOffset}; + } +` + +export default getGlobalFocusStyles From a891a19a21385585ef987d9c42a17041493c2070 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 12:00:10 -0400 Subject: [PATCH 05/49] format --- .../src/LabelGroup/LabelGroup.module.css | 2 +- .../deprecated/ActionList/Header.module.css | 17 ++++++++-------- .../UnderlineNav/UnderlineNav.module.css | 3 +-- .../ValidationAnimationContainer.module.css | 20 +++++++++---------- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/packages/react/src/LabelGroup/LabelGroup.module.css b/packages/react/src/LabelGroup/LabelGroup.module.css index 4f3d1f25bc4..08cdc11fe77 100644 --- a/packages/react/src/LabelGroup/LabelGroup.module.css +++ b/packages/react/src/LabelGroup/LabelGroup.module.css @@ -11,7 +11,7 @@ flex-wrap: wrap; } - &:where([data-list]){ + &:where([data-list]) { padding-inline-start: 0; margin-block-start: 0; margin-block-end: 0; diff --git a/packages/react/src/deprecated/ActionList/Header.module.css b/packages/react/src/deprecated/ActionList/Header.module.css index 2041cbbf3c1..8108123d67b 100644 --- a/packages/react/src/deprecated/ActionList/Header.module.css +++ b/packages/react/src/deprecated/ActionList/Header.module.css @@ -10,15 +10,14 @@ font-weight: var(--text-title-weight-large); color: var(--fgColor-muted); - &:where([data-filled]){ - background: var(--bgColor-muted); - margin: var(--base-size-8) 0; - border-top: var(--borderWidth-thin) solid var(--borderColor-default); - border-bottom: var(--borderWidth-thin) solid var(--borderColor-default); + &:where([data-filled]) { + background: var(--bgColor-muted); + margin: var(--base-size-8) 0; + border-top: var(--borderWidth-thin) solid var(--borderColor-default); + border-bottom: var(--borderWidth-thin) solid var(--borderColor-default); - &:first-child { - margin-top: 0; - } + &:first-child { + margin-top: 0; } + } } - diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index 37929259dfd..b803de30c8c 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -42,7 +42,7 @@ border-bottom: 2px solid transparent; text-decoration: none; - /* fallback :focus state */ + /* fallback :focus state */ &:focus:not(:disabled) { box-shadow: none; outline: 2px solid var(--fgColor-accent); @@ -60,7 +60,6 @@ outline: 2px solid var(--fgColor-accent); outline-offset: -8px; } - } .UnderlineNavLink:hover, diff --git a/packages/react/src/internal/components/ValidationAnimationContainer.module.css b/packages/react/src/internal/components/ValidationAnimationContainer.module.css index 747a7e24082..32cbe1cbce6 100644 --- a/packages/react/src/internal/components/ValidationAnimationContainer.module.css +++ b/packages/react/src/internal/components/ValidationAnimationContainer.module.css @@ -1,19 +1,19 @@ .Animation:where([data-show]) { animation: 170ms fadeIn cubic-bezier(0.44, 0.74, 0.36, 1); - + @media (prefers-reduced-motion) { animation: none; } } @keyframes fadeIn { - from { - opacity: 0; - transform: translateY(-100%); - } + from { + opacity: 0; + transform: translateY(-100%); + } - to { - opacity: 1; - transform: translateY(0); - } -} \ No newline at end of file + to { + opacity: 1; + transform: translateY(0); + } +} From 794628bb08444e90bb5942b8d1a7af10c9996840 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 12:14:13 -0400 Subject: [PATCH 06/49] fix tests --- .../deprecated/UnderlineNav/UnderlineNav.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx index 9f9140af5f0..6b56a9d42aa 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx @@ -11,17 +11,16 @@ export type UnderlineNavProps = { } & React.ComponentProps<'nav'> function UnderlineNav({actions, className, align, children, full, label, ...rest}: UnderlineNavProps) { - const navClasses = clsx( - className, - classes.UnderlineNav, - 'PRC-UnderlineNav', - align && classes['UnderlineNav--right'], - full && classes['UnderlineNav--full'], - ) + const navClasses = clsx(className, classes.UnderlineNav, 'PRC-UnderlineNav', { + [classes['UnderlineNav--right']]: align === 'right', + [classes['UnderlineNav--full']]: full, + 'PRC-UnderlineNav--full': full, + 'PRC-UnderlineNav--right': align, + }) return ( ) } From 6a9da2509db342a783a3de997117a3962eeb1613 Mon Sep 17 00:00:00 2001 From: Marie Lucca <40550942+francinelucca@users.noreply.github.com> Date: Fri, 3 Oct 2025 12:49:32 -0400 Subject: [PATCH 07/49] Update version of @primer/react and change message --- .changeset/chilled-spoons-roll.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilled-spoons-roll.md diff --git a/.changeset/chilled-spoons-roll.md b/.changeset/chilled-spoons-roll.md new file mode 100644 index 00000000000..9eeb87d668a --- /dev/null +++ b/.changeset/chilled-spoons-roll.md @@ -0,0 +1,5 @@ +--- +"@primer/react": major +--- + +Chore/remove styled. components From 9eb678ee257c5e0189177ef060fdde5c98516cf8 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 12:57:06 -0400 Subject: [PATCH 08/49] tooltip --- packages/react/src/Tooltip/Tooltip.module.css | 161 ++++++++++++++ packages/react/src/Tooltip/Tooltip.tsx | 196 +----------------- 2 files changed, 170 insertions(+), 187 deletions(-) create mode 100644 packages/react/src/Tooltip/Tooltip.module.css diff --git a/packages/react/src/Tooltip/Tooltip.module.css b/packages/react/src/Tooltip/Tooltip.module.css new file mode 100644 index 00000000000..7c89d166772 --- /dev/null +++ b/packages/react/src/Tooltip/Tooltip.module.css @@ -0,0 +1,161 @@ +/* stylelint-disable primer/typography, primer/borders, selector-class-pattern */ + +.Tooltip { + position: relative; + display: inline-block; +} + +.Tooltip::after { + position: absolute; + z-index: 1000000; + display: none; + /* stylelint-disable-next-line primer/spacing */ + padding: 0.5em 0.75em; + font: normal normal var(--text-body-size-small) / var(--text-body-lineHeight-small) var(--fontStack-system); + -webkit-font-smoothing: subpixel-antialiased; + color: var(--tooltip-fgColor, var(--fgColor-onEmphasis)); + text-align: center; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-wrap: break-word; + white-space: pre; + pointer-events: none; + content: attr(aria-label); + background: var(--tooltip-bgColor, var(--bgColor-emphasis)); + border-radius: var(--borderRadius-medium); + opacity: 0; +} + +/* delay animation for tooltip */ +@keyframes tooltip-appear { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +.Tooltip:hover::after, +.Tooltip:active::after, +.Tooltip:focus::after, +.Tooltip:focus-within::after { + display: inline-block; + text-decoration: none; + animation-name: tooltip-appear; + animation-duration: 0.1s; + animation-fill-mode: forwards; + animation-timing-function: ease-in; + animation-delay: 0s; +} + +.Tooltip--noDelay:hover::after, +.Tooltip--noDelay:active::after, +.Tooltip--noDelay:focus::after, +.Tooltip--noDelay:focus-within::after { + animation-delay: 0s; +} + +.Tooltip--multiline:hover::after, +.Tooltip--multiline:active::after, +.Tooltip--multiline:focus::after, +.Tooltip--multiline:focus-within::after { + display: table-cell; +} + +/* Tooltipped south */ +.Tooltip--s::after, +.Tooltip--se::after, +.Tooltip--sw::after { + top: 100%; + right: 50%; + /* stylelint-disable-next-line primer/spacing */ + margin-top: 6px; +} + +.Tooltip--se::after { + right: auto; + left: 50%; + margin-left: calc(-1 * var(--base-size-16)); +} + +.Tooltip--sw::after { + margin-right: calc(-1 * var(--base-size-16)); +} + +/* Tooltips above the object */ +.Tooltip--n::after, +.Tooltip--ne::after, +.Tooltip--nw::after { + right: 50%; + bottom: 100%; + /* stylelint-disable-next-line primer/spacing */ + margin-bottom: 6px; +} + +.Tooltip--ne::after { + right: auto; + left: 50%; + margin-left: calc(-1 * var(--base-size-16)); +} + +.Tooltip--nw::after { + margin-right: calc(-1 * var(--base-size-16)); +} + +/* Move the tooltip body to the center of the object. */ +.Tooltip--s::after, +.Tooltip--n::after { + transform: translateX(50%); +} + +/* Tooltipped to the left */ +.Tooltip--w::after { + right: 100%; + bottom: 50%; + /* stylelint-disable-next-line primer/spacing */ + margin-right: 6px; + transform: translateY(50%); +} + +/* tooltipped to the right */ +.Tooltip--e::after { + bottom: 50%; + left: 100%; + /* stylelint-disable-next-line primer/spacing */ + margin-left: 6px; + transform: translateY(50%); +} + +.Tooltip--multiline::after { + width: max-content; + max-width: 250px; + word-wrap: break-word; + white-space: pre-line; + border-collapse: separate; +} + +.Tooltip--multiline.Tooltip--s::after, +.Tooltip--multiline.Tooltip--n::after { + right: auto; + left: 50%; + transform: translateX(-50%); +} + +.Tooltip--multiline.Tooltip--w::after, +.Tooltip--multiline.Tooltip--e::after { + right: 100%; +} + +.Tooltip--alignRight::after { + right: 0; + margin-right: 0; +} + +.Tooltip--alignLeft::after { + left: 0; + margin-left: 0; +} \ No newline at end of file diff --git a/packages/react/src/Tooltip/Tooltip.tsx b/packages/react/src/Tooltip/Tooltip.tsx index 1701bb7528f..0ba1a456846 100644 --- a/packages/react/src/Tooltip/Tooltip.tsx +++ b/packages/react/src/Tooltip/Tooltip.tsx @@ -1,187 +1,11 @@ import {clsx} from 'clsx' import React, {useMemo} from 'react' -import styled from 'styled-components' -import {get} from '../constants' import type {ComponentProps} from '../utils/types' import {useId} from '../hooks' +import classes from './Tooltip.module.css' /* Tooltip v1 */ -const TooltipBase = styled.span` - position: relative; - display: inline-block; - - &::after { - position: absolute; - z-index: 1000000; - display: none; - padding: 0.5em 0.75em; - font: normal normal 11px/1.5 ${get('fonts.normal')}; - -webkit-font-smoothing: subpixel-antialiased; - color: var(--tooltip-fgColor, ${get('colors.fg.onEmphasis')}); - text-align: center; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-wrap: break-word; - white-space: pre; - pointer-events: none; - content: attr(aria-label); - background: var(--tooltip-bgColor, ${get('colors.neutral.emphasisPlus')}); - border-radius: ${get('radii.2')}; - opacity: 0; - } - - // delay animation for tooltip - @keyframes tooltip-appear { - from { - opacity: 0; - } - - to { - opacity: 1; - } - } - - &:hover, - &:active, - &:focus, - &:focus-within { - &::after { - display: inline-block; - text-decoration: none; - animation-name: tooltip-appear; - animation-duration: 0.1s; - animation-fill-mode: forwards; - animation-timing-function: ease-in; - animation-delay: 0s; - } - } - - &.tooltipped-no-delay:hover, - &.tooltipped-no-delay:active, - &.tooltipped-no-delay:focus, - &.tooltipped-no-delay:focus-within { - &::after { - animation-delay: 0s; - } - } - - &.tooltipped-multiline:hover, - &.tooltipped-multiline:active, - &.tooltipped-multiline:focus, - &.tooltipped-multiline:focus-within { - &::after { - display: table-cell; - } - } - - // Tooltipped south - &.tooltipped-s, - &.tooltipped-se, - &.tooltipped-sw { - &::after { - top: 100%; - right: 50%; - margin-top: 6px; - } - } - - &.tooltipped-se { - &::after { - right: auto; - left: 50%; - margin-left: -${get('space.3')}; - } - } - - &.tooltipped-sw::after { - margin-right: -${get('space.3')}; - } - - // Tooltips above the object - &.tooltipped-n, - &.tooltipped-ne, - &.tooltipped-nw { - &::after { - right: 50%; - bottom: 100%; - margin-bottom: 6px; - } - } - - &.tooltipped-ne { - &::after { - right: auto; - left: 50%; - margin-left: -${get('space.3')}; - } - } - - &.tooltipped-nw::after { - margin-right: -${get('space.3')}; - } - - // Move the tooltip body to the center of the object. - &.tooltipped-s::after, - &.tooltipped-n::after { - transform: translateX(50%); - } - - // Tooltipped to the left - &.tooltipped-w { - &::after { - right: 100%; - bottom: 50%; - margin-right: 6px; - transform: translateY(50%); - } - } - - // tooltipped to the right - &.tooltipped-e { - &::after { - bottom: 50%; - left: 100%; - margin-left: 6px; - transform: translateY(50%); - } - } - - &.tooltipped-multiline { - &::after { - width: max-content; - max-width: 250px; - word-wrap: break-word; - white-space: pre-line; - border-collapse: separate; - } - - &.tooltipped-s::after, - &.tooltipped-n::after { - right: auto; - left: 50%; - transform: translateX(-50%); - } - - &.tooltipped-w::after, - &.tooltipped-e::after { - right: 100%; - } - } - - &.tooltipped-align-right-2::after { - right: 0; - margin-right: 0; - } - - &.tooltipped-align-left-2::after { - left: 0; - margin-left: 0; - } -` - /** * @deprecated */ @@ -191,7 +15,7 @@ export type TooltipProps = { noDelay?: boolean align?: 'left' | 'right' wrap?: boolean -} & ComponentProps +} & React.ComponentProps<'span'> export const TooltipContext = React.createContext<{tooltipId?: string}>({}) @@ -200,21 +24,19 @@ export const TooltipContext = React.createContext<{tooltipId?: string}>({}) */ function Tooltip({direction = 'n', children, className, text, noDelay, align, wrap, id, ...rest}: TooltipProps) { const tooltipId = useId(id) - const classes = clsx( - className, - `tooltipped-${direction}`, - align && `tooltipped-align-${align}-2`, - noDelay && 'tooltipped-no-delay', - wrap && 'tooltipped-multiline', - ) + const tooltipClasses = clsx(className, classes.Tooltip, classes[`Tooltip--${direction}`], { + [classes[`Tooltip--align${align === 'left' ? 'Left' : 'Right'}`]]: align, + [classes['Tooltip--noDelay']]: noDelay, + [classes['Tooltip--multiline']]: wrap, + }) const value = useMemo(() => ({tooltipId}), [tooltipId]) return ( // This provider is used to check if an icon button is wrapped with tooltip or not. - + {children} - + ) } From bb8340e4b782bb3c638e8b694317c80be78c2e02 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 12:59:27 -0400 Subject: [PATCH 09/49] format --- packages/react/src/Tooltip/Tooltip.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/Tooltip/Tooltip.module.css b/packages/react/src/Tooltip/Tooltip.module.css index 7c89d166772..4a7266dc6d9 100644 --- a/packages/react/src/Tooltip/Tooltip.module.css +++ b/packages/react/src/Tooltip/Tooltip.module.css @@ -158,4 +158,4 @@ .Tooltip--alignLeft::after { left: 0; margin-left: 0; -} \ No newline at end of file +} From cebcc492403f9da2993525b58a6930f81511b27e Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 13:01:53 -0400 Subject: [PATCH 10/49] change color --- .../react/src/deprecated/UnderlineNav/UnderlineNav.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index b803de30c8c..92dc537ce3f 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -77,7 +77,7 @@ .UnderlineNavLink:where([data-selected]) { color: var(--fgColor-default); - border-bottom-color: var(--borderColor-accent-emphasis); + border-bottom-color: var(--underlineNav-borderColor-active); } .UnderlineNavLink:where([data-selected]) .UnderlineNavOcticon { From 865a922a858c17e19941ce0a119fa006e82edbf2 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 13:04:42 -0400 Subject: [PATCH 11/49] remove unused import --- packages/react/src/Tooltip/Tooltip.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/Tooltip/Tooltip.tsx b/packages/react/src/Tooltip/Tooltip.tsx index 0ba1a456846..8645a0bf6a7 100644 --- a/packages/react/src/Tooltip/Tooltip.tsx +++ b/packages/react/src/Tooltip/Tooltip.tsx @@ -1,6 +1,5 @@ import {clsx} from 'clsx' import React, {useMemo} from 'react' -import type {ComponentProps} from '../utils/types' import {useId} from '../hooks' import classes from './Tooltip.module.css' From ace1d27bd74314b45e588aa20fc62ad7ed0d8a27 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 13:29:56 -0400 Subject: [PATCH 12/49] tooltip classes --- packages/react/src/Tooltip/Tooltip.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react/src/Tooltip/Tooltip.tsx b/packages/react/src/Tooltip/Tooltip.tsx index 8645a0bf6a7..0e1bc60f215 100644 --- a/packages/react/src/Tooltip/Tooltip.tsx +++ b/packages/react/src/Tooltip/Tooltip.tsx @@ -27,6 +27,11 @@ function Tooltip({direction = 'n', children, className, text, noDelay, align, wr [classes[`Tooltip--align${align === 'left' ? 'Left' : 'Right'}`]]: align, [classes['Tooltip--noDelay']]: noDelay, [classes['Tooltip--multiline']]: wrap, + // maintaining feature parity with old classes + [`tooltipped-${direction}`]: true, + [`tooltipped-align-${align === 'left' ? 'left' : 'right'}-2`]: align, + 'tooltipped-no-delay': noDelay, + 'tooltipped-multiline': wrap, }) const value = useMemo(() => ({tooltipId}), [tooltipId]) From 634dbad739f6dafd05b8b4bf57d36395f30346dc Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:08:05 -0400 Subject: [PATCH 13/49] item migration --- .../src/deprecated/ActionList/Item.module.css | 244 ++++++++++++++++ .../react/src/deprecated/ActionList/Item.tsx | 269 +++--------------- 2 files changed, 277 insertions(+), 236 deletions(-) create mode 100644 packages/react/src/deprecated/ActionList/Item.module.css diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css new file mode 100644 index 00000000000..a7aff8c97e3 --- /dev/null +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -0,0 +1,244 @@ +/* stylelint-disable selector-max-specificity */ +.DividedContent { + display: flex; + min-width: 0; + + /* Required for dividers */ + position: relative; + flex-grow: 1; +} + +.MainContent { + align-items: baseline; + display: flex; + min-width: 0; + /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */ + flex-direction: var(--main-content-flex-direction); + flex-grow: 1; +} + +.Item { + /* 6px vertical padding + 20px line height = 32px total height */ + /* stylelint-disable-next-line primer/spacing */ + padding: 6px var(--base-size-8); + display: flex; + border-radius: var(--borderRadius-medium); + color: var(--fgColor-default); + /* 2 frames on a 60hz monitor */ + transition: background 33.333ms linear; + text-decoration: none; +} + +.Item--danger { + color: var(--fgColor-danger); +} + +.Item--disabled { + color: var(--fgColor-disabled); + cursor: default; +} + +@media (hover: hover) and (pointer: fine) { + .Item:hover { + /* allow override in case another item in the list is active/focused */ + background: var(--item-hover-bg-override, var(--bgColor-muted)); + cursor: pointer; + } + + .Item--danger:hover { + background: var(--item-hover-bg-override, var(--bgColor-danger-muted)); + color: var(--fgColor-danger); + } + + .Item--disabled:hover { + background: transparent; + cursor: default; + } +} + +/* Item dividers */ +.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not( + [data-component='ActionList.Header'] + * + ) { + margin-top: 0; + + :where([data-divider]) { + /* stylelint-disable-next-line primer/spacing */ + margin-top: 1px; + } +} + +.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not( + [data-component='ActionList.Header'] + * + ) + .DividedContent::before { + content: ' '; + display: block; + position: absolute; + width: 100%; + /* stylelint-disable-next-line primer/spacing */ + top: -7px; + border: 0 solid var(--borderColor-muted); + border-top-width: 0; + } + + :where([data-divider]){ + .DividedContent::before { + /* stylelint-disable-next-line primer/borders */ + border-top-width: 1px; + } + } + +/* Item dividers should not be visible above/below hovered items */ +.Item:hover .DividedContent::before, +:hover + .Item .DividedContent::before { + /* allow override in case another item in the list is active/focused */ + /* stylelint-disable-next-line primer/colors */ + border-color: var(--item-hover-divider-border-color-override, transparent) !important; +} + +/* Item dividers should not be visible above/below focused items */ +.Item:focus .DividedContent::before, +:focus + .Item .DividedContent::before, +.Item[data-is-active-descendant] .DividedContent::before, +[data-active-descendant] + .Item .DividedContent::before { + /* !important because all the :not's above give higher specificity */ + border-color: transparent !important; +} + +/* Active Descendant */ +.Item[data-is-active-descendant='activated-directly'] { + background: var(--bgColor-accent-muted); +} + +.Item[data-is-active-descendant='activated-indirectly'] { + background: var(--bgColor-muted); +} + +.Item:focus { + background: var(--bgColor-accent-muted); + outline: none; +} + +.Item:active { + background: var(--bgColor-accent-muted); +} + +.MainContent--inline { + flex-direction: row; +} + +.MainContent--block { + flex-direction: column; +} + +.BaseVisualContainer { + /* Match visual height to adjacent text line height. */ + height: 20px; + width: var(--base-size-16); + margin-right: var(--base-size-8); + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; +} + +.LeadingVisualContainer { + /* Match visual height to adjacent text line height. */ + height: 20px; + width: var(--base-size-16); + margin-right: var(--base-size-8); + align-items: center; + flex-shrink: 0; + display: flex; + flex-direction: column; + justify-content: center; +} + +.LeadingVisualContainer svg { + fill: var(--fgColor-muted); + font-size: var(--text-body-size-small); +} + +.LeadingVisualContainer:where([data-variant="danger"]) svg { + fill: var(--fgColor-danger); +} + +.LeadingVisualContainer:where([data-disabled]) svg { + fill: var(--fgColor-disabled); +} + +.TrailingContent { + /* Match visual height to adjacent text line height. */ + color: var(--fgColor-muted); + height: 20px; + width: auto; + margin-left: var(--base-size-8); + margin-right: 0; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; +} + +.TrailingContent:where([data-variant="danger"]) { + color: var(--fgColor-muted); +} + +.TrailingContent:where([data-disabled]) { + color: var(--fgColor-disabled); +} + +.TrailingContent div:nth-child(2) { + margin-left: var(--base-size-8); +} + +.TrailingContent svg { + fill: var(--fgColor-muted); + font-size: var(--text-body-size-small); +} + +.TrailingContent:where([data-variant="danger"]) svg { + fill: var(--fgColor-danger); +} + +.TrailingContent:where([data-disabled]) svg { + fill: var(--fgColor-disabled); +} + +.DescriptionContainer { + color: var(--fgColor-muted); + font-size: var(--text-body-size-small); + line-height: var(--text-body-lineHeight-small); + min-width: 0; + flex-grow: 1; + /* stylelint-disable-next-line primer/spacing, csstools/value-no-unknown-custom-properties */ + margin-left: var(--description-container-margin-left); + /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */ + flex-basis: var(--description-container-flex-basis); +} + +.MultiSelectIcon { + color: var(--fgColor-muted); +} + +.MultiSelectIcon rect { + fill: var(--bgColor-default); + stroke: var(--borderColor-default); + shape-rendering: auto; /* this is a workaround to override global style in github/github, see primer/react#1666 */ +} + +.MultiSelectIcon path { + fill: var(--fgColor-onEmphasis); + box-shadow: var(--shadow-resting-small); + opacity: 0; +} + +.MultiSelectIcon:where([data-selected]) rect { + fill: var(--bgColor-accent-emphasis); + stroke: var(--bgColor-accent-emphasis); +} + +.MultiSelectIcon:where([data-selected]) path { + opacity: 1; +} diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index 834f61e7a38..708367daa9f 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -3,20 +3,16 @@ import React, {useCallback} from 'react' import {isValidElementType} from 'react-is' import {get} from '../../constants' import type {SxProp} from '../../sx' -import sx from '../../sx' import Truncate from '../../Truncate' import type {ItemInput} from './List' import styled from 'styled-components' import {useTheme} from '../../ThemeProvider' -import { - activeDescendantActivatedDirectly, - activeDescendantActivatedIndirectly, - isActiveDescendantAttribute, -} from '@primer/behaviors' import {useId} from '../../hooks/useId' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../../utils/polymorphic' import type {AriaRole} from '../../utils/types' +import classes from './Item.module.css' + /** * Contract for props passed to the `Item` component. */ @@ -118,220 +114,15 @@ export interface ItemProps extends SxProp { * An item to pass back in the `onAction` callback, meant as */ item?: ItemInput + as?: React.ElementType } -const getItemVariant = (variant = 'default', disabled?: boolean) => { - if (disabled) { - return { - color: get('colors.primer.fg.disabled'), - iconColor: get('colors.primer.fg.disabled'), - annotationColor: get('colors.primer.fg.disabled'), - hoverCursor: 'default', - } - } - - switch (variant) { - case 'danger': - return { - color: get('colors.danger.fg'), - iconColor: get('colors.danger.fg'), - annotationColor: get('colors.fg.muted'), - hoverCursor: 'pointer', - hoverBg: get('colors.actionListItem.danger.hoverBg'), - focusBg: get('colors.actionListItem.danger.activeBg'), - hoverText: get('colors.actionListItem.danger.hoverText'), - } - default: - return { - color: get('colors.fg.default'), - iconColor: get('colors.fg.muted'), - annotationColor: get('colors.fg.muted'), - hoverCursor: 'pointer', - hoverBg: get('colors.actionListItem.default.hoverBg'), - focusBg: get('colors.actionListItem.default.activeBg'), - } - } -} - -const DividedContent = styled.div` - display: flex; - min-width: 0; - - /* Required for dividers */ - position: relative; - flex-grow: 1; -` - -const MainContent = styled.div` - align-items: baseline; - display: flex; - min-width: 0; - flex-direction: var(--main-content-flex-direction); - flex-grow: 1; -` - -const StyledItem = styled.div< - { - variant: ItemProps['variant'] - showDivider: ItemProps['showDivider'] - item?: ItemInput - } & SxProp ->` - /* 6px vertical padding + 20px line height = 32px total height - * - * TODO: When rem-based spacing on a 4px scale lands, replace - * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'. - */ - padding: 6px ${get('space.2')}; - display: flex; - border-radius: ${get('radii.2')}; - color: ${({variant, item}) => getItemVariant(variant, item?.disabled).color}; - // 2 frames on a 60hz monitor - transition: background 33.333ms linear; - text-decoration: none; - - @media (hover: hover) and (pointer: fine) { - :hover { - // allow override in case another item in the list is active/focused - background: var( - --item-hover-bg-override, - ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverBg} - ); - color: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverText}; - cursor: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverCursor}; - } - } - - // Item dividers - :not(:first-of-type):not([data-component='ActionList.Divider'] + &):not([data-component='ActionList.Header'] + &) { - margin-top: ${({showDivider}) => (showDivider ? `1px` : '0')}; - - ${DividedContent}::before { - content: ' '; - display: block; - position: absolute; - width: 100%; - top: -7px; - // NB: This 'get' won’t execute if it’s moved into the arrow function below. - border: 0 solid ${get('colors.border.muted')}; - border-top-width: ${({showDivider}) => (showDivider ? `1px` : '0')}; - } - } - - // Item dividers should not be visible: - // - above Hovered - &:hover ${DividedContent}::before, - // - below Hovered - // '*' instead of '&' because '&' maps to separate class names depending on 'variant' - :hover + * ${DividedContent}::before { - // allow override in case another item in the list is active/focused - border-color: var(--item-hover-divider-border-color-override, transparent) !important; - } - - // - above Focused - &:focus ${DividedContent}::before, - // - below Focused - // '*' instead of '&' because '&' maps to separate class names depending on 'variant' - :focus + * ${DividedContent}::before, - // - above Active Descendent - &[${isActiveDescendantAttribute}] ${DividedContent}::before, - // - below Active Descendent - [${isActiveDescendantAttribute}] + & ${DividedContent}::before { - // '!important' because all the ':not's above give higher specificity - border-color: transparent !important; - } - - // Active Descendant - &[${isActiveDescendantAttribute}='${activeDescendantActivatedDirectly}'] { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg}; - } - &[${isActiveDescendantAttribute}='${activeDescendantActivatedIndirectly}'] { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverBg}; - } - - &:focus { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg}; - outline: none; - } - - &:active { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg}; - } - - ${sx} -` - -export const TextContainer = styled.span<{ - dangerouslySetInnerHtml?: React.DOMAttributes['dangerouslySetInnerHTML'] -}>`` - -const BaseVisualContainer = styled.div<{variant?: ItemProps['variant']; disabled?: boolean}>` - // Match visual height to adjacent text line height. - // TODO: When rem-based spacing on a 4px scale lands, replace - // hardcoded '20px' with '${get('space.s20')}'. - height: 20px; - width: ${get('space.3')}; - margin-right: ${get('space.2')}; - display: flex; - justify-content: center; - align-items: center; - flex-shrink: 0; -` - -const ColoredVisualContainer = styled(BaseVisualContainer)` - svg { - fill: ${({variant, disabled}) => getItemVariant(variant, disabled).iconColor}; - font-size: ${get('fontSizes.0')}; - } -` - -const LeadingVisualContainer = styled(ColoredVisualContainer)` - display: flex; - flex-direction: column; - justify-content: center; -` - -const TrailingContent = styled(ColoredVisualContainer)` - color: ${({variant, disabled}) => getItemVariant(variant, disabled).annotationColor}; - margin-left: ${get('space.2')}; - margin-right: 0; - width: auto; - div:nth-child(2) { - margin-left: ${get('space.2')}; - } -` - -const DescriptionContainer = styled.span` - color: ${get('colors.fg.muted')}; - font-size: ${get('fontSizes.0')}; - // TODO: When rem-based spacing on a 4px scale lands, replace - // hardcoded '16px' with '${get('lh-12')}'. - line-height: 16px; - margin-left: var(--description-container-margin-left); - min-width: 0; - flex-grow: 1; - flex-basis: var(--description-container-flex-basis); -` - -const MultiSelectIcon = styled.svg<{selected?: boolean}>` - rect { - fill: ${({selected}) => (selected ? get('colors.accent.fg') : get('colors.canvas.default'))}; - stroke: ${({selected}) => (selected ? get('colors.accent.fg') : get('colors.border.default'))}; - shape-rendering: auto; // this is a workaround to override global style in github/github, see primer/react#1666 - } - path { - fill: ${get('colors.fg.onEmphasis')}; - boxshadow: ${get('shadow.small')}; - opacity: ${({selected}) => (selected ? 1 : 0)}; - } -` - /** * An actionable or selectable `Item` with an optional icon and description. */ export const Item = React.forwardRef((itemProps, ref) => { const { - as: Component, + as: Component = 'div', text, description, descriptionVariant = 'inline', @@ -385,22 +176,21 @@ export const Item = React.forwardRef((itemProps, ref) => { const {theme} = useTheme() return ( - {!!selected === selected && ( - +
    {selectionVariant === 'multiple' ? ( <> {/** @@ -408,8 +198,9 @@ export const Item = React.forwardRef((itemProps, ref) => { * be an interactive element inside an option * svg copied from primer/css */} - { strokeWidth="0" d="M4.03231 8.69862C3.84775 8.20646 4.49385 7.77554 4.95539 7.77554C5.41693 7.77554 6.80154 9.85246 6.80154 9.85246C6.80154 9.85246 10.2631 4.314 10.4938 4.08323C10.7246 3.85246 11.8785 4.08323 11.4169 5.00631C11.0081 5.82388 7.26308 11.4678 7.26308 11.4678C7.26308 11.4678 6.80154 12.1602 6.34 11.4678C5.87846 10.7755 4.21687 9.19077 4.03231 8.69862Z" /> - + ) : ( selected && )} - +
    )} {LeadingVisual && ( - +
    - +
    )} - - +
    {children} - {text ? {text} : null} + {text ? {text} : null} {description ? ( - { '--description-container-flex-basis': descriptionVariant === 'inline' ? 0 : 'auto', } as React.CSSProperties } + className={classes.DescriptionContainer} > {descriptionVariant === 'block' ? ( description @@ -459,26 +256,26 @@ export const Item = React.forwardRef((itemProps, ref) => { {description} )} - + ) : null} - +
    {/* backward compatibility: prefer TrailingVisual but fallback to TrailingIcon */} {TrailingVisual ? ( - +
    {typeof TrailingVisual !== 'string' && isValidElementType(TrailingVisual) ? ( ) : ( TrailingVisual )} - +
    ) : TrailingIcon || trailingText ? ( - +
    {trailingText} {TrailingIcon && } - +
    ) : null} -
    -
    +
    + ) }) as PolymorphicForwardRefComponent<'div', ItemProps> From 54f59cde3e898230b1fdc1cf2401f97c82c7541c Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:10:00 -0400 Subject: [PATCH 14/49] format --- .../src/deprecated/ActionList/Item.module.css | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css index a7aff8c97e3..4c1cf04708e 100644 --- a/packages/react/src/deprecated/ActionList/Item.module.css +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -57,9 +57,7 @@ } /* Item dividers */ -.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not( - [data-component='ActionList.Header'] + * - ) { +.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not([data-component='ActionList.Header'] + *) { margin-top: 0; :where([data-divider]) { @@ -68,9 +66,7 @@ } } -.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not( - [data-component='ActionList.Header'] + * - ) +.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not([data-component='ActionList.Header'] + *) .DividedContent::before { content: ' '; display: block; @@ -80,14 +76,14 @@ top: -7px; border: 0 solid var(--borderColor-muted); border-top-width: 0; - } +} - :where([data-divider]){ - .DividedContent::before { - /* stylelint-disable-next-line primer/borders */ - border-top-width: 1px; - } - } +:where([data-divider]) { + .DividedContent::before { + /* stylelint-disable-next-line primer/borders */ + border-top-width: 1px; + } +} /* Item dividers should not be visible above/below hovered items */ .Item:hover .DividedContent::before, @@ -160,7 +156,7 @@ font-size: var(--text-body-size-small); } -.LeadingVisualContainer:where([data-variant="danger"]) svg { +.LeadingVisualContainer:where([data-variant='danger']) svg { fill: var(--fgColor-danger); } @@ -181,7 +177,7 @@ flex-shrink: 0; } -.TrailingContent:where([data-variant="danger"]) { +.TrailingContent:where([data-variant='danger']) { color: var(--fgColor-muted); } @@ -198,7 +194,7 @@ font-size: var(--text-body-size-small); } -.TrailingContent:where([data-variant="danger"]) svg { +.TrailingContent:where([data-variant='danger']) svg { fill: var(--fgColor-danger); } From ab8d2ac7e47658d08606a3baa406c1618e77f3a2 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:11:14 -0400 Subject: [PATCH 15/49] remove unused import --- packages/react/src/deprecated/ActionList/Item.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index 708367daa9f..2c55628a701 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -5,7 +5,6 @@ import {get} from '../../constants' import type {SxProp} from '../../sx' import Truncate from '../../Truncate' import type {ItemInput} from './List' -import styled from 'styled-components' import {useTheme} from '../../ThemeProvider' import {useId} from '../../hooks/useId' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../../utils/polymorphic' From aac29d7bb503248c93d9e7a0fcd045dd708e6077 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:29:39 -0400 Subject: [PATCH 16/49] type fix --- packages/react/src/deprecated/ActionList/Item.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index 2c55628a701..bc02f8fdd84 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -113,7 +113,6 @@ export interface ItemProps extends SxProp { * An item to pass back in the `onAction` callback, meant as */ item?: ItemInput - as?: React.ElementType } /** From ab804c22bb15603bb01fc22ec4fe1781b2c60499 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:35:50 -0400 Subject: [PATCH 17/49] remove needless disables --- packages/react/src/deprecated/ActionList/Item.module.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css index 4c1cf04708e..24cd6531fd2 100644 --- a/packages/react/src/deprecated/ActionList/Item.module.css +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -12,7 +12,7 @@ align-items: baseline; display: flex; min-width: 0; - /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */ + /* stylelint-disable-next-line */ flex-direction: var(--main-content-flex-direction); flex-grow: 1; } @@ -208,9 +208,9 @@ line-height: var(--text-body-lineHeight-small); min-width: 0; flex-grow: 1; - /* stylelint-disable-next-line primer/spacing, csstools/value-no-unknown-custom-properties */ + /* stylelint-disable-next-line primer/spacing */ margin-left: var(--description-container-margin-left); - /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */ + /* stylelint-disable-next-line */ flex-basis: var(--description-container-flex-basis); } From eb2196f08eb6df706a1b32e092af1975343bb5f4 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:41:47 -0400 Subject: [PATCH 18/49] remove needless disables --- packages/react/src/deprecated/ActionList/Item.module.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css index 24cd6531fd2..56c0848da9a 100644 --- a/packages/react/src/deprecated/ActionList/Item.module.css +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -12,7 +12,6 @@ align-items: baseline; display: flex; min-width: 0; - /* stylelint-disable-next-line */ flex-direction: var(--main-content-flex-direction); flex-grow: 1; } @@ -210,7 +209,6 @@ flex-grow: 1; /* stylelint-disable-next-line primer/spacing */ margin-left: var(--description-container-margin-left); - /* stylelint-disable-next-line */ flex-basis: var(--description-container-flex-basis); } From ae92cb49aaeefb226b2c8f0050818a8c3754a7e7 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:43:23 -0400 Subject: [PATCH 19/49] forward as --- packages/react/src/Tooltip/Tooltip.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/react/src/Tooltip/Tooltip.tsx b/packages/react/src/Tooltip/Tooltip.tsx index 0e1bc60f215..2dec29b89d8 100644 --- a/packages/react/src/Tooltip/Tooltip.tsx +++ b/packages/react/src/Tooltip/Tooltip.tsx @@ -2,6 +2,7 @@ import {clsx} from 'clsx' import React, {useMemo} from 'react' import {useId} from '../hooks' import classes from './Tooltip.module.css' +import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' /* Tooltip v1 */ @@ -21,7 +22,10 @@ export const TooltipContext = React.createContext<{tooltipId?: string}>({}) /** * @deprecated */ -function Tooltip({direction = 'n', children, className, text, noDelay, align, wrap, id, ...rest}: TooltipProps) { +const Tooltip = React.forwardRef(function Tooltip( + {as: Component = 'span', direction = 'n', children, className, text, noDelay, align, wrap, id, ...rest}, + ref, +) { const tooltipId = useId(id) const tooltipClasses = clsx(className, classes.Tooltip, classes[`Tooltip--${direction}`], { [classes[`Tooltip--align${align === 'left' ? 'Left' : 'Right'}`]]: align, @@ -38,11 +42,14 @@ function Tooltip({direction = 'n', children, className, text, noDelay, align, wr return ( // This provider is used to check if an icon button is wrapped with tooltip or not. - + {children} - + ) +}) as PolymorphicForwardRefComponent<'span', TooltipProps> & { + alignments: string[] + directions: string[] } Tooltip.alignments = ['left', 'right'] From 9ceb936d30f796d2324b5a7c3fe1ad82a4f67690 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:48:49 -0400 Subject: [PATCH 20/49] type fix? --- packages/styled-react/src/components/Tooltip.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/styled-react/src/components/Tooltip.tsx b/packages/styled-react/src/components/Tooltip.tsx index da923ed48b6..c2cc4e09037 100644 --- a/packages/styled-react/src/components/Tooltip.tsx +++ b/packages/styled-react/src/components/Tooltip.tsx @@ -19,7 +19,8 @@ export {Tooltip, type TooltipProps} type DeprecatedTooltipProps = PrimerDeprecatedTooltipProps & SxProp -function DeprecatedTooltip(props: DeprecatedTooltipProps) { - return -} +const DeprecatedTooltip: ForwardRefExoticComponent> = + forwardRef(function DeprecatedTooltip(props, ref) { + return + }) export {DeprecatedTooltip, type DeprecatedTooltipProps} From 548f739df74de6abf9fd83849c2a7e130ff7e9d5 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Fri, 3 Oct 2025 14:53:14 -0400 Subject: [PATCH 21/49] type fixes --- packages/styled-react/src/components/Tooltip.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/styled-react/src/components/Tooltip.tsx b/packages/styled-react/src/components/Tooltip.tsx index c2cc4e09037..594133c4134 100644 --- a/packages/styled-react/src/components/Tooltip.tsx +++ b/packages/styled-react/src/components/Tooltip.tsx @@ -4,7 +4,7 @@ import { type TooltipProps as PrimerDeprecatedTooltipProps, } from '@primer/react/deprecated' import {Box} from './Box' -import {forwardRef, type ForwardRefExoticComponent, type RefAttributes} from 'react' +import {forwardRef, type ForwardRefExoticComponent, type RefAttributes, type ComponentType} from 'react' type TooltipProps = PrimerTooltipProps & SxProp @@ -19,8 +19,11 @@ export {Tooltip, type TooltipProps} type DeprecatedTooltipProps = PrimerDeprecatedTooltipProps & SxProp +// Cast the polymorphic component to a compatible type for styled-components +const DeprecatedTooltipComponent = PrimerDeprecatedTooltip as ComponentType + const DeprecatedTooltip: ForwardRefExoticComponent> = forwardRef(function DeprecatedTooltip(props, ref) { - return + return }) export {DeprecatedTooltip, type DeprecatedTooltipProps} From f87687376055f7687cecb28c49cdd7bc00106120 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 12:00:43 -0700 Subject: [PATCH 22/49] remove sx --- packages/react/src/deprecated/ActionList/Group.tsx | 3 +-- packages/react/src/deprecated/ActionList/Header.tsx | 3 +-- packages/react/src/deprecated/ActionList/Item.tsx | 3 +-- packages/react/src/deprecated/ActionList/List.tsx | 6 +++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Group.tsx b/packages/react/src/deprecated/ActionList/Group.tsx index fb9a93b60a6..cbf6769bb50 100644 --- a/packages/react/src/deprecated/ActionList/Group.tsx +++ b/packages/react/src/deprecated/ActionList/Group.tsx @@ -1,12 +1,11 @@ import type React from 'react' -import type {SxProp} from '../../sx' import type {HeaderProps} from './Header' import {Header} from './Header' /** * Contract for props passed to the `Group` component. */ -export interface GroupProps extends React.ComponentPropsWithoutRef<'div'>, SxProp { +export interface GroupProps extends React.ComponentPropsWithoutRef<'div'> { /** * Props for a `Header` to render in the `Group`. */ diff --git a/packages/react/src/deprecated/ActionList/Header.tsx b/packages/react/src/deprecated/ActionList/Header.tsx index 837d7fd0f47..dc820d4eee8 100644 --- a/packages/react/src/deprecated/ActionList/Header.tsx +++ b/packages/react/src/deprecated/ActionList/Header.tsx @@ -1,12 +1,11 @@ import type React from 'react' import {clsx} from 'clsx' -import type {SxProp} from '../../sx' import classes from './Header.module.css' /** * Contract for props passed to the `Header` component. */ -export interface HeaderProps extends React.ComponentPropsWithoutRef<'div'>, SxProp { +export interface HeaderProps extends React.ComponentPropsWithoutRef<'div'> { /** * Style variations. Usage is discretionary. * diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index bc02f8fdd84..b753f66de3d 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -2,7 +2,6 @@ import {CheckIcon} from '@primer/octicons-react' import React, {useCallback} from 'react' import {isValidElementType} from 'react-is' import {get} from '../../constants' -import type {SxProp} from '../../sx' import Truncate from '../../Truncate' import type {ItemInput} from './List' import {useTheme} from '../../ThemeProvider' @@ -15,7 +14,7 @@ import classes from './Item.module.css' /** * Contract for props passed to the `Item` component. */ -export interface ItemProps extends SxProp { +export interface ItemProps { /** * Primary text which names an `Item`. */ diff --git a/packages/react/src/deprecated/ActionList/List.tsx b/packages/react/src/deprecated/ActionList/List.tsx index 3550c817062..46891a69c75 100644 --- a/packages/react/src/deprecated/ActionList/List.tsx +++ b/packages/react/src/deprecated/ActionList/List.tsx @@ -175,7 +175,7 @@ export const List = React.forwardRef((props, forwarde selectionVariant={restProps.selectionVariant} {...itemProps} key={key} - sx={{...itemStyle, ...itemProps.sx}} + style={itemStyle} item={item} /> ) @@ -249,7 +249,7 @@ export const List = React.forwardRef((props, forwarde {shouldShowDivider ? : null} {renderGroup({ - sx: { + style: { ...(index === 0 && firstGroupStyle), ...(index === groups.length - 1 && lastGroupStyle), ...(index > 0 && !shouldShowDivider && {mt: 2}), @@ -257,7 +257,7 @@ export const List = React.forwardRef((props, forwarde ...(header && { header: { ...header, - sx: {...headerStyle, ...header.sx}, + style: headerStyle, }, }), ...groupProps, From 5cbd7ab14157726f96c88f590643b1b8470edddd Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 12:51:47 -0700 Subject: [PATCH 23/49] export in styled-react and remove sx from stories --- .../stories/deprecated/ActionList.stories.tsx | 8 +--- .../deprecated/ActionListStories.module.css | 7 +++ .../src/components/deprecated/ActionList.tsx | 47 +++++++++++++++++++ .../components/{ => deprecated}/DialogV1.tsx | 6 +-- .../components/{ => deprecated}/TabNav.tsx | 2 +- packages/styled-react/src/deprecated.tsx | 4 +- 6 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 packages/styled-react/src/components/deprecated/ActionList.tsx rename packages/styled-react/src/components/{ => deprecated}/DialogV1.tsx (90%) rename packages/styled-react/src/components/{ => deprecated}/TabNav.tsx (96%) diff --git a/packages/react/src/stories/deprecated/ActionList.stories.tsx b/packages/react/src/stories/deprecated/ActionList.stories.tsx index 0b802a0434b..c6e0e70212c 100644 --- a/packages/react/src/stories/deprecated/ActionList.stories.tsx +++ b/packages/react/src/stories/deprecated/ActionList.stories.tsx @@ -176,9 +176,7 @@ export function ComplexListInsetVariantStory(): JSX.Element { )} /> ), - renderGroup: ({sx: sxProps, ...props}) => ( - - ), + renderGroup: props => , }, ]} items={[ @@ -250,9 +248,7 @@ export function ComplexListFullVariantStory(): JSX.Element { )} /> ), - renderGroup: ({sx: sxProps, ...props}) => ( - - ), + renderGroup: props => , }, ]} items={[ diff --git a/packages/react/src/stories/deprecated/ActionListStories.module.css b/packages/react/src/stories/deprecated/ActionListStories.module.css index 02f8e972f5f..d1df4fdf176 100644 --- a/packages/react/src/stories/deprecated/ActionListStories.module.css +++ b/packages/react/src/stories/deprecated/ActionListStories.module.css @@ -16,3 +16,10 @@ /* stylelint-disable-next-line color-named */ fill: white; } + +.CustomGroupStyle { + /* stylelint-disable-next-line color-named */ + background-color: cornflowerblue; + /* stylelint-disable-next-line color-named */ + color: white; +} diff --git a/packages/styled-react/src/components/deprecated/ActionList.tsx b/packages/styled-react/src/components/deprecated/ActionList.tsx new file mode 100644 index 00000000000..88908a51ac0 --- /dev/null +++ b/packages/styled-react/src/components/deprecated/ActionList.tsx @@ -0,0 +1,47 @@ +import {type PropsWithChildren, forwardRef} from 'react' +import { + ActionList as PrimerActionList, + type ActionListProps as PrimerActionListProps, + type ActionListItemProps as PrimerActionListItemProps, + type ActionListGroupProps as PrimerActionListGroupProps, +} from '@primer/react/deprecated' +import {sx, type SxProp} from '../../sx' +import {Box} from '../Box' +import styled from 'styled-components' +import type {ActionListDividerProps} from '@primer/react' + +type ActionListProps = PropsWithChildren +// Add explicit `as` prop since polymorphic typing doesn't carry over to type aliases +type ActionListItemProps = PropsWithChildren +type ActionListGroupProps = PropsWithChildren + +const ActionListImpl = forwardRef(function ActionList(props, ref) { + return +}) + +const StyledActionListItem = styled(PrimerActionList.Item).withConfig({ + shouldForwardProp: prop => (prop as keyof ActionListItemProps) !== 'sx', +})` + ${sx} +` + +const ActionListItem = forwardRef(({as, ...props}, ref) => ( + +)) + +function ActionListGroup(props: ActionListGroupProps) { + return +} + +function ActionListDivider(props: ActionListDividerProps) { + return +} + +const ActionList = Object.assign(ActionListImpl, { + Item: ActionListItem, + Group: ActionListGroup, + Divider: ActionListDivider, +}) + +export type {ActionListProps, ActionListItemProps, ActionListGroupProps} +export {ActionList} diff --git a/packages/styled-react/src/components/DialogV1.tsx b/packages/styled-react/src/components/deprecated/DialogV1.tsx similarity index 90% rename from packages/styled-react/src/components/DialogV1.tsx rename to packages/styled-react/src/components/deprecated/DialogV1.tsx index a9907282fde..98eba8edcb1 100644 --- a/packages/styled-react/src/components/DialogV1.tsx +++ b/packages/styled-react/src/components/deprecated/DialogV1.tsx @@ -3,10 +3,10 @@ import type { DialogProps as PrimerDialogProps, DialogHeaderProps as PrimerDialogHeaderProps, } from '@primer/react/deprecated' -import {Box} from './Box' -import type {SxProp} from '../sx' +import {Box} from '../Box' +import type {SxProp} from '../../sx' import {forwardRef} from 'react' -import type {ForwardRefComponent} from '../polymorphic' +import type {ForwardRefComponent} from '../../polymorphic' type DialogProps = PrimerDialogProps & SxProp diff --git a/packages/styled-react/src/components/TabNav.tsx b/packages/styled-react/src/components/deprecated/TabNav.tsx similarity index 96% rename from packages/styled-react/src/components/TabNav.tsx rename to packages/styled-react/src/components/deprecated/TabNav.tsx index 961d66ee51f..7cbdd34f46e 100644 --- a/packages/styled-react/src/components/TabNav.tsx +++ b/packages/styled-react/src/components/deprecated/TabNav.tsx @@ -1,6 +1,6 @@ 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 {sx, type SxProp} from '../../sx' import styled from 'styled-components' import {forwardRef} from 'react' diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index 2c91cbbd61f..ff96c215f77 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -1,4 +1,4 @@ -export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/TabNav' -export {Dialog, type DialogProps, type DialogHeaderProps} from './components/DialogV1' +export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/deprecated/TabNav' +export {Dialog, type DialogProps, type DialogHeaderProps} from './components/deprecated/DialogV1' export {Octicon} from '@primer/react/deprecated' export {DeprecatedTooltip as Tooltip, type DeprecatedTooltipProps as TooltipProps} from './components/Tooltip' From 55dd0ac0c54832e924c82a82348e491ad7ec2bc0 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 13:17:27 -0700 Subject: [PATCH 24/49] line height --- .../react/src/deprecated/UnderlineNav/UnderlineNav.module.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index 92dc537ce3f..789eadc51cf 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -35,7 +35,7 @@ padding: var(--base-size-16) var(--base-size-8); margin-right: var(--base-size-16); font-size: var(--text-body-size-medium); - line-height: var(--text-body-lineHeight-medium); + line-height: var(--text-title-lineHeight-large); color: var(--fgColor-default); text-align: center; /* stylelint-disable-next-line primer/borders */ @@ -82,4 +82,4 @@ .UnderlineNavLink:where([data-selected]) .UnderlineNavOcticon { color: var(--fgColor-default); -} +} \ No newline at end of file From ff011f4e9d9cdb31a3d56507c390739e1803ce28 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 13:19:32 -0700 Subject: [PATCH 25/49] changeset --- .changeset/afraid-teams-throw.md | 6 ++++++ .../src/deprecated/UnderlineNav/UnderlineNav.module.css | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/afraid-teams-throw.md diff --git a/.changeset/afraid-teams-throw.md b/.changeset/afraid-teams-throw.md new file mode 100644 index 00000000000..45e461705de --- /dev/null +++ b/.changeset/afraid-teams-throw.md @@ -0,0 +1,6 @@ +--- +'@primer/react': major +'@primer/styled-react': minor +--- + +Remove sx from deprecated ActionList component. diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index 789eadc51cf..c5f326861ad 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -82,4 +82,4 @@ .UnderlineNavLink:where([data-selected]) .UnderlineNavOcticon { color: var(--fgColor-default); -} \ No newline at end of file +} From dbb553f81b93f18858c3c8490c05fcd30609051d Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 13:39:51 -0700 Subject: [PATCH 26/49] margin left --- .../react/src/deprecated/UnderlineNav/UnderlineNav.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index c5f326861ad..aefc96bf7ce 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -33,7 +33,7 @@ .UnderlineNavLink { padding: var(--base-size-16) var(--base-size-8); - margin-right: var(--base-size-16); + margin-left: var(--base-size-16); font-size: var(--text-body-size-medium); line-height: var(--text-title-lineHeight-large); color: var(--fgColor-default); From d9d3bf3c319f3937de65b32a19cf0eb6df7e3c10 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 14:48:56 -0700 Subject: [PATCH 27/49] classname --- .../UnderlineNav/UnderlineNav.module.css | 44 ++++++++++--------- .../deprecated/UnderlineNav/UnderlineNav.tsx | 2 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index aefc96bf7ce..3865ebce3b7 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -4,36 +4,38 @@ border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted); } -.UnderlineNav--right { - justify-content: flex-end; -} +.UnderlineNav { + &.UnderlineNav--right { + justify-content: flex-end; + } -.UnderlineNav--right .UnderlineNavItem { - margin-right: 0; - margin-left: var(--base-size-16); -} + &.UnderlineNav--right .UnderlineNavItem { + margin-right: 0; + margin-left: var(--base-size-16); + } -.UnderlineNav--right .UnderlineNavActions { - flex: 1 1 auto; -} + &.UnderlineNav--right .UnderlineNavActions { + flex: 1 1 auto; + } -.UnderlineNav--full { - display: block; -} + &.UnderlineNav--full { + display: block; + } -.UnderlineNavBody { - display: flex; - /* stylelint-disable-next-line primer/spacing */ - margin-bottom: -1px; -} + .UnderlineNavBody { + display: flex; + /* stylelint-disable-next-line primer/spacing */ + margin-bottom: -1px; + } -.UnderlineNavActions { - align-self: center; + .UnderlineNavActions { + align-self: center; + } } .UnderlineNavLink { padding: var(--base-size-16) var(--base-size-8); - margin-left: var(--base-size-16); + margin-right: var(--base-size-16); font-size: var(--text-body-size-medium); line-height: var(--text-title-lineHeight-large); color: var(--fgColor-default); diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx index 6b56a9d42aa..96e11bf7a53 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx @@ -36,7 +36,7 @@ const UnderlineNavLink = React.forwardRef }) From 96d5d920d706b96532b39f333444fc39bb4b3ed8 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 15:03:48 -0700 Subject: [PATCH 28/49] lint fix --- .../src/deprecated/UnderlineNav/UnderlineNav.module.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index 3865ebce3b7..305a006258a 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -1,9 +1,3 @@ -.UnderlineNav { - display: flex; - justify-content: space-between; - border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted); -} - .UnderlineNav { &.UnderlineNav--right { justify-content: flex-end; From f1ffe0de3d627542ea318c8d56cb8a1734724f38 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 15:22:54 -0700 Subject: [PATCH 29/49] remove Box --- .../src/deprecated/ActionList/List.module.css | 5 +++++ .../react/src/deprecated/ActionList/List.tsx | 19 ++++++++----------- .../UnderlineNav/UnderlineNav.module.css | 4 ++++ 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 packages/react/src/deprecated/ActionList/List.module.css diff --git a/packages/react/src/deprecated/ActionList/List.module.css b/packages/react/src/deprecated/ActionList/List.module.css new file mode 100644 index 00000000000..2c97f08a294 --- /dev/null +++ b/packages/react/src/deprecated/ActionList/List.module.css @@ -0,0 +1,5 @@ +.List[data-has-active-descendant], +.List:focus-within { + --item-hover-bg-override: none; + --item-hover-divider-border-color-override: var(--borderColor-muted); +} diff --git a/packages/react/src/deprecated/ActionList/List.tsx b/packages/react/src/deprecated/ActionList/List.tsx index 46891a69c75..a598e596745 100644 --- a/packages/react/src/deprecated/ActionList/List.tsx +++ b/packages/react/src/deprecated/ActionList/List.tsx @@ -6,9 +6,9 @@ import {Group} from './Group' import type {ItemProps} from './Item' import {Item} from './Item' import {Divider} from './Divider' -import {hasActiveDescendantAttribute} from '@primer/behaviors' +import {clsx} from 'clsx' import type {Merge} from '../../utils/types/Merge' -import {BoxWithFallback} from '../../internal/components/BoxWithFallback' +import classes from './List.module.css' export type RenderItemFn = (props: ItemProps) => React.ReactElement @@ -71,6 +71,8 @@ export interface ListPropsBase { * Whether to display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`. */ showItemDividers?: boolean + + className?: string } /** @@ -228,19 +230,14 @@ export const List = React.forwardRef((props, forwarde } return ( - {groups.map(({header, ...groupProps}, index) => { const hasFilledHeader = header?.variant === 'filled' @@ -252,7 +249,7 @@ export const List = React.forwardRef((props, forwarde style: { ...(index === 0 && firstGroupStyle), ...(index === groups.length - 1 && lastGroupStyle), - ...(index > 0 && !shouldShowDivider && {mt: 2}), + ...(index > 0 && !shouldShowDivider && {marginTop: 'var(--base-size-8, 8px)'}), }, ...(header && { header: { @@ -265,7 +262,7 @@ export const List = React.forwardRef((props, forwarde ) })} - +
    ) }) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index 305a006258a..f4825c09be2 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -1,4 +1,8 @@ .UnderlineNav { + display: flex; + justify-content: space-between; + border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted); + &.UnderlineNav--right { justify-content: flex-end; } From b3659c1f52a790ff2b49072def3dbcc345f14b29 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 15:34:55 -0700 Subject: [PATCH 30/49] vrt test fix --- .../UnderlineNav/UnderlineNav.module.css | 44 +++++++++---------- .../deprecated/UnderlineNav/UnderlineNav.tsx | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css index 92dc537ce3f..f4825c09be2 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css @@ -2,40 +2,40 @@ display: flex; justify-content: space-between; border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted); -} -.UnderlineNav--right { - justify-content: flex-end; -} + &.UnderlineNav--right { + justify-content: flex-end; + } -.UnderlineNav--right .UnderlineNavItem { - margin-right: 0; - margin-left: var(--base-size-16); -} + &.UnderlineNav--right .UnderlineNavItem { + margin-right: 0; + margin-left: var(--base-size-16); + } -.UnderlineNav--right .UnderlineNavActions { - flex: 1 1 auto; -} + &.UnderlineNav--right .UnderlineNavActions { + flex: 1 1 auto; + } -.UnderlineNav--full { - display: block; -} + &.UnderlineNav--full { + display: block; + } -.UnderlineNavBody { - display: flex; - /* stylelint-disable-next-line primer/spacing */ - margin-bottom: -1px; -} + .UnderlineNavBody { + display: flex; + /* stylelint-disable-next-line primer/spacing */ + margin-bottom: -1px; + } -.UnderlineNavActions { - align-self: center; + .UnderlineNavActions { + align-self: center; + } } .UnderlineNavLink { padding: var(--base-size-16) var(--base-size-8); margin-right: var(--base-size-16); font-size: var(--text-body-size-medium); - line-height: var(--text-body-lineHeight-medium); + line-height: var(--text-title-lineHeight-large); color: var(--fgColor-default); text-align: center; /* stylelint-disable-next-line primer/borders */ diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx index 6b56a9d42aa..96e11bf7a53 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx @@ -36,7 +36,7 @@ const UnderlineNavLink = React.forwardRef }) From 69cfa2fbb9e921c094f7e808845399543069d8fd Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 15:48:57 -0700 Subject: [PATCH 31/49] export --- packages/styled-react/src/deprecated.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index ff96c215f77..82b34230006 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -2,3 +2,9 @@ export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/depre export {Dialog, type DialogProps, type DialogHeaderProps} from './components/deprecated/DialogV1' export {Octicon} from '@primer/react/deprecated' export {DeprecatedTooltip as Tooltip, type DeprecatedTooltipProps as TooltipProps} from './components/Tooltip' +export { + ActionList, + type ActionListProps, + type ActionListItemProps, + type ActionListGroupProps, +} from './components/deprecated/ActionList' From 2013b6459f50b365dd6c19c7c5d62d243f84e770 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 16:27:58 -0700 Subject: [PATCH 32/49] check ref type --- packages/styled-react/src/components/Tooltip.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/styled-react/src/components/Tooltip.tsx b/packages/styled-react/src/components/Tooltip.tsx index 594133c4134..59912e38474 100644 --- a/packages/styled-react/src/components/Tooltip.tsx +++ b/packages/styled-react/src/components/Tooltip.tsx @@ -4,7 +4,7 @@ import { type TooltipProps as PrimerDeprecatedTooltipProps, } from '@primer/react/deprecated' import {Box} from './Box' -import {forwardRef, type ForwardRefExoticComponent, type RefAttributes, type ComponentType} from 'react' +import {forwardRef, type ForwardRefExoticComponent, type RefAttributes, type ComponentType, type RefObject} from 'react' type TooltipProps = PrimerTooltipProps & SxProp @@ -19,11 +19,15 @@ export {Tooltip, type TooltipProps} type DeprecatedTooltipProps = PrimerDeprecatedTooltipProps & SxProp -// Cast the polymorphic component to a compatible type for styled-components const DeprecatedTooltipComponent = PrimerDeprecatedTooltip as ComponentType const DeprecatedTooltip: ForwardRefExoticComponent> = - forwardRef(function DeprecatedTooltip(props, ref) { + forwardRef(function DeprecatedTooltip(props, forwardedRef) { + // Filter out legacy string refs that styled-components doesn't support + const ref: RefObject | ((instance: HTMLSpanElement | null) => void) | null | undefined = + typeof forwardedRef === 'string' ? null : forwardedRef + return }) + export {DeprecatedTooltip, type DeprecatedTooltipProps} From edccc60e10d0f769c446fd168d60b7620a18b32c Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 16:37:15 -0700 Subject: [PATCH 33/49] update snapshots --- .../src/__tests__/__snapshots__/exports.test.ts.snap | 1 + 1 file changed, 1 insertion(+) 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 3cf32383c51..8c3a1edd8d6 100644 --- a/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap @@ -57,6 +57,7 @@ exports[`@primer/styled-react exports 1`] = ` exports[`@primer/styled-react/deprecated exports 1`] = ` [ + "ActionList", "Dialog", "Octicon", "TabNav", From 16f8460ed189b65b2489998f1c9aef12c6abf864 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 16:52:10 -0700 Subject: [PATCH 34/49] styled? --- .../styled-react/src/components/Tooltip.tsx | 21 +--------------- .../src/components/deprecated/Tooltip.tsx | 24 +++++++++++++++++++ packages/styled-react/src/deprecated.tsx | 2 +- 3 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 packages/styled-react/src/components/deprecated/Tooltip.tsx diff --git a/packages/styled-react/src/components/Tooltip.tsx b/packages/styled-react/src/components/Tooltip.tsx index 59912e38474..ba3bbfb34d8 100644 --- a/packages/styled-react/src/components/Tooltip.tsx +++ b/packages/styled-react/src/components/Tooltip.tsx @@ -1,10 +1,6 @@ import {Tooltip as PrimerTooltip, type TooltipProps as PrimerTooltipProps, type SxProp} from '@primer/react' -import { - Tooltip as PrimerDeprecatedTooltip, - type TooltipProps as PrimerDeprecatedTooltipProps, -} from '@primer/react/deprecated' import {Box} from './Box' -import {forwardRef, type ForwardRefExoticComponent, type RefAttributes, type ComponentType, type RefObject} from 'react' +import {forwardRef, type ForwardRefExoticComponent, type RefAttributes} from 'react' type TooltipProps = PrimerTooltipProps & SxProp @@ -16,18 +12,3 @@ const Tooltip: ForwardRefExoticComponent - -const DeprecatedTooltip: ForwardRefExoticComponent> = - forwardRef(function DeprecatedTooltip(props, forwardedRef) { - // Filter out legacy string refs that styled-components doesn't support - const ref: RefObject | ((instance: HTMLSpanElement | null) => void) | null | undefined = - typeof forwardedRef === 'string' ? null : forwardedRef - - return - }) - -export {DeprecatedTooltip, type DeprecatedTooltipProps} diff --git a/packages/styled-react/src/components/deprecated/Tooltip.tsx b/packages/styled-react/src/components/deprecated/Tooltip.tsx new file mode 100644 index 00000000000..bf91e8cc26e --- /dev/null +++ b/packages/styled-react/src/components/deprecated/Tooltip.tsx @@ -0,0 +1,24 @@ +import {Tooltip as PrimerTooltip, type TooltipProps as PrimerTooltipProps} from '@primer/react/deprecated' +import {Box} from '../Box' +import {forwardRef, type RefAttributes, type ComponentType} from 'react' +import {sx, type SxProp} from '../../sx' +import {type PropsWithChildren} from 'react' +import styled from 'styled-components' +import type {ForwardRefComponent} from '../../polymorphic' + +type TooltipProps = PropsWithChildren + +const StyledTooltip = styled(PrimerTooltip).withConfig({ + shouldForwardProp: prop => (prop as keyof TooltipProps) !== 'sx', +})` + ${sx} +` + +const Tooltip = forwardRef(function Tooltip({as, ...props}, ref) { + return +}) as ForwardRefComponent<'span', TooltipProps> & { + alignments: string[] + directions: string[] +} + +export {Tooltip, type TooltipProps} diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index 82b34230006..ebeda158358 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -1,7 +1,7 @@ export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/deprecated/TabNav' export {Dialog, type DialogProps, type DialogHeaderProps} from './components/deprecated/DialogV1' export {Octicon} from '@primer/react/deprecated' -export {DeprecatedTooltip as Tooltip, type DeprecatedTooltipProps as TooltipProps} from './components/Tooltip' +export {Tooltip, type TooltipProps} from './components/deprecated/Tooltip' export { ActionList, type ActionListProps, From 91e48dca48f8fb53ac78dc288ceaafc4c13b3443 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Fri, 3 Oct 2025 17:07:10 -0700 Subject: [PATCH 35/49] tooltip fix? --- .../src/components/deprecated/Tooltip.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/styled-react/src/components/deprecated/Tooltip.tsx b/packages/styled-react/src/components/deprecated/Tooltip.tsx index bf91e8cc26e..9af0200ffb9 100644 --- a/packages/styled-react/src/components/deprecated/Tooltip.tsx +++ b/packages/styled-react/src/components/deprecated/Tooltip.tsx @@ -1,12 +1,11 @@ import {Tooltip as PrimerTooltip, type TooltipProps as PrimerTooltipProps} from '@primer/react/deprecated' -import {Box} from '../Box' -import {forwardRef, type RefAttributes, type ComponentType} from 'react' +import {forwardRef} from 'react' import {sx, type SxProp} from '../../sx' import {type PropsWithChildren} from 'react' import styled from 'styled-components' -import type {ForwardRefComponent} from '../../polymorphic' -type TooltipProps = PropsWithChildren +// Add explicit `as` prop since polymorphic typing doesn't carry over to type aliases +type TooltipProps = PropsWithChildren const StyledTooltip = styled(PrimerTooltip).withConfig({ shouldForwardProp: prop => (prop as keyof TooltipProps) !== 'sx', @@ -14,11 +13,17 @@ const StyledTooltip = styled(PrimerTooltip).withConfig({ ${sx} ` -const Tooltip = forwardRef(function Tooltip({as, ...props}, ref) { +const TooltipImpl = forwardRef(function Tooltip({as, ...props}, ref) { return -}) as ForwardRefComponent<'span', TooltipProps> & { +}) + +const Tooltip = TooltipImpl as typeof TooltipImpl & { alignments: string[] directions: string[] } +// Preserve static properties from the original component +Tooltip.alignments = PrimerTooltip.alignments +Tooltip.directions = PrimerTooltip.directions + export {Tooltip, type TooltipProps} From 72bc3811ac2238737775ebe71e1a304a0a7788ad Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 12:43:06 -0400 Subject: [PATCH 36/49] revert unneeded changes --- .../UnderlineNav/UnderlineNav.module.css | 85 -------------- .../deprecated/UnderlineNav/UnderlineNav.tsx | 110 ++++++++++++++---- 2 files changed, 86 insertions(+), 109 deletions(-) delete mode 100644 packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css deleted file mode 100644 index f4825c09be2..00000000000 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.module.css +++ /dev/null @@ -1,85 +0,0 @@ -.UnderlineNav { - display: flex; - justify-content: space-between; - border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted); - - &.UnderlineNav--right { - justify-content: flex-end; - } - - &.UnderlineNav--right .UnderlineNavItem { - margin-right: 0; - margin-left: var(--base-size-16); - } - - &.UnderlineNav--right .UnderlineNavActions { - flex: 1 1 auto; - } - - &.UnderlineNav--full { - display: block; - } - - .UnderlineNavBody { - display: flex; - /* stylelint-disable-next-line primer/spacing */ - margin-bottom: -1px; - } - - .UnderlineNavActions { - align-self: center; - } -} - -.UnderlineNavLink { - padding: var(--base-size-16) var(--base-size-8); - margin-right: var(--base-size-16); - font-size: var(--text-body-size-medium); - line-height: var(--text-title-lineHeight-large); - color: var(--fgColor-default); - text-align: center; - /* stylelint-disable-next-line primer/borders */ - border-bottom: 2px solid transparent; - text-decoration: none; - - /* fallback :focus state */ - &:focus:not(:disabled) { - box-shadow: none; - outline: 2px solid var(--fgColor-accent); - outline-offset: -8px; - - /* remove fallback :focus if :focus-visible is supported */ - &:not(:focus-visible) { - outline: solid 1px transparent; - } - } - - /* default focus state */ - &:focus-visible:not(:disabled) { - box-shadow: none; - outline: 2px solid var(--fgColor-accent); - outline-offset: -8px; - } -} - -.UnderlineNavLink:hover, -.UnderlineNavLink:focus { - color: var(--fgColor-default); - text-decoration: none; - border-bottom-color: var(--borderColor-muted); - transition: border-bottom-color 0.2s ease; -} - -.UnderlineNavLink:hover .UnderlineNavOcticon, -.UnderlineNavLink:focus .UnderlineNavOcticon { - color: var(--fgColor-muted); -} - -.UnderlineNavLink:where([data-selected]) { - color: var(--fgColor-default); - border-bottom-color: var(--underlineNav-borderColor-active); -} - -.UnderlineNavLink:where([data-selected]) .UnderlineNavOcticon { - color: var(--fgColor-default); -} diff --git a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx index 96e11bf7a53..2ecc4372336 100644 --- a/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/deprecated/UnderlineNav/UnderlineNav.tsx @@ -1,27 +1,63 @@ import {clsx} from 'clsx' import type {To} from 'history' -import React from 'react' -import classes from './UnderlineNav.module.css' +import type React from 'react' +import styled from 'styled-components' +import {get} from '../../constants' +import type {ComponentProps} from '../../utils/types' +import getGlobalFocusStyles from '../../internal/utils/getGlobalFocusStyles' + +const ITEM_CLASS = 'PRC-UnderlineNav-item' +const SELECTED_CLASS = 'PRC-selected' + +const UnderlineNavBase = styled.nav` + display: flex; + justify-content: space-between; + border-bottom: 1px solid ${get('colors.border.muted')}; + &.PRC-UnderlineNav--right { + justify-content: flex-end; + + .PRC-UnderlineNav-item { + margin-right: 0; + margin-left: ${get('space.3')}; + } + + .PRC-UnderlineNav-actions { + flex: 1 1 auto; + } + } + &.PRC-UnderlineNav--full { + display: block; + } + + .PRC-UnderlineNav-body { + display: flex; + margin-bottom: -1px; + } + + .PRC-UnderlineNav-actions { + align-self: center; + } +` export type UnderlineNavProps = { actions?: React.ReactNode align?: 'right' full?: boolean label?: string -} & React.ComponentProps<'nav'> - -function UnderlineNav({actions, className, align, children, full, label, ...rest}: UnderlineNavProps) { - const navClasses = clsx(className, classes.UnderlineNav, 'PRC-UnderlineNav', { - [classes['UnderlineNav--right']]: align === 'right', - [classes['UnderlineNav--full']]: full, - 'PRC-UnderlineNav--full': full, - 'PRC-UnderlineNav--right': align, - }) +} & ComponentProps + +function UnderlineNav({actions, className, align, children, full, label, theme, ...rest}: UnderlineNavProps) { + const classes = clsx( + className, + 'PRC-UnderlineNav', + align && `PRC-UnderlineNav--${align}`, + full && 'PRC-UnderlineNav--full', + ) return ( - + +
    {children}
    + {actions &&
    {actions}
    } +
    ) } @@ -30,19 +66,45 @@ type StyledUnderlineNavLinkProps = { selected?: boolean } -type UnderlineNavLinkProps = React.ComponentProps<'a'> & StyledUnderlineNavLinkProps +const UnderlineNavLink = styled.a.attrs(props => ({ + className: clsx(ITEM_CLASS, props.selected && SELECTED_CLASS, props.className), +}))` + padding: ${get('space.3')} ${get('space.2')}; + margin-right: ${get('space.3')}; + font-size: ${get('fontSizes.1')}; + line-height: ${get('lineHeights.default')}; + color: ${get('colors.fg.default')}; + text-align: center; + border-bottom: 2px solid transparent; + text-decoration: none; + + &:hover, + &:focus { + color: ${get('colors.fg.default')}; + text-decoration: none; + border-bottom-color: ${get('colors.neutral.muted')}; + transition: border-bottom-color 0.2s ease; + + .PRC-UnderlineNav-octicon { + color: ${get('colors.fg.muted')}; + } + } + + &.PRC-selected { + color: ${get('colors.fg.default')}; + border-bottom-color: ${get('colors.primer.border.active')}; + + .PRC-UnderlineNav-octicon { + color: ${get('colors.fg.default')}; + } + } -const UnderlineNavLink = React.forwardRef(function UnderlineNavLink( - {className, selected, ...props}, - forwardRef, -) { - const linkClasses = clsx(classes.UnderlineNavItem, className, classes.UnderlineNavLink) - return
    -}) + ${getGlobalFocusStyles('-8px')}; +` UnderlineNavLink.displayName = 'UnderlineNav.Link' -export type {UnderlineNavLinkProps} +export type UnderlineNavLinkProps = ComponentProps /** * @deprecated UnderlineNav is deprecated and will be replaced by the draft `UnderlineNav` in the next major release. See https://primer.style/react/drafts/UnderlineNav2 for more details. */ From 0c70cafb4ccc0d3d0c3d157af163c33d4964a3dd Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 12:44:59 -0400 Subject: [PATCH 37/49] revert unneeded changes --- .../ValidationAnimationContainer.module.css | 19 --------------- .../ValidationAnimationContainer.tsx | 24 ++++++++++++++++--- 2 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 packages/react/src/internal/components/ValidationAnimationContainer.module.css diff --git a/packages/react/src/internal/components/ValidationAnimationContainer.module.css b/packages/react/src/internal/components/ValidationAnimationContainer.module.css deleted file mode 100644 index 32cbe1cbce6..00000000000 --- a/packages/react/src/internal/components/ValidationAnimationContainer.module.css +++ /dev/null @@ -1,19 +0,0 @@ -.Animation:where([data-show]) { - animation: 170ms fadeIn cubic-bezier(0.44, 0.74, 0.36, 1); - - @media (prefers-reduced-motion) { - animation: none; - } -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(-100%); - } - - to { - opacity: 1; - transform: translateY(0); - } -} diff --git a/packages/react/src/internal/components/ValidationAnimationContainer.tsx b/packages/react/src/internal/components/ValidationAnimationContainer.tsx index cb502eec431..00e3f2cfa67 100644 --- a/packages/react/src/internal/components/ValidationAnimationContainer.tsx +++ b/packages/react/src/internal/components/ValidationAnimationContainer.tsx @@ -1,11 +1,29 @@ import type {HTMLProps} from 'react' import type React from 'react' import {useEffect, useState} from 'react' -import classes from './ValidationAnimationContainer.module.css' +import styled, {keyframes, css} from 'styled-components' interface Props extends HTMLProps { show?: boolean } + +const fadeIn = keyframes` + 0% { + opacity: 0; + transform: translateY(-100%); + } + 100% { + opacity: 1; + transform: translateY(0); + } + ` +// using easeOutQuint easing fn https://easings.net/#easeOutQuint +const AnimatedElement = styled.div` + animation: ${props => props.show && css`170ms ${fadeIn} cubic-bezier(0.44, 0.74, 0.36, 1);`}; + @media (prefers-reduced-motion) { + animation: none; + } +` const ValidationAnimationContainer: React.FC> = ({show, children}) => { const [shouldRender, setRender] = useState(show) @@ -19,9 +37,9 @@ const ValidationAnimationContainer: React.FC> = ( return shouldRender ? (
    -
    + {children} -
    +
    ) : null } From 3660b6389d3a5ef5556b837846aa1780855eb2b4 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 12:46:22 -0400 Subject: [PATCH 38/49] revert changes --- packages/react/src/Tooltip/Tooltip.module.css | 161 ------------- packages/react/src/Tooltip/Tooltip.tsx | 211 ++++++++++++++++-- 2 files changed, 189 insertions(+), 183 deletions(-) delete mode 100644 packages/react/src/Tooltip/Tooltip.module.css diff --git a/packages/react/src/Tooltip/Tooltip.module.css b/packages/react/src/Tooltip/Tooltip.module.css deleted file mode 100644 index 4a7266dc6d9..00000000000 --- a/packages/react/src/Tooltip/Tooltip.module.css +++ /dev/null @@ -1,161 +0,0 @@ -/* stylelint-disable primer/typography, primer/borders, selector-class-pattern */ - -.Tooltip { - position: relative; - display: inline-block; -} - -.Tooltip::after { - position: absolute; - z-index: 1000000; - display: none; - /* stylelint-disable-next-line primer/spacing */ - padding: 0.5em 0.75em; - font: normal normal var(--text-body-size-small) / var(--text-body-lineHeight-small) var(--fontStack-system); - -webkit-font-smoothing: subpixel-antialiased; - color: var(--tooltip-fgColor, var(--fgColor-onEmphasis)); - text-align: center; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-wrap: break-word; - white-space: pre; - pointer-events: none; - content: attr(aria-label); - background: var(--tooltip-bgColor, var(--bgColor-emphasis)); - border-radius: var(--borderRadius-medium); - opacity: 0; -} - -/* delay animation for tooltip */ -@keyframes tooltip-appear { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -.Tooltip:hover::after, -.Tooltip:active::after, -.Tooltip:focus::after, -.Tooltip:focus-within::after { - display: inline-block; - text-decoration: none; - animation-name: tooltip-appear; - animation-duration: 0.1s; - animation-fill-mode: forwards; - animation-timing-function: ease-in; - animation-delay: 0s; -} - -.Tooltip--noDelay:hover::after, -.Tooltip--noDelay:active::after, -.Tooltip--noDelay:focus::after, -.Tooltip--noDelay:focus-within::after { - animation-delay: 0s; -} - -.Tooltip--multiline:hover::after, -.Tooltip--multiline:active::after, -.Tooltip--multiline:focus::after, -.Tooltip--multiline:focus-within::after { - display: table-cell; -} - -/* Tooltipped south */ -.Tooltip--s::after, -.Tooltip--se::after, -.Tooltip--sw::after { - top: 100%; - right: 50%; - /* stylelint-disable-next-line primer/spacing */ - margin-top: 6px; -} - -.Tooltip--se::after { - right: auto; - left: 50%; - margin-left: calc(-1 * var(--base-size-16)); -} - -.Tooltip--sw::after { - margin-right: calc(-1 * var(--base-size-16)); -} - -/* Tooltips above the object */ -.Tooltip--n::after, -.Tooltip--ne::after, -.Tooltip--nw::after { - right: 50%; - bottom: 100%; - /* stylelint-disable-next-line primer/spacing */ - margin-bottom: 6px; -} - -.Tooltip--ne::after { - right: auto; - left: 50%; - margin-left: calc(-1 * var(--base-size-16)); -} - -.Tooltip--nw::after { - margin-right: calc(-1 * var(--base-size-16)); -} - -/* Move the tooltip body to the center of the object. */ -.Tooltip--s::after, -.Tooltip--n::after { - transform: translateX(50%); -} - -/* Tooltipped to the left */ -.Tooltip--w::after { - right: 100%; - bottom: 50%; - /* stylelint-disable-next-line primer/spacing */ - margin-right: 6px; - transform: translateY(50%); -} - -/* tooltipped to the right */ -.Tooltip--e::after { - bottom: 50%; - left: 100%; - /* stylelint-disable-next-line primer/spacing */ - margin-left: 6px; - transform: translateY(50%); -} - -.Tooltip--multiline::after { - width: max-content; - max-width: 250px; - word-wrap: break-word; - white-space: pre-line; - border-collapse: separate; -} - -.Tooltip--multiline.Tooltip--s::after, -.Tooltip--multiline.Tooltip--n::after { - right: auto; - left: 50%; - transform: translateX(-50%); -} - -.Tooltip--multiline.Tooltip--w::after, -.Tooltip--multiline.Tooltip--e::after { - right: 100%; -} - -.Tooltip--alignRight::after { - right: 0; - margin-right: 0; -} - -.Tooltip--alignLeft::after { - left: 0; - margin-left: 0; -} diff --git a/packages/react/src/Tooltip/Tooltip.tsx b/packages/react/src/Tooltip/Tooltip.tsx index 2dec29b89d8..1701bb7528f 100644 --- a/packages/react/src/Tooltip/Tooltip.tsx +++ b/packages/react/src/Tooltip/Tooltip.tsx @@ -1,11 +1,187 @@ import {clsx} from 'clsx' import React, {useMemo} from 'react' +import styled from 'styled-components' +import {get} from '../constants' +import type {ComponentProps} from '../utils/types' import {useId} from '../hooks' -import classes from './Tooltip.module.css' -import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' /* Tooltip v1 */ +const TooltipBase = styled.span` + position: relative; + display: inline-block; + + &::after { + position: absolute; + z-index: 1000000; + display: none; + padding: 0.5em 0.75em; + font: normal normal 11px/1.5 ${get('fonts.normal')}; + -webkit-font-smoothing: subpixel-antialiased; + color: var(--tooltip-fgColor, ${get('colors.fg.onEmphasis')}); + text-align: center; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-wrap: break-word; + white-space: pre; + pointer-events: none; + content: attr(aria-label); + background: var(--tooltip-bgColor, ${get('colors.neutral.emphasisPlus')}); + border-radius: ${get('radii.2')}; + opacity: 0; + } + + // delay animation for tooltip + @keyframes tooltip-appear { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + &:hover, + &:active, + &:focus, + &:focus-within { + &::after { + display: inline-block; + text-decoration: none; + animation-name: tooltip-appear; + animation-duration: 0.1s; + animation-fill-mode: forwards; + animation-timing-function: ease-in; + animation-delay: 0s; + } + } + + &.tooltipped-no-delay:hover, + &.tooltipped-no-delay:active, + &.tooltipped-no-delay:focus, + &.tooltipped-no-delay:focus-within { + &::after { + animation-delay: 0s; + } + } + + &.tooltipped-multiline:hover, + &.tooltipped-multiline:active, + &.tooltipped-multiline:focus, + &.tooltipped-multiline:focus-within { + &::after { + display: table-cell; + } + } + + // Tooltipped south + &.tooltipped-s, + &.tooltipped-se, + &.tooltipped-sw { + &::after { + top: 100%; + right: 50%; + margin-top: 6px; + } + } + + &.tooltipped-se { + &::after { + right: auto; + left: 50%; + margin-left: -${get('space.3')}; + } + } + + &.tooltipped-sw::after { + margin-right: -${get('space.3')}; + } + + // Tooltips above the object + &.tooltipped-n, + &.tooltipped-ne, + &.tooltipped-nw { + &::after { + right: 50%; + bottom: 100%; + margin-bottom: 6px; + } + } + + &.tooltipped-ne { + &::after { + right: auto; + left: 50%; + margin-left: -${get('space.3')}; + } + } + + &.tooltipped-nw::after { + margin-right: -${get('space.3')}; + } + + // Move the tooltip body to the center of the object. + &.tooltipped-s::after, + &.tooltipped-n::after { + transform: translateX(50%); + } + + // Tooltipped to the left + &.tooltipped-w { + &::after { + right: 100%; + bottom: 50%; + margin-right: 6px; + transform: translateY(50%); + } + } + + // tooltipped to the right + &.tooltipped-e { + &::after { + bottom: 50%; + left: 100%; + margin-left: 6px; + transform: translateY(50%); + } + } + + &.tooltipped-multiline { + &::after { + width: max-content; + max-width: 250px; + word-wrap: break-word; + white-space: pre-line; + border-collapse: separate; + } + + &.tooltipped-s::after, + &.tooltipped-n::after { + right: auto; + left: 50%; + transform: translateX(-50%); + } + + &.tooltipped-w::after, + &.tooltipped-e::after { + right: 100%; + } + } + + &.tooltipped-align-right-2::after { + right: 0; + margin-right: 0; + } + + &.tooltipped-align-left-2::after { + left: 0; + margin-left: 0; + } +` + /** * @deprecated */ @@ -15,41 +191,32 @@ export type TooltipProps = { noDelay?: boolean align?: 'left' | 'right' wrap?: boolean -} & React.ComponentProps<'span'> +} & ComponentProps export const TooltipContext = React.createContext<{tooltipId?: string}>({}) /** * @deprecated */ -const Tooltip = React.forwardRef(function Tooltip( - {as: Component = 'span', direction = 'n', children, className, text, noDelay, align, wrap, id, ...rest}, - ref, -) { +function Tooltip({direction = 'n', children, className, text, noDelay, align, wrap, id, ...rest}: TooltipProps) { const tooltipId = useId(id) - const tooltipClasses = clsx(className, classes.Tooltip, classes[`Tooltip--${direction}`], { - [classes[`Tooltip--align${align === 'left' ? 'Left' : 'Right'}`]]: align, - [classes['Tooltip--noDelay']]: noDelay, - [classes['Tooltip--multiline']]: wrap, - // maintaining feature parity with old classes - [`tooltipped-${direction}`]: true, - [`tooltipped-align-${align === 'left' ? 'left' : 'right'}-2`]: align, - 'tooltipped-no-delay': noDelay, - 'tooltipped-multiline': wrap, - }) + const classes = clsx( + className, + `tooltipped-${direction}`, + align && `tooltipped-align-${align}-2`, + noDelay && 'tooltipped-no-delay', + wrap && 'tooltipped-multiline', + ) const value = useMemo(() => ({tooltipId}), [tooltipId]) return ( // This provider is used to check if an icon button is wrapped with tooltip or not. - + {children} - + ) -}) as PolymorphicForwardRefComponent<'span', TooltipProps> & { - alignments: string[] - directions: string[] } Tooltip.alignments = ['left', 'right'] From 1f90ea081202f9099f73828f990017164f145a18 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 12:48:05 -0400 Subject: [PATCH 39/49] revert changes --- .../src/LabelGroup/LabelGroup.module.css | 38 ---------- packages/react/src/LabelGroup/LabelGroup.tsx | 73 +++++++++++++++---- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/packages/react/src/LabelGroup/LabelGroup.module.css b/packages/react/src/LabelGroup/LabelGroup.module.css index 08cdc11fe77..4009aae37e8 100644 --- a/packages/react/src/LabelGroup/LabelGroup.module.css +++ b/packages/react/src/LabelGroup/LabelGroup.module.css @@ -1,41 +1,3 @@ -.Container { - display: flex; - flex-wrap: nowrap; - gap: var(--base-size-4); - /* stylelint-disable-next-line primer/typography */ - line-height: 1; - max-width: 100%; - overflow: hidden; - - &:where([data-overflow='inline']) { - flex-wrap: wrap; - } - - &:where([data-list]) { - padding-inline-start: 0; - margin-block-start: 0; - margin-block-end: 0; - list-style-type: none; - } -} - -.ItemWrapper { - display: flex; - align-items: center; - /* This min-height matches the height of the expand/collapse button. - Without it, the labels/tokens will do a slight layout shift when expanded. - This is because the height of the first row will match the token sizes, - but the height of the second row will be the height of the collapse button. - */ - min-height: 28px; -} - -.ItemWrapper--hidden { - order: 9999; - pointer-events: none; - visibility: hidden; -} - .OverlayContainer { align-items: flex-start; display: flex; diff --git a/packages/react/src/LabelGroup/LabelGroup.tsx b/packages/react/src/LabelGroup/LabelGroup.tsx index dac8b77a9fb..a5babffb2e4 100644 --- a/packages/react/src/LabelGroup/LabelGroup.tsx +++ b/packages/react/src/LabelGroup/LabelGroup.tsx @@ -1,10 +1,12 @@ import React from 'react' +import styled from 'styled-components' import {XIcon} from '@primer/octicons-react' import {getFocusableChild} from '@primer/behaviors/utils' +import {get} from '../constants' import VisuallyHidden from '../_VisuallyHidden' import {AnchoredOverlay} from '../AnchoredOverlay' import {Button, IconButton} from '../Button' -import {clsx} from 'clsx' +import {useTheme} from '../ThemeProvider' import classes from './LabelGroup.module.css' export type LabelGroupProps = { @@ -16,6 +18,45 @@ export type LabelGroupProps = { visibleChildCount?: 'auto' | number className?: string } + +const StyledLabelGroupContainer = styled.div` + display: flex; + flex-wrap: nowrap; + gap: ${get('space.1')}; + line-height: 1; + max-width: 100%; + overflow: hidden; + + &[data-overflow='inline'] { + flex-wrap: wrap; + } + + &[data-list] { + padding-inline-start: 0; + margin-block-start: 0; + margin-block-end: 0; + list-style-type: none; + } +` + +const ItemWrapper = styled.div` + display: flex; + align-items: center; + + /* This min-height matches the height of the expand/collapse button. + Without it, the labels/tokens will do a slight layout shift when expanded. + This is because the height of the first row will match the token sizes, + but the height of the second row will be the height of the collapse button. + */ + min-height: 28px; + + &.ItemWrapper--hidden { + order: 9999; + pointer-events: none; + visibility: hidden; + } +` + // Calculates the width of the overlay to cover the labels/tokens and the expand button. const getOverlayWidth = ( buttonClientRect: DOMRect, @@ -110,7 +151,7 @@ const LabelGroup: React.FC> = ({ children, visibleChildCount, overflowStyle = 'overlay', - as: Component = 'ul', + as = 'ul', className, }) => { const containerRef = React.useRef(null) @@ -130,7 +171,9 @@ const LabelGroup: React.FC> = ({ toJSON: () => undefined, }) - const overlayPaddingPx = 8 // equivalent to var(--base-size-8) + const {theme} = useTheme() + + const overlayPaddingPx = parseInt(get('space.2')(theme), 10) const hiddenItemIds = Object.keys(visibilityMap).filter(key => !visibilityMap[key]) @@ -279,30 +322,28 @@ const LabelGroup: React.FC> = ({ } }, [overflowStyle, isOverflowShown]) - const isList = Component === 'ul' || Component === 'ol' + const isList = as === 'ul' || as === 'ol' const ToggleWrapper = isList ? 'li' : React.Fragment - const ItemWrapperComponent = isList ? 'li' : 'span' - // If truncation is enabled, we need to render based on truncation logic. return visibleChildCount ? ( - {React.Children.map(children, (child, index) => ( - {child} - + ))} {overflowStyle === 'inline' ? ( @@ -330,15 +371,15 @@ const LabelGroup: React.FC> = ({ )} - + ) : ( - + {isList ? React.Children.map(children, (child, index) => { return
  • {child}
  • }) : children} -
    + ) } From 4af66c17cb0076c41f2949d15c2b4d78d250664d Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 12:49:22 -0400 Subject: [PATCH 40/49] revert changes --- .changeset/chilled-spoons-roll.md | 5 ----- packages/react/src/UnderlineNav/UnderlineNav.tsx | 11 +++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) delete mode 100644 .changeset/chilled-spoons-roll.md diff --git a/.changeset/chilled-spoons-roll.md b/.changeset/chilled-spoons-roll.md deleted file mode 100644 index 9eeb87d668a..00000000000 --- a/.changeset/chilled-spoons-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@primer/react": major ---- - -Chore/remove styled. components diff --git a/packages/react/src/UnderlineNav/UnderlineNav.tsx b/packages/react/src/UnderlineNav/UnderlineNav.tsx index ba9fdc6c540..0c8a417773d 100644 --- a/packages/react/src/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNav.tsx @@ -8,6 +8,7 @@ import type {ChildWidthArray, ResponsiveProps, ChildSize} from './types' import VisuallyHidden from '../_VisuallyHidden' import {moreBtnStyles, getDividerStyle, menuStyles, menuItemStyles, baseMenuStyles, baseMenuMinWidth} from './styles' import {UnderlineItemList, UnderlineWrapper, LoadingCounter, GAP} from '../internal/components/UnderlineTabbedInterface' +import styled from 'styled-components' import {Button} from '../Button' import {TriangleDownIcon} from '@primer/octicons-react' import {useOnEscapePress} from '../hooks/useOnEscapePress' @@ -40,6 +41,12 @@ export const MORE_BTN_WIDTH = 86 // The height is needed to make sure we don't have a layout shift when the more button is the only item in the nav. const MORE_BTN_HEIGHT = 45 +export const MoreMenuListItem = styled.li` + display: flex; + align-items: center; + height: ${MORE_BTN_HEIGHT}px; +` + const overflowEffect = ( navWidth: number, moreMenuWidth: number, @@ -303,7 +310,7 @@ export const UnderlineNav = forwardRef( {listItems} {menuItems.length > 0 && ( -
  • + {!onlyMenuVisible &&
    }
  • + )}
    From f11be387a82d9a6c67077f682656f3b0ec303053 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 12:53:27 -0400 Subject: [PATCH 41/49] revert unneeded changes --- .../PageLayout.examples.stories.tsx | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/packages/react/src/PageLayout/PageLayout.examples.stories.tsx b/packages/react/src/PageLayout/PageLayout.examples.stories.tsx index 675119d4ac5..4605bb722e6 100644 --- a/packages/react/src/PageLayout/PageLayout.examples.stories.tsx +++ b/packages/react/src/PageLayout/PageLayout.examples.stories.tsx @@ -677,6 +677,201 @@ export const FiltersBottomSheetTwoLevels: StoryFn = () => { FiltersBottomSheetTwoLevels.storyName = 'Filters w/ 2 levels (btm sheet on narrow)' +// +// TODO: uncomment this story if we decide we want to allow this pattern for separate sets of filters +// +// export const ResponsiveNavCombo2: Story = () => { +// const [currentHash, setCurrentHash] = React.useState(window.location.hash) +// const [isOpen, setIsOpen] = React.useState(false) + +// const categories = [ +// { +// hash: '#fruits', +// name: 'Fruits', +// }, +// { +// hash: '#vegetables', +// name: 'Vegetables', +// }, +// { +// hash: '#animals', +// name: 'Animals', +// }, +// ] +// const selectedCategory = currentHash ? categories.find(option => currentHash.includes(option.hash)) : categories[0] + +// const buttonRef = React.useRef(null) + +// const onDialogClose = React.useCallback(() => setIsOpen(false), []) +// const getFilteredItems = (category: keyof typeof filterableItems) => +// filterableItems[category].filter(item => +// currentHash.includes('filter') ? currentHash.includes(`filter=${item.color}`) : true, +// ) + +// // Fake routing to mimic the behavior of a single page application +// React.useEffect(() => { +// const handleHashChange = () => { +// setCurrentHash(window.location.hash) +// } +// window.addEventListener('hashchange', handleHashChange) +// return () => { +// window.removeEventListener('hashchange', handleHashChange) +// } +// }, []) + +// return ( +// <> +// {/* +// Filters only work when you open the canvas in a new tab without the Storybook chrome. +// */} +// +// +// +// +// +// +// ) +// } + // ResponsiveNavCombo2.storyName = 'Responsive nav combo - action menu + btm sheet' ///////////////////////////////////////////////////////////////// From f542c92650aa5b361dd16a6939693df1fdf9ca9f Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 6 Oct 2025 13:12:11 -0700 Subject: [PATCH 42/49] fix visual regressions in Actions story --- .../react/src/deprecated/ActionList/Divider.tsx | 2 +- .../src/deprecated/ActionList/Item.module.css | 16 +++++++++++++++- .../react/src/deprecated/ActionList/Item.tsx | 8 ++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Divider.tsx b/packages/react/src/deprecated/ActionList/Divider.tsx index 8119f96eb9c..2679e7b2da4 100644 --- a/packages/react/src/deprecated/ActionList/Divider.tsx +++ b/packages/react/src/deprecated/ActionList/Divider.tsx @@ -1,7 +1,7 @@ import classes from './Divider.module.css' /** - * Visually separates `Item`s or `Group`s in an `ActionList`. + * Visually separates `Items or `Groups in an `ActionList`. */ export function Divider(): JSX.Element { return
    diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css index 56c0848da9a..42ae9fb0b6d 100644 --- a/packages/react/src/deprecated/ActionList/Item.module.css +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -204,7 +204,7 @@ .DescriptionContainer { color: var(--fgColor-muted); font-size: var(--text-body-size-small); - line-height: var(--text-body-lineHeight-small); + line-height: var(--text-caption-lineHeight); min-width: 0; flex-grow: 1; /* stylelint-disable-next-line primer/spacing */ @@ -236,3 +236,17 @@ .MultiSelectIcon:where([data-selected]) path { opacity: 1; } + +.Item[data-divider]:not(:first-of-type):not([data-component='ActionList.Divider'] + .Item):not( + [data-component='ActionList.Header'] + .Item + ) + .DividedContent::before { + border-top-width: var(--borderWidth-thin); +} + +.Item[data-divider]:not(:first-of-type):not([data-component='ActionList.Divider'] + .Item):not( + [data-component='ActionList.Header'] + .Item + ) { + /* stylelint-disable-next-line primer/spacing */ + margin-top: 1px; +} diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index b753f66de3d..bf9711409aa 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -10,6 +10,7 @@ import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../../ import type {AriaRole} from '../../utils/types' import classes from './Item.module.css' +import {clsx} from 'clsx' /** * Contract for props passed to the `Item` component. @@ -137,6 +138,7 @@ export const Item = React.forwardRef((itemProps, ref) => { children, onClick, id, + className, ...props } = itemProps @@ -185,6 +187,12 @@ export const Item = React.forwardRef((itemProps, ref) => { data-id={id} onKeyPress={keyPressHandler} onClick={clickHandler} + className={clsx( + classes.Item, + variant === 'danger' && classes['Item--danger'], + disabled && classes['Item--disabled'], + className, + )} > {!!selected === selected && (
    From 53792e0badb7bbdca443e93bae0f0806834bfe2c Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 6 Oct 2025 13:39:34 -0700 Subject: [PATCH 43/49] fix visual regressions in Complex List stories --- .../stories/deprecated/ActionList.stories.tsx | 8 ++++---- .../deprecated/ActionListStories.module.css | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/react/src/stories/deprecated/ActionList.stories.tsx b/packages/react/src/stories/deprecated/ActionList.stories.tsx index c6e0e70212c..06586452d06 100644 --- a/packages/react/src/stories/deprecated/ActionList.stories.tsx +++ b/packages/react/src/stories/deprecated/ActionList.stories.tsx @@ -163,7 +163,7 @@ export function ComplexListInsetVariantStory(): JSX.Element { {groupId: '0'}, {groupId: '1', header: {title: 'Live query', variant: 'filled', 'aria-level': 3}}, {groupId: '2', header: {title: 'Layout', variant: 'subtle', 'aria-level': 3}, showItemDividers: true}, - {groupId: '3', renderItem: props => }, + {groupId: '3', renderItem: props => }, { groupId: '4', renderItem: ({leadingVisual: LeadingVisual, ...props}) => ( @@ -192,7 +192,7 @@ export function ComplexListInsetVariantStory(): JSX.Element { leadingVisual: SearchIcon, text: 'repo:github/memex,github/github', groupId: '1', - renderItem: props => , + renderItem: props => , }, { leadingVisual: NoteIcon, @@ -235,7 +235,7 @@ export function ComplexListFullVariantStory(): JSX.Element { {groupId: '0'}, {groupId: '1', header: {title: 'Live query', variant: 'filled', 'aria-level': 3}}, {groupId: '2', header: {title: 'Layout', variant: 'subtle', 'aria-level': 3}}, - {groupId: '3', renderItem: props => }, + {groupId: '3', renderItem: props => }, { groupId: '4', renderItem: ({leadingVisual: LeadingVisual, ...props}) => ( @@ -258,7 +258,7 @@ export function ComplexListFullVariantStory(): JSX.Element { leadingVisual: SearchIcon, text: 'repo:github/memex,github/github', groupId: '1', - renderItem: props => , + renderItem: props => , }, { leadingVisual: NoteIcon, diff --git a/packages/react/src/stories/deprecated/ActionListStories.module.css b/packages/react/src/stories/deprecated/ActionListStories.module.css index d1df4fdf176..b8dc0fe5aa4 100644 --- a/packages/react/src/stories/deprecated/ActionListStories.module.css +++ b/packages/react/src/stories/deprecated/ActionListStories.module.css @@ -23,3 +23,19 @@ /* stylelint-disable-next-line color-named */ color: white; } + +.ActionListEmphasis { + /* stylelint-disable-next-line primer/typography */ + font-weight: 700; +} + +.ActionListAccent { + /* stylelint-disable-next-line color-named */ + color: rebeccapurple; +} + +.Item[data-is-active-descendant] .ActionListAccent, +.Item:focus .ActionListAccent, +.Item:active .ActionListAccent { + color: var(--fgColor-onEmphasis); +} From 96995d18b18d95d7e633a033727e7e1daa3c82ef Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 6 Oct 2025 13:53:35 -0700 Subject: [PATCH 44/49] fix regression in Item.tsx that was visible in Multi Select story --- packages/react/src/deprecated/ActionList/Item.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index bf9711409aa..176491d8f7f 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -195,14 +195,9 @@ export const Item = React.forwardRef((itemProps, ref) => { )} > {!!selected === selected && ( -
    +
    {selectionVariant === 'multiple' ? ( <> - {/** - * we use a svg instead of an input because there should not - * be an interactive element inside an option - * svg copied from primer/css - */} Date: Mon, 6 Oct 2025 14:19:56 -0700 Subject: [PATCH 45/49] hardcode line-height for consistency --- packages/react/src/deprecated/ActionList/Item.module.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css index 42ae9fb0b6d..39500c1c5fa 100644 --- a/packages/react/src/deprecated/ActionList/Item.module.css +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -204,7 +204,8 @@ .DescriptionContainer { color: var(--fgColor-muted); font-size: var(--text-body-size-small); - line-height: var(--text-caption-lineHeight); + /* stylelint-disable-next-line primer/typography */ + line-height: 16px; min-width: 0; flex-grow: 1; /* stylelint-disable-next-line primer/spacing */ From 7864e98af4abf5fd5c6c1b1648e9a6d43ab512a9 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 6 Oct 2025 14:39:13 -0700 Subject: [PATCH 46/49] fix border color discrepancy --- packages/react/src/deprecated/ActionList/Header.module.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react/src/deprecated/ActionList/Header.module.css b/packages/react/src/deprecated/ActionList/Header.module.css index 8108123d67b..f9cbf6c2bee 100644 --- a/packages/react/src/deprecated/ActionList/Header.module.css +++ b/packages/react/src/deprecated/ActionList/Header.module.css @@ -13,8 +13,10 @@ &:where([data-filled]) { background: var(--bgColor-muted); margin: var(--base-size-8) 0; - border-top: var(--borderWidth-thin) solid var(--borderColor-default); - border-bottom: var(--borderWidth-thin) solid var(--borderColor-default); + /* stylelint-disable-next-line primer/colors */ + border-top: var(--borderWidth-thin) solid var(--bgColor-neutral-muted); + /* stylelint-disable-next-line primer/colors */ + border-bottom: var(--borderWidth-thin) solid var(--bgColor-neutral-muted); &:first-child { margin-top: 0; From 80915d64ac93f0199f3c06166c2399268829be8d Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 6 Oct 2025 16:17:37 -0700 Subject: [PATCH 47/49] fix lint problem --- packages/styled-react/src/deprecated.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index 680ceb4ce31..ebeda158358 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -7,4 +7,4 @@ export { type ActionListProps, type ActionListItemProps, type ActionListGroupProps, -} from './components/deprecated/ActionList' \ No newline at end of file +} from './components/deprecated/ActionList' From 1627f968a272c0c90fc1dae804e1f0d7746d31f4 Mon Sep 17 00:00:00 2001 From: llastflowers Date: Mon, 6 Oct 2025 16:23:28 -0700 Subject: [PATCH 48/49] update merge conflict fix --- packages/styled-react/src/deprecated.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index ebeda158358..a14631cb41b 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -1,6 +1,6 @@ export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/deprecated/TabNav' export {Dialog, type DialogProps, type DialogHeaderProps} from './components/deprecated/DialogV1' -export {Octicon} from '@primer/react/deprecated' +export {Octicon, type OcticonProps} from './components/Octicon' export {Tooltip, type TooltipProps} from './components/deprecated/Tooltip' export { ActionList, From 31fac89722e693e018cf7537747cfc1521faf438 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Mon, 6 Oct 2025 20:03:40 -0400 Subject: [PATCH 49/49] move octicons to deprecated --- .../styled-react/src/components/{ => deprecated}/Octicon.tsx | 2 +- packages/styled-react/src/deprecated.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/styled-react/src/components/{ => deprecated}/Octicon.tsx (95%) diff --git a/packages/styled-react/src/components/Octicon.tsx b/packages/styled-react/src/components/deprecated/Octicon.tsx similarity index 95% rename from packages/styled-react/src/components/Octicon.tsx rename to packages/styled-react/src/components/deprecated/Octicon.tsx index 3a7422824cd..beb019beb83 100644 --- a/packages/styled-react/src/components/Octicon.tsx +++ b/packages/styled-react/src/components/deprecated/Octicon.tsx @@ -1,6 +1,6 @@ import {Octicon as PrimerOcticon, type OcticonProps as PrimerOcticonProps} from '@primer/react/deprecated' import styled from 'styled-components' -import {type SxProp, sx} from '../sx' +import {type SxProp, sx} from '../../sx' import {forwardRef} from 'react' /** diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index a14631cb41b..0d06314dd35 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -1,6 +1,6 @@ export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/deprecated/TabNav' export {Dialog, type DialogProps, type DialogHeaderProps} from './components/deprecated/DialogV1' -export {Octicon, type OcticonProps} from './components/Octicon' +export {Octicon, type OcticonProps} from './components/deprecated/Octicon' export {Tooltip, type TooltipProps} from './components/deprecated/Tooltip' export { ActionList,