-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Conversation
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
packages/mdc-chips/chip/index.js
Outdated
@@ -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(), |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
packages/mdc-chips/README.md
Outdated
@@ -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 |
There was a problem hiding this comment.
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?)
There was a problem hiding this comment.
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
test/unit/mdc-chips/mdc-chip.test.js
Outdated
@@ -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', () => { |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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', () => { |
There was a problem hiding this comment.
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', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add -> adds
Add remove functionality + animation to chips and hook it up to
mdc-chip__icon--remove
.Fixes #2010.