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

Commit

Permalink
fix(drawer): Fix exports and closure tests (#3424)
Browse files Browse the repository at this point in the history
  • Loading branch information
kfranqueiro authored Aug 27, 2018
1 parent 3aa211d commit 8d53068
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 63 deletions.
2 changes: 1 addition & 1 deletion closure_externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ class FocusTrapInstance {
* @param {!Element} element
* @param {FocusTrapCreateOptions=} createOptions
*/
mdc.thirdparty.focusTrap.default;
mdc.thirdparty.focusTrap;
37 changes: 27 additions & 10 deletions packages/mdc-drawer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@
import {MDCComponent} from '@material/base/index';
import MDCDismissibleDrawerFoundation from './dismissible/foundation';
import MDCModalDrawerFoundation from './modal/foundation';
import MDCDrawerAdapter from './adapter';
import {MDCList} from '@material/list/index';
import MDCListFoundation from '@material/list/foundation';
import {strings} from './constants';
import * as util from './util';
import createFocusTrap from 'focus-trap';

/**
* @extends {MDCComponent<!MDCDismissibleDrawerFoundation>}
* @final
*/
export class MDCDrawer extends MDCComponent {
class MDCDrawer extends MDCComponent {
/**
* @param {...?} args
*/
Expand All @@ -49,6 +51,15 @@ export class MDCDrawer extends MDCComponent {
this.handleTransitionEnd_;

/** @private {!Function} */
this.focusTrapFactory_;

/** @private {!FocusTrapInstance} */
this.focusTrap_;

/** @private {?Element} */
this.scrim_;

/** @private {?Function} */
this.handleScrimClick_;
}

Expand Down Expand Up @@ -80,20 +91,23 @@ export class MDCDrawer extends MDCComponent {
}
}

initialize() {
const list = MDCList.attachTo(this.root_.querySelector(`.${MDCListFoundation.cssClasses.ROOT}`));
initialize(
focusTrapFactory = createFocusTrap) {
const listEl = /** @type {!Element} */ (this.root_.querySelector(`.${MDCListFoundation.cssClasses.ROOT}`));
const list = MDCList.attachTo(listEl);
list.wrapFocus = true;
this.focusTrapFactory_ = focusTrapFactory;
}

initialSyncWithDOM() {
const {MODAL} = MDCDismissibleDrawerFoundation.cssClasses;

if (this.root_.classList.contains(MODAL)) {
const {SCRIM_SELECTOR} = MDCDismissibleDrawerFoundation.strings;
this.scrim_ = this.root_.parentElement.querySelector(SCRIM_SELECTOR);
this.handleScrimClick_ = () => this.foundation_.handleScrimClick();
this.scrim_ = /** @type {!Element} */ (this.root_.parentElement.querySelector(SCRIM_SELECTOR));
this.handleScrimClick_ = () => /** @type {!MDCModalDrawerFoundation} */ (this.foundation_).handleScrimClick();
this.scrim_.addEventListener('click', this.handleScrimClick_);
this.focusTrap_ = util.createFocusTrapInstance(this.root_);
this.focusTrap_ = util.createFocusTrapInstance(this.root_, this.focusTrapFactory_);
}

this.handleKeydown_ = (evt) => this.foundation_.handleKeydown(evt);
Expand All @@ -109,8 +123,9 @@ export class MDCDrawer extends MDCComponent {

const {MODAL} = MDCDismissibleDrawerFoundation.cssClasses;
if (this.root_.classList.contains(MODAL)) {
this.scrim_.removeEventListener('click', this.handleScrimClick_);
this.focusTrap_.destroy();
this.scrim_.removeEventListener('click', /** @type {!Function} */ (this.handleScrimClick_));
// Ensure drawer is closed to hide scrim and release focus
this.open = false;
}
}

Expand All @@ -137,8 +152,8 @@ export class MDCDrawer extends MDCComponent {
activeNavItemEl.focus();
}
},
notifyClose: () => this.emit(strings.CLOSE_EVENT, null, true /* shouldBubble */),
notifyOpen: () => this.emit(strings.OPEN_EVENT, null, true /* shouldBubble */),
notifyClose: () => this.emit(strings.CLOSE_EVENT, {}, true /* shouldBubble */),
notifyOpen: () => this.emit(strings.OPEN_EVENT, {}, true /* shouldBubble */),
trapFocus: () => this.focusTrap_.activate(),
releaseFocus: () => this.focusTrap_.deactivate(),
}));
Expand All @@ -154,3 +169,5 @@ export class MDCDrawer extends MDCComponent {
}
}
}

export {MDCDrawer, MDCDismissibleDrawerFoundation, MDCModalDrawerFoundation, util};
9 changes: 8 additions & 1 deletion packages/mdc-drawer/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@

import createFocusTrap from 'focus-trap';

export function createFocusTrapInstance(surfaceEl, focusTrapFactory = createFocusTrap) {
/**
* @param {!Element} surfaceEl
* @param {!Function} focusTrapFactory
* @return {!FocusTrapInstance}
*/
function createFocusTrapInstance(surfaceEl, focusTrapFactory = createFocusTrap) {
return focusTrapFactory(surfaceEl, {
clickOutsideDeactivates: true,
initialFocus: false, // Navigation drawer handles focusing on active nav item.
escapeDeactivates: false, // Navigation drawer handles ESC.
returnFocusOnDeactivate: false, // Navigation drawer handles restore focus.
});
}

export {createFocusTrapInstance};
80 changes: 29 additions & 51 deletions test/unit/mdc-drawer/mdc-drawer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import domEvents from 'dom-events';
import td from 'testdouble';

import {MDCDrawer} from '../../../packages/mdc-drawer';
import * as util from '../../../packages/mdc-drawer/util';
import {strings, cssClasses} from '../../../packages/mdc-drawer/constants';
import {MDCListFoundation} from '../../../packages/mdc-list';
import MDCDismissibleDrawerFoundation from '../../../packages/mdc-drawer/dismissible/foundation';
Expand Down Expand Up @@ -59,13 +58,21 @@ function setupTest(variantClass = cssClasses.DISMISSIBLE) {
const root = getFixture(variantClass);
const drawer = root.querySelector('.mdc-drawer');
const component = new MDCDrawer(drawer);
const MockFoundationCtor = td.constructor(MDCDismissibleDrawerFoundation);
const mockFoundation = new MockFoundationCtor();
return {root, drawer, component, mockFoundation};
return {root, drawer, component};
}

function hasClassMatcher(className) {
return td.matchers.argThat((el) => el.classList && el.classList.contains(className));

function setupTestWithMocks(variantClass = cssClasses.DISMISSIBLE) {
const root = getFixture(variantClass);
const drawer = root.querySelector('.mdc-drawer');
const MockFoundationCtor = td.constructor(MDCDismissibleDrawerFoundation);
const mockFoundation = new MockFoundationCtor();
const mockFocusTrapInstance = td.object({
activate: () => {},
deactivate: () => {},
});
const component = new MDCDrawer(drawer, mockFoundation, () => mockFocusTrapInstance);
return {root, drawer, component, mockFoundation, mockFocusTrapInstance};
}

suite('MDCDrawer');
Expand All @@ -76,39 +83,34 @@ test('attachTo initializes and returns a MDCDrawer instance', () => {
});

test('#get open calls foundation.isOpen', () => {
const {component} = setupTest();
component.foundation_.isOpen = td.func();
const {component, mockFoundation} = setupTestWithMocks();
component.open;
td.verify(component.foundation_.isOpen(), {times: 1});
td.verify(mockFoundation.isOpen(), {times: 1});
});

test('#set open true calls foundation.open', () => {
const {component} = setupTest();
component.foundation_.open = td.func();
const {component, mockFoundation} = setupTestWithMocks();
component.open = true;
td.verify(component.foundation_.open(), {times: 1});
td.verify(mockFoundation.open(), {times: 1});
});

test('#set open false calls foundation.close', () => {
const {component} = setupTest();
component.foundation_.close = td.func();
const {component, mockFoundation} = setupTestWithMocks();
component.open = false;
td.verify(component.foundation_.close(), {times: 1});
td.verify(mockFoundation.close(), {times: 1});
});

test('keydown event calls foundation.handleKeydown method', () => {
const {component, drawer} = setupTest();
component.foundation_.handleKeydown = td.func();
const {drawer, mockFoundation} = setupTestWithMocks();
drawer.querySelector('.mdc-list-item').focus();
domEvents.emit(drawer, 'keydown');
td.verify(component.foundation_.handleKeydown(td.matchers.isA(Object)), {times: 1});
td.verify(mockFoundation.handleKeydown(td.matchers.isA(Object)), {times: 1});
});

test('transitionend event calls foundation.handleTransitionEnd method', () => {
const {component, drawer} = setupTest();
component.foundation_.handleTransitionEnd = td.func();
const {drawer, mockFoundation} = setupTestWithMocks();
domEvents.emit(drawer, 'transitionend');
td.verify(component.foundation_.handleTransitionEnd(td.matchers.isA(Object)), {times: 1});
td.verify(mockFoundation.handleTransitionEnd(td.matchers.isA(Object)), {times: 1});
});

test('component should throw error when invalid variant class name is used or no variant specified', () => {
Expand All @@ -117,15 +119,15 @@ test('component should throw error when invalid variant class name is used or no
});

test('#destroy removes keydown event listener', () => {
const {component, drawer, mockFoundation} = setupTest();
const {component, drawer, mockFoundation} = setupTestWithMocks();
component.destroy();
drawer.querySelector('.mdc-list-item').focus();
domEvents.emit(drawer, 'keydown');
td.verify(mockFoundation.handleKeydown(td.matchers.isA(Object)), {times: 0});
});

test('#destroy removes transitionend event listener', () => {
const {component, drawer, mockFoundation} = setupTest();
const {component, drawer, mockFoundation} = setupTestWithMocks();
component.destroy();

domEvents.emit(drawer, 'transitionend');
Expand Down Expand Up @@ -218,41 +220,17 @@ test('adapter#restoreFocus focus is not restored if saveFocus never called', ()
});

test('adapter#trapFocus traps focus on root element', () => {
const {createFocusTrapInstance} = util;
util.createFocusTrapInstance = td.func('util.createFocusTrapInstance');

const fakeFocusTrapInstance = td.object({
activate: () => {},
deactivate: () => {},
});
td.when(
util.createFocusTrapInstance(hasClassMatcher('mdc-drawer'))
).thenReturn(fakeFocusTrapInstance);

const {component} = setupTest(cssClasses.MODAL);
const {component, mockFocusTrapInstance} = setupTestWithMocks(cssClasses.MODAL);
component.getDefaultFoundation().adapter_.trapFocus();
util.createFocusTrapInstance = createFocusTrapInstance;

td.verify(fakeFocusTrapInstance.activate());
td.verify(mockFocusTrapInstance.activate());
});

test('adapter#releaseFocus releases focus on root element after trap focus', () => {
const {createFocusTrapInstance} = util;
util.createFocusTrapInstance = td.func('util.createFocusTrapInstance');

const fakeFocusTrapInstance = td.object({
activate: () => {},
deactivate: () => {},
});
td.when(
util.createFocusTrapInstance(hasClassMatcher('mdc-drawer'))
).thenReturn(fakeFocusTrapInstance);

const {component} = setupTest(cssClasses.MODAL);
const {component, mockFocusTrapInstance} = setupTestWithMocks(cssClasses.MODAL);
component.getDefaultFoundation().adapter_.releaseFocus();
util.createFocusTrapInstance = createFocusTrapInstance;

td.verify(fakeFocusTrapInstance.deactivate());
td.verify(mockFocusTrapInstance.deactivate());
});

test('adapter#computeBoundingRect calls getBoundingClientRect() on root', () => {
Expand Down

0 comments on commit 8d53068

Please sign in to comment.