Skip to content
This repository was archived by the owner on Jul 29, 2025. It is now read-only.

Commit 14353ab

Browse files
authored
feat(chips): Update chips to use ids and add input chips (#246)
BREAKING CHANGE: Updated API for `selectedChipIds` and `handleSelect` props in ChipSet and `id` prop in Chip.
1 parent e1918fa commit 14353ab

File tree

13 files changed

+878
-272
lines changed

13 files changed

+878
-272
lines changed

package-lock.json

Lines changed: 37 additions & 68 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"allowed": [
3030
"button",
3131
"card",
32+
"chips",
3233
"fab",
3334
"floating-label",
3435
"line-ripple",
@@ -104,6 +105,7 @@
104105
"resemblejs": "^2.10.3",
105106
"sass-loader": "^6.0.7",
106107
"testdouble": "^3.6.0",
108+
"uuid": "^3.3.2",
107109
"validate-commit-msg": "^2.14.0",
108110
"webpack": "^3.11.0",
109111
"webpack-dev-server": "^2.11.2"

packages/chips/Chip.js

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import withRipple from '../ripple';
2727
import {MDCChipFoundation} from '@material/chips/dist/mdc.chips';
2828

2929
export class Chip extends Component {
30+
chipElement_ = null;
3031
foundation_ = null;
3132
state = {
3233
classList: new Set(),
@@ -35,18 +36,28 @@ export class Chip extends Component {
3536
componentDidMount() {
3637
this.foundation_ = new MDCChipFoundation(this.adapter);
3738
this.foundation_.init();
39+
this.foundation_.setSelected(this.props.selected);
40+
}
41+
42+
componentDidUpdate(prevProps) {
43+
if (this.props.selected !== prevProps.selected) {
44+
this.foundation_.setSelected(this.props.selected);
45+
}
3846
}
3947

4048
componentWillUnmount() {
4149
this.foundation_.destroy();
4250
}
4351

52+
init = (el) => {
53+
this.chipElement_ = el;
54+
this.props.initRipple(el);
55+
}
56+
4457
get classes() {
4558
const {classList} = this.state;
46-
const {className, selected} = this.props;
47-
return classnames('mdc-chip', Array.from(classList), className, {
48-
'mdc-chip--selected': selected,
49-
});
59+
const {className} = this.props;
60+
return classnames('mdc-chip', Array.from(classList), className);
5061
}
5162

5263
get adapter() {
@@ -62,63 +73,126 @@ export class Chip extends Component {
6273
this.setState({classList});
6374
},
6475
hasClass: (className) => this.classes.split(' ').includes(className),
76+
eventTargetHasClass: (target, className) => target.classList.contains(className),
77+
getComputedStyleValue:
78+
(propertyName) => window.getComputedStyle(this.chipElement_).getPropertyValue(propertyName),
79+
setStyleProperty: (propertyName, value) => this.chipElement_.style.setProperty(propertyName, value),
80+
notifyRemoval: () => this.props.handleRemove(this.props.id),
6581
};
6682
}
6783

68-
handleClick = (e) => {
69-
if (typeof this.props.onClick === 'function') {
70-
this.props.onClick(e);
71-
}
72-
this.props.handleSelect(this.props.id);
84+
onClick = (e) => {
85+
const {onClick, handleSelect, id} = this.props;
86+
onClick(e);
87+
handleSelect(id);
7388
}
7489

90+
handleRemoveIconClick = (e) => this.foundation_.handleTrailingIconInteraction(e);
91+
92+
handleTransitionEnd = (e) => this.foundation_.handleTransitionEnd(e);
93+
94+
renderLeadingIcon = (leadingIcon) => {
95+
const {
96+
className,
97+
...otherProps
98+
} = leadingIcon.props;
99+
100+
const props = {
101+
className: classnames(
102+
className,
103+
'mdc-chip__icon',
104+
'mdc-chip__icon--leading',
105+
),
106+
...otherProps,
107+
};
108+
109+
return React.cloneElement(leadingIcon, props);
110+
};
111+
112+
renderRemoveIcon = (removeIcon) => {
113+
const {
114+
className,
115+
...otherProps
116+
} = removeIcon.props;
117+
118+
const props = {
119+
className: classnames(
120+
className,
121+
'mdc-chip__icon',
122+
'mdc-chip__icon--trailing',
123+
),
124+
onClick: this.handleRemoveIconClick,
125+
onKeyDown: this.handleRemoveIconClick,
126+
tabIndex: 0,
127+
role: 'button',
128+
...otherProps,
129+
};
130+
131+
return React.cloneElement(removeIcon, props);
132+
};
133+
75134
render() {
76135
const {
77-
className, // eslint-disable-line no-unused-vars
78-
label,
79-
handleSelect, // eslint-disable-line no-unused-vars
80-
onClick, // eslint-disable-line no-unused-vars
81-
chipCheckmark,
82-
computeBoundingRect, // eslint-disable-line no-unused-vars
136+
/* eslint-disable no-unused-vars */
137+
id,
138+
className,
139+
selected,
140+
handleSelect,
141+
handleRemove,
142+
onClick,
143+
computeBoundingRect,
83144
initRipple,
84-
unbounded, // eslint-disable-line no-unused-vars
145+
unbounded,
146+
/* eslint-enable no-unused-vars */
147+
chipCheckmark,
148+
leadingIcon,
149+
removeIcon,
150+
label,
85151
...otherProps
86152
} = this.props;
87153

88154
return (
89155
<div
156+
tabIndex='0'
90157
className={this.classes}
91-
onClick={this.handleClick}
92-
ref={initRipple}
158+
onClick={this.onClick}
159+
onTransitionEnd={this.handleTransitionEnd}
160+
ref={this.init}
93161
{...otherProps}
94162
>
163+
{leadingIcon ? this.renderLeadingIcon(leadingIcon) : null}
95164
{chipCheckmark}
96165
<div className='mdc-chip__text'>{label}</div>
166+
{removeIcon ? this.renderRemoveIcon(removeIcon) : null}
97167
</div>
98168
);
99169
}
100170
}
101171

102172
Chip.propTypes = {
103-
id: PropTypes.number,
173+
id: PropTypes.string.isRequired,
104174
label: PropTypes.string,
105175
className: PropTypes.string,
106176
selected: PropTypes.bool,
107177
handleSelect: PropTypes.func,
178+
handleRemove: PropTypes.func,
108179
onClick: PropTypes.func,
109-
// The following props are handled by withRipple and do not require defaults.
110180
initRipple: PropTypes.func,
111181
unbounded: PropTypes.bool,
112182
chipCheckmark: PropTypes.node,
183+
leadingIcon: PropTypes.element,
184+
removeIcon: PropTypes.element,
113185
computeBoundingRect: PropTypes.func,
114186
};
115187

116188
Chip.defaultProps = {
117-
id: -1,
118189
label: '',
119190
className: '',
120191
selected: false,
192+
onClick: () => {},
193+
initRipple: () => {},
121194
handleSelect: () => {},
195+
handleRemove: () => {},
122196
};
123197

124198
export default withRipple(Chip);

0 commit comments

Comments
 (0)