-
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(drawer): Implement persistent drawer #488
Conversation
…ry.foundation.test.js
Codecov Report
@@ Coverage Diff @@
## master #488 +/- ##
==========================================
- Coverage 99.05% 99.02% -0.04%
==========================================
Files 44 45 +1
Lines 2016 2050 +34
Branches 249 251 +2
==========================================
+ Hits 1997 2030 +33
- Misses 19 20 +1
Continue to review full report at Codecov.
|
Looks like the coverage diff is failing. Once that's fixed I'll review! |
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.
Really excellent work here! And thank you especially for taking the time to refactor out the common logic between temp and persistent drawers. Besides the inline comments, a few general comments:
-
I'd add a very simple README inside of the
slidable
dir denoting its purpose and intent to be used only as a base class for our own drawers, not for public API use. This will make it clear to contributors and users what its purpose is without digging through our code. -
It doesn't look like the main README has been updated with any usage docs around persistent drawers. This info needs to be added to the README, along with the adapter API table which is required for all components. One note here is that the adapter API should outline all methods needed for the SlidableDrawer adapter as well, as if it were just one object. This denotes how using a slidable drawer base class is purely an implementation detail on our end, and has no effect on how adapters are shaped and passed to the foundation from a client code POV.
|
||
import {focusableElements} from '../slidable'; | ||
|
||
const ROOT = 'mdc-persistent-drawer'; |
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.
I know we have this littered throughout our codebase already, but we actually need to ensure moving forward that these are all static strings so that they interop with closure stylesheet's renaming of css classes / strings the correct way.
export const cssClasses = {
ROOT: 'mdc-persistent-drawer',
OPEN: 'mdc-persistent-drawer--open',
ANIMATING: 'mdc-persistent-drawer--animating',
};
export const strings = {
DRAWER_SELECTOR: '.mdc-persistent-drawer__drawer',
FOCUSABLE_ELEMENTS: focusableElements,
};
Our bad 😐
} | ||
|
||
static get defaultAdapter() { | ||
const defaultAdapter = MDCSlidableDrawerFoundation.defaultAdapter; |
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.
nit: You could simplify this by using Object.assign
return Object.assign(MDCSlidableDrawerFoundation.defaultAdapter, {
isRoot: () => false,
});
MDCPersistentDrawerFoundation.cssClasses.OPEN); | ||
} | ||
|
||
isRootTransitioningElement_(el) { |
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.
Per our authoring components guide, we try very hard not to make references to _element_s or anything that makes assumptions about a host environment in our foundation / adapter code.
I imagine that this method gets called likely in response to an event, to which the event target is passed to this method. In that case, we usually tend to use the term eventTarget in place of element. E.g.
getIndexForEventTarget: (/* target: EventTarget */) => /* number */ 0, |
import * as util from '../util'; | ||
|
||
export {MDCPersistentDrawerFoundation}; | ||
|
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.
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.
I went ahead and exported util for temporary/index.js too
} | ||
|
||
&--animating { | ||
transition: mdc-animation-enter(width, $mdc-slidable-drawer-transition-time); |
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.
Hmm, you definitely don't want to be animating width
here if at all possible, as it has severe performance implications, especially considering that this will tend to have implications on how the entire page is laid out. I'd be okay with a jump-cut here. Otherwise, I'd look into using something like the FLIP model to see if we can achieve the same affect using transforms.
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.
Going with jump-cut for now...but I left the extra div element (aka .mdc-persistent-drawer and .mdc-persistent-drawer__drawer) since I figured clients will want to animate something like width?
@include mdc-rtl-reflexive-position(left, 0); | ||
|
||
@include mdc-theme-dark { | ||
background: #212121; |
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.
Not sure we want to hard-code BG colors here, as this gives the user a lot less thematic control and assumes that this is the color of the background we want to use for dark themes. I'd take an approach similar to dialog here (source).
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.
I went ahead and refactored permanent drawers to "not hard-code BG colors" too
packages/mdc-drawer/index.js
Outdated
@@ -15,5 +15,7 @@ | |||
*/ | |||
|
|||
import * as util from './util'; | |||
export {MDCSlidableDrawerFoundation} from './slidable'; |
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.
Not sure if we want to export this particular foundation here since it's meant to be subclassed by our drawer implementations, and we want to reduce the API surface such that we're not encouraging users to make their own versions of Material Design drawers.
* limitations under the License. | ||
*/ | ||
|
||
export const focusableElements = |
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.
nit: This should be named FOCUSABLE_ELEMENTS
to adhere to constants conventions.
* limitations under the License. | ||
*/ | ||
|
||
@import "@material/rtl/mixins"; |
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.
nit: looks like there are a few magic numbers (107%
, 20px
) here. Perhaps they should be refactored into scss vars?
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.
Honestly...I have no idea, they were existing magic numbers in temporary drawers.
I've isolated them to slideable's _mixins.scss in this Pull Request. I think a follow up pull request might want to investigate these magic numbers more
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.
didn't realize they came from temporary drawer. SGTM for addressing in a different PR.
getDrawerWidth: () => /* number */ 0, | ||
isDrawer: (/* el: Element */) => /* boolean */ false, | ||
}; | ||
const defaultAdapter = MDCSlidableDrawerFoundation.defaultAdapter; |
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.
Same comment as in persistent drawer regarding Object.assign
.
Updated based on comments, PTAL |
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.
Mostly just nits / small changes this time around!
packages/mdc-drawer/README.md
Outdated
@@ -70,6 +70,131 @@ CSS classes: | |||
| `mdc-permanent-drawer__content` | Mandatory. Needs to be set on the container node for the drawer content. | | |||
| `mdc-permanent-drawer__toolbar-spacer` | Optional. Add to node to provide the matching amount of space for toolbar. | | |||
|
|||
## Persistent drawer usage | |||
|
|||
A persistent drawers can toggle open or closed. The drawer sits on the same surface elevation as the content. It is closed by default. When the drawer is outside of the page grid and opens, the drawer forces other content to change size and adapt to the smaller viewport. Persistent drawers stay open until closed by the user. |
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.
Maybe change this first sentence to "Persistent drawers can be toggled open or closed"?
|
||
export const strings = { | ||
DRAWER_SELECTOR: '.mdc-persistent-drawer__drawer', | ||
FOCUSABLE_ELEMENTS: FOCUSABLE_ELEMENTS, |
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.
nit: you can just the object shorthand property here
export const strings = {
DRAWER_SELECTOR: '.mdc-persistent-drawer__drawer',
FOCUSABLE_ELEMENTS,
};
@include mdc-rtl-reflexive-position(left, 0); | ||
|
||
@include mdc-theme-dark { | ||
background-color: $mdc-persistent-drawer-dark-theme-bg-color; |
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.
supernit: seems like the indentation is off here. V surprised stylelint didn't catch that.
* limitations under the License. | ||
*/ | ||
|
||
@import "@material/rtl/mixins"; |
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.
didn't realize they came from temporary drawer. SGTM for addressing in a different PR.
this.openCssClass_ = openCssClass; | ||
|
||
this.transitionEndHandler_ = (ev) => { | ||
if (this.isRootTransitioningElement_(ev.target)) { |
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 this be isRootTransitioningEventTarget_
?
@@ -0,0 +1,284 @@ | |||
/** |
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 should be named mdc-persistent-drawer.test.js
. It actually might not be getting run otherwise...
Also FYI chai has assert.{isTrue,isFalse}
which is a strict boolean test rather than just a test for truthiness / falsiness.
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, totally was not running...well done lynn...
`; | ||
document.body.appendChild(root); | ||
const component = new MDCPersistentDrawer(root); | ||
assert.isOk(component.getDefaultFoundation().adapter_.isRtl()); |
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.
you'll want to make sure to remove the root element from document.body
before the test ends, here and below.
}); | ||
|
||
test('defaultAdapter returns a complete adapter implementation', () => { | ||
const {defaultAdapter} = MDCPersistentDrawerFoundation; |
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.
there's a verifyDefaultAdapter
helper method in test/helpers/foundation
which you can use for this
suite('MDCSlidableDrawerFoundation'); | ||
|
||
test('defaultAdapter returns a complete adapter implementation', () => { | ||
const {defaultAdapter} = MDCSlidableDrawerFoundation; |
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.
same comment as above re verifyDefaultAdapter
</nav> | ||
</aside> | ||
<div class="demo-content"> | ||
<header class="mdc-toolbar mdc-elevation--z4"> |
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.
I think this DOM needs to be updated to reflect recent toolbar changes:
<header class="mdc-toolbar mdc-elevation--z4">
<div class="mdc-toolbar__row">
<section class="mdc-toolbar__section mdc-toolbar__section--align-start">
<button class="demo-menu material-icons">menu</button>
</section>
</div>
</header>
…e 'drawer' element as the root transitioning element, now that I removed the transition property from the component's root element
Updated based on comments, PTAL |
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.
💎 💯 🔥 🚀 One nit but besides that LGTM! Awesome stuff here, especially around the refactoring.
packages/mdc-drawer/README.md
Outdated
| `restoreElementTabState(el: Element) => void` | Restores the saved tab index (if any) for an element. | | ||
| `makeElementUntabbable(el: Element) => void` | Makes an element untabbable. | | ||
| `isRtl() => boolean` | Returns boolean indicating whether the current environment is RTL. | | ||
| `isRoot(el: Element) => boolean` | Returns boolean indicating whether the provided element is the root container 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.
Doesn't look like this method is used anywhere anymore. Left over from a previous iteration?
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.
hmm nope. fixed
Resolves #15, Implement persistent drawer by re-using the "slidable" logic from temporary drawer.