From 62408113cb3f92c46a45457e30bdbf139a9ec61d Mon Sep 17 00:00:00 2001
From: Rory Hunter
Date: Mon, 2 Apr 2018 11:59:40 +0100
Subject: [PATCH 1/3] Allow accordions to change height while open, and permit
value on radio inputs
---
CHANGELOG.md | 8 ++-
src/components/accordion/accordion.js | 18 ++---
.../radio/__snapshots__/radio.test.js.snap | 16 +++++
.../__snapshots__/radio_group.test.js.snap | 45 +++++++++++++
src/components/form/radio/radio.js | 3 +
src/components/form/radio/radio.test.js | 9 +++
src/components/form/radio/radio_group.js | 4 +-
src/components/form/radio/radio_group.test.js | 66 ++++++++++++++++++-
8 files changed, 158 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d78cfdca77..dbc28eeb80c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
# [`master`](https://github.com/elastic/eui/tree/master)
-No public interface changes since `0.0.38`.
+**Bug fixes**
+
+- Allow accordions to dynamically change height, and support values on radio inputs ([#613](https://github.com/elastic/eui/pull/613))
+
+**Breaking changes**
+
+- `EuiSelect` can pass any node as a value rather than just a string ([603](https://github.com/elastic/eui/pull/603))
# [`0.0.38`](https://github.com/elastic/eui/tree/v0.0.38)
diff --git a/src/components/accordion/accordion.js b/src/components/accordion/accordion.js
index 90abdad0ecf..23462412cbe 100644
--- a/src/components/accordion/accordion.js
+++ b/src/components/accordion/accordion.js
@@ -24,18 +24,20 @@ export class EuiAccordion extends Component {
this.onToggle = this.onToggle.bind(this);
}
+ componentDidUpdate() {
+ const height = this.state.isOpen ? this.childContent.clientHeight: 0
+
+ this.childWrapper.setAttribute('style', `height: ${height}px`);
+ }
+
onToggle() {
- const currentState = this.state.isOpen;
- const height = this.childContent.clientHeight;
+ const isOpen = !this.state.isOpen;
this.setState({
- isOpen: !currentState,
+ isOpen: isOpen,
});
- if (!currentState) {
- this.childWrapper.setAttribute('style', `height: ${height}px`);
- } else {
- this.childWrapper.setAttribute('style', `height: 0px`);
- }
+ const height = isOpen ? this.childContent.clientHeight : 0;
+ this.childWrapper.setAttribute('style', `height: ${height}px`);
}
render() {
diff --git a/src/components/form/radio/__snapshots__/radio.test.js.snap b/src/components/form/radio/__snapshots__/radio.test.js.snap
index 005cb1b2df7..c8185389f55 100644
--- a/src/components/form/radio/__snapshots__/radio.test.js.snap
+++ b/src/components/form/radio/__snapshots__/radio.test.js.snap
@@ -71,3 +71,19 @@ exports[`EuiRadio props label is rendered 1`] = `
`;
+
+exports[`EuiRadio props value is rendered 1`] = `
+
+`;
diff --git a/src/components/form/radio/__snapshots__/radio_group.test.js.snap b/src/components/form/radio/__snapshots__/radio_group.test.js.snap
index 59d16f99958..eafc29ab7fe 100644
--- a/src/components/form/radio/__snapshots__/radio_group.test.js.snap
+++ b/src/components/form/radio/__snapshots__/radio_group.test.js.snap
@@ -133,3 +133,48 @@ exports[`EuiRadioGroup props options are rendered 1`] = `
`;
+
+exports[`EuiRadioGroup props value is propagated to radios 1`] = `
+
+`;
diff --git a/src/components/form/radio/radio.js b/src/components/form/radio/radio.js
index a4798fb5923..a6ec70fa3fd 100644
--- a/src/components/form/radio/radio.js
+++ b/src/components/form/radio/radio.js
@@ -8,6 +8,7 @@ export const EuiRadio = ({
name,
checked,
label,
+ value,
onChange,
disabled,
...rest
@@ -43,6 +44,7 @@ export const EuiRadio = ({
type="radio"
id={id}
name={name}
+ value={value}
checked={checked}
onChange={onChange}
disabled={disabled}
@@ -60,6 +62,7 @@ EuiRadio.propTypes = {
id: PropTypes.string.isRequired,
checked: PropTypes.bool.isRequired,
label: PropTypes.node,
+ value: PropTypes.string,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.bool,
};
diff --git a/src/components/form/radio/radio.test.js b/src/components/form/radio/radio.test.js
index 153f04ac4d8..b0a36b9370c 100644
--- a/src/components/form/radio/radio.test.js
+++ b/src/components/form/radio/radio.test.js
@@ -33,6 +33,15 @@ describe('EuiRadio', () => {
.toMatchSnapshot();
});
+ test('value is rendered', () => {
+ const component = render(
+ {}} value={'bobbins'}/>
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+
test('disabled is rendered', () => {
const component = render(
{}} disabled/>
diff --git a/src/components/form/radio/radio_group.js b/src/components/form/radio/radio_group.js
index 044bd204142..d8e2f09e327 100644
--- a/src/components/form/radio/radio_group.js
+++ b/src/components/form/radio/radio_group.js
@@ -22,8 +22,9 @@ export const EuiRadioGroup = ({
name={name}
checked={option.id === idSelected}
label={option.label}
+ value={option.value}
disabled={disabled}
- onChange={onChange.bind(null, option.id)}
+ onChange={onChange.bind(null, option.value || option.id)}
/>
);
})}
@@ -35,6 +36,7 @@ EuiRadioGroup.propTypes = {
PropTypes.shape({
id: PropTypes.string.isRequired,
label: PropTypes.node,
+ value: PropTypes.string,
}),
).isRequired,
idSelected: PropTypes.string,
diff --git a/src/components/form/radio/radio_group.test.js b/src/components/form/radio/radio_group.test.js
index de17a99e389..627f82c5c6f 100644
--- a/src/components/form/radio/radio_group.test.js
+++ b/src/components/form/radio/radio_group.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { render } from 'enzyme';
+import { render, mount } from 'enzyme';
import { requiredProps } from '../../../test';
import { EuiRadioGroup } from './radio_group';
@@ -63,5 +63,69 @@ describe('EuiRadioGroup', () => {
expect(component)
.toMatchSnapshot();
});
+
+ test('value is propagated to radios', () => {
+ const component = render(
+ {}}
+ />
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('callbacks', () => {
+ test('id is used in callbacks when no value is available', () => {
+ const callback = jest.fn()
+
+ const component = mount(
+
+ );
+
+ component.find('input[id="2"]').simulate('change')
+
+ expect(callback.mock.calls.length).toEqual(1);
+ expect(callback.mock.calls[0].length).toBeGreaterThan(0);
+ // Only check the first argument - the callback is also invoked with
+ // the event, but that's not assert-able.
+ expect(callback.mock.calls[0][0]).toEqual('2');
+ });
+
+ test('value is used in callbacks when available', () => {
+ const callback = jest.fn()
+
+ const component = mount(
+
+ );
+
+ component.find('input[id="2"]').simulate('change')
+
+ expect(callback.mock.calls.length).toEqual(1);
+ expect(callback.mock.calls[0].length).toBeGreaterThan(0);
+ // Only check the first argument - the callback is also invoked with
+ // the event, but that's not assert-able.
+ expect(callback.mock.calls[0][0]).toEqual('Value #2');
+ });
});
});
From d9abcefa27d98339f21ff843818a528b43db9886 Mon Sep 17 00:00:00 2001
From: Rory Hunter
Date: Wed, 4 Apr 2018 12:06:28 +0100
Subject: [PATCH 2/3] Address review feedback
---
CHANGELOG.md | 4 ++
.../src/views/accordion/accordion_example.js | 20 +++++++
.../src/views/accordion/accordion_grow.js | 55 +++++++++++++++++++
src/components/accordion/accordion.js | 10 +---
.../form/radio/radio_group.behavior.test.js | 26 ---------
src/components/form/radio/radio_group.js | 2 +-
src/components/form/radio/radio_group.test.js | 16 +++---
7 files changed, 92 insertions(+), 41 deletions(-)
create mode 100644 src-docs/src/views/accordion/accordion_grow.js
delete mode 100644 src/components/form/radio/radio_group.behavior.test.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbc28eeb80c..2a1575bb2de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@
**Breaking changes**
+- Support values on radio inputs. This is breaking because now the second argument to the radio `onChange` callback is the value, which bumps the change event to the third argument ([#613](https://github.com/elastic/eui/pull/613))
+
+**Breaking changes**
+
- `EuiSelect` can pass any node as a value rather than just a string ([603](https://github.com/elastic/eui/pull/603))
# [`0.0.38`](https://github.com/elastic/eui/tree/v0.0.38)
diff --git a/src-docs/src/views/accordion/accordion_example.js b/src-docs/src/views/accordion/accordion_example.js
index d18c4cd39d0..ece47c01b3d 100644
--- a/src-docs/src/views/accordion/accordion_example.js
+++ b/src-docs/src/views/accordion/accordion_example.js
@@ -27,6 +27,10 @@ import AccordionOpen from './accordion_open';
const accordionOpenSource = require('!!raw-loader!./accordion_open');
const accordionOpenHtml = renderToHtml(AccordionOpen);
+import AccordionGrow from './accordion_grow';
+const accordionGrowSource = require('!!raw-loader!./accordion_grow');
+const accordionGrowHtml = renderToHtml(AccordionGrow);
+
export const AccordionExample = {
title: 'Accordion',
sections: [{
@@ -94,6 +98,22 @@ export const AccordionExample = {
),
demo: ,
+ }, {
+ title: 'Accordion content can dynamically change height',
+ source: [{
+ type: GuideSectionTypes.JS,
+ code: accordionGrowSource,
+ }, {
+ type: GuideSectionTypes.HTML,
+ code: accordionGrowHtml,
+ }],
+ text: (
+
+ If an accordion’s content changes height while the accordion is open,
+ it will resize dynamically.
+
+ ),
+ demo: ,
}, {
title: 'Accordion for forms',
source: [{
diff --git a/src-docs/src/views/accordion/accordion_grow.js b/src-docs/src/views/accordion/accordion_grow.js
new file mode 100644
index 00000000000..213d9b75b0a
--- /dev/null
+++ b/src-docs/src/views/accordion/accordion_grow.js
@@ -0,0 +1,55 @@
+import React, { Component } from 'react';
+
+import {
+ EuiAccordion,
+ EuiButton,
+ EuiSpacer,
+ EuiText,
+} from '../../../../src/components';
+
+
+class AccordionGrow extends Component {
+ state = {
+ counter: 1
+ }
+
+ render() {
+
+ const rows = []
+ for (let i = 1; i <= this.state.counter; i++) {
+ rows.push(Row {i}
);
+ }
+
+ return (
+
+
+
+
+ this.onIncrease()}>Increase height
+ {' '}
+ this.onDecrease()}>Decrease height
+
+ { rows }
+
+
+ );
+ }
+
+ onIncrease() {
+ this.setState(prevState => ({
+ counter: prevState.counter + 1
+ }))
+ }
+
+ onDecrease() {
+ this.setState(prevState => ({
+ counter: Math.max(0, prevState.counter - 1)
+ }))
+ }
+}
+
+export default AccordionGrow;
diff --git a/src/components/accordion/accordion.js b/src/components/accordion/accordion.js
index 23462412cbe..3420b95dd68 100644
--- a/src/components/accordion/accordion.js
+++ b/src/components/accordion/accordion.js
@@ -31,13 +31,9 @@ export class EuiAccordion extends Component {
}
onToggle() {
- const isOpen = !this.state.isOpen;
- this.setState({
- isOpen: isOpen,
- });
-
- const height = isOpen ? this.childContent.clientHeight : 0;
- this.childWrapper.setAttribute('style', `height: ${height}px`);
+ this.setState(prevState => ({
+ isOpen: !prevState.isOpen
+ }));
}
render() {
diff --git a/src/components/form/radio/radio_group.behavior.test.js b/src/components/form/radio/radio_group.behavior.test.js
deleted file mode 100644
index 74664870feb..00000000000
--- a/src/components/form/radio/radio_group.behavior.test.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import sinon from 'sinon';
-
-import { EuiRadioGroup } from './radio_group';
-
-// This exists because we need to run the following tests
-// without mocking the Radio component, such as testing
-// an interaction that is handled by the Radio component.
-describe('EuiRadioGroup behavior', () => {
- test('id is bound to onChange', () => {
- const onChangeHandler = sinon.stub();
- const component = mount(
-
- );
-
- component.find('input[type="radio"]').simulate('change');
- sinon.assert.calledWith(onChangeHandler, '1');
- });
-});
diff --git a/src/components/form/radio/radio_group.js b/src/components/form/radio/radio_group.js
index d8e2f09e327..f4be843b6b0 100644
--- a/src/components/form/radio/radio_group.js
+++ b/src/components/form/radio/radio_group.js
@@ -24,7 +24,7 @@ export const EuiRadioGroup = ({
label={option.label}
value={option.value}
disabled={disabled}
- onChange={onChange.bind(null, option.value || option.id)}
+ onChange={onChange.bind(null, option.id, option.value)}
/>
);
})}
diff --git a/src/components/form/radio/radio_group.test.js b/src/components/form/radio/radio_group.test.js
index 627f82c5c6f..838872f1002 100644
--- a/src/components/form/radio/radio_group.test.js
+++ b/src/components/form/radio/radio_group.test.js
@@ -99,10 +99,11 @@ describe('EuiRadioGroup', () => {
component.find('input[id="2"]').simulate('change')
expect(callback.mock.calls.length).toEqual(1);
- expect(callback.mock.calls[0].length).toBeGreaterThan(0);
- // Only check the first argument - the callback is also invoked with
- // the event, but that's not assert-able.
+ expect(callback.mock.calls[0].length).toEqual(3);
+
expect(callback.mock.calls[0][0]).toEqual('2');
+ expect(callback.mock.calls[0][1]).toBeUndefined();
+ // The callback is also invoked with the event, but that's not assert-able.
});
test('value is used in callbacks when available', () => {
@@ -122,10 +123,11 @@ describe('EuiRadioGroup', () => {
component.find('input[id="2"]').simulate('change')
expect(callback.mock.calls.length).toEqual(1);
- expect(callback.mock.calls[0].length).toBeGreaterThan(0);
- // Only check the first argument - the callback is also invoked with
- // the event, but that's not assert-able.
- expect(callback.mock.calls[0][0]).toEqual('Value #2');
+ expect(callback.mock.calls[0].length).toEqual(3);
+
+ expect(callback.mock.calls[0][0]).toEqual('2');
+ expect(callback.mock.calls[0][1]).toEqual('Value #2');
+ // The callback is also invoked with the event, but that's not assert-able.
});
});
});
From 56cb55b70dd4786032001daba747780a9acd1ab9 Mon Sep 17 00:00:00 2001
From: Rory Hunter
Date: Wed, 4 Apr 2018 20:34:54 +0100
Subject: [PATCH 3/3] Address review feedback
---
CHANGELOG.md | 5 +---
.../src/views/accordion/accordion_grow.js | 7 +++--
.../__snapshots__/accordion.test.js.snap | 18 ++++++-------
src/components/accordion/accordion.js | 4 +--
src/components/form/radio/radio_group.test.js | 26 +++++++------------
5 files changed, 25 insertions(+), 35 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a1575bb2de..17adfd2e670 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,15 +3,12 @@
**Bug fixes**
- Allow accordions to dynamically change height, and support values on radio inputs ([#613](https://github.com/elastic/eui/pull/613))
+- Accordion toggle layout is no longer flagged responsive, in order to prevent unwanted stacking on mobile ([#613](https://github.com/elastic/eui/pull/613))
**Breaking changes**
- Support values on radio inputs. This is breaking because now the second argument to the radio `onChange` callback is the value, which bumps the change event to the third argument ([#613](https://github.com/elastic/eui/pull/613))
-**Breaking changes**
-
-- `EuiSelect` can pass any node as a value rather than just a string ([603](https://github.com/elastic/eui/pull/603))
-
# [`0.0.38`](https://github.com/elastic/eui/tree/v0.0.38)
- Modified drop shadow intensities and color. ([#607](https://github.com/elastic/eui/pull/607))
diff --git a/src-docs/src/views/accordion/accordion_grow.js b/src-docs/src/views/accordion/accordion_grow.js
index 213d9b75b0a..b8767c1933b 100644
--- a/src-docs/src/views/accordion/accordion_grow.js
+++ b/src-docs/src/views/accordion/accordion_grow.js
@@ -14,8 +14,7 @@ class AccordionGrow extends Component {
}
render() {
-
- const rows = []
+ const rows = [];
for (let i = 1; i <= this.state.counter; i++) {
rows.push(Row {i}
);
}
@@ -42,13 +41,13 @@ class AccordionGrow extends Component {
onIncrease() {
this.setState(prevState => ({
counter: prevState.counter + 1
- }))
+ }));
}
onDecrease() {
this.setState(prevState => ({
counter: Math.max(0, prevState.counter - 1)
- }))
+ }));
}
}
diff --git a/src/components/accordion/__snapshots__/accordion.test.js.snap b/src/components/accordion/__snapshots__/accordion.test.js.snap
index 813c6c91220..532cc47649b 100644
--- a/src/components/accordion/__snapshots__/accordion.test.js.snap
+++ b/src/components/accordion/__snapshots__/accordion.test.js.snap
@@ -37,11 +37,11 @@ exports[`EuiAccordion behavior closes when clicked twice 1`] = `
component="div"
gutterSize="s"
justifyContent="flexStart"
- responsive={true}
+ responsive={false}
wrap={false}
>
-
+
{icon}
diff --git a/src/components/form/radio/radio_group.test.js b/src/components/form/radio/radio_group.test.js
index 838872f1002..8c7e67dc755 100644
--- a/src/components/form/radio/radio_group.test.js
+++ b/src/components/form/radio/radio_group.test.js
@@ -83,7 +83,7 @@ describe('EuiRadioGroup', () => {
describe('callbacks', () => {
test('id is used in callbacks when no value is available', () => {
- const callback = jest.fn()
+ const callback = jest.fn();
const component = mount(
{
/>
);
- component.find('input[id="2"]').simulate('change')
+ component.find('input[id="2"]').simulate('change');
- expect(callback.mock.calls.length).toEqual(1);
- expect(callback.mock.calls[0].length).toEqual(3);
-
- expect(callback.mock.calls[0][0]).toEqual('2');
- expect(callback.mock.calls[0][1]).toBeUndefined();
- // The callback is also invoked with the event, but that's not assert-able.
+ expect(callback).toHaveBeenCalledTimes(1);
+ const event = expect.any(Object);
+ expect(callback).toHaveBeenCalledWith('2', undefined, event);
});
test('value is used in callbacks when available', () => {
- const callback = jest.fn()
+ const callback = jest.fn();
const component = mount(
{
/>
);
- component.find('input[id="2"]').simulate('change')
-
- expect(callback.mock.calls.length).toEqual(1);
- expect(callback.mock.calls[0].length).toEqual(3);
+ component.find('input[id="2"]').simulate('change');
- expect(callback.mock.calls[0][0]).toEqual('2');
- expect(callback.mock.calls[0][1]).toEqual('Value #2');
- // The callback is also invoked with the event, but that's not assert-able.
+ expect(callback).toHaveBeenCalledTimes(1);
+ const event = expect.any(Object);
+ expect(callback).toHaveBeenCalledWith('2', 'Value #2', event);
});
});
});