Skip to content

Commit

Permalink
Toggle Group Control: add tooltip (#36726)
Browse files Browse the repository at this point in the history
* Adding a ToolTip to the font-size-picker using the current label.
To avoid the TS linter freaking out, we add a default of `null` for the shortcut.
The 'position' prop, though helpful, is required for the same reason.

* Allow a new prop `tooltipText` to be passed down to `ToggleGroupControlOption` to determine whether to show tooltip
Update stories.

* Changed the prop from text to a boolean in order to flag displaying the tooltip.
Adding tests.
Updated README.md for ToggleGroupControl
Updated CHANGELOG.md

Updated label condition
Updated story
Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>

Removed unneeded export and __ import

* Updated CHANGELOG.md
Destructuring Tooltip props to hide them from the TS linter
Updating tests to account for the Tooltip timers.

* testing tooltip active state using the focus event, which doesn't employ useDebounce for now.
  • Loading branch information
ramonjd authored and noisysocks committed Nov 28, 2021
1 parent 2632356 commit 1a709be
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 25 deletions.
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- Added a `showTooltip` prop to `ToggleGroupControlOption` in order to display tooltip text (using `<Tooltip />`). ([#36726](https://github.com/WordPress/gutenberg/pull/36726)).

## 19.0.2 (2021-11-15)

- Remove erroneous use of `??=` syntax from `build-module`.
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/font-size-picker/index.js
Expand Up @@ -186,6 +186,7 @@ function FontSizePicker(
value={ option.value }
label={ option.label }
aria-label={ option.name }
showTooltip={ true }
/>
) ) }
</ToggleGroupControl>
Expand Down
15 changes: 15 additions & 0 deletions packages/components/src/toggle-group-control/stories/index.js
Expand Up @@ -71,6 +71,11 @@ const _default = ( { options } ) => {
opt[ 'aria-label' ],
`${ KNOBS_GROUPS.ToggleGroupControlOption }-${ index + 1 }`
) }
showTooltip={ boolean(
`${ KNOBS_GROUPS.ToggleGroupControlOption }: showTooltip`,
opt.showTooltip,
`${ KNOBS_GROUPS.ToggleGroupControlOption }-${ index + 1 }`
) }
/>
) );

Expand Down Expand Up @@ -101,6 +106,16 @@ Default.args = {
],
};

export const WithTooltip = _default.bind( {} );
WithTooltip.args = {
...Default.args,
options: [
{ value: 1, label: '1', showTooltip: true, 'aria-label': 'One' },
{ value: 2, label: '2', showTooltip: true, 'aria-label': 'Two' },
{ value: 3, label: '3', showTooltip: true, 'aria-label': 'Three' },
],
};

export const WithAriaLabel = _default.bind( {} );
WithAriaLabel.args = {
...Default.args,
Expand Down
57 changes: 57 additions & 0 deletions packages/components/src/toggle-group-control/test/index.js
Expand Up @@ -15,16 +15,35 @@ describe( 'ToggleGroupControl', () => {
<ToggleGroupControlOption value="jack" label="J" />
</>
);
const optionsWithTooltip = (
<>
<ToggleGroupControlOption
value="gnocchi"
label="Delicious Gnocchi"
aria-label="Click for Delicious Gnocchi"
showTooltip={ true }
/>
<ToggleGroupControlOption
value="caponata"
label="Sumptuous Caponata"
aria-label="Click for Sumptuous Caponata"
/>
</>
);

it( 'should render correctly', () => {
const { container } = render(
<ToggleGroupControl label="Test Toggle Group Control">
{ options }
</ToggleGroupControl>
);

expect( container.firstChild ).toMatchSnapshot();
} );

it( 'should call onChange with proper value', () => {
const mockOnChange = jest.fn();

render(
<ToggleGroupControl
value="jack"
Expand All @@ -34,8 +53,46 @@ describe( 'ToggleGroupControl', () => {
{ options }
</ToggleGroupControl>
);

const firstRadio = screen.getByRole( 'radio', { name: 'R' } );

fireEvent.click( firstRadio );

expect( mockOnChange ).toHaveBeenCalledWith( 'rigas' );
} );
it( 'should render tooltip where `showTooltip` === `true`', () => {
render(
<ToggleGroupControl label="Test Toggle Group Control">
{ optionsWithTooltip }
</ToggleGroupControl>
);

const firstRadio = screen.getByLabelText(
'Click for Delicious Gnocchi'
);

fireEvent.focus( firstRadio );

expect(
screen.getByText( 'Click for Delicious Gnocchi' )
).toBeInTheDocument();
} );

it( 'should not render tooltip', () => {
render(
<ToggleGroupControl label="Test Toggle Group Control">
{ optionsWithTooltip }
</ToggleGroupControl>
);

const secondRadio = screen.getByLabelText(
'Click for Sumptuous Caponata'
);

fireEvent.focus( secondRadio );

expect(
screen.queryByText( 'Click for Sumptuous Caponata' )
).not.toBeInTheDocument();
} );
} );
Expand Up @@ -18,7 +18,7 @@ import {
function Example() {
return (
<ToggleGroupControl label="my label" value="vertical" isBlock>
<ToggleGroupControlOption value="horizontal" label="Horizontal" />
<ToggleGroupControlOption value="horizontal" label="Horizontal" showTooltip={ true } />
<ToggleGroupControlOption value="vertical" label="Vertical" />
</ToggleGroupControl>
);
Expand All @@ -38,3 +38,10 @@ Label for the option. If needed, the `aria-label` prop can be used in addition t
The value of the `ToggleGroupControlOption`.

- Required: Yes

### `showTooltip`: `boolean`

Whether to show a tooltip when hovering over the option. The tooltip will attempt to use the `aria-label` prop text first, then the `label` prop text if no `aria-label` prop is found.

- Required: No

Expand Up @@ -9,7 +9,6 @@ import { Radio } from 'reakit';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useInstanceId } from '@wordpress/compose';

/**
Expand All @@ -20,13 +19,25 @@ import {
useContextSystem,
WordPressComponentProps,
} from '../../ui/context';
import type { ToggleGroupControlOptionProps } from '../types';
import type { ToggleGroupControlOptionProps, WithToolTipProps } from '../types';
import { useToggleGroupControlContext } from '../context';
import * as styles from './styles';
import { useCx } from '../../utils/hooks';
import Tooltip from '../../tooltip';

const { ButtonContentView, LabelPlaceholderView, LabelView } = styles;

const WithToolTip = ( { showTooltip, text, children }: WithToolTipProps ) => {
if ( showTooltip && text ) {
return (
<Tooltip text={ text } position="top center">
{ children }
</Tooltip>
);
}
return <>{ children }</>;
};

function ToggleGroupControlOption(
props: WordPressComponentProps< ToggleGroupControlOptionProps, 'button' >,
forwardedRef: Ref< any >
Expand All @@ -41,7 +52,14 @@ function ToggleGroupControlOption(
'ToggleGroupControlOption'
);

const { className, isBlock = false, label, value, ...radioProps } = {
const {
className,
isBlock = false,
label,
value,
showTooltip = false,
...radioProps
} = {
...toggleGroupControlContext,
...buttonProps,
};
Expand All @@ -54,23 +72,28 @@ function ToggleGroupControlOption(
className,
isActive && styles.buttonActive
);
const optionLabel = !! radioProps[ 'aria-label' ]
? radioProps[ 'aria-label' ]
: label;

return (
<LabelView className={ labelViewClasses } data-active={ isActive }>
<Radio
{ ...radioProps }
as="button"
aria-label={ radioProps[ 'aria-label' ] ?? label }
className={ classes }
data-value={ value }
ref={ forwardedRef }
value={ value }
>
<ButtonContentView>{ label }</ButtonContentView>
<LabelPlaceholderView aria-hidden>
{ label }
</LabelPlaceholderView>
</Radio>
<WithToolTip showTooltip={ showTooltip } text={ optionLabel }>
<Radio
{ ...radioProps }
as="button"
aria-label={ optionLabel }
className={ classes }
data-value={ value }
ref={ forwardedRef }
value={ value }
>
<ButtonContentView>{ label }</ButtonContentView>
<LabelPlaceholderView aria-hidden>
{ label }
</LabelPlaceholderView>
</Radio>
</WithToolTip>
</LabelView>
);
}
Expand Down
24 changes: 24 additions & 0 deletions packages/components/src/toggle-group-control/types.ts
Expand Up @@ -18,6 +18,30 @@ export type ToggleGroupControlOptionProps = {
* to specify a different label for assistive technologies.
*/
label: string;
/**
* Whether to display a Tooltip for the control option. If set to `true`, the tooltip will
* show the aria-label or the label prop text.
*
* @default false
*/
showTooltip?: boolean;
};

export type WithToolTipProps = {
/**
* React children
*/
children: ReactNode;
/**
* Label for the Tooltip component.
*/
text: string;
/**
* Whether to wrap the control option in a Tooltip component.
*
* @default false
*/
showTooltip?: boolean;
};

export type ToggleGroupControlProps = Omit<
Expand Down
9 changes: 2 additions & 7 deletions packages/components/src/tooltip/index.js
Expand Up @@ -92,13 +92,8 @@ const emitToChild = ( children, eventName, event ) => {
}
};

function Tooltip( {
children,
position,
text,
shortcut,
delay = TOOLTIP_DELAY,
} ) {
function Tooltip( props ) {
const { children, position, text, shortcut, delay = TOOLTIP_DELAY } = props;
/**
* Whether a mouse is currently pressed, used in determining whether
* to handle a focus event as displaying the tooltip immediately.
Expand Down

0 comments on commit 1a709be

Please sign in to comment.