Skip to content

Commit

Permalink
feat: make section collapsible
Browse files Browse the repository at this point in the history
  • Loading branch information
ma-efremoff committed Nov 1, 2023
1 parent e09e7cb commit 4ebff66
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 20 deletions.
11 changes: 11 additions & 0 deletions src/dialog/CollapsibleSection/CollapsibleSection.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.df-collapsible-section {
overflow: hidden;

&__caption_collapsible {
cursor: pointer;
}

&__content_collapsed {
display: none;
}
}
51 changes: 51 additions & 0 deletions src/dialog/CollapsibleSection/CollapsibleSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {ChevronDown, ChevronUp} from '@gravity-ui/icons';

import {dfCN} from '../../helpers/cn';

import './CollapsibleSection.scss';

const block = dfCN('collapsible-section');

export type CollapsibleSectionProps = {
className?: string;

alwaysExpanded?: boolean;
initialCollapsed?: boolean;

caption: string;
captionClassName?: string;

children?: React.ReactNode;
};

export function CollapsibleSection({
className,
alwaysExpanded,
initialCollapsed,
children,
caption,
captionClassName,
}: CollapsibleSectionProps) {
const [collapsed, setCollapsed] = React.useState(initialCollapsed && !alwaysExpanded);
const handleToggle = React.useCallback(() => {
setCollapsed(!collapsed);
}, [collapsed]);

return (
<section className={block(null, className)}>
<h2
className={block(
'caption',
{collapsed, collapsible: !alwaysExpanded},
captionClassName,
)}
onClick={alwaysExpanded ? undefined : handleToggle}
>
{caption}
{!alwaysExpanded && <span> {collapsed ? <ChevronDown /> : <ChevronUp />}</span>}
</h2>
<section className={block('content', {collapsed})}>{children}</section>
</section>
);
}
49 changes: 30 additions & 19 deletions src/dialog/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
TabControlStaticApi,
ValidatorType,
} from '../types';
import {CollapsibleSection} from '../CollapsibleSection/CollapsibleSection';

const bDialog = dfCN('dialog');
const bPage = dfCN('page-dialog');
Expand Down Expand Up @@ -224,6 +225,9 @@ type SectionType<FieldT> = {
fields?: FieldT[] | SeparatorType;
wrapTo?: (sectionNode: React.ReactNode) => React.ReactNode;

collapsible?: boolean;
initialCollapsed?: boolean;

type?: never;
};

Expand Down Expand Up @@ -475,10 +479,6 @@ class Dialog<
return <FieldWithExtras field={field} input={input} meta={meta} extras={extras} />;
}

static renderSectionTitle<FieldT>(field: SectionType<FieldT>) {
return <h2 className={bDialog('section-title')}>{field.section}</h2>;
}

static renderSeparator(_field: SeparatorType, index: number) {
return <div className={bDialog('separator')} key={`separator-${index}`} />;
}
Expand Down Expand Up @@ -750,24 +750,35 @@ class Dialog<
}

renderSection(field: SectionType<FieldT>, index: number, fieldPrefix?: string) {
let sectionNode: React.ReactNode;
const {
wrapTo = (sectionNode) => sectionNode,
section,
collapsible,
initialCollapsed,
} = field;
let children: React.ReactNode;
if (!Array.isArray(field.fields) || field.fields.length === 0) {
sectionNode = Dialog.renderSectionTitle(field);
children = [];
} else {
sectionNode = (
<section className={bDialog('section')}>
{Dialog.renderSectionTitle(field)}
{this.renderFields(field.fields, fieldPrefix)}
</section>
);
}

if (typeof field.wrapTo === 'function') {
sectionNode = field.wrapTo(sectionNode);
children = this.renderFields(field.fields, fieldPrefix);
}

const key = `section-field-${index}`;
return <React.Fragment key={key}>{sectionNode}</React.Fragment>;
return (
<React.Fragment key={key}>
{wrapTo(
<CollapsibleSection
className={bDialog('section')}
caption={section}
captionClassName={bDialog('section-title')}
initialCollapsed={initialCollapsed}
alwaysExpanded={!collapsible}
>
{children}
</CollapsibleSection>,
)}
</React.Fragment>
);
}

renderFields(fields: Array<DialogField<FieldT>>, fieldPrefix?: string) {
Expand Down Expand Up @@ -1113,9 +1124,9 @@ class Dialog<
handleSubmit: () => void;
form: FormApi<FormValues, InitialFormValues>;
}): React.ReactNode => {
const {className} = this.props;
const {className, modal} = this.props;
return (
<div className={bPage('wrapper', className)}>
<div className={bPage('wrapper', {modal}, className)}>
<div className={bPage()}>{this.renderDialogContent(handleSubmit, form)}</div>
</div>
);
Expand Down
21 changes: 20 additions & 1 deletion src/stories/dialog/03_Sections.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,11 @@ function DialogDemo({
caption: 'Last name',
},
],
wrapTo: wrapTo.bind(null, 'lightgray'),
wrapTo: wrapTo.bind(null, undefined),
},
{
section: 'Contacts',
collapsible: true,
fields: [
{
name: 'email',
Expand All @@ -104,6 +105,24 @@ function DialogDemo({
],
wrapTo: wrapTo.bind(null, 'cyan'),
},
{
section: 'Address',
collapsible: true,
initialCollapsed: true,
fields: [
{
name: 'city',
type: 'text',
caption: 'City',
},
{
name: 'street',
type: 'text',
caption: 'Street',
},
],
wrapTo: wrapTo.bind(null, 'magenta'),
},
{
type: 'block',
name: 'block',
Expand Down

0 comments on commit 4ebff66

Please sign in to comment.