Skip to content

Commit

Permalink
Add CheckableCard component (#2555)
Browse files Browse the repository at this point in the history
* Add CheckablePanel and type Checkbox and CheckboxGroup

* Add changelog entry

* Fix responsiveness

* Update package.json

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox_group.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox_group.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Update src/components/form/checkbox/checkbox_group.tsx

Co-Authored-By: Chandler Prall <[email protected]>

* Address PR feedback

* Cleanup

* Moved example to Card page

* Renamed `EuiCheckablePanel` -> `EuiCheckableCard`

And moved to `card` folder

* Simplify elements and styles

* A11y checks and fixes

* Somewhat unrelated style changes

- Selectable EuiCard title needed underline on hover
- Unified spacing between radio and checkbox items (https://share.getcloudapp.com/YEudqoYp)

* Update CHANGELOG.md

Co-Authored-By: Caroline Horn <[email protected]>

* Remove orphaned package

* Address PR feedback
  • Loading branch information
zinckiwi authored Dec 2, 2019
1 parent d294649 commit 705de53
Show file tree
Hide file tree
Showing 26 changed files with 499 additions and 145 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ No public interface changes since `16.1.0`.
- Added `disabled` prop to the `EuiCheckboxGroup` definition ([#2545](https://github.com/elastic/eui/pull/2545))
- Added `disabled` option to the `option` attribute of the `options` object that is passed to the `EuiCheckboxGroup` so that checkboxes in a group can be individually disabled ([#2548](https://github.com/elastic/eui/pull/2548))
- Added `EuiAspectRatio` component that allows for responsively resizing embeds ([#2535](https://github.com/elastic/eui/pull/2535))
- Added `EuiCheckableCard` component, for radio buttons or checkboxes with complex child content ([#2555](https://github.com/elastic/eui/pull/2555))
- Updated `EuiCheckbox` and `EuiCheckboxGroup` to TypeScript. ([#2555](https://github.com/elastic/eui/pull/2555))
- Added `display` and `titleSize` props to `EuiCard` ([#2566](https://github.com/elastic/eui/pull/2566))
- Added `accessibility` glyph to `EuiIcon` ([#2566](https://github.com/elastic/eui/pull/2566))

Expand Down
101 changes: 101 additions & 0 deletions src-docs/src/views/card/card_checkable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { Component, Fragment } from 'react';

import {
EuiCheckableCard,
EuiSpacer,
EuiRadioGroup,
EuiTitle,
} from '../../../../src/components';

import makeId from '../../../../src/components/form/form_row/make_id';

export default class extends Component {
state = {
radioName: makeId(),
radio: 'radio2',
nestedRadio: 'nestedRadio1',
checkbox: false,
};

render() {
const { radioName } = this.state;

const nestedRadios = [
{
id: 'nestedRadio1',
label: 'Nested option one',
},
{
id: 'nestedRadio2',
label: 'Nested option two',
},
{
id: 'nestedRadio3',
label: 'Nested option three',
},
];

return (
<Fragment>
<fieldset>
<legend>
<EuiTitle size="xs">
<span>Checkable card radio group with legend</span>
</EuiTitle>
</legend>

<EuiSpacer size="m" />

<EuiCheckableCard
id={makeId()}
label="Option one"
name={radioName}
value="radio1"
checked={this.state.radio === 'radio1'}
onChange={() => this.setState({ radio: 'radio1' })}
/>

<EuiSpacer size="m" />

<EuiCheckableCard
id={makeId()}
label="Option two"
name={radioName}
value="radio2"
checked={this.state.radio === 'radio2'}
onChange={() => this.setState({ radio: 'radio2' })}>
<EuiRadioGroup
options={nestedRadios}
idSelected={this.state.nestedRadio}
onChange={nestedRadio => this.setState({ nestedRadio })}
disabled={this.state.radio !== 'radio2'}
/>
</EuiCheckableCard>

<EuiSpacer size="m" />

<EuiCheckableCard
id={makeId()}
label="Option three (disabled)"
name={radioName}
value="radio3"
checked={this.state.radio === 'radio3'}
onChange={() => this.setState({ radio: 'radio3' })}
disabled
/>
</fieldset>

<EuiSpacer size="xl" />

<EuiCheckableCard
id={makeId()}
label="I am a checkbox"
checkableType="checkbox"
value="checkbox1"
checked={this.state.checkbox}
onChange={() => this.setState({ checkbox: !this.state.checkbox })}
/>
</Fragment>
);
}
}
47 changes: 46 additions & 1 deletion src-docs/src/views/card/card_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { renderToHtml } from '../../services';

import { GuideSectionTypes } from '../../components';

import { EuiCode, EuiCard, EuiCallOut } from '../../../../src/components';
import {
EuiCode,
EuiCard,
EuiCallOut,
EuiCheckableCard,
} from '../../../../src/components';

import { EuiCardSelect } from '../../../../src/components/card/card_select';

Expand Down Expand Up @@ -36,6 +41,10 @@ import CardChildren from './card_children';
const cardChildrenSource = require('!!raw-loader!./card_children');
const cardChildrenHtml = renderToHtml(CardChildren);

import CardCheckable from './card_checkable';
const cardCheckableSource = require('!!raw-loader!./card_checkable');
const cardCheckableHtml = renderToHtml(CardCheckable);

export const CardExample = {
title: 'Card',
sections: [
Expand Down Expand Up @@ -293,6 +302,42 @@ export const CardExample = {
/>}
/>`,
},
{
title: 'Checkable',
text: (
<Fragment>
<p>
<EuiCode>EuiCheckableCard</EuiCode> wraps an{' '}
<EuiCode>EuiRadio</EuiCode> or <EuiCode>EuiCheckbox</EuiCode> with a
more-prominent panel, allowing for children to be displayed.
</p>
<EuiCallOut
color="warning"
title={
<span>
When used as a radio group, you must provide a{' '}
<EuiCode>fieldset</EuiCode> with a <EuiCode>legend</EuiCode> for
accessibility.
</span>
}
/>
</Fragment>
),
source: [
{
type: GuideSectionTypes.JS,
code: cardCheckableSource,
},
{
type: GuideSectionTypes.HTML,
code: cardCheckableHtml,
},
],
props: {
EuiCheckableCard,
},
demo: <CardCheckable />,
},
{
title: 'Custom children',
source: [
Expand Down
1 change: 1 addition & 0 deletions src-docs/src/views/form_controls/radio_group.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class extends Component {
options={this.radios}
idSelected={this.state.radioIdSelected}
onChange={this.onChange}
name="radio group"
/>

<EuiSpacer size="m" />
Expand Down
1 change: 1 addition & 0 deletions src/components/card/_card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@

&:focus,
&:hover {
.euiCard__title,
.euiCard__titleAnchor,
.euiCard__titleButton {
text-decoration: underline;
Expand Down
1 change: 1 addition & 0 deletions src/components/card/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
@import 'mixins';
@import 'card';
@import 'card_select';
@import 'checkable_card/index';
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiCheckableCard is rendered 1`] = `
<div
class="euiCheckableCard testClass1 testClass2"
>
<div
class="euiCheckableCard__row"
>
<div
class="euiCheckableCard__control"
>
<div
aria-label="aria-label"
class="euiRadio euiRadio--noLabel"
data-test-subj="test subject string"
>
<input
class="euiRadio__input"
id="id"
type="radio"
/>
<div
class="euiRadio__circle"
/>
</div>
</div>
<label
class="euiCheckableCard__label"
for="id"
>
Label
</label>
</div>
</div>
`;

exports[`EuiCheckableCard renders a checkbox when specified 1`] = `
<div
class="euiCheckableCard testClass1 testClass2"
>
<div
class="euiCheckableCard__row"
>
<div
class="euiCheckableCard__control"
>
<div
class="euiCheckbox euiCheckbox--noLabel"
>
<input
aria-label="aria-label"
class="euiCheckbox__input"
data-test-subj="test subject string"
id="id"
type="checkbox"
/>
<div
class="euiCheckbox__square"
/>
</div>
</div>
<label
class="euiCheckableCard__label"
for="id"
>
Label
</label>
</div>
</div>
`;
55 changes: 55 additions & 0 deletions src/components/card/checkable_card/_checkable_card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
@import '../../panel/mixins';

@include euiPanel('euiCheckableCard');

.euiCheckableCard {
transition: border-color $euiAnimSpeedNormal ease-in;
overflow: hidden; // Hides background color inside panel rounded corners

&:not(.euiCheckableCard-isDisabled) {
&:focus-within {
@include euiFocusRing;
}
}
}

.euiCheckableCard-isChecked {
border-color: $euiColorPrimary;
}



.euiCheckableCard__row {
display: flex;
align-items: stretch;
}

.euiCheckableCard__control {
display: flex;
flex: 0 0 $euiSizeXXL;
justify-content: center;
align-items: center;
background-color: map-get($euiCardSelectButtonBackgrounds, 'text');
transition: background-color $euiAnimSpeedNormal ease-in;

.euiCheckableCard-isChecked & {
background-color: map-get($euiCardSelectButtonBackgrounds, 'primary');
}
}

.euiCheckableCard__label {
flex-grow: 1;
font-size: $euiFontSize;
line-height: $euiSizeL;
padding: $euiSizeS $euiSizeS $euiSizeS $euiSize;
cursor: pointer;
}

.euiCheckableCard__label-isDisabled {
color: $euiFormControlDisabledColor;
cursor: not-allowed;
}

.euiCheckableCard__children {
padding: 0 $euiSizeS $euiSizeS $euiSize;
}
1 change: 1 addition & 0 deletions src/components/card/checkable_card/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import 'checkable_card';
33 changes: 33 additions & 0 deletions src/components/card/checkable_card/checkable_card.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { render } from 'enzyme';
import { requiredProps } from '../../../test/required_props';

import { EuiCheckableCard } from './checkable_card';

const checkablePanelRequiredProps = {
label: 'Label',
id: 'id',
onChange: () => {},
};

describe('EuiCheckableCard', () => {
test('is rendered', () => {
const component = render(
<EuiCheckableCard {...requiredProps} {...checkablePanelRequiredProps} />
);

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

test('renders a checkbox when specified', () => {
const component = render(
<EuiCheckableCard
{...requiredProps}
{...checkablePanelRequiredProps}
checkableType="checkbox"
/>
);

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

0 comments on commit 705de53

Please sign in to comment.