Skip to content

Commit

Permalink
fix(Section): defaultIsOpen should behave as expected (#6326)
Browse files Browse the repository at this point in the history
Co-authored-by: Justin Williams <[email protected]>
  • Loading branch information
nerdstep and Justin Williams authored Aug 18, 2023
1 parent f1f1cee commit f113fc1
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 6 deletions.
6 changes: 5 additions & 1 deletion packages/core/src/components/section/section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export interface SectionProps extends Props, Omit<HTMLDivProps, "title">, React.
* This prop has no effect if `collapsible={false}`.
*/
collapseProps?: Pick<CollapseProps, "className" | "keepChildrenMounted" | "transitionDuration"> & {
/**
* @default true
*/
defaultIsOpen?: boolean;
};

Expand Down Expand Up @@ -110,7 +113,8 @@ export const Section: React.FC<SectionProps> = React.forwardRef((props, ref) =>
title,
...htmlProps
} = props;
const [isCollapsed, setIsCollapsed] = React.useState<boolean>(collapseProps?.defaultIsOpen ?? false);
// The initial useState value is negated in order to conform to the `isCollapsed` expectation.
const [isCollapsed, setIsCollapsed] = React.useState<boolean>(!(collapseProps?.defaultIsOpen ?? true));
const toggleIsCollapsed = React.useCallback(() => setIsCollapsed(!isCollapsed), [isCollapsed]);

const isHeaderLeftContainerVisible = title != null || icon != null || subtitle != null;
Expand Down
9 changes: 5 additions & 4 deletions packages/core/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ import "./common/utilsTests";

// components
import "./alert/alertTests";
import "./breadcrumbs/breadcrumbsTests";
import "./breadcrumbs/breadcrumbTests";
import "./breadcrumbs/breadcrumbsTests";
import "./buttons/buttonTests";
import "./callout/calloutTests";
import "./card/cardTests";
import "./card-list/cardListTests";
import "./card/cardTests";
import "./collapse/collapseTests";
import "./context-menu/contextMenuTests";
import "./context-menu/contextMenuSingletonTests";
import "./context-menu/contextMenuTests";
import "./controls/controlsTests";
import "./controls/inputGroupTests";
import "./controls/numericInputTests";
Expand All @@ -45,8 +45,8 @@ import "./forms/textAreaTests";
import "./hotkeys/hotkeyTests";
import "./hotkeys/hotkeysParserTests";
import "./hotkeys/keyComboTagTests";
import "./html/htmlTests";
import "./html-select/htmlSelectTests";
import "./html/htmlTests";
import "./icon/iconTests";
import "./menu/menuItemTests";
import "./menu/menuTests";
Expand All @@ -61,6 +61,7 @@ import "./popover/popperUtilTests";
import "./portal/portalTests";
import "./progress/progressBarTests";
import "./resize-sensor/resizeSensorTests";
import "./section/sectionTests";
import "./slider/handleTests";
import "./slider/multiSliderTests";
import "./slider/rangeSliderTests";
Expand Down
101 changes: 101 additions & 0 deletions packages/core/test/section/sectionTests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2015 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { assert } from "chai";
import { mount } from "enzyme";
import * as React from "react";

import { IconNames } from "@blueprintjs/icons";

import { Classes, H6, Section, SectionCard } from "../../src";

describe("<Section>", () => {
let containerElement: HTMLElement | undefined;

beforeEach(() => {
containerElement = document.createElement("div");
document.body.appendChild(containerElement);
});
afterEach(() => {
containerElement?.remove();
});

it("supports className", () => {
const wrapper = mount(<Section className="foo" />, {
attachTo: containerElement,
});
assert.isTrue(wrapper.find(`.${Classes.SECTION}`).hostNodes().exists());
assert.isTrue(wrapper.find(`.foo`).hostNodes().exists());
});

it("supports icon", () => {
const wrapper = mount(<Section icon={IconNames.GRAPH} title="title" />, {
attachTo: containerElement,
});
assert.isTrue(wrapper.find(`[data-icon="${IconNames.GRAPH}"]`).exists());
});

it("renders optional title element", () => {
const wrapper = mount(<Section title="title" />, {
attachTo: containerElement,
});
assert.isTrue(wrapper.find(H6).exists());
});

it("renders optional sub-title element", () => {
const wrapper = mount(<Section title="title" subtitle="subtitle" />, {
attachTo: containerElement,
});
assert.isTrue(wrapper.find(`.${Classes.SECTION_HEADER_SUB_TITLE}`).hostNodes().exists());
});

it("collapsible is open when defaultIsOpen={undefined}", () => {
const wrapper = mount(
<Section collapsible={true} collapseProps={{ defaultIsOpen: undefined }}>
<SectionCard>is open</SectionCard>
</Section>,
{
attachTo: containerElement,
},
);
assert.isTrue(wrapper.find(`[data-icon="${IconNames.CHEVRON_UP}"]`).exists());
});

it("collapsible is open when defaultIsOpen={true}", () => {
const wrapper = mount(
<Section collapsible={true} collapseProps={{ defaultIsOpen: true }}>
<SectionCard>is open</SectionCard>
</Section>,
{
attachTo: containerElement,
},
);
assert.isTrue(wrapper.find(`[data-icon="${IconNames.CHEVRON_UP}"]`).exists());
});

it("collapsible is closed when defaultIsOpen={false}", () => {
const wrapper = mount(
<Section collapsible={true} collapseProps={{ defaultIsOpen: false }}>
<SectionCard>is closed</SectionCard>
</Section>,
{
attachTo: containerElement,
},
);

assert.isTrue(wrapper.find(`[data-icon="${IconNames.CHEVRON_DOWN}"]`).exists());
});
});
20 changes: 19 additions & 1 deletion packages/docs-app/src/examples/core-examples/sectionExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
EditableText,
Elevation,
H5,
H6,
Label,
Section,
SectionCard,
Expand All @@ -35,6 +36,7 @@ import { IconNames } from "@blueprintjs/icons";

export interface SectionExampleState {
collapsible: boolean;
defaultIsOpen: boolean;
elevation: SectionElevation;
hasDescription: boolean;
hasIcon: boolean;
Expand All @@ -54,6 +56,7 @@ const BASIL_DESCRIPTION_TEXT = dedent`
export class SectionExample extends React.PureComponent<ExampleProps, SectionExampleState> {
public state: SectionExampleState = {
collapsible: false,
defaultIsOpen: true,
elevation: Elevation.ZERO,
hasDescription: false,
hasIcon: false,
Expand All @@ -68,6 +71,7 @@ export class SectionExample extends React.PureComponent<ExampleProps, SectionExa
public render() {
const {
collapsible,
defaultIsOpen,
elevation,
hasDescription,
hasIcon,
Expand All @@ -82,9 +86,15 @@ export class SectionExample extends React.PureComponent<ExampleProps, SectionExa
<H5>Section Props</H5>
<Switch checked={isCompact} label="Compact" onChange={this.toggleIsCompact} />
<Switch checked={hasIcon} label="Icon" onChange={this.toggleHasIcon} />
<Switch checked={hasDescription} label="Description" onChange={this.toggleHasDescription} />
<Switch checked={hasDescription} label="Sub-title" onChange={this.toggleHasDescription} />
<Switch checked={hasRightElement} label="Right element" onChange={this.toggleHasRightElement} />
<Switch checked={collapsible} label="Collapsible" onChange={this.toggleCollapsible} />
{collapsible && (
<>
<H6>Collapse Props</H6>
<Switch checked={defaultIsOpen} label="Default is open" onChange={this.toggleDefaultIsOpen} />
</>
)}
<Label>
Elevation
<Slider
Expand Down Expand Up @@ -134,8 +144,14 @@ export class SectionExample extends React.PureComponent<ExampleProps, SectionExa
return (
<Example options={options} {...this.props}>
<Section
// A `key` is provided here to force the component to
// re-mount when `defaultIsOpen` is changed, otherwise
// the local state in the `Collapse` component is not
// updated.
key={String(defaultIsOpen)}
collapsible={collapsible}
compact={isCompact}
collapseProps={{ defaultIsOpen }}
elevation={elevation}
icon={hasIcon ? IconNames.BOOK : undefined}
rightElement={
Expand Down Expand Up @@ -170,6 +186,8 @@ export class SectionExample extends React.PureComponent<ExampleProps, SectionExa

private toggleCollapsible = () => this.setState({ collapsible: !this.state.collapsible });

private toggleDefaultIsOpen = () => this.setState({ defaultIsOpen: !this.state.defaultIsOpen });

private togglePanelIsPadded = () => this.setState({ isPanelPadded: !this.state.isPanelPadded });

private handleElevationChange = (elevation: SectionElevation) => this.setState({ elevation });
Expand Down

1 comment on commit f113fc1

@adidahiya
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix(Section): defaultIsOpen should behave as expected (#6326)

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.