Skip to content

Commit 3a7c146

Browse files
authored
feat: Questions block keyboard access improved (#505)
* feat: improved Questions block accessibility
1 parent 684a855 commit 3a7c146

File tree

14 files changed

+192
-119
lines changed

14 files changed

+192
-119
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
},
104104
"peerDependencies": {
105105
"@doc-tools/transform": "^3.3.2",
106-
"@gravity-ui/uikit": "^5.1.0",
106+
"@gravity-ui/uikit": "^5.4.1",
107107
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
108108
},
109109
"devDependencies": {
@@ -119,7 +119,7 @@
119119
"@gravity-ui/prettier-config": "^1.0.1",
120120
"@gravity-ui/stylelint-config": "^1.0.0",
121121
"@gravity-ui/tsconfig": "^1.0.0",
122-
"@gravity-ui/uikit": "^5.1.0",
122+
"@gravity-ui/uikit": "^5.4.1",
123123
"@storybook/addon-actions": "^7.1.0",
124124
"@storybook/addon-essentials": "^7.1.0",
125125
"@storybook/addon-knobs": "^7.0.2",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@import '../../../../styles/variables';
2+
@import '../../../../styles/mixins';
3+
4+
$block: '.#{$ns}QuestionsBlockItem';
5+
6+
#{$block} {
7+
padding-bottom: $indentM;
8+
border-bottom: 1px solid var(--g-color-line-generic);
9+
10+
& + & {
11+
padding-top: $indentM;
12+
}
13+
14+
&__title {
15+
@include heading4();
16+
17+
position: relative;
18+
padding-right: 24px;
19+
cursor: pointer;
20+
21+
a {
22+
@include link();
23+
}
24+
}
25+
26+
&__arrow {
27+
position: absolute;
28+
right: 0;
29+
top: 0;
30+
color: var(--g-color-text-primary);
31+
}
32+
33+
&__link {
34+
@include text-size(body-2);
35+
}
36+
37+
&__text {
38+
@include text-size(body-2);
39+
40+
margin-top: $indentXXS;
41+
}
42+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React from 'react';
2+
3+
import {useActionHandlers} from '@gravity-ui/uikit';
4+
5+
import {Foldable, HTML, ToggleArrow, YFMWrapper} from '../../../components';
6+
import Link from '../../../components/Link/Link';
7+
import {QuestionBlockItemProps} from '../../../models';
8+
import {block} from '../../../utils';
9+
import {FaqMicrodataValues} from '../models';
10+
11+
import './QuestionBlockItem.scss';
12+
13+
const b = block('QuestionsBlockItem');
14+
15+
export const QuestionBlockItem = ({
16+
title: itemTitle,
17+
text: itemText,
18+
link,
19+
listStyle = 'dash',
20+
isOpened,
21+
onClick,
22+
}: QuestionBlockItemProps) => {
23+
const {onKeyDown} = useActionHandlers(onClick);
24+
25+
return (
26+
<div
27+
className={b()}
28+
itemScope
29+
itemProp={FaqMicrodataValues.QuestionProp}
30+
itemType={FaqMicrodataValues.QuestionType}
31+
role={'listitem'}
32+
>
33+
<h3
34+
className={b('title')}
35+
onClick={onClick}
36+
aria-expanded={isOpened}
37+
role={'button'}
38+
tabIndex={0}
39+
onKeyDown={onKeyDown}
40+
>
41+
<HTML itemProp={FaqMicrodataValues.QuestionNameProp}>{itemTitle}</HTML>
42+
<ToggleArrow
43+
open={isOpened}
44+
size={16}
45+
type={'vertical'}
46+
iconType="navigation"
47+
className={b('arrow')}
48+
/>
49+
</h3>
50+
<Foldable isOpened={isOpened}>
51+
<div
52+
className={b('text')}
53+
itemScope
54+
itemProp={FaqMicrodataValues.AnswerProp}
55+
itemType={FaqMicrodataValues.AnswerType}
56+
aria-hidden={!isOpened}
57+
>
58+
<YFMWrapper
59+
content={itemText}
60+
modifiers={{
61+
constructor: true,
62+
constructorListStyle: true,
63+
constructorListStyleDash: listStyle === 'dash',
64+
}}
65+
itemProp={FaqMicrodataValues.QuestionTextProp}
66+
/>
67+
{link && <Link {...link} tabIndex={isOpened ? 0 : -1} className={b('link')} />}
68+
</div>
69+
</Foldable>
70+
</div>
71+
);
72+
};

src/blocks/Questions/Questions.scss

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,6 @@ $block: '.#{$ns}QuestionsBlock';
1212
margin-right: $indentXL;
1313
}
1414

15-
&__item {
16-
padding-bottom: $indentM;
17-
border-bottom: 1px solid var(--g-color-line-generic);
18-
19-
& + & {
20-
padding-top: $indentM;
21-
}
22-
}
23-
24-
&__item-title {
25-
@include heading4();
26-
27-
position: relative;
28-
padding-right: 24px;
29-
30-
cursor: pointer;
31-
32-
a {
33-
@include link();
34-
}
35-
}
36-
37-
&__text {
38-
@include text-size(body-2);
39-
40-
margin-top: $indentXXS;
41-
}
42-
43-
&__arrow {
44-
position: absolute;
45-
right: 0;
46-
top: 0;
47-
color: var(--g-color-text-primary);
48-
}
49-
50-
&__link {
51-
@include text-size(body-2);
52-
}
53-
5415
@media (max-width: map-get($gridBreakpoints, 'md')) {
5516
&__title {
5617
margin-right: 0;

src/blocks/Questions/Questions.tsx

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
11
import React, {useState} from 'react';
22

3-
import {Foldable, HTML, ToggleArrow, YFMWrapper} from '../../components';
4-
import Link from '../../components/Link/Link';
53
import {Col, Row} from '../../grid';
64
import {QuestionsProps} from '../../models';
75
import {Content} from '../../sub-blocks';
86
import {block} from '../../utils';
97

8+
import {QuestionBlockItem} from './QuestionBlockItem/QuestionBlockItem';
9+
import {FaqMicrodataValues} from './models';
10+
1011
import './Questions.scss';
1112

1213
const b = block('QuestionsBlock');
1314

14-
const FaqMicrodataValues = {
15-
PageType: 'https://schema.org/FAQPage',
16-
QuestionType: 'https://schema.org/Question',
17-
QuestionProp: 'mainEntity',
18-
QuestionNameProp: 'name',
19-
QuestionTextProp: 'text',
20-
AnswerType: 'https://schema.org/Answer',
21-
AnswerProp: 'acceptedAnswer',
22-
AnswerTextProp: 'text',
23-
} as const;
24-
2515
const QuestionsBlock = (props: QuestionsProps) => {
2616
const {title, text, additionalInfo, links, buttons, items} = props;
2717
const [opened, setOpened] = useState<number[]>([0]);
@@ -30,7 +20,7 @@ const QuestionsBlock = (props: QuestionsProps) => {
3020
let newState;
3121

3222
if (opened.includes(index)) {
33-
newState = opened.filter((intemIndex: number) => intemIndex !== index);
23+
newState = opened.filter((itemIndex: number) => itemIndex !== index);
3424
} else {
3525
newState = [...opened, index];
3626
}
@@ -53,54 +43,22 @@ const QuestionsBlock = (props: QuestionsProps) => {
5343
/>
5444
</div>
5545
</Col>
56-
<Col sizes={{all: 12, md: 8}}>
46+
<Col sizes={{all: 12, md: 8}} role={'list'}>
5747
{items.map(
5848
({title: itemTitle, text: itemText, link, listStyle = 'dash'}, index) => {
5949
const isOpened = opened.includes(index);
50+
const onClick = () => toggleItem(index);
6051

6152
return (
62-
<div
53+
<QuestionBlockItem
6354
key={itemTitle}
64-
className={b('item')}
65-
itemScope
66-
itemProp={FaqMicrodataValues.QuestionProp}
67-
itemType={FaqMicrodataValues.QuestionType}
68-
>
69-
<h3
70-
className={b('item-title')}
71-
onClick={() => toggleItem(index)}
72-
>
73-
<HTML itemProp={FaqMicrodataValues.QuestionNameProp}>
74-
{itemTitle}
75-
</HTML>
76-
<ToggleArrow
77-
open={isOpened}
78-
size={16}
79-
type={'vertical'}
80-
iconType="navigation"
81-
className={b('arrow')}
82-
/>
83-
</h3>
84-
<Foldable isOpened={isOpened}>
85-
<div
86-
className={b('text')}
87-
itemScope
88-
itemProp={FaqMicrodataValues.AnswerProp}
89-
itemType={FaqMicrodataValues.AnswerType}
90-
>
91-
<YFMWrapper
92-
content={itemText}
93-
modifiers={{
94-
constructor: true,
95-
constructorListStyle: true,
96-
constructorListStyleDash: listStyle === 'dash',
97-
}}
98-
itemProp={FaqMicrodataValues.QuestionTextProp}
99-
/>
100-
{link && <Link {...link} className={b('link')} />}
101-
</div>
102-
</Foldable>
103-
</div>
55+
title={itemTitle}
56+
text={itemText}
57+
link={link}
58+
listStyle={listStyle}
59+
isOpened={isOpened}
60+
onClick={onClick}
61+
/>
10462
);
10563
},
10664
)}

src/blocks/Questions/models.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const FaqMicrodataValues = {
2+
PageType: 'https://schema.org/FAQPage',
3+
QuestionType: 'https://schema.org/Question',
4+
QuestionProp: 'mainEntity',
5+
QuestionNameProp: 'name',
6+
QuestionTextProp: 'text',
7+
AnswerType: 'https://schema.org/Answer',
8+
AnswerProp: 'acceptedAnswer',
9+
AnswerTextProp: 'text',
10+
} as const;

src/components/BackLink/BackLink.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import {Button, ButtonSize, Icon} from '@gravity-ui/uikit';
55
import {LocationContext} from '../../context/locationContext';
66
import {useAnalytics} from '../../hooks';
77
import {ArrowSidebar} from '../../icons';
8-
import {DefaultEventNames} from '../../models';
8+
import {DefaultEventNames, Tabbable} from '../../models';
99

1010
export type Theme = 'default' | 'special';
1111

12-
export interface BackLinkProps {
12+
export interface BackLinkProps extends Tabbable {
1313
url: string;
1414
title: ReactNode;
1515
theme?: Theme;
@@ -29,6 +29,7 @@ export default function BackLink(props: BackLinkProps) {
2929
className,
3030
shouldHandleBackAction = false,
3131
onClick,
32+
tabIndex,
3233
} = props;
3334
const handleAnalytics = useAnalytics(DefaultEventNames.ShareButton, url);
3435

@@ -57,6 +58,7 @@ export default function BackLink(props: BackLinkProps) {
5758
size={size}
5859
href={shouldHandleBackAction ? undefined : url}
5960
onClick={shouldHandleBackAction ? backActionHandler : undefined}
61+
tabIndex={tabIndex}
6062
>
6163
<Icon data={ArrowSidebar} size={24} />
6264
<span>{title}</span>

src/components/FileLink/FileLink.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const FileLink = (props: WithChildren<FileLinkProps>) => {
5555
className,
5656
theme = 'default',
5757
onClick,
58+
tabIndex,
5859
} = props;
5960
const fileExt = getFileExt(href) as FileExtension;
6061
const labelTheme = (FileExtensionThemes[fileExt] || 'unknown') as LabelProps['theme'];
@@ -66,7 +67,12 @@ const FileLink = (props: WithChildren<FileLinkProps>) => {
6667
{fileExt}
6768
</Label>
6869
<div className={b('link')}>
69-
<a href={href} {...getLinkProps(href, hostname)} onClick={onClick}>
70+
<a
71+
href={href}
72+
onClick={onClick}
73+
tabIndex={tabIndex}
74+
{...getLinkProps(href, hostname)}
75+
>
7076
{text}
7177
</a>
7278
</div>

0 commit comments

Comments
 (0)