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/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts index 7a1112d7e37..d5a7aa7de0d 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 38519ec7f5a..8d3dd29fdbe 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 2717c72816b..03e5ae6d477 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.module.css +++ b/packages/react/src/SegmentedControl/SegmentedControl.module.css @@ -124,6 +124,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; @@ -183,7 +194,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 7506c9fc151..9a7b2aac5c9 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.test.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControl.test.tsx @@ -5,7 +5,6 @@ import {describe, expect, it, vi} from 'vitest' import BaseStyles from '../BaseStyles' import theme from '../theme' import ThemeProvider from '../ThemeProvider' -import {FeatureFlags} from '../FeatureFlags' import {SegmentedControl} from '../SegmentedControl' const segmentData = [ @@ -144,19 +143,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) { @@ -167,41 +160,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 b7cb8760152..1395ebd0fe8 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControl.tsx @@ -163,13 +163,19 @@ 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 + 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 + 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 76198ac823d..879bf1e9ecf 100644 --- a/packages/react/src/SegmentedControl/SegmentedControlButton.tsx +++ b/packages/react/src/SegmentedControl/SegmentedControlButton.tsx @@ -16,6 +16,10 @@ export type SegmentedControlButtonProps = { defaultSelected?: boolean /** The leading icon comes before item label */ leadingIcon?: React.FunctionComponent> | React.ReactElement + /** Applies `aria-disabled` to the button. This will disable certain functionality, such as `onClick` events. */ + disabled?: boolean + /** Applies `aria-disabled` to the button. This will disable certain functionality, such as `onClick` events. */ + 'aria-disabled'?: boolean /** Optional counter to display on the right side of the button */ count?: number | string } & ButtonHTMLAttributes @@ -25,6 +29,8 @@ const SegmentedControlButton: React.FC { return (
  • - - -
  • - ) - } else { - // This can be removed when primer_react_segmented_control_tooltip feature flag is GA-ed. - return ( -
  • + return ( +
  • + -
  • - ) - } + + + ) } export default SegmentedControlIconButton