diff --git a/.changeset/strong-mangos-rest.md b/.changeset/strong-mangos-rest.md new file mode 100644 index 00000000000..03b2ff7b98d --- /dev/null +++ b/.changeset/strong-mangos-rest.md @@ -0,0 +1,6 @@ +--- +"@primer/react": minor +--- + +Remove the feature flag for `primer_react_segmented_control_tooltip` and GA tooltip by default behavior. +- Ensure that when `disabled` is applied, the tooltip is still triggered. diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-colorblind-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-colorblind-linux.png new file mode 100644 index 00000000000..0b4853a52cf Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-dimmed-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-dimmed-linux.png new file mode 100644 index 00000000000..b9420c333d4 Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-high-contrast-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-high-contrast-linux.png new file mode 100644 index 00000000000..b8ffa8afa83 Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-linux.png new file mode 100644 index 00000000000..0b4853a52cf Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-tritanopia-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-tritanopia-linux.png new file mode 100644 index 00000000000..0b4853a52cf Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-colorblind-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-colorblind-linux.png new file mode 100644 index 00000000000..c292cf7a970 Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-high-contrast-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-high-contrast-linux.png new file mode 100644 index 00000000000..756e738f993 Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-linux.png new file mode 100644 index 00000000000..c292cf7a970 Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-linux.png differ diff --git a/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-tritanopia-linux.png b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-tritanopia-linux.png new file mode 100644 index 00000000000..c292cf7a970 Binary files /dev/null and b/.playwright/snapshots/components/SegmentedControl.test.ts-snapshots/SegmentedControl-With-Disabled-Buttons-light-tritanopia-linux.png differ diff --git a/e2e/components/SegmentedControl.test.ts b/e2e/components/SegmentedControl.test.ts index 24e09749352..beb82f849cb 100644 --- a/e2e/components/SegmentedControl.test.ts +++ b/e2e/components/SegmentedControl.test.ts @@ -64,6 +64,10 @@ const stories = [ title: 'Dev: With Css', id: 'components-segmentedcontrol-dev--with-css', }, + { + title: 'With Disabled Buttons', + id: 'components-segmentedcontrol-examples--with-disabled-buttons', + }, ] as const test.describe('SegmentedControl', () => { diff --git a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts index f60c9687834..a7f8d2efe39 100644 --- a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts +++ b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts @@ -4,7 +4,6 @@ export const DefaultFeatureFlags = FeatureFlagScope.create({ primer_react_action_list_item_as_button: false, primer_react_breadcrumbs_overflow_menu: false, primer_react_overlay_overflow: false, - primer_react_segmented_control_tooltip: false, primer_react_select_panel_fullscreen_on_narrow: false, primer_react_select_panel_order_selected_at_top: false, primer_react_select_panel_remove_active_descendant: false, diff --git a/packages/react/src/SegmentedControl/SegmentedControl.dev.stories.tsx b/packages/react/src/SegmentedControl/SegmentedControl.dev.stories.tsx index b02a67139e6..ca0574b42d4 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.dev.stories.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControl.dev.stories.tsx @@ -13,6 +13,82 @@ export default { parameters: {controls: {exclude: excludedControlKeys}}, } as Meta +export const WithAriaDisabled = () => { + const handleOnClick = () => { + alert('Button clicked!') + } + + return ( + + + Preview + + + Raw + + + Blame + + + ) +} + +export const WithDisabled = () => { + const handleOnClick = () => { + alert('Button clicked!') + } + + return ( + + + Preview + + + Raw + + + Blame + + + ) +} + export const WithCss = () => ( + +export const WithDisabledButtons = () => ( + + + Preview + + + Raw + + + Blame + + +) diff --git a/packages/react/src/SegmentedControl/SegmentedControl.module.css b/packages/react/src/SegmentedControl/SegmentedControl.module.css index 77270b6d227..22b80ae8f33 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.module.css +++ b/packages/react/src/SegmentedControl/SegmentedControl.module.css @@ -259,6 +259,17 @@ width: 0; } + &[aria-disabled='true']:not([aria-current='true']) { + cursor: not-allowed; + color: var(--fgColor-disabled); + background-color: transparent; + + & svg { + fill: var(--fgColor-disabled); + color: var(--fgColor-disabled); + } + } + @media (pointer: coarse) { &::before { position: absolute; @@ -313,7 +324,7 @@ } } -.Button:not([aria-current='true']) { +.Button:not([aria-current='true'], [aria-disabled='true']) { &:hover .Content { background-color: var(--controlTrack-bgColor-hover); } diff --git a/packages/react/src/SegmentedControl/SegmentedControl.test.tsx b/packages/react/src/SegmentedControl/SegmentedControl.test.tsx index 284cc9a1906..fd7aa2e1966 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.test.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControl.test.tsx @@ -3,7 +3,6 @@ import {EyeIcon, FileCodeIcon, PeopleIcon} from '@primer/octicons-react' import userEvent from '@testing-library/user-event' import {describe, expect, it, vi} from 'vitest' import BaseStyles from '../BaseStyles' -import {FeatureFlags} from '../FeatureFlags' import {SegmentedControl} from '../SegmentedControl' const segmentData = [ @@ -142,19 +141,13 @@ describe('SegmentedControl', () => { } }) - it('renders icon button with tooltip as label when feature flag is enabled', () => { + it('renders icon button with tooltip as label', () => { const {getByRole, getByText} = render( - - - {segmentData.map(({label, icon}) => ( - - ))} - - , + + {segmentData.map(({label, icon}) => ( + + ))} + , ) for (const datum of segmentData) { @@ -165,41 +158,20 @@ describe('SegmentedControl', () => { } }) - it('renders icon button with tooltip description when feature flag is enabled', () => { + it('renders icon button with tooltip description', () => { const {getByRole, getByText} = render( - - - {segmentData.map(({label, icon, description}) => ( - - ))} - - , - ) - - for (const datum of segmentData) { - const labelledButton = getByRole('button', {name: datum.label}) - const tooltipElement = getByText(datum.description) - expect(labelledButton).toHaveAttribute('aria-describedby', tooltipElement.id) - expect(labelledButton).toHaveAccessibleName(datum.label) - expect(labelledButton).toHaveAttribute('aria-label', datum.label) - } - }) - - it('renders icon button with aria-label and no tooltip', () => { - const {getByRole} = render( - {segmentData.map(({label, icon}) => ( - + {segmentData.map(({label, icon, description}) => ( + ))} , ) for (const datum of segmentData) { const labelledButton = getByRole('button', {name: datum.label}) + const tooltipElement = getByText(datum.description) + expect(labelledButton).toHaveAttribute('aria-describedby', tooltipElement.id) + expect(labelledButton).toHaveAccessibleName(datum.label) expect(labelledButton).toHaveAttribute('aria-label', datum.label) } }) diff --git a/packages/react/src/SegmentedControl/SegmentedControl.tsx b/packages/react/src/SegmentedControl/SegmentedControl.tsx index 201d7c601fa..0963b8bccf8 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControl.tsx @@ -177,13 +177,25 @@ const Root: React.FC> = ({ const sharedChildProps = { onClick: onChange ? (event: React.MouseEvent) => { - onChange(index) - isUncontrolled && setSelectedIndexInternalState(index) - child.props.onClick && child.props.onClick(event) + const isDisabled = + child.props.disabled === true || + child.props['aria-disabled'] === 'true' || + child.props['aria-disabled'] === true + if (!isDisabled) { + onChange(index) + isUncontrolled && setSelectedIndexInternalState(index) + child.props.onClick && child.props.onClick(event) + } } : (event: React.MouseEvent) => { - child.props.onClick && child.props.onClick(event) - isUncontrolled && setSelectedIndexInternalState(index) + const isDisabled = + child.props.disabled === true || + child.props['aria-disabled'] === 'true' || + child.props['aria-disabled'] === true + if (!isDisabled) { + child.props.onClick && child.props.onClick(event) + isUncontrolled && setSelectedIndexInternalState(index) + } }, selected: index === selectedIndex, style: { diff --git a/packages/react/src/SegmentedControl/SegmentedControlButton.tsx b/packages/react/src/SegmentedControl/SegmentedControlButton.tsx index e549b43dc87..075bc8f3e6e 100644 --- a/packages/react/src/SegmentedControl/SegmentedControlButton.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControlButton.tsx @@ -21,6 +21,8 @@ export type SegmentedControlButtonProps = { /** @deprecated Use `leadingVisual` instead. The leading icon comes before item label */ // eslint-disable-next-line @typescript-eslint/no-explicit-any leadingIcon?: React.FunctionComponent> | React.ReactElement + /** Applies `aria-disabled` to the button. This will disable certain functionality, such as `onClick` events. */ + disabled?: boolean /** Optional counter to display on the right side of the button */ count?: number | string } & ButtonHTMLAttributes @@ -31,17 +33,25 @@ const SegmentedControlButton: FCWithSlotMarker { + const {'aria-disabled': ariaDisabled, ...rest} = props // Use leadingVisual if provided, otherwise fall back to leadingIcon for backwards compatibility const LeadingVisual = leadingVisual ?? leadingIcon return (
  • - - -
  • - ) - } else { - // This can be removed when primer_react_segmented_control_tooltip feature flag is GA-ed. - return ( -
  • + const {'aria-disabled': ariaDisabled, ...rest} = props + + return ( +
  • + -
  • - ) - } + + + ) } SegmentedControlIconButton.__SLOT__ = Symbol('SegmentedControl.IconButton')