Skip to content

Commit

Permalink
Allow EuiAccordion state to be controlled from outside (#3240)
Browse files Browse the repository at this point in the history
* Allow  state to be controlled from outside

* CL

* snippet

* prop definition

* added test, snap

* allowed state change from outside

* changed prop name, used toggle buttons

* left out changes

* fixed all errors

* updated Snap, Example description

* example added

* updated snippet

* changed accordion title to more relevant

* Update src-docs/src/views/accordion/accordion_example.js

Co-Authored-By: Chandler Prall <chandler.prall@gmail.com>

* Update src-docs/src/views/accordion/accordion_example.js

Co-Authored-By: Chandler Prall <chandler.prall@gmail.com>

* Update CHANGELOG.md

Co-Authored-By: Chandler Prall <chandler.prall@gmail.com>

* fixed prettier issue

* fixed accessibility issues

* fixed id problem

Co-authored-by: Chandler Prall <chandler.prall@gmail.com>
  • Loading branch information
anishagg17 and chandlerprall authored Apr 10, 2020
1 parent 6f44f92 commit 60f885f
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [`master`](https://github.com/elastic/eui/tree/master)

- Added `forceState` prop to control `EuiAccordion` state from outside ([#3240](https://github.com/elastic/eui/pull/3240))
- Fixed the inline styles being overwritten by consumer-passed inline styles in EuiBadge ([#3284](https://github.com/elastic/eui/pull/3284))
- Added support for `href`, `onClick`, and related props in `EuiBasicTable` default actions ([#3115](https://github.com/elastic/eui/pull/3115))
- Added support for `EuiCodeEditor` to set `readonly` and `id` on `<textarea />` ([#3212](https://github.com/elastic/eui/pull/3212))
Expand Down
32 changes: 32 additions & 0 deletions src-docs/src/views/accordion/accordion_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ import AccordionGrow from './accordion_grow';
const accordionGrowSource = require('!!raw-loader!./accordion_grow');
const accordionGrowHtml = renderToHtml(AccordionGrow);

import AccordionForceState from './accordion_forceState';
const accordionForceStateSource = require('!!raw-loader!./accordion_forceState');
const accordionForceStateHtml = renderToHtml(AccordionForceState);
const accordionForceStateSnippet = `<EuiAccordion
id={accordionId}
forceState="open"
buttonContent="Controlled via outside prop">
<!-- Content to show when expanded -->
</EuiAccordion>`;

export const AccordionExample = {
title: 'Accordion',
intro: (
Expand Down Expand Up @@ -279,6 +289,7 @@ export const AccordionExample = {
snippet: accordionCallbackSnippet,
demo: <AccordionCallback />,
},

{
title: 'Accordion content can dynamically change height',
source: [
Expand Down Expand Up @@ -320,5 +331,26 @@ export const AccordionExample = {
),
demo: <AccordionForm />,
},
{
title: 'Force accordion state',
source: [
{
type: GuideSectionTypes.JS,
code: accordionForceStateSource,
},
{
type: GuideSectionTypes.HTML,
code: accordionForceStateHtml,
},
],
text: (
<p>
Use the <EuiCode>forceState</EuiCode> prop to control open and close
state.
</p>
),
snippet: accordionForceStateSnippet,
demo: <AccordionForceState />,
},
],
};
54 changes: 54 additions & 0 deletions src-docs/src/views/accordion/accordion_forceState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { useState } from 'react';

import {
EuiAccordion,
EuiText,
EuiButtonGroup,
EuiSpacer,
} from '../../../../src/components';
import { htmlIdGenerator } from '../../../../src/services';

const idPrefix = htmlIdGenerator()();

export default () => {
const [trigger, setTrigger] = useState('closed');
const [toggleIdSelected, setID] = useState(`${idPrefix}--closed`);
const toggleButtons = [
{
id: `${idPrefix}--open`,
label: 'Open',
},
{
id: `${idPrefix}--closed`,
label: 'Close',
},
];

const onChange = id => {
setTrigger(id === toggleButtons[0].id ? 'open' : 'closed');
setID(id);
};

return (
<div>
<EuiButtonGroup
legend="This is a basic group"
options={toggleButtons}
idSelected={toggleIdSelected}
onChange={onChange}
/>
<EuiSpacer />
<EuiAccordion
id="accordion--forceState"
forceState={trigger}
buttonContent="I am controlled via prop">
<EuiText>
<p>
Any content inside of <strong>EuiAccordion</strong> will appear
here.
</p>
</EuiText>
</EuiAccordion>
</div>
);
};
55 changes: 49 additions & 6 deletions src/components/accordion/__snapshots__/accordion.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
exports[`EuiAccordion behavior closes when clicked twice 1`] = `
<EuiAccordion
arrowDisplay="left"
id="8"
id="9"
initialIsOpen={false}
paddingSize="none"
>
Expand All @@ -14,7 +14,7 @@ exports[`EuiAccordion behavior closes when clicked twice 1`] = `
className="euiAccordion__triggerWrapper"
>
<button
aria-controls="8"
aria-controls="9"
aria-expanded={false}
className="euiAccordion__button"
onClick={[Function]}
Expand Down Expand Up @@ -42,7 +42,7 @@ exports[`EuiAccordion behavior closes when clicked twice 1`] = `
</div>
<div
className="euiAccordion__childWrapper"
id="8"
id="9"
>
<EuiResizeObserver
onResize={[Function]}
Expand All @@ -61,7 +61,7 @@ exports[`EuiAccordion behavior closes when clicked twice 1`] = `
exports[`EuiAccordion behavior opens when clicked once 1`] = `
<EuiAccordion
arrowDisplay="left"
id="7"
id="8"
initialIsOpen={false}
paddingSize="none"
>
Expand All @@ -72,7 +72,7 @@ exports[`EuiAccordion behavior opens when clicked once 1`] = `
className="euiAccordion__triggerWrapper"
>
<button
aria-controls="7"
aria-controls="8"
aria-expanded={true}
className="euiAccordion__button"
onClick={[Function]}
Expand Down Expand Up @@ -100,7 +100,7 @@ exports[`EuiAccordion behavior opens when clicked once 1`] = `
</div>
<div
className="euiAccordion__childWrapper"
id="7"
id="8"
>
<EuiResizeObserver
onResize={[Function]}
Expand Down Expand Up @@ -363,6 +363,49 @@ exports[`EuiAccordion props extraAction is rendered 1`] = `
</div>
`;

exports[`EuiAccordion props forceState is rendered 1`] = `
<div
class="euiAccordion"
>
<div
class="euiAccordion__triggerWrapper"
>
<button
aria-controls="7"
aria-expanded="false"
class="euiAccordion__button"
type="button"
>
<span
class="euiAccordion__iconWrapper"
>
<div
class="euiAccordion__icon"
data-euiicon-type="arrowRight"
/>
</span>
<span
class="euiIEFlexWrapFix"
/>
</button>
</div>
<div
class="euiAccordion__childWrapper"
id="7"
>
<div>
<div
class=""
>
<p>
You can not see me
</p>
</div>
</div>
</div>
</div>
`;

exports[`EuiAccordion props initialIsOpen is rendered 1`] = `
<div
class="euiAccordion euiAccordion-isOpen"
Expand Down
12 changes: 12 additions & 0 deletions src/components/accordion/accordion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ describe('EuiAccordion', () => {
expect(component).toMatchSnapshot();
});
});

describe('forceState', () => {
it('is rendered', () => {
const component = render(
<EuiAccordion id={getId()} forceState="closed">
<p>You can not see me</p>
</EuiAccordion>
);

expect(component).toMatchSnapshot();
});
});
});

describe('behavior', () => {
Expand Down
23 changes: 18 additions & 5 deletions src/components/accordion/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export type EuiAccordionProps = HTMLAttributes<HTMLDivElement> &
* Placing on the `right` doesn't work with `extraAction` and so it will be ignored
*/
arrowDisplay?: 'left' | 'right' | 'none';
/**
* Control the opening of accordin via prop
*/
forceState?: 'closed' | 'open';
};

export class EuiAccordion extends Component<
Expand All @@ -70,13 +74,17 @@ export class EuiAccordion extends Component<
childWrapper: HTMLDivElement | null = null;

state = {
isOpen: this.props.initialIsOpen,
isOpen: this.props.forceState
? this.props.forceState === 'open'
: this.props.initialIsOpen,
};

setChildContentHeight = () => {
const { forceState } = this.props;
requestAnimationFrame(() => {
const height =
this.childContent && this.state.isOpen
this.childContent &&
(forceState ? forceState === 'open' : this.state.isOpen)
? this.childContent.clientHeight
: 0;
this.childWrapper &&
Expand All @@ -93,6 +101,8 @@ export class EuiAccordion extends Component<
}

onToggle = () => {
const { forceState } = this.props;
if (forceState) return this.setState({ isOpen: forceState === 'open' });
this.setState(
prevState => ({
isOpen: !prevState.isOpen,
Expand All @@ -119,13 +129,16 @@ export class EuiAccordion extends Component<
paddingSize,
initialIsOpen,
arrowDisplay,
forceState,
...rest
} = this.props;

const isOpen = forceState ? forceState === 'open' : this.state.isOpen;

const classes = classNames(
'euiAccordion',
{
'euiAccordion-isOpen': this.state.isOpen,
'euiAccordion-isOpen': isOpen,
},
className
);
Expand All @@ -143,7 +156,7 @@ export class EuiAccordion extends Component<
);

const iconClasses = classNames('euiAccordion__icon', {
'euiAccordion__icon-isOpen': this.state.isOpen,
'euiAccordion__icon-isOpen': isOpen,
});

let icon;
Expand All @@ -168,7 +181,7 @@ export class EuiAccordion extends Component<
<div className="euiAccordion__triggerWrapper">
<button
aria-controls={id}
aria-expanded={!!this.state.isOpen}
aria-expanded={isOpen}
onClick={this.onToggle}
className={buttonClasses}
type="button">
Expand Down

0 comments on commit 60f885f

Please sign in to comment.