Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(chips): Allow close icon and exit animation #2571

Merged
merged 21 commits into from
Apr 20, 2018

Conversation

bonniezhou
Copy link
Contributor

@bonniezhou bonniezhou commented Apr 16, 2018

Add remove functionality + animation to chips and hook it up to mdc-chip__icon--remove.
Fixes #2010.

@codecov-io
Copy link

codecov-io commented Apr 16, 2018

Codecov Report

Merging #2571 into master will decrease coverage by 0.22%.
The diff coverage is 92.3%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2571      +/-   ##
==========================================
- Coverage   98.68%   98.46%   -0.23%     
==========================================
  Files          98       98              
  Lines        4198     4233      +35     
  Branches      533      537       +4     
==========================================
+ Hits         4143     4168      +25     
- Misses         55       65      +10
Impacted Files Coverage Δ
packages/mdc-chips/chip/constants.js 100% <ø> (ø) ⬆️
packages/mdc-chips/chip-set/foundation.js 100% <100%> (ø) ⬆️
packages/mdc-chips/chip-set/index.js 95.45% <100%> (+0.45%) ⬆️
packages/mdc-chips/chip/index.js 82.14% <66.66%> (+4.14%) ⬆️
packages/mdc-chips/chip/foundation.js 97.29% <94.11%> (-0.95%) ⬇️
packages/mdc-grid-list/index.js 46.15% <0%> (-53.85%) ⬇️
packages/mdc-grid-list/foundation.js 93.75% <0%> (-6.25%) ⬇️
packages/mdc-menu/foundation.js 99.02% <0%> (-0.33%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 698e0d4...b269621. Read the comment docs.

@@ -125,6 +125,9 @@ class MDCChip extends MDCComponent {
notifyInteraction: () => this.emit(strings.INTERACTION_EVENT, {chip: this}, true /* shouldBubble */),
notifyTrailingIconInteraction: () => this.emit(
strings.TRAILING_ICON_INTERACTION_EVENT, {chip: this}, true /* shouldBubble */),
getComputedStyleValue: (propertyName) => window.getComputedStyle(this.root_).getPropertyValue(propertyName),
setStyleProperty: (propertyName, value) => this.root_.style.setProperty(propertyName, value),
removeFromDOM: () => this.root_.remove(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work in IE. You're going to have to do this.root_.parentNode.removeChild(this.root_)

.mdc-chip--exit {
transition:
opacity 75ms $mdc-animation-standard-curve-timing-function,
width 150ms $mdc-animation-deceleration-curve-timing-function,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be width/padding/margin or can we do a scale transform instead, to take advantage of GPU compositing and eliminate potential for text truncation/wrapping issues?

I'm also a tad confused why we have the non-opacity transitions taking longer than the opacity (i.e. the element will already be invisible at that point).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline - transitioning the transform would be extremely complicated due to chips being wrapped to the next line.

* @param {!Event} evt
*/
handleTransitionEnd_(evt) {
// Handle transition end event on the chip when the it is about to be removed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"when the it" -> "when it"

// Handle transition end event on the chip when the it is about to be removed.
if (this.adapter_.eventTargetHasClass(/** @type {!EventTarget} */ (evt.target), cssClasses.CHIP_EXIT)) {
if (evt.propertyName === 'width') {
this.adapter_.removeFromDOM();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also be calling this.destroy() here? AFAIK we're never destroying these chips even though they're effectively removed.

Also, just checking, chip set isn't holding any references to entry chips, is it? (I think it only holds references to selected chips for filter/choice.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's a really good point, the chip set does hold a reference to all its chips, regardless of the type of chip set.

I refactored it so that the chip set is in charge of removing the chip object from its reference, and also calls remove on the chip, which will destroy and remove itself from the DOM.

Note that this means the chip set foundation has to pass a chip object to its adapter, and the chip object needs to have an explicit remove method.

@@ -217,6 +219,9 @@ Method Signature | Description
`deregisterTrailingIconInteractionHandler(evtType: string, handler: EventListener) => void` | Deregisters an event listener on the trailing icon element
`notifyInteraction() => void` | Emits a custom event `MDCChip:interaction` denoting the chip has been interacted with
`notifyTrailingIconInteraction() => void` | Emits a custom event `MDCChip:trailingIconInteraction` denoting the chip's trailing icon has been interacted with
`notifyRemoval() => void` | Emits a custom event `MDCChip:removal` denoting the chip will be removed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you preferred removal over remove (or removed) here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just using a noun to mirror "interaction". Is "remove" more understandable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I guess that's true. I was thinking of e.g. "selected" on some other components. I guess removal works here.

@@ -229,6 +234,7 @@ Method Signature | Description
`deregisterInteractionHandler(evtType: string, handler: EventListener) => void` | Deregisters an event handler on the root element for a given event
`createChipElement(text: string, leadingIcon: Element, trailingIcon: Element) => Element` | Returns a chip element with the given text, leading icon, and trailing icon
`appendChild(el: Element) => void` | Appends the given element as a child of the root element
`removeChip(chip: MDCChip) => void` | Removes the chip object from the chip set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would make sense to combine createChipElement and appendChild into a single appendChip API, to mirror removeChip? (I think those other two are only ever used together back to back anyway?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a really good idea

@@ -49,6 +49,16 @@ test('get ripple returns MDCRipple instance', () => {
assert.isTrue(component.ripple instanceof MDCRipple);
});

test('#remove removes the root element from the DOM', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way for us to also test that destroy is run?

const component = new MDCChipSet(root, undefined, (el) => new FakeChip(el));
const chip = component.chips[0];
component.getDefaultFoundation().adapter_.removeChip(chip);
td.verify(chip.remove());
Copy link
Contributor

@kfranqueiro kfranqueiro Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also verify that component.chips' length is 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. (It should have length 2 since we're only removing 1 chip)

@@ -83,8 +91,7 @@ class MDCChipSetFoundation extends MDCFoundation {
* @return {!Element}
*/
addChip(text, leadingIcon, trailingIcon) {
const chipEl = this.adapter_.createChipElement(text, leadingIcon, trailingIcon);
this.adapter_.appendChild(chipEl);
const chipEl = this.adapter_.appendChip(text, leadingIcon, trailingIcon);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these still need to return the chip element? Would it make more sense to return the chip itself, or nothing at all?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, nevermind, forgot the component needs it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The foundation needs to return the chip element so the component can push it to its array of chip references.

@@ -184,3 +185,20 @@ test('in filter chips, on custom MDCChip:interaction event deselects selected ch
td.verify(chipA.foundation.setSelected(false));
assert.equal(foundation.selectedChips_.length, 0);
});

test('on custom MDCChip:removal event removes chip', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not have a test in this file for addChip? (We're testing it at the component level, but IIUC that's mocking the foundation, so we should be testing it here too.)

test('#adapter.createChipElement returns a new chip element', () => {
const {component} = setupTest();
const chipEl = component.getDefaultFoundation().adapter_.createChipElement('hello world');
test('#adapter.appendChip add a new chip to the chip set element', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add -> adds

@bonniezhou bonniezhou merged commit 3d8a27b into master Apr 20, 2018
@bonniezhou bonniezhou deleted the feat/chips/exit-animation branch April 20, 2018 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants