Skip to content

Commit 7079de4

Browse files
authored
feat: adding labelledby and describedby attributes for post card (#142)
* feat: adding labelledby and describedby attributes
1 parent cc971a9 commit 7079de4

File tree

7 files changed

+76
-14
lines changed

7 files changed

+76
-14
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
},
7979
"peerDependencies": {
8080
"@doc-tools/transform": "^3.3.2",
81-
"@gravity-ui/page-constructor": "^4.26.0",
81+
"@gravity-ui/page-constructor": "^4.31.0",
8282
"@gravity-ui/uikit": "^5.12.0",
8383
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
8484
},

src/components/PostCard/PostCard.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React, {useContext, useMemo} from 'react';
2-
32
import {CardBase, HTML, MetrikaGoal, YFMWrapper} from '@gravity-ui/page-constructor';
3+
import {useUniqId} from '@gravity-ui/uikit';
44

55
import {LikesContext} from '../../contexts/LikesContext';
66
import {PostCardSize, PostCardTitleHeadingLevel, PostData} from '../../models/common';
77
import {block} from '../../utils/cn';
88
import {SuggestPostInfo} from '../PostInfo/SuggestPostInfo';
9+
import {useAriaAttributes} from '../../hooks/useAriaAttributes';
910

1011
import './PostCard.scss';
1112

@@ -62,22 +63,43 @@ export const PostCard = ({
6263
: undefined,
6364
[hasUserLike, likes, toggleLike, hasLikes],
6465
);
66+
const titleId = useUniqId();
67+
const descriptionId = useUniqId();
68+
const dateId = useUniqId();
69+
const tagId = useUniqId();
70+
const readingTimeId = useUniqId();
71+
const isTagVisible = showTag && tags?.[0]?.name;
72+
const ariaAttributes = useAriaAttributes({
73+
labelIds: [isTagVisible && tagId, title && titleId],
74+
descriptionIds: [
75+
description && descriptionId,
76+
date && dateId,
77+
readingTime && readingTimeId,
78+
],
79+
});
6580

6681
return (
67-
<CardBase url={url} metrikaGoals={metrikaGoals} className={b('card', {fullWidth})}>
82+
<CardBase
83+
url={url}
84+
metrikaGoals={metrikaGoals}
85+
className={b('card', {fullWidth})}
86+
extraProps={ariaAttributes}
87+
>
6888
<CardBase.Header image={image} className={b('header', {fullWidth})}>
6989
<div className={b('image-container')} data-qa="blog-suggest-header" />
7090
</CardBase.Header>
7191
<CardBase.Content>
72-
{showTag && tags?.[0]?.name && (
73-
<div className={b('tag', {size})}>{tags[0].name}</div>
92+
{isTagVisible && (
93+
<div id={tagId} className={b('tag', {size})}>
94+
{tags[0].name}
95+
</div>
7496
)}
7597
{title &&
7698
React.createElement(
7799
titleHeadingLevel,
78100
{className: b('title', {size})},
79101
<span>
80-
<HTML>{title}</HTML>
102+
<HTML id={titleId}>{title}</HTML>
81103
</span>,
82104
)}
83105
{description && (
@@ -88,6 +110,7 @@ export const PostCard = ({
88110
blog: size === 'm',
89111
blogCard: true,
90112
}}
113+
id={descriptionId}
91114
/>
92115
)}
93116
</CardBase.Content>
@@ -100,6 +123,8 @@ export const PostCard = ({
100123
likes={likesProps}
101124
size={size}
102125
qa="blog-suggest-block"
126+
dateId={dateId}
127+
readingTimeId={readingTimeId}
103128
/>
104129
</CardBase.Footer>
105130
</CardBase>

src/components/PostInfo/SuggestPostInfo.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {block} from '../../utils/cn';
77
import {Date} from './components/Date';
88
import {ReadingTime} from './components/ReadingTime';
99
import {Save} from './components/Save';
10-
1110
import './PostInfo.scss';
1211

1312
const b = block('post-info');
@@ -22,6 +21,8 @@ export interface SuggestPostInfoProps
2221
hasUserLike?: boolean;
2322
toggleLike?: ToggleLikeCallbackType;
2423
};
24+
dateId?: string;
25+
readingTimeId?: string;
2526
}
2627

2728
/**
@@ -35,6 +36,8 @@ export interface SuggestPostInfoProps
3536
* @param qa - test-attr
3637
* @param size - text size
3738
* @param isModernIcon - flag what we need render 'bookmark' icon
39+
* @param dateId - id value for element with post date. Useful when providing accessible description
40+
* @param readingTimeId - id value for element with reading time. Useful when providing accessible description
3841
*
3942
* @returns jsx
4043
*/
@@ -45,6 +48,8 @@ export const SuggestPostInfo = ({
4548
likes,
4649
size = PostCardSize.SMALL,
4750
qa,
51+
dateId,
52+
readingTimeId,
4853
}: SuggestPostInfoProps) => {
4954
const {hasUserLike, likesCount, handleLike} = useLikes({
5055
hasLike: likes?.hasUserLike,
@@ -56,8 +61,10 @@ export const SuggestPostInfo = ({
5661
return (
5762
<div className={b('container')}>
5863
<div className={b('suggest-container')}>
59-
{date && <Date date={date} size={size} />}
60-
{readingTime && <ReadingTime readingTime={readingTime} size={size} />}
64+
{date && <Date date={date} size={size} id={dateId} />}
65+
{readingTime && (
66+
<ReadingTime readingTime={readingTime} size={size} id={readingTimeId} />
67+
)}
6168
</div>
6269
{likes && postId && (
6370
<Save

src/components/PostInfo/components/Date.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ const b = block('post-info');
1212
type DateProps = {
1313
date: string | number;
1414
size?: PostCardSize;
15+
id?: string;
1516
};
1617

17-
export const Date = ({date, size = PostCardSize.SMALL}: DateProps) => {
18+
export const Date = ({date, size = PostCardSize.SMALL, id}: DateProps) => {
1819
const {locale} = useContext(LocaleContext);
1920

20-
return <div className={b('item', {size})}>{format(date, 'longDate', locale?.code)}</div>;
21+
return (
22+
<div className={b('item', {size})} id={id}>
23+
{format(date, 'longDate', locale?.code)}
24+
</div>
25+
);
2126
};

src/components/PostInfo/components/ReadingTime.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ const ICON_SIZE = 16;
1515
type ReadingTimeProps = {
1616
readingTime: number;
1717
size?: 's' | 'm';
18+
id?: string;
1819
};
1920

20-
export const ReadingTime = ({readingTime, size = 's'}: ReadingTimeProps) => (
21-
<div className={b('item', {size})}>
21+
export const ReadingTime = ({readingTime, size = 's', id}: ReadingTimeProps) => (
22+
<div className={b('item', {size})} id={id}>
2223
<span className={b('icon')}>
2324
<Icon data={Time} size={ICON_SIZE} className={b('icon-color')} />
2425
</span>

src/hooks/useAriaAttributes.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {useMemo} from 'react';
2+
3+
type Labels = string | number | boolean | undefined;
4+
type Description = string | number | boolean | undefined;
5+
interface UseAriaAttributesProps {
6+
labelIds?: Labels[];
7+
descriptionIds: Description[];
8+
}
9+
10+
/**
11+
* Returns aria-attributes
12+
* @param labelIds - labels ids. Falsy values will be ignored
13+
* @param descriptionIds - descriptions ids. Falsy values will be ignored
14+
* @returns aria attributes for the element to be labelled
15+
*/
16+
export const useAriaAttributes = ({labelIds = [], descriptionIds = []}: UseAriaAttributesProps) => {
17+
const labelledBy = useMemo(() => labelIds.filter(Boolean).join(' '), [labelIds]);
18+
const describedBy = useMemo(() => descriptionIds.filter(Boolean).join(' '), [descriptionIds]);
19+
20+
return {
21+
'aria-labelledby': labelledBy,
22+
'aria-describedby': describedBy,
23+
};
24+
};

0 commit comments

Comments
 (0)