diff --git a/package-lock.json b/package-lock.json
index 652e715ae..ea3b51e9b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -244,33 +244,40 @@
"@material/ripple": "^0.39.0"
}
},
- "@material/switch": {
+ "@material/tab": {
"version": "0.39.0",
- "resolved": "https://registry.npmjs.org/@material/switch/-/switch-0.39.0.tgz",
- "integrity": "sha512-CV655JTw10wZpb63L4sKtY1HN6miCFO5gxdNxAS2YXbHTvJShJljvhXZZuJ0SxJRAhqgj6fC/wd3KePZ5F0U0A==",
+ "resolved": "https://registry.npmjs.org/@material/tab/-/tab-0.39.0.tgz",
+ "integrity": "sha512-I7jc7Me6bDhGm9z/3ZCNo6IdE3ZIX3+5tG3yNNvTKC/+x9o72lROY4b+Zhp0YdQvdiKB4ejh+r4FYQ9PAJbULw==",
"dev": true,
"requires": {
- "@material/animation": "^0.39.0",
"@material/base": "^0.39.0",
- "@material/elevation": "^0.39.0",
"@material/ripple": "^0.39.0",
"@material/rtl": "^0.39.0",
- "@material/selection-control": "^0.39.0",
+ "@material/tab-indicator": "^0.39.0",
+ "@material/theme": "^0.39.0",
+ "@material/typography": "^0.39.0"
+ }
+ },
+ "@material/tab-indicator": {
+ "version": "0.39.0",
+ "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-0.39.0.tgz",
+ "integrity": "sha512-VcmPbhWiisCniiR9sVboiS0oCavIiy/w1EPMD1VOZWIMxQk5nGm6R/PNwZONlFaISLRzmOjtimrafsizsUlphw==",
+ "dev": true,
+ "requires": {
+ "@material/animation": "^0.39.0",
+ "@material/base": "^0.39.0",
"@material/theme": "^0.39.0"
}
},
- "@material/tab": {
+ "@material/tab-scroller": {
"version": "0.39.3",
- "resolved": "https://registry.npmjs.org/@material/tab/-/tab-0.39.3.tgz",
- "integrity": "sha512-7MMsO5jSdwlwgEuULBLAdzz8g/jKGkjqCCBgiHMe+E6dNQsQUB9yQyN3EUs+6+nVTdxKGQf5SN7ApFPgtJYMDA==",
+ "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-0.39.3.tgz",
+ "integrity": "sha512-CHWKuzhvR75P6pliLbwBkSIWDbC6YH+DLQEETTmeZXtLXDdvPpNpycfxBRdWK8KuDfhmWOjji+jCx4QYAmILNQ==",
"dev": true,
"requires": {
+ "@material/animation": "^0.39.0",
"@material/base": "^0.39.0",
- "@material/ripple": "^0.39.3",
- "@material/rtl": "^0.39.1",
- "@material/tab-indicator": "^0.39.3",
- "@material/theme": "^0.39.1",
- "@material/typography": "^0.39.0"
+ "@material/tab": "^0.39.3"
},
"dependencies": {
"@material/ripple": {
@@ -290,44 +297,28 @@
"integrity": "sha512-6RcXv6tQladbl+SboEHUykiDAz7dE5DjBuLNV0KD7yEUCcUV41WXZ5e4oUd1nDWnK0vHzFtNM7D6BGMPZ2T3zA==",
"dev": true
},
- "@material/tab-indicator": {
+ "@material/tab": {
"version": "0.39.3",
- "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-0.39.3.tgz",
- "integrity": "sha512-o6RU7PIJrDjbI/Sm//fftNtjl1XUftDKNnZfb27pCzT+IxHRt1prOJmee5y0sZqNAzb9kWb2J8GenIVAmqDzeg==",
+ "resolved": "https://registry.npmjs.org/@material/tab/-/tab-0.39.3.tgz",
+ "integrity": "sha512-7MMsO5jSdwlwgEuULBLAdzz8g/jKGkjqCCBgiHMe+E6dNQsQUB9yQyN3EUs+6+nVTdxKGQf5SN7ApFPgtJYMDA==",
"dev": true,
"requires": {
- "@material/animation": "^0.39.0",
"@material/base": "^0.39.0",
- "@material/theme": "^0.39.1"
+ "@material/ripple": "^0.39.3",
+ "@material/rtl": "^0.39.1",
+ "@material/tab-indicator": "^0.39.3",
+ "@material/theme": "^0.39.1",
+ "@material/typography": "^0.39.0"
}
},
- "@material/theme": {
- "version": "0.39.1",
- "resolved": "https://registry.npmjs.org/@material/theme/-/theme-0.39.1.tgz",
- "integrity": "sha512-c7xjzzHdF4kBl66VJfATBAV9cwD/6TOyVPOWiK5rPxmu9g7uEAe1HmupqJRR1pMFCPX2w85gfvIsxh79EU6YJA==",
- "dev": true
- }
- }
- },
- "@material/tab-bar": {
- "version": "0.39.3",
- "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-0.39.3.tgz",
- "integrity": "sha512-DF33bY39cnoLg0zK9EjXz4GOVj0pk7J8DSup7aLhHr0UgLOEmVX/o3XmQBG7esdSScQcwwluiEiYbwqqYkWcZQ==",
- "dev": true,
- "requires": {
- "@material/base": "^0.39.0",
- "@material/elevation": "^0.39.1",
- "@material/tab": "^0.39.3",
- "@material/tab-scroller": "^0.39.3"
- },
- "dependencies": {
- "@material/elevation": {
- "version": "0.39.1",
- "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-0.39.1.tgz",
- "integrity": "sha512-ir0lqwpkXhJxUQ6KdCTsM2wZze3Oc54knUMbCUnitOWzIRBvlXUO0zlpld5pj3Mb6ApESA6BiaJb4JfEZmX2MA==",
+ "@material/tab-indicator": {
+ "version": "0.39.3",
+ "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-0.39.3.tgz",
+ "integrity": "sha512-o6RU7PIJrDjbI/Sm//fftNtjl1XUftDKNnZfb27pCzT+IxHRt1prOJmee5y0sZqNAzb9kWb2J8GenIVAmqDzeg==",
"dev": true,
"requires": {
"@material/animation": "^0.39.0",
+ "@material/base": "^0.39.0",
"@material/theme": "^0.39.1"
}
},
@@ -339,28 +330,6 @@
}
}
},
- "@material/tab-indicator": {
- "version": "0.39.0",
- "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-0.39.0.tgz",
- "integrity": "sha512-VcmPbhWiisCniiR9sVboiS0oCavIiy/w1EPMD1VOZWIMxQk5nGm6R/PNwZONlFaISLRzmOjtimrafsizsUlphw==",
- "dev": true,
- "requires": {
- "@material/animation": "^0.39.0",
- "@material/base": "^0.39.0",
- "@material/theme": "^0.39.0"
- }
- },
- "@material/tab-scroller": {
- "version": "0.39.3",
- "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-0.39.3.tgz",
- "integrity": "sha512-CHWKuzhvR75P6pliLbwBkSIWDbC6YH+DLQEETTmeZXtLXDdvPpNpycfxBRdWK8KuDfhmWOjji+jCx4QYAmILNQ==",
- "dev": true,
- "requires": {
- "@material/animation": "^0.39.0",
- "@material/base": "^0.39.0",
- "@material/tab": "^0.39.3"
- }
- },
"@material/textfield": {
"version": "0.39.0",
"resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-0.39.0.tgz",
@@ -15399,9 +15368,9 @@
"dev": true
},
"uuid": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
- "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
"dev": true
},
"uws": {
diff --git a/package.json b/package.json
index 62e6fd063..abd990fb6 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"allowed": [
"button",
"card",
+ "chips",
"fab",
"floating-label",
"line-ripple",
@@ -104,6 +105,7 @@
"resemblejs": "^2.10.3",
"sass-loader": "^6.0.7",
"testdouble": "^3.6.0",
+ "uuid": "^3.3.2",
"validate-commit-msg": "^2.14.0",
"webpack": "^3.11.0",
"webpack-dev-server": "^2.11.2"
diff --git a/packages/chips/Chip.js b/packages/chips/Chip.js
index 5787f4323..ed27a9895 100644
--- a/packages/chips/Chip.js
+++ b/packages/chips/Chip.js
@@ -27,6 +27,7 @@ import withRipple from '../ripple';
import {MDCChipFoundation} from '@material/chips/dist/mdc.chips';
export class Chip extends Component {
+ chipElement_ = null;
foundation_ = null;
state = {
classList: new Set(),
@@ -35,18 +36,28 @@ export class Chip extends Component {
componentDidMount() {
this.foundation_ = new MDCChipFoundation(this.adapter);
this.foundation_.init();
+ this.foundation_.setSelected(this.props.selected);
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.selected !== prevProps.selected) {
+ this.foundation_.setSelected(this.props.selected);
+ }
}
componentWillUnmount() {
this.foundation_.destroy();
}
+ init = (el) => {
+ this.chipElement_ = el;
+ this.props.initRipple(el);
+ }
+
get classes() {
const {classList} = this.state;
- const {className, selected} = this.props;
- return classnames('mdc-chip', Array.from(classList), className, {
- 'mdc-chip--selected': selected,
- });
+ const {className} = this.props;
+ return classnames('mdc-chip', Array.from(classList), className);
}
get adapter() {
@@ -62,63 +73,126 @@ export class Chip extends Component {
this.setState({classList});
},
hasClass: (className) => this.classes.split(' ').includes(className),
+ eventTargetHasClass: (target, className) => target.classList.contains(className),
+ getComputedStyleValue:
+ (propertyName) => window.getComputedStyle(this.chipElement_).getPropertyValue(propertyName),
+ setStyleProperty: (propertyName, value) => this.chipElement_.style.setProperty(propertyName, value),
+ notifyRemoval: () => this.props.handleRemove(this.props.id),
};
}
- handleClick = (e) => {
- if (typeof this.props.onClick === 'function') {
- this.props.onClick(e);
- }
- this.props.handleSelect(this.props.id);
+ onClick = (e) => {
+ const {onClick, handleSelect, id} = this.props;
+ onClick(e);
+ handleSelect(id);
}
+ handleRemoveIconClick = (e) => this.foundation_.handleTrailingIconInteraction(e);
+
+ handleTransitionEnd = (e) => this.foundation_.handleTransitionEnd(e);
+
+ renderLeadingIcon = (leadingIcon) => {
+ const {
+ className,
+ ...otherProps
+ } = leadingIcon.props;
+
+ const props = {
+ className: classnames(
+ className,
+ 'mdc-chip__icon',
+ 'mdc-chip__icon--leading',
+ ),
+ ...otherProps,
+ };
+
+ return React.cloneElement(leadingIcon, props);
+ };
+
+ renderRemoveIcon = (removeIcon) => {
+ const {
+ className,
+ ...otherProps
+ } = removeIcon.props;
+
+ const props = {
+ className: classnames(
+ className,
+ 'mdc-chip__icon',
+ 'mdc-chip__icon--trailing',
+ ),
+ onClick: this.handleRemoveIconClick,
+ onKeyDown: this.handleRemoveIconClick,
+ tabIndex: 0,
+ role: 'button',
+ ...otherProps,
+ };
+
+ return React.cloneElement(removeIcon, props);
+ };
+
render() {
const {
- className, // eslint-disable-line no-unused-vars
- label,
- handleSelect, // eslint-disable-line no-unused-vars
- onClick, // eslint-disable-line no-unused-vars
- chipCheckmark,
- computeBoundingRect, // eslint-disable-line no-unused-vars
+ /* eslint-disable no-unused-vars */
+ id,
+ className,
+ selected,
+ handleSelect,
+ handleRemove,
+ onClick,
+ computeBoundingRect,
initRipple,
- unbounded, // eslint-disable-line no-unused-vars
+ unbounded,
+ /* eslint-enable no-unused-vars */
+ chipCheckmark,
+ leadingIcon,
+ removeIcon,
+ label,
...otherProps
} = this.props;
return (
+ {leadingIcon ? this.renderLeadingIcon(leadingIcon) : null}
{chipCheckmark}
{label}
+ {removeIcon ? this.renderRemoveIcon(removeIcon) : null}
);
}
}
Chip.propTypes = {
- id: PropTypes.number,
+ id: PropTypes.string.isRequired,
label: PropTypes.string,
className: PropTypes.string,
selected: PropTypes.bool,
handleSelect: PropTypes.func,
+ handleRemove: PropTypes.func,
onClick: PropTypes.func,
- // The following props are handled by withRipple and do not require defaults.
initRipple: PropTypes.func,
unbounded: PropTypes.bool,
chipCheckmark: PropTypes.node,
+ leadingIcon: PropTypes.element,
+ removeIcon: PropTypes.element,
computeBoundingRect: PropTypes.func,
};
Chip.defaultProps = {
- id: -1,
label: '',
className: '',
selected: false,
+ onClick: () => {},
+ initRipple: () => {},
handleSelect: () => {},
+ handleRemove: () => {},
};
export default withRipple(Chip);
diff --git a/packages/chips/ChipSet.js b/packages/chips/ChipSet.js
index 6d419b33f..7ddfb6fc5 100644
--- a/packages/chips/ChipSet.js
+++ b/packages/chips/ChipSet.js
@@ -23,24 +23,86 @@
import React, {Component} from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
+import {MDCChipSetFoundation} from '@material/chips';
import ChipCheckmark from './ChipCheckmark';
-import Chip from './Chip';
export default class ChipSet extends Component {
checkmarkWidth_ = 0;
+ foundation_ = null;
+ state = {
+ selectedChipIds: new Set(this.props.selectedChipIds),
+ };
+
+ componentDidMount() {
+ this.foundation_ = new MDCChipSetFoundation(this.adapter);
+ this.foundation_.init();
+ this.updateChipSelection();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.selectedChipIds !== prevProps.selectedChipIds) {
+ this.updateChipSelection();
+ }
+ }
+
+ componentWillUnmount() {
+ this.foundation_.destroy();
+ }
get classes() {
- const {className} = this.props;
- return classnames('mdc-chip-set', className);
+ const {className, filter, choice, input} = this.props;
+ return classnames('mdc-chip-set', className, {
+ 'mdc-chip-set--filter': filter,
+ 'mdc-chip-set--choice': choice,
+ 'mdc-chip-set--input': input,
+ });
}
get adapter() {
return {
hasClass: (className) => this.classes.split(' ').includes(className),
+ setSelected: (chipId, selected) => {
+ const {selectedChipIds} = this.state;
+ if (selected) {
+ selectedChipIds.add(chipId);
+ } else {
+ selectedChipIds.delete(chipId);
+ }
+ this.setState({selectedChipIds});
+ },
};
}
+ updateChipSelection() {
+ React.Children.forEach(this.props.children, (child) => {
+ const {id} = child.props;
+ if (this.props.selectedChipIds.indexOf(id) > -1) {
+ this.foundation_.select(id);
+ } else {
+ this.foundation_.deselect(id);
+ }
+ });
+ }
+
+ handleSelect = (chipId) => {
+ const {handleSelect, choice, filter} = this.props;
+ if (filter || choice) {
+ this.foundation_.toggleSelect(chipId);
+ }
+ handleSelect(this.foundation_.getSelectedChipIds());
+ }
+
+ handleRemove = (chipId) => {
+ const {input, handleRemove} = this.props;
+ if (input) {
+ // this should be calling foundation_.handleChipRemoval, but we would
+ // need to pass evt.detail.chipId
+ this.foundation_.deselect(chipId);
+ }
+ handleRemove(chipId);
+ }
+
setCheckmarkWidth = (checkmark) => {
if (!!this.checkmarkWidth_) {
return;
@@ -49,18 +111,24 @@ export default class ChipSet extends Component {
}
computeBoundingRect = (chipElement) => {
- const height = chipElement.getBoundingClientRect().height;
- const width = chipElement.getBoundingClientRect().width + this.checkmarkWidth_;
+ const {height, width: chipWidth} = chipElement.getBoundingClientRect();
+ const width = chipWidth + this.checkmarkWidth_;
return {height, width};
}
renderChip = (chip) => {
- return (
- : null}
- computeBoundingRect={this.props.filter ? this.computeBoundingRect : null}
- {...chip.props} />
- );
+ const {filter} = this.props;
+ const {selectedChipIds} = this.state;
+
+ const props = Object.assign({
+ selected: selectedChipIds.has(chip.props.id),
+ handleSelect: this.handleSelect,
+ handleRemove: this.handleRemove,
+ chipCheckmark: filter ? : null,
+ computeBoundingRect: filter ? this.computeBoundingRect : null,
+ }, ...chip.props);
+
+ return React.cloneElement(chip, props);
}
render() {
@@ -74,12 +142,22 @@ export default class ChipSet extends Component {
ChipSet.propTypes = {
className: PropTypes.string,
+ selectedChipIds: PropTypes.array,
+ handleSelect: PropTypes.func,
+ handleRemove: PropTypes.func,
+ choice: PropTypes.bool,
filter: PropTypes.bool,
+ input: PropTypes.bool,
children: PropTypes.node,
};
ChipSet.defaultProps = {
className: '',
+ selectedChipIds: [],
+ handleSelect: () => {},
+ handleRemove: () => {},
+ choice: false,
filter: false,
+ input: false,
children: null,
};
diff --git a/packages/chips/README.md b/packages/chips/README.md
index d8912d64e..c96200327 100644
--- a/packages/chips/README.md
+++ b/packages/chips/README.md
@@ -32,8 +32,8 @@ class MyApp extends Component {
render() {
return (
-
-
+
+
);
}
@@ -51,27 +51,19 @@ There are two types of chips that allow for selection: [choice chips](https://ma
```js
class MyChoiceChips extends React.Component {
state = {
- selectedChipId: -1,
+ selectedChipIds: ['chip2'],
};
- isSelected = (id) => {
- return this.state.selectedChipId === id;
- }
-
- handleSelect = (id) => {
- if (this.isSelected(id)) {
- this.setState({selectedChipId: -1});
- } else {
- this.setState({selectedChipId: id});
- }
- }
-
render() {
return (
-
-
-
-
+ this.setState(selectedChipIds)}
+ >
+
+
+
);
}
@@ -85,30 +77,76 @@ Filter chips include a leading checkmark to indicate selection. To define a set
```js
class MyFilterChips extends React.Component {
state = {
- selectedChipIds: new Set(),
+ selectedChipIds: ['chip1', 'chip2'],
};
- isSelected = (id) => {
- return this.state.selectedChipIds.has(id);
+ render() {
+ return (
+ this.setState(selectedChipIds)}
+ >
+
+
+
+
+ );
}
+}
+```
+
+### Input chips
+
+Input chips are a variant of chips which enable user input by converting text into chips. Chips may be dynamically added and removed from the chip set. To define a set of chips as input chips, add the `input` prop to the `ChipSet`. To remove a chip, pass a callback to the `Chip` through the `handleRemove` prop.
- handleSelect = (id) => {
- const selectedChipIds = new Set(this.state.selectedChipIds);
- if (this.isSelected(id)) {
- selectedChipIds.delete(id);
- } else {
- selectedChipIds.add(id);
+> _NOTE_: We recommend you store an array of chip labels and their respective IDs in the `state` to manage adding/removing chips. Do _NOT_ use the chip's index as its ID or key, because its index may change due to the addition/removal of other chips.
+
+```js
+class MyInputChips extends React.Component {
+ state = {
+ chips: [
+ {label: 'Jane Smith', id: 'janesmith'},
+ {label: 'John Doe', id: 'johndoe'}
+ ],
+ };
+
+ handleKeyDown = (e) => {
+ // If you have a more complex input, you may want to store the value in the state.
+ const label = e.target.value;
+ if (!!label && e.key === 'Enter') {
+ const id = label.replace(/\s/g,'');
+ // Create a new chips array to ensure that a re-render occurs.
+ // See: https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
+ const chips = [...this.state.chips];
+ chips.push({label, id});
+ this.setState({chips});
+ e.target.value = '';
}
- this.setState({selectedChipIds});
+ }
+
+ // Removes the chip element from the page
+ handleRemove = (id) => {
+ const chips = [...this.state.chips];
+ const index = chips.findIndex(chip => chip.id === id);
+ chips.splice(index, 1);
+ this.setState({chips});
}
render() {
return (
-
-
-
-
-
+
+
+
+ {this.state.chips.map((chip) =>
+ }
+ />
+ )}
+
+
);
}
}
@@ -121,7 +159,12 @@ class MyFilterChips extends React.Component {
Prop Name | Type | Description
--- | --- | ---
className | String | Classes to be applied to the chip set element
+selectedChipIds | Array | Array of ids of chips that are selected
+handleSelect | Function(id: string) => void | Callback for selecting the chip with the given id
+handleRemove | Function(id: string) => void | Callback for removing the chip with the given id
+choice | Boolean | Indicates that the chips in the set are choice chips, which allow single selection from a set of options
filter | Boolean | Indicates that the chips in the set are filter chips, which allow multiple selection from a set of options
+input | Boolean | Indicates that the chips in the set are input chips, where chips can be added or removed
### Chip
@@ -129,10 +172,13 @@ filter | Boolean | Indicates that the chips in the set are filter chips, which a
Prop Name | Type | Description
--- | --- | ---
className | String | Classes to be applied to the chip element
-id | Number | Unique identifier for the chip
+id | Number | Required. Unique identifier for the chip
label | String | Text to be shown on the chip
+leadingIcon | Element | An icon element that appears as the leading icon.
+removeIcon | Element | An icon element that appears as the remove icon. Clicking on it should remove the chip.
selected | Boolean | Indicates whether the chip is selected
-handleSelect | Function(id: number) => void | Callback to call when the chip with the given id is selected
+handleSelect | Function(id: string) => void | Callback for selecting the chip with the given id
+handleRemove | Function(id: string) => void | Callback for removing the chip with the given id
## Sass Mixins
diff --git a/packages/chips/package_skip.json b/packages/chips/package.json
similarity index 100%
rename from packages/chips/package_skip.json
rename to packages/chips/package.json
diff --git a/scripts/screenshot-directory-reader.js b/scripts/screenshot-directory-reader.js
index 2c9684288..a66aa73a1 100644
--- a/scripts/screenshot-directory-reader.js
+++ b/scripts/screenshot-directory-reader.js
@@ -1,10 +1,8 @@
const {lstatSync, readdirSync} = require('fs');
const {basename, join, resolve} = require('path');
-const denyList = [
- 'chips',
- 'images'
-];
+const denyList = ['images'];
+
const isDirectory = source => lstatSync(source).isDirectory();
const getDirectories = source =>
readdirSync(source).map(name => join(source, name)).filter(isDirectory);
diff --git a/test/screenshot/chips/index.js b/test/screenshot/chips/index.js
index 3eda2c87b..80f8d79e5 100644
--- a/test/screenshot/chips/index.js
+++ b/test/screenshot/chips/index.js
@@ -2,73 +2,143 @@ import React from 'react';
import './index.scss';
import '../../../packages/chips/index.scss';
-import {Chip, ChipSet} from '../../../packages/chips';
+import MaterialIcon from '../../../packages/material-icon';
+import {Chip, ChipSet} from '../../../packages/chips/index';
+import uuidv1 from 'uuid/v1';
-class ShirtSizes extends React.Component {
+class ChoiceChipsTest extends React.Component {
state = {
- selectedChipId: 0,
+ selectedChipIds: this.props.selectedChipIds, // eslint-disable-line react/prop-types
};
- isSelected = (id) => {
- return this.state.selectedChipId === id;
+ render() {
+ const {children} = this.props; // eslint-disable-line react/prop-types
+ return (
+
+ this.setState({selectedChipIds})}
+ >
+ {children}
+
+
+ );
}
+}
- handleSelect = (id) => {
- if (this.isSelected(id)) {
- this.setState({selectedChipId: -1});
- } else {
- this.setState({selectedChipId: id});
- }
- }
+class FilterChipsTest extends React.Component {
+ state = {
+ selectedChipIds: this.props.selectedChipIds, // eslint-disable-line react/prop-types
+ };
render() {
+ const {children} = this.props; // eslint-disable-line react/prop-types
return (
-
-
-
-
-
+
+ this.setState({selectedChipIds})}
+ >
+ {children}
+
+ this.setState({selectedChipIds: ['2chip', '0chip']})}>
+ Select first and last
+
+
);
}
}
-class ShoppingFilters extends React.Component {
+class InputChipsTest extends React.Component {
state = {
- selectedChipIds: new Set([0, 1]),
+ chips: this.props.labels.map((label) => { // eslint-disable-line react/prop-types
+ return {label: label, id: uuidv1()};
+ }),
};
- isSelected = (id) => {
- return this.state.selectedChipIds.has(id);
+ addChip(label) {
+ // Create a new chips array to ensure that a re-render occurs.
+ // See: https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
+ const chips = [...this.state.chips];
+ chips.push({label, id: uuidv1()});
+ this.setState({chips});
}
- handleSelect = (id) => {
- const selectedChipIds = new Set(this.state.selectedChipIds);
- if (this.isSelected(id)) {
- selectedChipIds.delete(id);
- } else {
- selectedChipIds.add(id);
+ handleKeyDown = (e) => {
+ if (e.key === 'Enter' && e.target.value) {
+ this.addChip(e.target.value);
+ e.target.value = '';
}
- this.setState({selectedChipIds});
}
render() {
return (
-
-
-
-
-
+
+
+ {
+ const {chips} = this.state;
+ for (let i = 0; i < chips.length; i ++) {
+ if (chips[i].id === chipId) {
+ chips.splice(index, 1);
+ this.setState(chips);
+ return;
+ }
+ }
+ }}>
+ {this.state.chips.map((chip) =>
+ }
+ removeIcon={ }
+ />
+ )}
+
+
);
}
}
+const seasons = ['Winter', 'Summer', 'Spring', 'Autumn'];
+const sizes = ['Small', 'Medium', 'Large'];
+const clothes = ['Tops', 'Bottoms', 'Shoes'];
+const contacts = ['Jane Smith', 'John Doe'];
+
+const renderChips = (list) => {
+ return list.map((label, index) => (
+
+ ));
+};
+
const ChipsScreenshotTest = () => {
return (
- Choice chips
-
- Filter chips
-
+ Default Chips
+
+ {renderChips(seasons)}
+
+
+ Choice Chips
+
+ {renderChips(sizes)}
+
+
+ Filter Chips
+
+ {renderChips(clothes)}
+
+
+ Input chips
+
);
};
diff --git a/test/screenshot/golden.json b/test/screenshot/golden.json
index 031492cd9..a15c1d1c5 100644
--- a/test/screenshot/golden.json
+++ b/test/screenshot/golden.json
@@ -2,6 +2,7 @@
"button": "a0cf6b2a5d09400657303bb2a7b220dbe33642a8f44fd7571cdfcf5517fb3586",
"card": "b2fd82763c383be438ff6578083bf9009711c7470333d07eb916ab690fc42d31",
+ "chips": "9cdf56c10f4badd9555433e84a52d52028f286f42392f4902251dcb8eb748e33",
"line-ripple": "56b136db2dc7e09260849447e6bde9b55a837af332a05d9f52506ab1c95e2e57",
"fab": "db36f52195c420062d91dd5ebe5432ad87247b3c1146fd547b0a195079bbce2f",
"floating-label": "1d4d4f2e57e1769b14fc84985d1e6f53410c49aef41c9cf4fde94f938adefe57",
@@ -22,4 +23,4 @@
"top-app-bar/standard": "90534d59d40f8c050aae022e94b0afa022a00d1e00c19e3f92dcc1908efcd831",
"top-app-bar/standardNoActionItems": "9102ece0efc0a040e0dc2b097c66d2ee5ecc9c91759aeb16ea5aa2baabe7307a",
"top-app-bar/standardWithNavigationIconElement": "91b10df9c4faf85ba63c84b1cf82a1c1143a3d8c4ba7ed8af477fabdc65ec25e"
-}
+}
diff --git a/test/screenshot/screenshot-test-urls.js b/test/screenshot/screenshot-test-urls.js
index db76d1fbc..f1b595b7e 100644
--- a/test/screenshot/screenshot-test-urls.js
+++ b/test/screenshot/screenshot-test-urls.js
@@ -3,7 +3,7 @@ import topAppBarVariants from './top-app-bar/variants';
const urls = [
'button',
'card',
- // 'chips',
+ 'chips',
'line-ripple',
'fab',
'floating-label',
diff --git a/test/unit/chips/Chip.test.js b/test/unit/chips/Chip.test.js
index e19a1ce84..c736b0923 100644
--- a/test/unit/chips/Chip.test.js
+++ b/test/unit/chips/Chip.test.js
@@ -1,54 +1,190 @@
-// import React from 'react';
-// import {assert} from 'chai';
-// import {mount} from 'enzyme';
-// import td from 'testdouble';
-// import {Chip, ChipCheckmark} from '../../../packages/chips/index';
-//
-// suite('Chip');
-//
-// test('creates foundation', () => {
-// const wrapper = mount(Hello world );
-// assert.exists(wrapper.instance().foundation_);
-// });
-//
-// test('classNames adds classes', () => {
-// const wrapper = mount( );
-// assert.isTrue(wrapper.hasClass('test-class-name'));
-// });
-//
-// test('renders chip', () => {
-// const wrapper = mount(Hello world );
-// assert.exists(wrapper.find('.mdc-chip'));
-// });
-//
-// test('#adapter.addClass adds class to state.classList', () => {
-// const wrapper = mount(Jane Doe );
-// wrapper.instance().foundation_.adapter_.addClass('test-class-name');
-// assert.isTrue(wrapper.state().classList.has('test-class-name'));
-// });
-//
-// test('#adapter.removeClass removes class from state.classList', () => {
-// const wrapper = mount(Jane Doe );
-// const classList = new Set();
-// classList.add('test-class-name');
-// wrapper.setState({classList});
-// wrapper.instance().foundation_.adapter_.removeClass('test-class-name');
-// assert.isFalse(wrapper.state().classList.has('test-class-name'));
-// });
-//
-// test('on click calls #props.handleSelect', () => {
-// const handleSelect = td.func();
-// const wrapper = mount(Jane Doe );
-// wrapper.simulate('click');
-// td.verify(handleSelect(123));
-// });
-//
-// test('renders chip checkmark if it exists', () => {
-// const wrapper = mount(}>Jane Doe );
-// assert.exists(wrapper.find('.mdc-chip__checkmark'));
-// });
-//
-// test('adds mndc-chip--selected class if selected prop is true', () => {
-// const wrapper = mount(Jane Doe );
-// assert.exists(wrapper.hasClass('.mdc-chip--selected'));
-// });
+import React from 'react';
+import {assert} from 'chai';
+import {mount, shallow} from 'enzyme';
+import td from 'testdouble';
+import {Chip} from '../../../packages/chips/Chip';
+import ChipCheckmark from '../../../packages/chips/ChipCheckmark';
+
+suite('Chip');
+
+test('creates foundation', () => {
+ const wrapper = mount( );
+ assert.exists(wrapper.instance().foundation_);
+});
+
+test('calls setSelected if props.selected is true (#foundation.setSelected)', () => {
+ const wrapper = mount(Hello world );
+ assert.isTrue(wrapper.state().classList.has('mdc-chip--selected'));
+});
+
+test('classNames adds classes', () => {
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.hasClass('test-class-name'));
+});
+
+test('classNames adds classes from state.classList', () => {
+ const wrapper = shallow( );
+ wrapper.setState({classList: new Set(['test-class-name'])});
+ assert.isTrue(wrapper.hasClass('test-class-name'));
+});
+
+test('#adapter.addClass adds class to state.classList', () => {
+ const wrapper = shallow( );
+ wrapper.instance().adapter.addClass('test-class-name');
+ assert.isTrue(wrapper.state().classList.has('test-class-name'));
+});
+
+test('#adapter.removeClass removes class from state.classList', () => {
+ const wrapper = shallow( );
+ const classList = new Set(['test-class-name']);
+ wrapper.setState({classList});
+ wrapper.instance().adapter.removeClass('test-class-name');
+ assert.isFalse(wrapper.state().classList.has('test-class-name'));
+});
+
+test('#adapter.hasClass returns true if component contains class', () => {
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.instance().adapter.hasClass('test-class-name'));
+});
+
+test('#adapter.eventTargetHasClass returns true if element contains class', () => {
+ const wrapper = shallow( );
+ const target = document.createElement('div');
+ target.classList.add('test-class-name');
+ assert.isTrue(wrapper.instance().adapter.eventTargetHasClass(target, 'test-class-name'));
+});
+
+test('#adapter.getComputedStyleValue should get styles from chip element', () => {
+ const div = document.createElement('div');
+ // needs to be attached to real DOM to get width
+ // https://github.com/airbnb/enzyme/issues/1525
+ document.body.append(div);
+ const options = {attachTo: div};
+
+ const wrapper = mount( , options);
+ const width = '10px';
+ const chipElement = wrapper.find('.mdc-chip').getDOMNode();
+ chipElement.style.setProperty('width', width);
+ assert.equal(wrapper.instance().adapter.getComputedStyleValue('width'), width);
+ div.remove();
+});
+
+test('#adapter.setStyleProperty should add styles to chip', () => {
+ const wrapper = mount( );
+ const width = '10px';
+ wrapper.instance().adapter.setStyleProperty('width', width);
+ const chipElement = wrapper.find('.mdc-chip').getDOMNode();
+ assert.equal(chipElement.style.width, width);
+});
+
+test('on click calls #props.onClick', () => {
+ const onClick = td.func();
+ const wrapper = shallow( );
+ const evt = {};
+ wrapper.simulate('click', evt);
+ td.verify(onClick(evt), {times: 1});
+});
+
+test('on click calls #props.handleSelect', () => {
+ const handleSelect = td.func();
+ const wrapper = shallow( );
+ wrapper.simulate('click');
+ td.verify(handleSelect('123'), {times: 1});
+});
+
+test('renders leading icon', () => {
+ const leadingIcon = ;
+ const wrapper = shallow( );
+ assert.equal(wrapper.children().first().type(), 'i');
+});
+
+test('renders leading icon with class name', () => {
+ const leadingIcon = ;
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.children().first().hasClass('leading-icon'));
+});
+
+test('renders leading icon with base class names', () => {
+ const leadingIcon = ;
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.children().first().hasClass('mdc-chip__icon'));
+ assert.isTrue(wrapper.children().first().hasClass('mdc-chip__icon--leading'));
+});
+
+test('renders remove icon', () => {
+ const removeIcon = ;
+ const wrapper = shallow( );
+ assert.equal(wrapper.children().last().type(), 'i');
+});
+
+test('renders remove icon with class name', () => {
+ const removeIcon = ;
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.children().last().hasClass('remove-icon'));
+});
+
+test('renders remove icon with base class names', () => {
+ const removeIcon = ;
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.children().last().hasClass('mdc-chip__icon'));
+ assert.isTrue(wrapper.children().last().hasClass('mdc-chip__icon--trailing'));
+});
+
+test('remove icon click calls #foundation.handleTrailingIconInteraction', () => {
+ const removeIcon = ;
+ const wrapper = shallow( );
+ wrapper.instance().foundation_.handleTrailingIconInteraction = td.func();
+ const evt = {};
+ wrapper.children().last().simulate('click', evt);
+ td.verify(wrapper.instance().foundation_.handleTrailingIconInteraction(evt), {times: 1});
+});
+
+test('remove icon keydown calls #foundation.handleTrailingIconInteraction', () => {
+ const removeIcon = ;
+ const wrapper = shallow( );
+ wrapper.instance().foundation_.handleTrailingIconInteraction = td.func();
+ const evt = {};
+ wrapper.children().last().simulate('keydown', evt);
+ td.verify(wrapper.instance().foundation_.handleTrailingIconInteraction(evt), {times: 1});
+});
+
+test('calls #foundation.handleTransitionEnd on transitionend event', () => {
+ const wrapper = shallow( );
+ wrapper.instance().foundation_.handleTransitionEnd = td.func();
+ const evt = {};
+ wrapper.simulate('transitionend', evt);
+ td.verify(wrapper.instance().foundation_.handleTransitionEnd(evt), {times: 1});
+});
+
+test('renders chip checkmark if it exists', () => {
+ const wrapper = mount(} />);
+ assert.equal(wrapper.find('.mdc-chip__checkmark').length, 1);
+});
+
+test('renders custom chip checkmark', () => {
+ const wrapper = shallow( } />);
+ assert.equal(wrapper.find('.meow-class').length, 1);
+});
+
+test('adds mdc-chip--selected class if selected prop is true', () => {
+ const wrapper = shallow( );
+ assert.exists(wrapper.hasClass('mdc-chip--selected'));
+});
+
+test('renders chip', () => {
+ const wrapper = shallow( );
+ assert.isTrue(wrapper.find('.mdc-chip').length === 1);
+});
+
+test('renders label text', () => {
+ const wrapper = shallow( );
+ assert.equal(wrapper.find('.mdc-chip__text').text(), 'Hello Jane');
+});
+
+test('#componentWillUnmount destroys foundation', () => {
+ const wrapper = shallow( );
+ const foundation = wrapper.instance().foundation_;
+ foundation.destroy = td.func();
+ wrapper.unmount();
+ td.verify(foundation.destroy());
+});
diff --git a/test/unit/chips/ChipCheckmark.test.js b/test/unit/chips/ChipCheckmark.test.js
new file mode 100644
index 000000000..962553ba9
--- /dev/null
+++ b/test/unit/chips/ChipCheckmark.test.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import {assert} from 'chai';
+import {mount} from 'enzyme';
+import ChipCheckmark from '../../../packages/chips/ChipCheckmark';
+
+suite('ChipCheckmark');
+
+test('renders with element and sets ref', () => {
+ const wrapper = mount( );
+ assert.equal(wrapper.instance().width, 0);
+});
diff --git a/test/unit/chips/ChipSet.test.js b/test/unit/chips/ChipSet.test.js
index 174705c2e..e3da31883 100644
--- a/test/unit/chips/ChipSet.test.js
+++ b/test/unit/chips/ChipSet.test.js
@@ -1,39 +1,260 @@
-// import React from 'react';
-// import {assert} from 'chai';
-// import {shallow} from 'enzyme';
-// import {Chip, ChipSet} from '../../../packages/chips/index';
-//
-// suite('ChipSet');
-
-// test('className prop adds classes', () => {
-// const wrapper = shallow( );
-// assert.isTrue(wrapper.hasClass('test-class-name'));
-// assert.isTrue(wrapper.hasClass('mdc-chip-set'));
-// });
-//
-// test('renders chip set and chip', () => {
-// const wrapper = shallow(
-//
-//
-//
-//
-// );
-// assert.exists(wrapper.find('.mdc-chip-set'));
-// assert.lengthOf(wrapper.find(Chip), 2);
-// });
-//
-// test('selected filter chip renders checkmark', () => {
-// const wrapper = shallow(
-//
-//
-//
-//
-// );
-// assert.exists(wrapper.find('.mdc-chip__checkmark'));
-// });
-//
-// test('#adapter.hasClass returns true if class was added in className prop', () => {
-// const wrapper = shallow( );
-// assert.isTrue(wrapper.instance().adapter.hasClass('test-class-name'));
-// assert.isTrue(wrapper.instance().adapter.hasClass('mdc-chip-set'));
-// });
+import React from 'react';
+import {assert} from 'chai';
+import td from 'testdouble';
+import {shallow, mount} from 'enzyme';
+import ChipSet from '../../../packages/chips/ChipSet';
+import {Chip} from '../../../packages/chips/index';
+import ChipCheckmark from '../../../packages/chips/ChipCheckmark';
+
+suite('ChipSet');
+
+test('creates foundation', () => {
+ const wrapper = mount( );
+ assert.exists(wrapper.instance().foundation_);
+});
+
+test('calls #foundation.select when the selectedChipIds change', () => {
+ const wrapper = shallow(
);
+ wrapper.instance().updateChipSelection = td.func();
+ const selectedChipIds = ['1'];
+ wrapper.setProps({selectedChipIds});
+ td.verify(wrapper.instance().updateChipSelection(), {times: 1});
+});
+
+test('filter classname is added if is filter variant', () => {
+ const wrapper = shallow(
);
+ wrapper.hasClass('mdc-chip-set--filter');
+});
+
+test('choice classname is added if is choice variant', () => {
+ const wrapper = shallow(
);
+ wrapper.hasClass('mdc-chip-set--choice');
+});
+
+test('input classname is added if is input variant', () => {
+ const wrapper = shallow(
);
+ wrapper.hasClass('mdc-chip-set--input');
+});
+
+test('#adapter.hasClass returns true if component contains class', () => {
+ const wrapper = shallow(
);
+ assert.isTrue(wrapper.instance().adapter.hasClass('test-class-name'));
+});
+
+test('#adapter.hasClass returns false if component does not contains class', () => {
+ const wrapper = shallow(
);
+ assert.isFalse(wrapper.instance().adapter.hasClass('test-class-name'));
+});
+
+test('#adapter.setSelected adds selectedChipId to state', () => {
+ const wrapper = shallow(
);
+ wrapper.instance().adapter.setSelected('1', true);
+ assert.isTrue(wrapper.state().selectedChipIds.has('1'));
+});
+
+test('#adapter.setSelected removes selectedChipId from state', () => {
+ const wrapper = shallow(
);
+ wrapper.setState({selectedChipIds: new Set(['1'])});
+ wrapper.instance().adapter.setSelected('1', false);
+ assert.isFalse(wrapper.state().selectedChipIds.has('1'));
+});
+
+test('#adapter.setSelected removes selectedChipId from state', () => {
+ const wrapper = shallow(
);
+ wrapper.setState({selectedChipIdss: new Set(['1'])});
+ wrapper.instance().adapter.setSelected('1', false);
+ assert.isFalse(wrapper.state().selectedChipIds.has('1'));
+});
+
+test('#foundation.select is called when the selectedChipIds change', () => {
+ const wrapper = shallow(
);
+ wrapper.instance().foundation_.select = td.func();
+ const selectedChipIds = ['1'];
+ wrapper.setProps({selectedChipIds});
+ td.verify(wrapper.instance().foundation_.select('1'), {times: 1});
+});
+
+test('#foundation.deselect is called when the selectedChipIds change', () => {
+ const wrapper = shallow(
);
+ const selectedChipIds = ['1'];
+ wrapper.setProps({selectedChipIds});
+ wrapper.instance().foundation_.deselect = td.func();
+ wrapper.setProps({selectedChipIds: []});
+
+ td.verify(wrapper.instance().foundation_.deselect('1'), {times: 1});
+});
+
+test('#handleSelect calls props.handleSelect', () => {
+ const handleSelect = td.func();
+ const wrapper = shallow( );
+ wrapper.instance().handleSelect();
+ td.verify(handleSelect([]), {times: 1});
+});
+
+test('#handleSelect calls props.handleSelect with selectedChipIds', () => {
+ const handleSelect = td.func();
+ const wrapper = shallow(
+
+ );
+ wrapper.instance().handleSelect();
+ td.verify(handleSelect(['1']), {times: 1});
+});
+
+test('#handleSelect calls foundation_.toggleSelect with chipId and is choice variant', () => {
+ const handleSelect = td.func();
+ const wrapper = shallow(
+
+ );
+ wrapper.instance().foundation_.toggleSelect = td.func();
+ wrapper.instance().handleSelect('1');
+ td.verify(wrapper.instance().foundation_.toggleSelect('1'), {times: 1});
+});
+
+test('#handleSelect calls foundation_.toggleSelect with chipId and is filter variant', () => {
+ const handleSelect = td.func();
+ const wrapper = shallow(
+
+ );
+ wrapper.instance().foundation_.toggleSelect = td.func();
+ wrapper.instance().handleSelect('1');
+ td.verify(wrapper.instance().foundation_.toggleSelect('1'), {times: 1});
+});
+
+test('#handleSelect does not call foundation_.toggleSelect with chipId if is neither filter nor choice', () => {
+ const handleSelect = td.func();
+ const wrapper = shallow(
+
+ );
+ wrapper.instance().foundation_.toggleSelect = td.func();
+ wrapper.instance().handleSelect('1');
+ td.verify(wrapper.instance().foundation_.toggleSelect('1'), {times: 0});
+});
+
+test('#handleRemove calls foundation_.deselect with chipId and is input variant', () => {
+ const handleRemove = td.func();
+ const wrapper = shallow(
+
+ );
+ wrapper.instance().foundation_.deselect = td.func();
+ wrapper.instance().handleRemove('1');
+ td.verify(wrapper.instance().foundation_.deselect('1'), {times: 1});
+});
+
+test('#handleRemove does not call foundation_.deselect with chipId if is not input variant', () => {
+ const handleRemove = td.func();
+ const wrapper = shallow(
+
+ );
+ wrapper.instance().foundation_.deselect = td.func();
+ wrapper.instance().handleRemove('1');
+ td.verify(wrapper.instance().foundation_.deselect('1'), {times: 0});
+});
+
+test('#handleRemove calls props.handleRemove with chipId', () => {
+ const handleRemove = td.func();
+ const wrapper = shallow( );
+ wrapper.instance().handleRemove('1');
+ td.verify(handleRemove('1'), {times: 1});
+});
+
+test('#setCheckmarkWidth sets checkmark width', () => {
+ const wrapper = shallow( );
+ wrapper.instance().setCheckmarkWidth({width: 20});
+ assert.equal(wrapper.instance().checkmarkWidth_, 20);
+});
+
+test('#setCheckmarkWidth does not set checkmark width if checkmark width is already set', () => {
+ const wrapper = shallow( );
+ wrapper.instance().checkmarkWidth_ = 20;
+ wrapper.instance().setCheckmarkWidth({width: 40});
+ assert.equal(wrapper.instance().checkmarkWidth_, 20);
+});
+
+test('#computeBoundingRect returns width and height', () => {
+ const wrapper = shallow( );
+ const chipWidth = 20;
+ const chipHeight = 50;
+ const chipElement = {
+ getBoundingClientRect: () => ({width: chipWidth, height: chipHeight}),
+ };
+ const {height, width} = wrapper.instance().computeBoundingRect(chipElement);
+ assert.equal(height, chipHeight);
+ assert.equal(width, chipWidth);
+});
+
+test('#computeBoundingRect returns width and height', () => {
+ const wrapper = shallow( );
+ const chipWidth = 20;
+ const chipHeight = 50;
+ wrapper.instance().checkmarkWidth_ = 20;
+ const chipElement = {
+ getBoundingClientRect: () => ({width: chipWidth, height: chipHeight}),
+ };
+ const {height, width} = wrapper.instance().computeBoundingRect(chipElement);
+ assert.equal(height, chipHeight);
+ assert.equal(width, chipWidth+20);
+});
+
+test('chip is rendered with selected prop as false', () => {
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ assert.isFalse(chip.props.selected);
+});
+
+test('chip is rendered with selected prop as true', () => {
+ const wrapper = mount( );
+ wrapper.setState({selectedChipIds: new Set(['1'])});
+ const chip = wrapper.children().props().children[0];
+ assert.isTrue(chip.props.selected);
+});
+
+test('chip is rendered with handleSelect method', () => {
+ const handleSelect = td.func();
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ chip.props.handleSelect('1');
+ td.verify(handleSelect([]), {times: 1});
+});
+
+test('chip is rendered with handleRemove method', () => {
+ const handleRemove = td.func();
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ chip.props.handleRemove('1');
+ td.verify(handleRemove('1'), {times: 1});
+});
+
+test('chip is rendered ChipCheckmark if is filter variants', () => {
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ assert.equal(chip.props.chipCheckmark.type, ChipCheckmark);
+});
+
+test('chip is rendered ChipCheckmark if is not filter variants', () => {
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ assert.equal(chip.props.chipCheckmark, null);
+});
+
+test('chip is rendered computeBoundingRect method prop if is filter variant', () => {
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ const chipElement = {getBoundingClientRect: td.func()};
+ td.when(chipElement.getBoundingClientRect()).thenReturn({height: 1, width: 1});
+ chip.props.computeBoundingRect(chipElement);
+ td.verify(chipElement.getBoundingClientRect(), {times: 1});
+});
+
+test('chip is rendered computeBoundingRect method prop if is not filter variant', () => {
+ const wrapper = mount( );
+ const chip = wrapper.children().props().children[0];
+ assert.equal(chip.props.computeBoundingRect, null);
+});
+
+test('#componentWillUnmount destroys foundation', () => {
+ const wrapper = shallow( );
+ const foundation = wrapper.instance().foundation_;
+ foundation.destroy = td.func();
+ wrapper.unmount();
+ td.verify(foundation.destroy());
+});