Skip to content

Commit

Permalink
feat(tooltip): trigger with "focus" by default to enhance a11y (#3028)
Browse files Browse the repository at this point in the history
Fixes #3022
  • Loading branch information
hasegao authored and maxokorokov committed Feb 19, 2019
1 parent 4c625fc commit be84733
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 6 deletions.
5 changes: 4 additions & 1 deletion e2e-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ import {DropdownAutoCloseComponent} from './dropdown/autoclose/dropdown-autoclos
import {ModalFocusComponent} from './modal/focus/modal-focus.component';
import {PopoverAutocloseComponent} from './popover/autoclose/popover-autoclose.component';
import {TooltipAutocloseComponent} from './tooltip/autoclose/tooltip-autoclose.component';
import {TooltipFocusComponent} from './tooltip/focus/tooltip-focus.component';
import {TooltipPositionComponent} from './tooltip/position/tooltip-position.component';
import {TypeaheadAutoCloseComponent} from './typeahead/autoclose/typeahead-autoclose.component';
import {TypeaheadFocusComponent} from './typeahead/focus/typeahead-focus.component';
import {TypeaheadValidationComponent} from './typeahead/validation/typeahead-validation.component';


@NgModule({
declarations: [
AppComponent, NavigationComponent, DatepickerAutoCloseComponent, DatepickerFocusComponent,
DropdownAutoCloseComponent, ModalFocusComponent, PopoverAutocloseComponent, TooltipAutocloseComponent,
TooltipPositionComponent, TypeaheadFocusComponent, TypeaheadValidationComponent, TypeaheadAutoCloseComponent
TooltipFocusComponent, TooltipPositionComponent, TypeaheadFocusComponent, TypeaheadValidationComponent,
TypeaheadAutoCloseComponent
],
imports: [BrowserModule, FormsModule, ReactiveFormsModule, routing, NgbModule],
bootstrap: [AppComponent]
Expand Down
7 changes: 5 additions & 2 deletions e2e-app/src/app/app.routing.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import {ModuleWithProviders} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {DatepickerFocusComponent} from './datepicker/focus/datepicker-focus.component';

import {DatepickerAutoCloseComponent} from './datepicker/autoclose/datepicker-autoclose.component';
import {DatepickerFocusComponent} from './datepicker/focus/datepicker-focus.component';
import {DropdownAutoCloseComponent} from './dropdown/autoclose/dropdown-autoclose.component';
import {ModalFocusComponent} from './modal/focus/modal-focus.component';
import {PopoverAutocloseComponent} from './popover/autoclose/popover-autoclose.component';
import {TooltipAutocloseComponent} from './tooltip/autoclose/tooltip-autoclose.component';
import {TooltipFocusComponent} from './tooltip/focus/tooltip-focus.component';
import {TooltipPositionComponent} from './tooltip/position/tooltip-position.component';
import {TypeaheadAutoCloseComponent} from './typeahead/autoclose/typeahead-autoclose.component';
import {TypeaheadFocusComponent} from './typeahead/focus/typeahead-focus.component';
import {TypeaheadValidationComponent} from './typeahead/validation/typeahead-validation.component';


export const routes: Routes = [
{
path: 'datepicker',
Expand All @@ -24,7 +27,7 @@ export const routes: Routes = [
{path: 'popover', children: [{path: 'autoclose', component: PopoverAutocloseComponent}]}, {
path: 'tooltip',
children: [
{path: 'autoclose', component: TooltipAutocloseComponent},
{path: 'autoclose', component: TooltipAutocloseComponent}, {path: 'focus', component: TooltipFocusComponent},
{path: 'position', component: TooltipPositionComponent}
]
},
Expand Down
16 changes: 16 additions & 0 deletions e2e-app/src/app/tooltip/focus/tooltip-focus.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<h3>
Tooltip focus tests
<span class="ml-1 badge {{d1.isOpen() ? 'badge-success' : 'badge-danger'}}" id="open-status">{{d1.isOpen() ? 'open' : 'closed'}}</span>
</h3>
<form id="default">
<div class="d-flex">
<div class="flex-row">
<button id="btn-before" type="button" class="btn btn-outline-secondary ml-2">Button Before</button>
<button #d1="ngbTooltip" ngbTooltip="Tooltip Content" id="btn-tooltip" type="button"
container="body" triggers="focus" class="btn btn-outline-secondary ml-2">
Tooltip
</button>
<button id="btn-after" type="button" class="btn btn-outline-secondary ml-2">Button After</button>
</div>
</div>
</form>
5 changes: 5 additions & 0 deletions e2e-app/src/app/tooltip/focus/tooltip-focus.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Component} from '@angular/core';

@Component({templateUrl: './tooltip-focus.component.html'})
export class TooltipFocusComponent {
}
32 changes: 32 additions & 0 deletions e2e-app/src/app/tooltip/focus/tooltip-focus.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {openUrl} from '../../tools.po';
import {TooltipFocusPage as TooltipFocusPage} from './tooltip-focus.po';
import {protractor} from 'protractor';

describe('Tooltip Focus', () => {
let page: TooltipFocusPage;

const expectTooltipToBeOpen = async(message: string) => {
expect(await page.getTooltip().isPresent()).toBeTruthy(message);
expect(await page.getOpenStatus().getText()).toBe('open', message);
};

const expectTooltipToBeClosed = async(message: string) => {
expect(await page.getTooltip().isPresent()).toBeFalsy(message);
expect(await page.getOpenStatus().getText()).toBe('closed', message);
};

beforeAll(() => page = new TooltipFocusPage());

beforeEach(async() => await openUrl('tooltip/focus'));

it(`should work when triggers === 'focus'`, async() => {
// focusin to show
await page.getButton('before').click();
await page.getButton('before').sendKeys(protractor.Key.TAB);
await expectTooltipToBeOpen(`Tooltip should be visible when tooltip button gains focus`);

// focusout to hide
await page.getButton('tooltip').sendKeys(protractor.Key.TAB);
await expectTooltipToBeClosed(`Tooltip should not be visible when tooltip button looses focus`);
});
});
9 changes: 9 additions & 0 deletions e2e-app/src/app/tooltip/focus/tooltip-focus.po.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {$} from 'protractor';

export class TooltipFocusPage {
getTooltip() { return $('ngb-tooltip-window'); }

getButton(type: string) { return $(`#btn-${type}`); }

getOpenStatus() { return $('#open-status'); }
}
2 changes: 1 addition & 1 deletion src/tooltip/tooltip-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('ngb-tooltip-config', () => {

expect(config.autoClose).toBe(true);
expect(config.placement).toBe('auto');
expect(config.triggers).toBe('hover');
expect(config.triggers).toBe('hover focus');
expect(config.container).toBeUndefined();
expect(config.disableTooltip).toBe(false);
expect(config.tooltipClass).toBeUndefined();
Expand Down
2 changes: 1 addition & 1 deletion src/tooltip/tooltip-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {PlacementArray} from '../util/positioning';
export class NgbTooltipConfig {
autoClose: boolean | 'inside' | 'outside' = true;
placement: PlacementArray = 'auto';
triggers = 'hover';
triggers = 'hover focus';
container: string;
disableTooltip = false;
tooltipClass: string;
Expand Down
13 changes: 13 additions & 0 deletions src/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,19 @@ describe('ngb-tooltip', () => {

describe('triggers', () => {

it('should support focus triggers', () => {
const fixture = createTestComponent(`<div ngbTooltip="Great tip!"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbTooltip));

directive.triggerEventHandler('focusin', {});
fixture.detectChanges();
expect(getWindow(fixture.nativeElement)).not.toBeNull();

directive.triggerEventHandler('focusout', {});
fixture.detectChanges();
expect(getWindow(fixture.nativeElement)).toBeNull();
});

it('should support toggle triggers', () => {
const fixture = createTestComponent(`<div ngbTooltip="Great tip!" triggers="click"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbTooltip));
Expand Down
3 changes: 2 additions & 1 deletion src/util/triggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export class Trigger {
}

const DEFAULT_ALIASES = {
'hover': ['mouseenter', 'mouseleave']
'hover': ['mouseenter', 'mouseleave'],
'focus': ['focusin', 'focusout'],
};

export function parseTriggers(triggers: string, aliases = DEFAULT_ALIASES): Trigger[] {
Expand Down

0 comments on commit be84733

Please sign in to comment.