Skip to content

Commit a5dca71

Browse files
authored
feat(FoldableList): add new block (#1157)
1 parent e219041 commit a5dca71

23 files changed

+589
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@import '../../../styles/variables';
2+
@import '../../../styles/mixins';
3+
4+
$block: '.#{$ns}FoldableList';
5+
6+
#{$block} {
7+
&__title {
8+
@include top-content-offset($offset: $indentM);
9+
10+
position: sticky;
11+
margin-bottom: $indentM;
12+
margin-right: $indentXL;
13+
}
14+
15+
@media (max-width: map-get($gridBreakpoints, 'md')) {
16+
&__title {
17+
margin-right: 0;
18+
}
19+
}
20+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as React from 'react';
2+
3+
import {Col, Row} from '../../grid';
4+
import {FoldableListProps} from '../../models';
5+
import {Content} from '../../sub-blocks';
6+
import {block} from '../../utils';
7+
8+
import {FoldableListBlockItem} from './FoldableListBlockItem/FoldableListBlockItem';
9+
10+
import './FoldableList.scss';
11+
12+
const b = block('FoldableList');
13+
14+
const FoldableListBlock = (props: FoldableListProps) => {
15+
const {title, text, additionalInfo, links, buttons, items, list} = props;
16+
const [opened, setOpened] = React.useState<number[]>([0]);
17+
18+
const toggleItem = (index: number) => {
19+
let newState;
20+
21+
if (opened.includes(index)) {
22+
newState = opened.filter((itemIndex: number) => itemIndex !== index);
23+
} else {
24+
newState = [...opened, index];
25+
}
26+
27+
setOpened(newState);
28+
};
29+
30+
return (
31+
<div className={b()} itemScope>
32+
<Row>
33+
<Col sizes={{all: 12, md: 4}}>
34+
<div className={b('title')}>
35+
<Content
36+
title={title}
37+
text={text}
38+
additionalInfo={additionalInfo}
39+
links={links}
40+
list={list}
41+
buttons={buttons}
42+
colSizes={{all: 12, md: 12}}
43+
/>
44+
</div>
45+
</Col>
46+
<Col sizes={{all: 12, md: 8}} role={'list'}>
47+
{items.map(
48+
({title: itemTitle, text: itemText, link, listStyle = 'dash'}, index) => {
49+
const isOpened = opened.includes(index);
50+
const onClick = () => toggleItem(index);
51+
52+
return (
53+
<FoldableListBlockItem
54+
key={itemTitle}
55+
title={itemTitle}
56+
text={itemText}
57+
link={link}
58+
listStyle={listStyle}
59+
isOpened={isOpened}
60+
onClick={onClick}
61+
/>
62+
);
63+
},
64+
)}
65+
</Col>
66+
</Row>
67+
</div>
68+
);
69+
};
70+
71+
export default FoldableListBlock;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
@import '../../../../styles/variables';
2+
@import '../../../../styles/mixins';
3+
4+
$block: '.#{$ns}FoldableListBlockItem';
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 reset-button-style();
16+
@include heading4();
17+
@include focusable();
18+
19+
position: relative;
20+
display: block;
21+
width: 100%;
22+
padding-right: 24px;
23+
text-align: start;
24+
cursor: pointer;
25+
border-radius: var(--g-focus-border-radius);
26+
27+
a {
28+
@include link();
29+
}
30+
}
31+
32+
&__arrow {
33+
position: absolute;
34+
right: 0;
35+
top: 0;
36+
bottom: 0;
37+
margin: auto;
38+
color: var(--g-color-text-primary);
39+
}
40+
41+
&__link {
42+
@include text-size(body-2);
43+
44+
@include add-specificity(&) {
45+
a {
46+
outline-offset: -2px; // as part of outline is hidden due to overflow: hidden from parent
47+
border-radius: calc(
48+
var(--g-focus-border-radius) + 2px
49+
); // as outline-offset is -2px
50+
display: inline-block; //fixes the Link to be clickable in entire row width
51+
}
52+
}
53+
}
54+
55+
&__text {
56+
@include text-size(body-2);
57+
58+
margin-top: $indentXXS;
59+
}
60+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {useActionHandlers} from '@gravity-ui/uikit';
2+
3+
import {Foldable, HTML, ToggleArrow, YFMWrapper} from '../../../components';
4+
import Link from '../../../components/Link/Link';
5+
import {FoldableListBlockItemProps} from '../../../models';
6+
import {block} from '../../../utils';
7+
8+
import './FoldableListBlockItem.scss';
9+
10+
const b = block('FoldableListBlockItem');
11+
12+
export const FoldableListBlockItem = ({
13+
title: itemTitle,
14+
text: itemText,
15+
link,
16+
listStyle = 'dash',
17+
isOpened,
18+
onClick,
19+
}: FoldableListBlockItemProps) => {
20+
const {onKeyDown} = useActionHandlers(onClick);
21+
22+
return (
23+
<div className={b()} itemScope role={'listitem'}>
24+
<button
25+
className={b('title')}
26+
onClick={onClick}
27+
aria-expanded={isOpened}
28+
onKeyDown={onKeyDown}
29+
>
30+
<HTML>{itemTitle}</HTML>
31+
<ToggleArrow
32+
open={isOpened}
33+
size={16}
34+
type={'vertical'}
35+
iconType="navigation"
36+
className={b('arrow')}
37+
/>
38+
</button>
39+
<Foldable isOpened={isOpened}>
40+
<div className={b('text')} aria-hidden={!isOpened}>
41+
<YFMWrapper
42+
content={itemText}
43+
modifiers={{
44+
constructor: true,
45+
constructorListStyle: true,
46+
constructorListStyleDash: listStyle === 'dash',
47+
}}
48+
/>
49+
{link && <Link {...link} tabIndex={isOpened ? 0 : -1} className={b('link')} />}
50+
</div>
51+
</Foldable>
52+
</div>
53+
);
54+
};
Loading
Loading
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)