-
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): Convert JS to TypeScript #4390
Conversation
Codecov Report
@@ Coverage Diff @@
## feat/typescript #4390 +/- ##
==================================================
+ Coverage 98.68% 98.9% +0.22%
==================================================
Files 94 95 +1
Lines 5987 6109 +122
Branches 805 802 -3
==================================================
+ Hits 5908 6042 +134
+ Misses 78 66 -12
Partials 1 1
Continue to review full report at Codecov.
|
All 621 screenshot tests passed for commit ed69731 vs. |
All 621 screenshot tests passed for commit c8e9df4 vs. |
All 621 screenshot tests passed for commit e7719ba vs. |
All 621 screenshot tests passed for commit cf36082 vs. |
All 621 screenshot tests passed for commit d53d1bc vs. |
All 621 screenshot tests passed for commit 01d0276 vs. |
All 621 screenshot tests passed for commit d6d7132 vs. |
All 621 screenshot tests passed for commit b6f32cd vs. |
All 624 screenshot tests passed for commit b152591 vs. |
All 624 screenshot tests passed for commit 24f442c vs. |
All 624 screenshot tests passed for commit b34993e vs. |
const {OPENING, CLOSING, OPEN, ANIMATE, ROOT} = cssClasses; | ||
|
||
// In Edge, transitionend on ripple pseudo-elements yields a target without classList, so check for Element first. | ||
const isElement = evt.target instanceof Element; | ||
if (!isElement || !this.adapter_.elementHasClass(/** @type {!Element} */ (evt.target), ROOT)) { | ||
if (!isElement || !this.adapter_.elementHasClass(evt.target as Element, ROOT)) { |
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 would be a good place for a guard:
function isElement(element: any): element is Element {
return element instanceof Element;
}
if (!this.adapter_.elementHasClass(isElement(evt.target), ROOT)
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.
Done. instanceof
checks make it harder to mock objects in unit tests, so I'm testing for the presence of classList
instead.
class MDCDismissibleDrawerFoundation {
handleTransitionEnd(evt: TransitionEvent) {
// In Edge, transitionend on ripple pseudo-elements yields a target without classList, so check for Element first.
const isRootElement = this.isElement_(evt.target) && this.adapter_.elementHasClass(evt.target, ROOT);
if (!isRootElement) {
return;
}
}
private isElement_(element: unknown): element is Element {
// In Edge, transitionend on ripple pseudo-elements yields a target without classList.
return Boolean((element as Element).classList);
}
}
private scrim_!: Element | null; // assigned in initialSyncWithDOM() | ||
private list_?: MDCList; // assigned in initialize() | ||
|
||
private focusTrap_?: createFocusTrap.FocusTrap; // assigned in initialSyncWithDOM() |
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 change this to focusTrap_!, then below we don't have to the do focusTrap_!.active...
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.
focusTrap_
is undefined
if the scrim is not present:
initialSyncWithDOM() {
const {MODAL} = MDCDismissibleDrawerFoundation.cssClasses;
const {SCRIM_SELECTOR} = MDCDismissibleDrawerFoundation.strings;
this.scrim_ = (this.root_.parentNode as Element).querySelector<HTMLElement>(SCRIM_SELECTOR);
if (this.scrim_ && this.root_.classList.contains(MODAL)) {
this.handleScrimClick_ = () => (this.foundation_ as MDCModalDrawerFoundation).handleScrimClick();
this.scrim_.addEventListener('click', this.handleScrimClick_);
this.focusTrap_ = util.createFocusTrapInstance(this.root_ as HTMLElement, this.focusTrapFactory_);
}
}
So we have to keep focusTrap_?
and focusTrap_!.active
All 624 screenshot tests passed for commit b5a06e5 vs. |
packages/mdc-dialog/index.ts
Outdated
@@ -70,7 +70,7 @@ class MDCDialog extends MDCComponent<MDCDialogFoundation> { | |||
private container_!: HTMLElement; // assigned in initialize() | |||
private content_!: HTMLElement | null; // assigned in initialize() | |||
private defaultButton_!: HTMLElement | null; // assigned in initialize() | |||
private initialFocusEl_!: HTMLElement | null; // assigned in initialize() | |||
private initialFocusEl_!: HTMLElement | undefined; // assigned in initialize() |
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 seems like a contradiction in terms. Isn't this effectively private initialFocusEl_?: HTMLElement
?
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.
Confusingly, the !
here has a slightly different meaning than when it's used after an expression.
When declaring member properties, !
is a "definite assignment assertion", meaning "I pinky swear that some value is assigned to this property, but it happens somewhere other than the constructor.
In an expression (e.g., querySelector('.foo')!
), the bang means "this value will never be null or undefined."
You make a good point though; this can be shortened to private initialFocusEl_?: HTMLElement
.
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.
Done
packages/mdc-dialog/index.ts
Outdated
@@ -93,7 +93,7 @@ class MDCDialog extends MDCComponent<MDCDialogFoundation> { | |||
this.buttons_ = [].slice.call(this.root_.querySelectorAll<HTMLElement>(strings.BUTTON_SELECTOR)); | |||
this.defaultButton_ = this.root_.querySelector<HTMLElement>(strings.DEFAULT_BUTTON_SELECTOR); | |||
this.focusTrapFactory_ = focusTrapFactory; | |||
this.initialFocusEl_ = initialFocusEl as HTMLElement; | |||
this.initialFocusEl_ = initialFocusEl as HTMLElement | undefined; |
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.
Why do we cast to HTMLElement
here when we're accepting Element
above? Is there something we need from HTMLElement
in particular?
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 was trying to make things simpler for Closure users (who typically pass an Element
).
However, this method is called indirectly via the MDCComponent
constructor (which takes an Element
), so it actually shouldn't be an issue for them.
I'll change the type of the initialFocusEl
arg to HTMLElement
. The TypeScript compiler seems fine with it.
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.
Done
packages/mdc-drawer/types.ts
Outdated
import {MDCList} from '@material/list/index'; | ||
import * as FocusTrapLib from 'focus-trap'; | ||
|
||
// TODO(acdvorak): Centralize this in mdc-base or mdc-dom? |
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.
RE this TODO: I wouldn't want to centralize a third-party dependency in a package that is depended upon by a large majority of our packages. Right now it's only a dependency of 2 packages.
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.
Fair point. I'll remove it.
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.
Done
All 624 screenshot tests passed for commit 1cd4d00 vs. |
Addressed Ken's comments; Matt approved
Refs #4225