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

Commit

Permalink
fix(drawer): Prevent scrolling on body when temporary drawer open (#807)
Browse files Browse the repository at this point in the history
Closes #777

BREAKING CHANGE: Adapter API for temporary drawers contains two new methods: `addBodyClass` and `removeBodyClass`.
  • Loading branch information
vinhlh authored and traviskaufman committed Jun 12, 2017
1 parent dec20ab commit 8686d85
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 7 deletions.
2 changes: 2 additions & 0 deletions packages/mdc-drawer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ The adapter for temporary drawers must provide the following functions, with cor
| `addClass(className: string) => void` | Adds a class to the root element. |
| `removeClass(className: string) => void` | Removes a class from the root element. |
| `hasClass(className: string) => boolean` | Returns boolean indicating whether element has a given class. |
| `addBodyClass(className: string) => void` | Adds a class to the body. |
| `removeBodyClass(className: string) => void` | Removes a class from the body. |
| `hasNecessaryDom() => boolean` | Returns boolean indicating whether the necessary DOM is present (namely, the `mdc-temporary-drawer__drawer` drawer container). |
| `registerInteractionHandler(evt: string, handler: EventListener) => void` | Adds an event listener to the root element, for the specified event name. |
| `deregisterInteractionHandler(evt: string, handler: EventListener) => void` | Removes an event listener from the root element, for the specified event name. |
Expand Down
14 changes: 8 additions & 6 deletions packages/mdc-drawer/slidable/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,7 @@ export class MDCSlidableDrawerFoundation extends MDCFoundation {
this.animatingCssClass_ = animatingCssClass;
this.openCssClass_ = openCssClass;

this.transitionEndHandler_ = (ev) => {
if (this.isRootTransitioningEventTarget_(ev.target)) {
this.adapter_.removeClass(this.animatingCssClass_);
this.adapter_.deregisterTransitionEndHandler(this.transitionEndHandler_);
}
};
this.transitionEndHandler_ = (evt) => this.handleTransitionEnd_(evt);

this.inert_ = false;

Expand Down Expand Up @@ -239,4 +234,11 @@ export class MDCSlidableDrawerFoundation extends MDCFoundation {
// if the event target is the root event target currently transitioning.
return false;
}

handleTransitionEnd_(evt) {
if (this.isRootTransitioningEventTarget_(evt.target)) {
this.adapter_.removeClass(this.animatingCssClass_);
this.adapter_.deregisterTransitionEndHandler(this.transitionEndHandler_);
}
};
}
1 change: 1 addition & 0 deletions packages/mdc-drawer/temporary/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const cssClasses = {
ROOT: 'mdc-temporary-drawer',
OPEN: 'mdc-temporary-drawer--open',
ANIMATING: 'mdc-temporary-drawer--animating',
SCROLL_LOCK: 'mdc-drawer-scroll-lock',
};

export const strings = {
Expand Down
19 changes: 19 additions & 0 deletions packages/mdc-drawer/temporary/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export default class MDCTemporaryDrawerFoundation extends MDCSlidableDrawerFound

static get defaultAdapter() {
return Object.assign(MDCSlidableDrawerFoundation.defaultAdapter, {
addBodyClass: (/* className: string */) => {},
removeBodyClass: (/* className: string */) => {},
isDrawer: () => false,
updateCssVariable: (/* value: string */) => {},
});
Expand Down Expand Up @@ -56,9 +58,11 @@ export default class MDCTemporaryDrawerFoundation extends MDCSlidableDrawerFound
super.destroy();

this.adapter_.deregisterInteractionHandler('click', this.componentClickHandler_);
this.enableScroll_();
}

open() {
this.disableScroll_();
// Make sure custom property values are cleared before starting.
this.adapter_.updateCssVariable('');

Expand Down Expand Up @@ -88,4 +92,19 @@ export default class MDCTemporaryDrawerFoundation extends MDCSlidableDrawerFound
isRootTransitioningEventTarget_(el) {
return this.adapter_.isDrawer(el);
}

handleTransitionEnd_(evt) {
super.handleTransitionEnd_(evt);
if (!this.isOpen_) {
this.enableScroll_();
}
};

disableScroll_() {
this.adapter_.addBodyClass(cssClasses.SCROLL_LOCK);
}

enableScroll_() {
this.adapter_.removeBodyClass(cssClasses.SCROLL_LOCK);
}
}
2 changes: 2 additions & 0 deletions packages/mdc-drawer/temporary/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export class MDCTemporaryDrawer extends MDCComponent {
addClass: (className) => this.root_.classList.add(className),
removeClass: (className) => this.root_.classList.remove(className),
hasClass: (className) => this.root_.classList.contains(className),
addBodyClass: (className) => document.body.classList.add(className),
removeBodyClass: (className) => document.body.classList.remove(className),
hasNecessaryDom: () => Boolean(this.drawer),
registerInteractionHandler: (evt, handler) =>
this.root_.addEventListener(util.remapEvent(evt), handler, util.applyPassive()),
Expand Down
5 changes: 5 additions & 0 deletions packages/mdc-drawer/temporary/mdc-temporary-drawer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,8 @@
}
}
}

.mdc-drawer-scroll-lock {
height: 100vh;
overflow: hidden;
}
15 changes: 15 additions & 0 deletions test/unit/mdc-drawer/mdc-temporary-drawer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,18 @@ test('adapter#isDrawer returns false for a non-drawer element', () => {
const {root, component} = setupTest();
assert.isNotOk(component.getDefaultFoundation().adapter_.isDrawer(root));
});

test('adapter#addBodyClass adds a class to the body', () => {
const {component} = setupTest();
component.getDefaultFoundation().adapter_.addBodyClass('mdc-drawer--scroll-lock');
assert.isOk(document.querySelector('body').classList.contains('mdc-drawer--scroll-lock'));
});

test('adapter#removeBodyClass remove a class from the body', () => {
const {component} = setupTest();
const body = document.querySelector('body');

body.classList.add('mdc-drawer--scroll-lock');
component.getDefaultFoundation().adapter_.removeBodyClass('mdc-drawer--scroll-lock');
assert.isNotOk(body.classList.contains('mdc-drawer--scroll-lock'));
});
24 changes: 23 additions & 1 deletion test/unit/mdc-drawer/temporary.foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ test('exports cssClasses', () => {

test('defaultAdapter returns a complete adapter implementation', () => {
verifyDefaultAdapter(MDCTemporaryDrawerFoundation, [
'addClass', 'removeClass', 'hasClass', 'hasNecessaryDom', 'registerInteractionHandler',
'addClass', 'removeClass', 'hasClass', 'addBodyClass', 'removeBodyClass',
'hasNecessaryDom', 'registerInteractionHandler',
'deregisterInteractionHandler', 'registerDrawerInteractionHandler', 'deregisterDrawerInteractionHandler',
'registerTransitionEndHandler', 'deregisterTransitionEndHandler', 'registerDocumentKeydownHandler',
'deregisterDocumentKeydownHandler', 'setTranslateX', 'getFocusableElements',
Expand Down Expand Up @@ -320,3 +321,24 @@ test('#isRootTransitioningEventTarget_ returns true if the element is the drawer
td.when(mockAdapter.isDrawer(fakeEl)).thenReturn(true);
assert.isTrue(foundation.isRootTransitioningEventTarget_(fakeEl));
});

test('#open adds scroll lock class to the body', () => {
const {foundation, mockAdapter} = setupTest();

foundation.open();

td.verify(mockAdapter.addBodyClass(cssClasses.SCROLL_LOCK));
});

test('#close removes the scroll lock class from the body', () => {
const {foundation, mockAdapter} = setupTest();

td.when(mockAdapter.registerTransitionEndHandler(td.callback)).thenCallback({target: {}});
td.when(mockAdapter.isDrawer(td.matchers.isA(Object))).thenReturn(true);
foundation.open();
td.when(mockAdapter.registerTransitionEndHandler(td.callback)).thenCallback({target: {}});
td.when(mockAdapter.isDrawer(td.matchers.isA(Object))).thenReturn(true);
foundation.close();

td.verify(mockAdapter.removeBodyClass(cssClasses.SCROLL_LOCK));
});

0 comments on commit 8686d85

Please sign in to comment.