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

feat(modal): [ngbAutofocus] option #2737

Merged

Conversation

benouat
Copy link
Member

@benouat benouat commented Sep 21, 2018

As mentioned in #2728 this PR introduced 2 new features:

  • ability to use a new ngbAutofocus attribute in your modal templates

    <!-- will be focused after the modal opens -->
    <input type="text" ngbAutofocus />
  • introduction of a new focusFirst Modal Config option (will basically works as ngbAutofocus but finding automatically the first focusable element)

    /**
     * Assign focus to the first focusable element when opened (true by default)
     */
    focusFirst?: boolean;

Any newly opened modal will now do in that order:

  • If ngbAutofocus is detected, it will be used to focus the decorated element.
  • Else, it will look at focusFirst value (default to be true), find the first focusable element, and focus it.
  • At this point, if nothing has been focus (not focusable element found, or focusFirst set to false) it will simply focus itself (the modal window).

Another PR will be opened to demonstrate the usage of these new features in the Modal demos

Closes #2718

@benouat
Copy link
Member Author

benouat commented Sep 21, 2018

After discussing with @pkozlowski-opensource I will update PR to drop the config option focusFirst and will hardcode focusFirst: true behaviour in the code.

So new rules are like so:

  • If ngbAutofocus is detected, it will be used to focus the decorated element.
  • Otherwise first tabbable element will get focused.
  • If no element matches these 2 previous rules, fallback is to give focus to modal window.


/**
* Assign focus to the first focusable element when opened (true by default)
* @since 3.3.0

Choose a reason for hiding this comment

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

Please don't add the version since we don't know when it is going to be merged. I will add those when cutting a release.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I will remove it !

* Assign focus to the first focusable element when opened (true by default)
* @since 3.3.0
*/
focusFirst?: boolean;

Choose a reason for hiding this comment

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

My understanding was that we don't want to introduce this option for now. Did anything change?

Copy link
Member Author

Choose a reason for hiding this comment

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

Did you see any new code push to this PR branch between my comment where I said I will remove it and now ? 😜

It will be removed...

@@ -75,4 +81,5 @@ export interface NgbModalOptions {
export class NgbModalConfig implements NgbModalOptions {
backdrop: boolean | 'static' = true;
keyboard = true;
focusFirst = true;

Choose a reason for hiding this comment

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

My understanding was that we don't want to introduce this option for now. Did anything change?

@@ -21,7 +21,8 @@ import {NgbModalWindow} from './modal-window';

@Injectable({providedIn: 'root'})
export class NgbModalStack {
private _windowAttributes = ['ariaLabelledBy', 'backdrop', 'centered', 'keyboard', 'size', 'windowClass'];
private _windowAttributes =
['ariaLabelledBy', 'backdrop', 'centered', 'keyboard', 'size', 'windowClass', 'focusFirst'];

Choose a reason for hiding this comment

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

My understanding was that we don't want to introduce this option for now. Did anything change?

Copy link
Member

Choose a reason for hiding this comment

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

+1, you forgot to delete the focusFirst option

import {Key} from '../util/key';

const FOCUSABLE_ELEMENTS_SELECTOR = [
export const FOCUSABLE_ELEMENTS_SELECTOR = [

Choose a reason for hiding this comment

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

maybe it would be better to export a utility function instead?

@benouat benouat changed the title feat(modal): [ngbAutofocus] and 'focusFirst' modal config option feat(modal): [ngbAutofocus] option Sep 24, 2018
@benouat
Copy link
Member Author

benouat commented Sep 24, 2018

@pkozlowski-opensource I made changes according to your review and also added a new demo for both explanation and illustration of the feature.

@@ -62,7 +63,17 @@ export class NgbModalWindow implements OnInit,

ngAfterViewInit() {
if (!this._elRef.nativeElement.contains(document.activeElement)) {
this._elRef.nativeElement['focus'].apply(this._elRef.nativeElement, []);
let elementToFocus = this._elRef.nativeElement.querySelector(`[ngbAutofocus]`) as HTMLElement;
if (!elementToFocus) {

Choose a reason for hiding this comment

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

suggestion: Can you please merge this two level if statement, it looks maybe confusing (at least form me)

Copy link
Member

@maxokorokov maxokorokov left a comment

Choose a reason for hiding this comment

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

Hey,

ok for me, but:

  • stackblitz doesn't work
  • you forgot the focusFirst option on modal

@@ -21,7 +21,8 @@ import {NgbModalWindow} from './modal-window';

@Injectable({providedIn: 'root'})
export class NgbModalStack {
private _windowAttributes = ['ariaLabelledBy', 'backdrop', 'centered', 'keyboard', 'size', 'windowClass'];
private _windowAttributes =
['ariaLabelledBy', 'backdrop', 'centered', 'keyboard', 'size', 'windowClass', 'focusFirst'];
Copy link
Member

Choose a reason for hiding this comment

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

+1, you forgot to delete the focusFirst option

src/modal/modal-window.ts Show resolved Hide resolved
@benouat
Copy link
Member Author

benouat commented Oct 5, 2018

I am having a look at failing stackblitz

@@ -57,15 +58,16 @@ function generateIndexHtml() {
return `<!DOCTYPE html>
<html>

<head>
<head>xx

Choose a reason for hiding this comment

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

xx?

Copy link
Member Author

Choose a reason for hiding this comment

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

what the o_O ?? I have no clue why there are here, will remove them of course

<title>ng-bootstrap demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/${versions.bootstrap}/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism.css" />

Choose a reason for hiding this comment

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

Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because of a snippet of code I have in the demo

image

@@ -110,10 +112,10 @@ import { AppComponent } from './app.component';
import { ${demoImports} } from '${demoImport}';

@NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, HttpClientModule, NgbModule.forRoot()],
imports: [BrowserModule, FormsModule, ReactiveFormsModule, HttpClientModule, NgbModule.forRoot()],

Choose a reason for hiding this comment

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

There are ws changes in this file and others - why?

BTW, I know it is not related to your change, but why do we have NgbModule.forRoot() here?

Copy link
Member Author

Choose a reason for hiding this comment

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

We never updated it i think... I will remove them

@@ -76,8 +81,7 @@ export class NgbModalWindow implements OnInit,
} else {
elementToFocus = body;
}
elementToFocus['focus'].apply(elementToFocus, []);

elementToFocus.focus();

Choose a reason for hiding this comment

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

Do you know why we've called it through apply before and don't do this here any more?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have no clue at all why you wrote all of these with apply(). The only thing I know is that it seems to be not needed anymore. It works fine without it. So it makes the code easier to read

import {NgbModalBackdrop} from './modal-backdrop';
import {NgbModalWindow} from './modal-window';
import {NgbModal} from './modal';

Choose a reason for hiding this comment

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

Why there are changes in this file?

Copy link
Member Author

@benouat benouat Oct 5, 2018

Choose a reason for hiding this comment

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

I have no clue... maybe formatting side effect....
[edit]: seems like a reordering of the import

simply decorate any element with this attribute to notify modal window
to put focus on it when open.
@pkozlowski-opensource
Copy link
Member

LGTM

@pkozlowski-opensource pkozlowski-opensource merged commit 10fd5e4 into ng-bootstrap:master Oct 5, 2018
@maxokorokov maxokorokov mentioned this pull request Dec 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants