Skip to content

Commit

Permalink
fix(chips): update to v0.41.0 (#424)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Goo committed Nov 20, 2018
1 parent bd7433b commit e40fec6
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 190 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@material/button": "^0.41.0",
"@material/card": "^0.41.0",
"@material/checkbox": "^0.41.0",
"@material/chips": "^0.40.1",
"@material/chips": "^0.41.0",
"@material/drawer": "^0.41.0",
"@material/fab": "^0.41.0",
"@material/floating-label": "^0.41.0",
Expand Down
12 changes: 7 additions & 5 deletions packages/chips/Chip.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export class Chip extends Component {
(propertyName) => window.getComputedStyle(this.chipElement_).getPropertyValue(propertyName),
setStyleProperty: (propertyName, value) => this.chipElement_.style.setProperty(propertyName, value),
notifyRemoval: () => this.props.handleRemove(this.props.id),
notifyInteraction: () => this.props.handleSelect(this.props.id),
notifyInteraction: () => this.props.handleInteraction(this.props.id),
notifySelection: (selected) => this.props.handleSelect(this.props.id, selected),
addClassToLeadingIcon: (className) => {
const leadingIconClassList = new Set(this.state.leadingIconClassList);
leadingIconClassList.add(className);
Expand All @@ -94,14 +95,12 @@ export class Chip extends Component {
}

onClick = (e) => {
const {onClick} = this.props;
onClick(e);
this.props.onClick(e);
this.foundation_.handleInteraction(e);
}

onKeyDown = (e) => {
const {onKeyDown} = this.props;
onKeyDown(e);
this.props.onKeyDown(e);
this.foundation_.handleInteraction(e);
}

Expand Down Expand Up @@ -161,6 +160,7 @@ export class Chip extends Component {
className,
selected,
handleSelect,
handleInteraction,
handleRemove,
onClick,
onKeyDown,
Expand Down Expand Up @@ -202,6 +202,7 @@ Chip.propTypes = {
selected: PropTypes.bool,
handleSelect: PropTypes.func,
handleRemove: PropTypes.func,
handleInteraction: PropTypes.func,
onClick: PropTypes.func,
onKeyDown: PropTypes.func,
onTransitionEnd: PropTypes.func,
Expand All @@ -223,6 +224,7 @@ Chip.defaultProps = {
initRipple: () => {},
handleSelect: () => {},
handleRemove: () => {},
handleInteraction: () => {},
};

export default withRipple(Chip);
91 changes: 44 additions & 47 deletions packages/chips/ChipSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,35 @@ import ChipCheckmark from './ChipCheckmark';

export default class ChipSet extends Component {
checkmarkWidth_ = 0;
foundation_ = null;
state = {
selectedChipIds: new Set(this.props.selectedChipIds),
};

constructor(props) {
super(props);
this.state = {
selectedChipIds: props.selectedChipIds,
foundation: null,
hasInitialized: false,
};
}

componentDidMount() {
this.foundation_ = new MDCChipSetFoundation(this.adapter);
this.foundation_.init();
this.updateChipSelection();
const foundation = new MDCChipSetFoundation(this.adapter);
this.setState({foundation});
foundation.init();
}

componentDidUpdate(prevProps) {
if (this.props.selectedChipIds !== prevProps.selectedChipIds) {
const selectedChipIds = new Set(this.props.selectedChipIds);
componentDidUpdate(prevProps, prevState) {
const {selectedChipIds} = this.props;

if (this.state.foundation !== prevState.foundation) {
this.initChipSelection();
}
if (selectedChipIds !== prevProps.selectedChipIds) {
this.setState({selectedChipIds});
this.updateChipSelection(selectedChipIds);
}
}

componentWillUnmount() {
this.foundation_.destroy();
this.state.foundation.destroy();
}

get classes() {
Expand All @@ -65,54 +73,39 @@ export default class ChipSet extends Component {
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});
const selectedChipIds = this.state.foundation.getSelectedChipIds();
this.setState({selectedChipIds}, () => {
this.props.handleSelect(selectedChipIds);
});
},
removeChip: this.removeChip,
};
}

updateChipSelection(ids = this.state.selectedChipIds) {
initChipSelection() {
React.Children.forEach(this.props.children, (child) => {
const {id} = child.props;
if (ids.has(id)) {
this.foundation_.select(id);
} else {
// remove deselect when MDC Web issue 3612 is fixed
this.foundation_.deselect(id);
const selected = this.state.selectedChipIds.indexOf(id) > -1;
if (selected) {
this.state.foundation.select(id);
}
});

this.setState({hasInitialized: true});
}

handleSelect = (chipId) => {
const {handleSelect, choice, filter} = this.props;
// update when mdc web issue is fix
// https://github.com/material-components/material-components-web/issues/3613
// filter || choice is duplicate logic found in MDC Web foundation code
if (filter || choice) {
this.foundation_.toggleSelect(chipId);
}
handleSelect(this.foundation_.getSelectedChipIds());
handleInteraction = (chipId) => {
this.state.foundation.handleChipInteraction(chipId);
}

handleSelect = (chipId, selected) => {
this.state.foundation.handleChipSelection(chipId, selected);
}

handleRemove = (chipId) => {
const {input} = this.props;
if (input) {
// this should be calling foundation_.handleChipRemoval, but we would
// need to pass evt.detail.chipId
// fix when MDC Web issue 3613 is fixed
this.foundation_.deselect(chipId);
}
this.removeChip(chipId);
this.state.foundation.handleChipRemoval(chipId);
}

// this should be adapter_.removeChip, but cannot be complete until
// https://github.com/material-components/material-components-web/issues/3613
// is fixed
removeChip = (chipId) => {
const {updateChips, children} = this.props;
if (!children) return;
Expand Down Expand Up @@ -145,10 +138,11 @@ export default class ChipSet extends Component {
renderChip = (chip) => {
const {filter} = this.props;
const {selectedChipIds} = this.state;

const selected = selectedChipIds.indexOf(chip.props.id) > -1;
const props = Object.assign({
selected: selectedChipIds.has(chip.props.id),
selected,
handleSelect: this.handleSelect,
handleInteraction: this.handleInteraction,
handleRemove: this.handleRemove,
chipCheckmark: filter ? <ChipCheckmark ref={this.setCheckmarkWidth}/> : null,
computeBoundingRect: filter ? this.computeBoundingRect : null,
Expand All @@ -158,6 +152,9 @@ export default class ChipSet extends Component {
}

render() {
// need foundation on state, because Chip calls a foundation method
// before ChipSet mounts.
if (!this.state.hasInitialized) return null;
return (
<div className={this.classes}>
{React.Children.map(this.props.children, this.renderChip)}
Expand Down
4 changes: 2 additions & 2 deletions packages/chips/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"url": "https://github.com/material-components/material-components-web-react.git"
},
"dependencies": {
"@material/chips": "^0.40.1",
"@material/react-ripple": "^0.2.0",
"@material/chips": "^0.41.0",
"@material/react-ripple": "^0.6.1",
"classnames": "^2.2.5",
"prop-types": "^15.6.1",
"react": "^16.4.2"
Expand Down
48 changes: 12 additions & 36 deletions test/screenshot/chips/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,27 @@ import MaterialIcon from '../../../packages/material-icon';
import {Chip, ChipSet} from '../../../packages/chips/index';
import uuidv1 from 'uuid/v1';

class ChoiceChipsTest extends React.Component {
class ChipsTest 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
const {children, variant} = this.props; // eslint-disable-line react/prop-types
const isChoice = variant === 'choice';
const isFilter = variant === 'filter';
return (
<div>
<ChipSet
choice
selectedChipIds={this.state.selectedChipIds}
handleSelect={(selectedChipIds) => this.setState({selectedChipIds})}
>
{children}
</ChipSet>
</div>
);
}
}

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 (
<div>
<ChipSet
filter
choice={isChoice}
filter={isFilter}
selectedChipIds={this.state.selectedChipIds}
handleSelect={(selectedChipIds) => this.setState({selectedChipIds})}
>
{children}
</ChipSet>
<button onClick={() => this.setState({selectedChipIds: ['2chip', '0chip']})}>
Select first and last
{isChoice ? 'Select first' : 'Select first and last'}
</button>
</div>
);
Expand Down Expand Up @@ -87,7 +69,7 @@ class InputChipsTest extends React.Component {
{this.state.chips.map((chip) =>
<Chip
id={chip.id}
key={chip.id} // The chip's key cannot be its index, because its index may change.
key={chip.id} // The chip s key cannot be its index, because its index may change
label={chip.label}
leadingIcon={<MaterialIcon icon='face' />}
removeIcon={<MaterialIcon icon='cancel' />}
Expand Down Expand Up @@ -124,21 +106,15 @@ const ChipsScreenshotTest = () => {
</ChipSet>

Choice Chips
<ChoiceChipsTest selectedChipIds={['2chip']}>
<ChipsTest variant='choice' selectedChipIds={['2chip']}>
{renderChips(sizes)}
</ChoiceChipsTest>
</ChipsTest>

Filter Chips with Leading Icon
<FilterChipsTest selectedChipIds={['1chip', '2chip']}>
<ChipsTest variant='filter' selectedChipIds={['1chip', '2chip']}>
{renderChips(clothes, true)}
</FilterChipsTest>

Filter Chips no Leading Icon
<FilterChipsTest selectedChipIds={['1chip', '2chip']}>
{renderChips(clothes)}
</FilterChipsTest>
</ChipsTest>

Input chips
<InputChipsTest labels={contacts}/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion test/screenshot/golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"button": "8941ec5719bb5c82418d335f37dd6e4d523bf8a1121b50a4df4c5a784ca41fbe",
"card": "b2fd82763c383be438ff6578083bf9009711c7470333d07eb916ab690fc42d31",
"checkbox": "9c61177f0f927e178e7c6687d74cdfa08abc15ea8fc3c381f570b7c7d1f46d2a",
"chips": "89fb955abe09193af4e2b0f8cb9f0df06495508d2321fee247fa1a50cfe16a61",
"chips": "e100a23df0b92c37920127c62d7d694ce3fe40c101c0ed05d535f5cafee62b27",
"fab": "db36f52195c420062d91dd5ebe5432ad87247b3c1146fd547b0a195079bbce2f",
"floating-label": "1d4d4f2e57e1769b14fc84985d1e6f53410c49aef41c9cf4fde94f938adefe57",
"icon-button": "5ffb1f7fbd06d2c0533f6ba8d4d9ea170cec1a248a61de1cc1bb626cb58ebcd2",
Expand Down
22 changes: 18 additions & 4 deletions test/unit/chips/Chip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ test('#adapter.removeClassFromLeadingIcon removes from state.leadingIconClassLis
assert.isFalse(wrapper.state().leadingIconClassList.has('test-leading-icon-class'));
});

test('#adapter.notifyInteraction calls #props.handleSelect w/ chipId', () => {
const handleSelect = td.func();
const wrapper = shallow(<Chip id='123' handleSelect={handleSelect} />);
test('#adapter.notifyInteraction calls #props.handleInteraction w/ chipId', () => {
const handleInteraction = td.func();
const wrapper = shallow(<Chip id='123' handleInteraction={handleInteraction} />);
wrapper.instance().foundation_.adapter_.notifyInteraction();
td.verify(handleSelect('123'), {times: 1});
td.verify(handleInteraction('123'), {times: 1});
});

test('#adapter.notifyRemoval calls #props.handleRemove w/ chipId', () => {
Expand All @@ -104,6 +104,20 @@ test('#adapter.notifyRemoval calls #props.handleRemove w/ chipId', () => {
td.verify(handleRemove('123'), {times: 1});
});

test('#adapter.notifySelection calls #props.handleSelect w/ chipId and selected false', () => {
const handleSelect = td.func();
const wrapper = shallow(<Chip id='123' handleSelect={handleSelect}/>);
wrapper.instance().foundation_.adapter_.notifySelection(false);
td.verify(handleSelect('123', false), {times: 2});
});

test('#adapter.notifySelection calls #props.handleSelect w/ chipId and selected true', () => {
const handleSelect = td.func();
const wrapper = shallow(<Chip id='123' handleSelect={handleSelect}/>);
wrapper.instance().foundation_.adapter_.notifySelection(true);
td.verify(handleSelect('123', true), {times: 1});
});

test('on click calls #props.onClick', () => {
const onClick = td.func();
const wrapper = shallow(<Chip id='1' onClick={onClick} />);
Expand Down
Loading

0 comments on commit e40fec6

Please sign in to comment.