Skip to content
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

Focus capturing for sidenav. #1695

Merged
merged 8 commits into from
Dec 6, 2016
Merged

Focus capturing for sidenav. #1695

merged 8 commits into from
Dec 6, 2016

Conversation

mmalerba
Copy link
Contributor

@mmalerba mmalerba commented Nov 2, 2016

Captures focus when sidenav is open in "over" or "push" mode, but not
when opened in "side" mode.

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Nov 2, 2016
this._elementFocusedBeforeDialogWasOpened = document.activeElement;
this._focusTrap.focusFirstTabbableElement();
});
this._elementFocusedBeforeDialogWasOpened = document.activeElement;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think its worth moving this to the FocusTrap as well? Maybe like this: focusOnDeactivate=""

@mmalerba
Copy link
Contributor Author

mmalerba commented Nov 2, 2016

fixes #50

@jelbourn
Copy link
Member

jelbourn commented Nov 3, 2016

cc @crisbeto, relevant to dialog

@jelbourn
Copy link
Member

jelbourn commented Nov 3, 2016

@marcysutton as far as you know, is there ever a time when you would want to trap focus within some subtree of the DOM and not automatically move focus into that subtree as soon as it's added to the page?

@@ -1,4 +1,6 @@
import {Component, ViewEncapsulation, ViewChild, ElementRef} from '@angular/core';
import {
Component, ViewEncapsulation, ViewChild, ElementRef, Input, AfterContentInit, NgZone
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are supposed to all be on separate lines.

set active(val: boolean) {
this._active = val;
if (val && this._contentReady) {
this._ngZone.onMicrotaskEmpty.first().subscribe(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is used here and repeated down inside ngAfterContentInit. It might a good idea to move it to a private method like waitForTabbableElement.

@@ -45,6 +41,9 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
/** Reference to the open dialog. */
dialogRef: MdDialogRef<any>;

/** Whether the focus trap is active. */
focusTrapActive: boolean = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the style guide says about this, but wouldn't the boolean be inferred from the default value anyway @jelbourn?

@marcysutton
Copy link

@jelbourn focus traps are usually enabled/disabled on command when the UI requires it, such as a modal dialog being shown. Otherwise you run the risk of trapping a keyboard user inside a menu at the wrong time.

You might want to check out HTML inert and the related polyfill for this purpose. https://github.com/GoogleChrome/inert-polyfill

@mmalerba
Copy link
Contributor Author

mmalerba commented Nov 4, 2016

focus traps are usually enabled/disabled on command when the UI requires it

@marcysutton That sounds like what I'm doing here, I added an active attribute to focus-trap when it changes to true the focus trap automatically focuses the first focusable element inside it.

@mmalerba
Copy link
Contributor Author

mmalerba commented Nov 9, 2016

Changed to not automatically focus the first element when it's activated.

private _contentReady: boolean = false;

/** Whether the focus trap is active. */
private _active: boolean = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about inverting this to disabled (with coerceBooleanProperty)? It might make more sense if active is the default.

// Trigger setter behavior.
if (this.active) {

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty if?

@@ -106,6 +111,10 @@ export class MdSidenav implements AfterContentInit {
/** Event emitted when the sidenav alignment changes. */
@Output('align-changed') onAlignChanged = new EventEmitter<void>();

get focusTrapActive() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isFocusTrapActive? (or isFocusTrapDisabled is we invert it)

Add a comment like

// The focus trap is only enabled when the sidenav is open in any mode
// except for side. 

link2Element.focus();
});

it('should trp focus when opened in "over" mode', fakeAsync(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trp -> trap

expect(document.activeElement).toBe(link1Element);
}));

it('should trap tabs when opened in "push" mode', fakeAsync(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trap tabs?

sidenav = fixture.debugElement.query(By.directive(MdSidenav)).componentInstance;
link1Element = fixture.debugElement.query(By.css('.link1')).nativeElement;
link2Element = fixture.debugElement.query(By.css('.link1')).nativeElement;
link2Element.focus();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call these something like firstFocusableElement and lastFocusableElement?

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker and removed pr: needs review labels Nov 14, 2016
@jelbourn
Copy link
Member

LGTM

@kara
Copy link
Contributor

kara commented Nov 15, 2016

@mmalerba Presubmit is unhappy because

Property 'active' does not exist on type 'FocusTrap'

@kara kara removed the action: merge The PR is ready for merge by the caretaker label Nov 15, 2016
@mmalerba
Copy link
Contributor Author

@kara fixed, strange that neither build nor lint caught that

@kara kara added the action: merge The PR is ready for merge by the caretaker label Nov 16, 2016
@kara
Copy link
Contributor

kara commented Nov 16, 2016

@mmalerba Property '_focusTrap' is private and only accessible within class 'MdSidenav'.

I think it needs to be internal instead of private.

@kara kara removed the action: merge The PR is ready for merge by the caretaker label Nov 16, 2016
@mmalerba mmalerba added the action: merge The PR is ready for merge by the caretaker label Nov 16, 2016
@jelbourn
Copy link
Member

jelbourn commented Dec 2, 2016

@mmalerba can you rebase?

Captures focus when sidenav is open in "over" or "push" mode, but not
when opened in "side" mode.
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants