Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
feat(tab-indicator): Remove transitionend event handling (#3337)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Removes handleTransitionEnd foundation API. Removes [de]registerEventHandler adapter APIs.
  • Loading branch information
kfranqueiro authored Aug 14, 2018
1 parent 1245573 commit c8af69b
Show file tree
Hide file tree
Showing 12 changed files with 26 additions and 207 deletions.
2 changes: 0 additions & 2 deletions packages/mdc-tab-indicator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,6 @@ Method Signature | Description
--- | ---
`addClass(className: string) => void` | Adds a class to the root element.
`removeClass(className: string) => void` | Removes a class from the root element.
`registerEventHandler(evtType: string, handler: EventListener) => void` | Registers an event listener on the root element.
`deregisterEventHandler(evtType: string, handler: EventListener) => void` | Deregisters an event listener on the root element.
`setContentStyleProp(property: string, value: string) => void` | Sets the style property of the content element.
`computeContentClientRect() => ClientRect` | Returns the content element's bounding client rect.

Expand Down
14 changes: 0 additions & 14 deletions packages/mdc-tab-indicator/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,6 @@
* @record
*/
class MDCTabIndicatorAdapter {
/**
* Registers an event listener on the root element for a given event.
* @param {string} evtType
* @param {function(!Event): undefined} handler
*/
registerEventHandler(evtType, handler) {}

/**
* Deregisters an event listener on the root element for a given event.
* @param {string} evtType
* @param {function(!Event): undefined} handler
*/
deregisterEventHandler(evtType, handler) {}

/**
* Adds the given className to the root element.
* @param {string} className The className to add
Expand Down
4 changes: 1 addition & 3 deletions packages/mdc-tab-indicator/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
const cssClasses = {
ACTIVE: 'mdc-tab-indicator--active',
FADE: 'mdc-tab-indicator--fade',
FADING_ACTIVATE: 'mdc-tab-indicator--fading-activate',
FADING_DEACTIVATE: 'mdc-tab-indicator--fading-deactivate',
SLIDING_ACTIVATE: 'mdc-tab-indicator--sliding-activate',
NO_TRANSITION: 'mdc-tab-indicator--no-transition',
};

/** @enum {string} */
Expand Down
19 changes: 0 additions & 19 deletions packages/mdc-tab-indicator/fading-foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,11 @@ import MDCTabIndicatorFoundation from './foundation';
* @final
*/
class MDCFadingTabIndicatorFoundation extends MDCTabIndicatorFoundation {
/** @param {...?} args */
constructor(...args) {
super(...args);

/** @private {function(?Event): undefined} */
this.handleTransitionEnd_ = () => this.handleTransitionEnd();
}

/** Handles the transitionend event */
handleTransitionEnd() {
this.adapter_.deregisterEventHandler('transitionend', this.handleTransitionEnd_);
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.FADING_ACTIVATE);
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.FADING_DEACTIVATE);
}

activate() {
this.adapter_.registerEventHandler('transitionend', this.handleTransitionEnd_);
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.FADING_ACTIVATE);
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.ACTIVE);
}

deactivate() {
this.adapter_.registerEventHandler('transitionend', this.handleTransitionEnd_);
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.FADING_DEACTIVATE);
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.ACTIVE);
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/mdc-tab-indicator/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ class MDCTabIndicatorFoundation extends MDCFoundation {
*/
static get defaultAdapter() {
return /** @type {!MDCTabIndicatorAdapter} */ ({
registerEventHandler: () => {},
deregisterEventHandler: () => {},
addClass: () => {},
removeClass: () => {},
computeContentClientRect: () => {},
Expand Down
2 changes: 0 additions & 2 deletions packages/mdc-tab-indicator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ class MDCTabIndicator extends MDCComponent {
*/
getDefaultFoundation() {
const adapter = /** @type {!MDCTabIndicatorAdapter} */ (Object.assign({
registerEventHandler: (evtType, handler) => this.root_.addEventListener(evtType, handler),
deregisterEventHandler: (evtType, handler) => this.root_.removeEventListener(evtType, handler),
addClass: (className) => this.root_.classList.add(className),
removeClass: (className) => this.root_.classList.remove(className),
computeContentClientRect: () => this.content_.getBoundingClientRect(),
Expand Down
14 changes: 9 additions & 5 deletions packages/mdc-tab-indicator/mdc-tab-indicator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,20 @@
opacity: 1;
}

.mdc-tab-indicator--sliding-activate > .mdc-tab-indicator__content {
// Slide by default
.mdc-tab-indicator > .mdc-tab-indicator__content {
transition: 250ms transform $mdc-animation-standard-curve-timing-function;
}

.mdc-tab-indicator--fading-activate > .mdc-tab-indicator__content,
.mdc-tab-indicator--fading-deactivate > .mdc-tab-indicator__content {
// --no-transition is applied in cases where styles need to be applied immediately to set up a transition
.mdc-tab-indicator--no-transition > .mdc-tab-indicator__content {
transition: none;
}

.mdc-tab-indicator--fade > .mdc-tab-indicator__content {
transition: 150ms opacity linear;
}

.mdc-tab-indicator--fading-activate > .mdc-tab-indicator__content {
.mdc-tab-indicator--active.mdc-tab-indicator--fade > .mdc-tab-indicator__content {
transition-delay: 100ms;
}

34 changes: 6 additions & 28 deletions packages/mdc-tab-indicator/sliding-foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,12 @@ import MDCTabIndicatorFoundation from './foundation';
* @final
*/
class MDCSlidingTabIndicatorFoundation extends MDCTabIndicatorFoundation {
/** @param {...?} args */
constructor(...args) {
super(...args);

/** @private {function(?Event): undefined} */
this.handleTransitionEnd_ = () => this.handleTransitionEnd();
}

/** Handles the transitionend event */
handleTransitionEnd() {
this.adapter_.deregisterEventHandler('transitionend', this.handleTransitionEnd_);
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.SLIDING_ACTIVATE);
}

/** @param {!ClientRect=} previousIndicatorClientRect */
activate(previousIndicatorClientRect) {
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.ACTIVE);

// Early exit if no indicator is present to handle cases where an indicator
// may be activated without a prior indicator state
if (!previousIndicatorClientRect) {
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.ACTIVE);
return;
}

Expand All @@ -53,26 +38,19 @@ class MDCSlidingTabIndicatorFoundation extends MDCTabIndicatorFoundation {
const currentClientRect = this.computeContentClientRect();
const widthDelta = previousIndicatorClientRect.width / currentClientRect.width;
const xPosition = previousIndicatorClientRect.left - currentClientRect.left;
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.NO_TRANSITION);
this.adapter_.setContentStyleProperty('transform', `translateX(${xPosition}px) scaleX(${widthDelta})`);

// Force repaint
// Force repaint before updating classes and transform to ensure the transform properly takes effect
this.computeContentClientRect();

// Add animating class and remove transformation in a new frame
requestAnimationFrame(() => {
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.SLIDING_ACTIVATE);
this.adapter_.setContentStyleProperty('transform', '');
});

this.adapter_.registerEventHandler('transitionend', this.handleTransitionEnd_);
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.NO_TRANSITION);
this.adapter_.addClass(MDCTabIndicatorFoundation.cssClasses.ACTIVE);
this.adapter_.setContentStyleProperty('transform', '');
}

deactivate() {
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.ACTIVE);
// We remove the animating class in deactivate in case the Tab is deactivated before the animation completes and
// the "transitionend" handler isn't called.
this.adapter_.removeClass(MDCTabIndicatorFoundation.cssClasses.SLIDING_ACTIVATE);
this.adapter_.deregisterEventHandler('transitionend', this.handleTransitionEnd_);
}
}

Expand Down
49 changes: 0 additions & 49 deletions test/unit/mdc-tab-indicator/fading-foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,70 +17,21 @@

import td from 'testdouble';

import {captureHandlers} from '../helpers/foundation';
import {setupFoundationTest} from '../helpers/setup';
import MDCFadingTabIndicatorFoundation from '../../../packages/mdc-tab-indicator/fading-foundation';

suite('MDCFadingTabIndicatorFoundation');

const setupTest = () => setupFoundationTest(MDCFadingTabIndicatorFoundation);

test('#activate registers a transitionend handler', () => {
const {foundation, mockAdapter} = setupTest();
foundation.activate();
td.verify(mockAdapter.registerEventHandler('transitionend', td.matchers.isA(Function)));
});

test(`#activate adds the ${MDCFadingTabIndicatorFoundation.cssClasses.ACTIVE} class`, () => {
const {foundation, mockAdapter} = setupTest();
foundation.activate();
td.verify(mockAdapter.addClass(MDCFadingTabIndicatorFoundation.cssClasses.ACTIVE));
});

test(`#activate adds the ${MDCFadingTabIndicatorFoundation.cssClasses.FADING_ACTIVATE} class`, () => {
const {foundation, mockAdapter} = setupTest();
foundation.activate();
td.verify(mockAdapter.addClass(MDCFadingTabIndicatorFoundation.cssClasses.FADING_ACTIVATE));
});

test('#deactivate registers a transitionend handler', () => {
const {foundation, mockAdapter} = setupTest();
foundation.deactivate();
td.verify(mockAdapter.registerEventHandler('transitionend', td.matchers.isA(Function)));
});

test(`#deactivate removes the ${MDCFadingTabIndicatorFoundation.cssClasses.ACTIVE} class`, () => {
const {foundation, mockAdapter} = setupTest();
foundation.deactivate();
td.verify(mockAdapter.removeClass(MDCFadingTabIndicatorFoundation.cssClasses.ACTIVE));
});

test(`#deactivate adds the ${MDCFadingTabIndicatorFoundation.cssClasses.FADING_DEACTIVATE} class`, () => {
const {foundation, mockAdapter} = setupTest();
foundation.deactivate();
td.verify(mockAdapter.addClass(MDCFadingTabIndicatorFoundation.cssClasses.FADING_DEACTIVATE));
});

test('on transitionend, deregister the transitionend handler', () => {
const {foundation, mockAdapter} = setupTest();
const handlers = captureHandlers(mockAdapter, 'registerEventHandler');
foundation.activate();
handlers.transitionend();
td.verify(mockAdapter.deregisterEventHandler('transitionend', td.matchers.isA(Function)));
});

test(`on transitionend, remove the ${MDCFadingTabIndicatorFoundation.cssClasses.FADING_ACTIVATE} class`, () => {
const {foundation, mockAdapter} = setupTest();
const handlers = captureHandlers(mockAdapter, 'registerEventHandler');
foundation.activate();
handlers.transitionend();
td.verify(mockAdapter.removeClass(MDCFadingTabIndicatorFoundation.cssClasses.FADING_ACTIVATE));
});

test(`on transitionend, remove the ${MDCFadingTabIndicatorFoundation.cssClasses.FADING_DEACTIVATE} class`, () => {
const {foundation, mockAdapter} = setupTest();
const handlers = captureHandlers(mockAdapter, 'registerEventHandler');
foundation.activate();
handlers.transitionend();
td.verify(mockAdapter.removeClass(MDCFadingTabIndicatorFoundation.cssClasses.FADING_DEACTIVATE));
});
9 changes: 4 additions & 5 deletions test/unit/mdc-tab-indicator/foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ test('exports strings', () => {

test('defaultAdapter returns a complete adapter implementation', () => {
verifyDefaultAdapter(MDCTabIndicatorFoundation, [
'registerEventHandler', 'deregisterEventHandler',
'addClass', 'removeClass',
'setContentStyleProperty',
'computeContentClientRect',
Expand All @@ -52,13 +51,13 @@ test('#computeContentClientRect returns the client rect', () => {
test('#activate is abstract and does nothing', () => {
const {foundation, mockAdapter} = setupTest();
foundation.activate();
td.verify(mockAdapter.addClass, {times: 0});
td.verify(mockAdapter.removeClass, {times: 0});
td.verify(mockAdapter.addClass(td.matchers.isA(String)), {times: 0});
td.verify(mockAdapter.removeClass(td.matchers.isA(String)), {times: 0});
});

test('#deactivate is abstract and does nothing', () => {
const {foundation, mockAdapter} = setupTest();
foundation.deactivate();
td.verify(mockAdapter.addClass, {times: 0});
td.verify(mockAdapter.removeClass, {times: 0});
td.verify(mockAdapter.addClass(td.matchers.isA(String)), {times: 0});
td.verify(mockAdapter.removeClass(td.matchers.isA(String)), {times: 0});
});
18 changes: 0 additions & 18 deletions test/unit/mdc-tab-indicator/mdc-tab-indicator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import bel from 'bel';
import {assert} from 'chai';
import td from 'testdouble';
import domEvents from 'dom-events';

import {
MDCTabIndicator,
Expand Down Expand Up @@ -69,23 +68,6 @@ test('#adapter.removeClass removes a class to the root element', () => {
assert.isFalse(root.classList.contains('foo'));
});

test('#adapter.registerEventHandler adds an event listener on the root element', () => {
const {root, component} = setupTest();
const handler = td.func('transitionend handler');
component.getDefaultFoundation().adapter_.registerEventHandler('transitionend', handler);
domEvents.emit(root, 'transitionend');
td.verify(handler(td.matchers.anything()));
});

test('#adapter.deregisterEventHandler remoes an event listener from the root element', () => {
const {root, component} = setupTest();
const handler = td.func('transitionend handler');
root.addEventListener('transitionend', handler);
component.getDefaultFoundation().adapter_.deregisterEventHandler('transitionend', handler);
domEvents.emit(root, 'transitionend');
td.verify(handler(td.matchers.anything()), {times: 0});
});

test('#adapter.computeContentClientRect returns the root element client rect', () => {
const {component, root, content} = setupTest();
document.body.appendChild(root);
Expand Down
Loading

0 comments on commit c8af69b

Please sign in to comment.