Skip to content

Commit

Permalink
feat(HelpPopover): improve a11y
Browse files Browse the repository at this point in the history
  • Loading branch information
zamkovskaya committed Oct 2, 2023
1 parent f7682ed commit 296f770
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 15 deletions.
15 changes: 15 additions & 0 deletions src/components/HelpPopover/HelpPopover.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@use '../variables';
@use '@gravity-ui/uikit/styles/mixins';

$block: '.#{variables.$ns}help-popover';

#{$block} {
&__button {
@include mixins.button-reset();
}

&__button:focus-visible {
outline: 2px solid var(--g-color-line-generic-active);
border-radius: 50%;
}
}
18 changes: 16 additions & 2 deletions src/components/HelpPopover/HelpPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,33 @@ import {block} from '../utils/cn';

import {QuestionMarkIcon} from './QuestionMarkIcon';

import './HelpPopover.scss';

const b = block('help-popover');

/**
* @see {@link https://github.com/microsoft/TypeScript/issues/28339}
*/
type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;

export type HelpPopoverProps = DistributiveOmit<PopoverProps, 'children'> & QAProps;
export interface AriaProps {
buttonAriaAttributes?: React.AriaAttributes;
buttonRole?: React.AriaRole;
buttonRef?: React.RefObject<HTMLButtonElement>;
}
export type HelpPopoverProps = DistributiveOmit<PopoverProps, 'children'> & QAProps & AriaProps;

export function HelpPopover(props: HelpPopoverProps) {
return (
<Popover {...props} className={b(null, props.className)}>
<Icon data={QuestionMarkIcon} size={16} />
<button
ref={props.buttonRef}
className={b('button')}
{...props.buttonAriaAttributes}
role={props.buttonRole}
>
<Icon data={QuestionMarkIcon} size={16} />
</button>
</Popover>
);
}
29 changes: 16 additions & 13 deletions src/components/HelpPopover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ Component to display popover with tips

### PropTypes

| Property | Type | Required | Values | Default | Description |
| :------------ | :---------- | :------- | :---------------- | :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| theme | `String` | | `info`, `special` | `info` | Appearance |
| className | `String` | | | | Control class name |
| placement | `Array` | | | [`right`, `bottom`] | Allowed popover positions |
| autoclosable | `Boolean` | | | `true` | Close popover when pointer is outside of control |
| delayClosing | `Number` | | | `300` | Timeout before closing popover (see `autoclosable`) |
| title | `String` | | | | Popover title |
| content | `ReactNode` | | | | Popover content |
| htmlContent | `String` | | | | Render HTML via `dangerouslySetInnerHTML` |
| links | `Array` | | | [] | Links below content, could be <br/> `{ text: 'Link 1', href: 'https://example.com'}` or <br/> `{ text: 'Link 2', onClick: () => onLinkClick() }` |
| tooltipButton | `Object` | | | | Render button with this value <br/> `{ text: 'Button', onClick: () => onClick() }` |
| offset | `Object` | | | `{ left: 4 }` | Control popup toggle position offset <br/> `{ top: 0, left: 0 }` |
| Property | Type | Required | Values | Default | Description |
| :------------------- | :----------------------------------- | :------- | :---------------- | :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| theme | `String` | | `info`, `special` | `info` | Appearance |
| className | `String` | | | | Control class name |
| placement | `Array` | | | [`right`, `bottom`] | Allowed popover positions |
| autoclosable | `Boolean` | | | `true` | Close popover when pointer is outside of control |
| delayClosing | `Number` | | | `300` | Timeout before closing popover (see `autoclosable`) |
| title | `String` | | | | Popover title |
| content | `ReactNode` | | | | Popover content |
| htmlContent | `String` | | | | Render HTML via `dangerouslySetInnerHTML` |
| links | `Array` | | | [] | Links below content, could be <br/> `{ text: 'Link 1', href: 'https://example.com'}` or <br/> `{ text: 'Link 2', onClick: () => onLinkClick() }` |
| tooltipButton | `Object` | | | | Render button with this value <br/> `{ text: 'Button', onClick: () => onClick() }` |
| offset | `Object` | | | `{ left: 4 }` | Control popup toggle position offset <br/> `{ top: 0, left: 0 }` |
| buttonAriaAttributes | `React.AriaAttributes` | | | | Set aria attributes to the underlying button element |
| buttonRole | `React.AriaRole` | | | | Set aria role to the underlying button element |
| buttonRef | `React.RefObject<HTMLButtonElement>` | | | | Ref to the underlying button element |

### Examples

Expand Down
48 changes: 48 additions & 0 deletions src/components/HelpPopover/__stories__/HelpPopover.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import React from 'react';

import type {Meta, StoryFn} from '@storybook/react';

import {cn} from '../../utils/cn';
import {HelpPopover} from '../HelpPopover';
import type {HelpPopoverProps} from '../HelpPopover';

import './HelpPopoverShowcase.scss';

const b = cn('help-popover-showcase');

export default {
title: 'Components/HelpPopover',
component: HelpPopover,
Expand All @@ -15,3 +20,46 @@ export const Default = DefaultTemplate.bind({});
Default.args = {
content: 'Some content',
};

export const Accessible: StoryFn = (args) => {
const helpPopoverWithoutActionsId = 'helpPopoverWithoutActionsId';
const helpPopoverWithActionsId = 'helpPopoverWithActionsId';
const [openPopover, setOpenPopover] = React.useState(false);
const ref = React.useRef<HTMLButtonElement>(null);
return (
<div>
<div className={b('container')}>
<span className={b('container-title')}>Without actions: </span>
<HelpPopover
{...args}
content={'Some content'}
tooltipId={helpPopoverWithoutActionsId}
aria-hidden={true}
buttonAriaAttributes={{
'aria-labelledby': helpPopoverWithoutActionsId,
}}
buttonRole="generic"
/>
</div>
<div className={b('container')}>
<span className={b('container-title')}>With actions: </span>
<HelpPopover
{...args}
tooltipId={helpPopoverWithActionsId}
content={<a href="https://ya.ru">Some link</a>}
openOnHover={false}
onOpenChange={setOpenPopover}
focusTrap
autoFocus
restoreFocusRef={ref}
buttonAriaAttributes={{
'aria-expanded': openPopover,
'aria-controls': helpPopoverWithActionsId,
'aria-label': 'More info',
}}
buttonRef={ref}
/>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.help-popover-showcase {
&__container {
display: flex;
margin-bottom: 10px;
}
&__container-title {
margin-right: 5px;
}
}

0 comments on commit 296f770

Please sign in to comment.