Skip to content

Commit 8d53068

Browse files
authored
fix(drawer): Fix exports and closure tests (material-components#3424)
1 parent 3aa211d commit 8d53068

File tree

4 files changed

+65
-63
lines changed

4 files changed

+65
-63
lines changed

closure_externs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,4 @@ class FocusTrapInstance {
112112
* @param {!Element} element
113113
* @param {FocusTrapCreateOptions=} createOptions
114114
*/
115-
mdc.thirdparty.focusTrap.default;
115+
mdc.thirdparty.focusTrap;

packages/mdc-drawer/index.js

+27-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,18 @@
2323
import {MDCComponent} from '@material/base/index';
2424
import MDCDismissibleDrawerFoundation from './dismissible/foundation';
2525
import MDCModalDrawerFoundation from './modal/foundation';
26+
import MDCDrawerAdapter from './adapter';
2627
import {MDCList} from '@material/list/index';
2728
import MDCListFoundation from '@material/list/foundation';
2829
import {strings} from './constants';
2930
import * as util from './util';
31+
import createFocusTrap from 'focus-trap';
3032

3133
/**
3234
* @extends {MDCComponent<!MDCDismissibleDrawerFoundation>}
3335
* @final
3436
*/
35-
export class MDCDrawer extends MDCComponent {
37+
class MDCDrawer extends MDCComponent {
3638
/**
3739
* @param {...?} args
3840
*/
@@ -49,6 +51,15 @@ export class MDCDrawer extends MDCComponent {
4951
this.handleTransitionEnd_;
5052

5153
/** @private {!Function} */
54+
this.focusTrapFactory_;
55+
56+
/** @private {!FocusTrapInstance} */
57+
this.focusTrap_;
58+
59+
/** @private {?Element} */
60+
this.scrim_;
61+
62+
/** @private {?Function} */
5263
this.handleScrimClick_;
5364
}
5465

@@ -80,20 +91,23 @@ export class MDCDrawer extends MDCComponent {
8091
}
8192
}
8293

83-
initialize() {
84-
const list = MDCList.attachTo(this.root_.querySelector(`.${MDCListFoundation.cssClasses.ROOT}`));
94+
initialize(
95+
focusTrapFactory = createFocusTrap) {
96+
const listEl = /** @type {!Element} */ (this.root_.querySelector(`.${MDCListFoundation.cssClasses.ROOT}`));
97+
const list = MDCList.attachTo(listEl);
8598
list.wrapFocus = true;
99+
this.focusTrapFactory_ = focusTrapFactory;
86100
}
87101

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

91105
if (this.root_.classList.contains(MODAL)) {
92106
const {SCRIM_SELECTOR} = MDCDismissibleDrawerFoundation.strings;
93-
this.scrim_ = this.root_.parentElement.querySelector(SCRIM_SELECTOR);
94-
this.handleScrimClick_ = () => this.foundation_.handleScrimClick();
107+
this.scrim_ = /** @type {!Element} */ (this.root_.parentElement.querySelector(SCRIM_SELECTOR));
108+
this.handleScrimClick_ = () => /** @type {!MDCModalDrawerFoundation} */ (this.foundation_).handleScrimClick();
95109
this.scrim_.addEventListener('click', this.handleScrimClick_);
96-
this.focusTrap_ = util.createFocusTrapInstance(this.root_);
110+
this.focusTrap_ = util.createFocusTrapInstance(this.root_, this.focusTrapFactory_);
97111
}
98112

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

110124
const {MODAL} = MDCDismissibleDrawerFoundation.cssClasses;
111125
if (this.root_.classList.contains(MODAL)) {
112-
this.scrim_.removeEventListener('click', this.handleScrimClick_);
113-
this.focusTrap_.destroy();
126+
this.scrim_.removeEventListener('click', /** @type {!Function} */ (this.handleScrimClick_));
127+
// Ensure drawer is closed to hide scrim and release focus
128+
this.open = false;
114129
}
115130
}
116131

@@ -137,8 +152,8 @@ export class MDCDrawer extends MDCComponent {
137152
activeNavItemEl.focus();
138153
}
139154
},
140-
notifyClose: () => this.emit(strings.CLOSE_EVENT, null, true /* shouldBubble */),
141-
notifyOpen: () => this.emit(strings.OPEN_EVENT, null, true /* shouldBubble */),
155+
notifyClose: () => this.emit(strings.CLOSE_EVENT, {}, true /* shouldBubble */),
156+
notifyOpen: () => this.emit(strings.OPEN_EVENT, {}, true /* shouldBubble */),
142157
trapFocus: () => this.focusTrap_.activate(),
143158
releaseFocus: () => this.focusTrap_.deactivate(),
144159
}));
@@ -154,3 +169,5 @@ export class MDCDrawer extends MDCComponent {
154169
}
155170
}
156171
}
172+
173+
export {MDCDrawer, MDCDismissibleDrawerFoundation, MDCModalDrawerFoundation, util};

packages/mdc-drawer/util.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,18 @@
2323

2424
import createFocusTrap from 'focus-trap';
2525

26-
export function createFocusTrapInstance(surfaceEl, focusTrapFactory = createFocusTrap) {
26+
/**
27+
* @param {!Element} surfaceEl
28+
* @param {!Function} focusTrapFactory
29+
* @return {!FocusTrapInstance}
30+
*/
31+
function createFocusTrapInstance(surfaceEl, focusTrapFactory = createFocusTrap) {
2732
return focusTrapFactory(surfaceEl, {
2833
clickOutsideDeactivates: true,
2934
initialFocus: false, // Navigation drawer handles focusing on active nav item.
3035
escapeDeactivates: false, // Navigation drawer handles ESC.
3136
returnFocusOnDeactivate: false, // Navigation drawer handles restore focus.
3237
});
3338
}
39+
40+
export {createFocusTrapInstance};

test/unit/mdc-drawer/mdc-drawer.test.js

+29-51
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import domEvents from 'dom-events';
2727
import td from 'testdouble';
2828

2929
import {MDCDrawer} from '../../../packages/mdc-drawer';
30-
import * as util from '../../../packages/mdc-drawer/util';
3130
import {strings, cssClasses} from '../../../packages/mdc-drawer/constants';
3231
import {MDCListFoundation} from '../../../packages/mdc-list';
3332
import MDCDismissibleDrawerFoundation from '../../../packages/mdc-drawer/dismissible/foundation';
@@ -59,13 +58,21 @@ function setupTest(variantClass = cssClasses.DISMISSIBLE) {
5958
const root = getFixture(variantClass);
6059
const drawer = root.querySelector('.mdc-drawer');
6160
const component = new MDCDrawer(drawer);
62-
const MockFoundationCtor = td.constructor(MDCDismissibleDrawerFoundation);
63-
const mockFoundation = new MockFoundationCtor();
64-
return {root, drawer, component, mockFoundation};
61+
return {root, drawer, component};
6562
}
6663

67-
function hasClassMatcher(className) {
68-
return td.matchers.argThat((el) => el.classList && el.classList.contains(className));
64+
65+
function setupTestWithMocks(variantClass = cssClasses.DISMISSIBLE) {
66+
const root = getFixture(variantClass);
67+
const drawer = root.querySelector('.mdc-drawer');
68+
const MockFoundationCtor = td.constructor(MDCDismissibleDrawerFoundation);
69+
const mockFoundation = new MockFoundationCtor();
70+
const mockFocusTrapInstance = td.object({
71+
activate: () => {},
72+
deactivate: () => {},
73+
});
74+
const component = new MDCDrawer(drawer, mockFoundation, () => mockFocusTrapInstance);
75+
return {root, drawer, component, mockFoundation, mockFocusTrapInstance};
6976
}
7077

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

7885
test('#get open calls foundation.isOpen', () => {
79-
const {component} = setupTest();
80-
component.foundation_.isOpen = td.func();
86+
const {component, mockFoundation} = setupTestWithMocks();
8187
component.open;
82-
td.verify(component.foundation_.isOpen(), {times: 1});
88+
td.verify(mockFoundation.isOpen(), {times: 1});
8389
});
8490

8591
test('#set open true calls foundation.open', () => {
86-
const {component} = setupTest();
87-
component.foundation_.open = td.func();
92+
const {component, mockFoundation} = setupTestWithMocks();
8893
component.open = true;
89-
td.verify(component.foundation_.open(), {times: 1});
94+
td.verify(mockFoundation.open(), {times: 1});
9095
});
9196

9297
test('#set open false calls foundation.close', () => {
93-
const {component} = setupTest();
94-
component.foundation_.close = td.func();
98+
const {component, mockFoundation} = setupTestWithMocks();
9599
component.open = false;
96-
td.verify(component.foundation_.close(), {times: 1});
100+
td.verify(mockFoundation.close(), {times: 1});
97101
});
98102

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

107110
test('transitionend event calls foundation.handleTransitionEnd method', () => {
108-
const {component, drawer} = setupTest();
109-
component.foundation_.handleTransitionEnd = td.func();
111+
const {drawer, mockFoundation} = setupTestWithMocks();
110112
domEvents.emit(drawer, 'transitionend');
111-
td.verify(component.foundation_.handleTransitionEnd(td.matchers.isA(Object)), {times: 1});
113+
td.verify(mockFoundation.handleTransitionEnd(td.matchers.isA(Object)), {times: 1});
112114
});
113115

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

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

127129
test('#destroy removes transitionend event listener', () => {
128-
const {component, drawer, mockFoundation} = setupTest();
130+
const {component, drawer, mockFoundation} = setupTestWithMocks();
129131
component.destroy();
130132

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

220222
test('adapter#trapFocus traps focus on root element', () => {
221-
const {createFocusTrapInstance} = util;
222-
util.createFocusTrapInstance = td.func('util.createFocusTrapInstance');
223-
224-
const fakeFocusTrapInstance = td.object({
225-
activate: () => {},
226-
deactivate: () => {},
227-
});
228-
td.when(
229-
util.createFocusTrapInstance(hasClassMatcher('mdc-drawer'))
230-
).thenReturn(fakeFocusTrapInstance);
231-
232-
const {component} = setupTest(cssClasses.MODAL);
223+
const {component, mockFocusTrapInstance} = setupTestWithMocks(cssClasses.MODAL);
233224
component.getDefaultFoundation().adapter_.trapFocus();
234-
util.createFocusTrapInstance = createFocusTrapInstance;
235225

236-
td.verify(fakeFocusTrapInstance.activate());
226+
td.verify(mockFocusTrapInstance.activate());
237227
});
238228

239229
test('adapter#releaseFocus releases focus on root element after trap focus', () => {
240-
const {createFocusTrapInstance} = util;
241-
util.createFocusTrapInstance = td.func('util.createFocusTrapInstance');
242-
243-
const fakeFocusTrapInstance = td.object({
244-
activate: () => {},
245-
deactivate: () => {},
246-
});
247-
td.when(
248-
util.createFocusTrapInstance(hasClassMatcher('mdc-drawer'))
249-
).thenReturn(fakeFocusTrapInstance);
250-
251-
const {component} = setupTest(cssClasses.MODAL);
230+
const {component, mockFocusTrapInstance} = setupTestWithMocks(cssClasses.MODAL);
252231
component.getDefaultFoundation().adapter_.releaseFocus();
253-
util.createFocusTrapInstance = createFocusTrapInstance;
254232

255-
td.verify(fakeFocusTrapInstance.deactivate());
233+
td.verify(mockFocusTrapInstance.deactivate());
256234
});
257235

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

0 commit comments

Comments
 (0)