Skip to content

Commit

Permalink
fix(esl-share): fix focus behavior to a loop inside esl-share-popup
Browse files Browse the repository at this point in the history
  • Loading branch information
dshovchko committed Dec 12, 2023
1 parent aa51e6a commit e3393fe
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 24 deletions.
4 changes: 3 additions & 1 deletion src/modules/esl-share/core/esl-share-popup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ExportNs} from '../../esl-utils/environment/export-ns';
import {ESLTooltip} from '../../esl-tooltip/core/esl-tooltip';
import {bind, listen, memoize} from '../../esl-utils/decorators';
import {bind, listen, memoize, prop} from '../../esl-utils/decorators';
import {ESLShareButton} from './esl-share-button';
import {ESLShareConfig} from './esl-share-config';

Expand Down Expand Up @@ -54,6 +54,8 @@ export class ESLSharePopup extends ESLTooltip {
return ESLSharePopup.create();
}

@prop(true) public override hasFocusLoop: boolean;

/** Hashstring with a list of buttons already rendered in the popup */
protected _list: string = '';

Expand Down
39 changes: 17 additions & 22 deletions src/modules/esl-tooltip/core/esl-tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {ExportNs} from '../../esl-utils/environment/export-ns';
import {ESLPopup} from '../../esl-popup/core';
import {memoize, attr, boolAttr, listen} from '../../esl-utils/decorators';
import {memoize, attr, boolAttr, listen, prop} from '../../esl-utils/decorators';
import {TAB} from '../../esl-utils/dom/keys';
import {getKeyboardFocusableElements} from '../../esl-utils/dom/focus';
import {getKeyboardFocusableElements, handleFocusChain} from '../../esl-utils/dom/focus';
import {CSSClassUtils} from '../../esl-utils/dom/class';

import type {PopupActionParams} from '../../esl-popup/core';
Expand All @@ -25,6 +25,8 @@ export interface TooltipActionParams extends PopupActionParams {
export class ESLTooltip extends ESLPopup {
static override is = 'esl-tooltip';

@prop(false) public hasFocusLoop: boolean;

/**
* Tooltip position relative to the trigger.
* Currently supported: 'top', 'bottom', 'left', 'right' position types ('top' by default)
Expand All @@ -43,21 +45,17 @@ export class ESLTooltip extends ESLPopup {
return document.createElement('esl-tooltip');
}

/** List of all focusable elements inside Tooltip */
public get focusableElements(): Element[] {
return getKeyboardFocusableElements(this);
}

/** First focusable element inside Tooltip */
public get firstFocusableElement(): Element | null {
const els = this.focusableElements;
return els.length ? els[0] : null;
/** List of all focusable elements inside instance */
public get $focusables(): HTMLElement[] {
return getKeyboardFocusableElements(this) as HTMLElement[];
}

/** Last focusable element inside Tooltip */
public get lastFocusableElement(): Element | null {
const els = this.focusableElements;
return els.length ? els[els.length - 1] : null;
/** First and last focusable elements inside instance */
public get $boundaryFocusable(): {$first: HTMLElement | undefined, $last: HTMLElement | undefined} {
const {$focusables} = this;
const $first = $focusables[0];
const $last = $focusables.pop();
return {$first, $last};
}

/** Active state marker */
Expand Down Expand Up @@ -137,16 +135,13 @@ export class ESLTooltip extends ESLPopup {
if (e.key === TAB) this._onTabKey(e);
}

/** Actions on TAB keypressed */
protected _onTabKey(e: KeyboardEvent): void {
if (!this.activator) return;
const {firstFocusableElement, lastFocusableElement} = this;
if (
!lastFocusableElement ||
e.target === lastFocusableElement && !e.shiftKey ||
e.target === firstFocusableElement && e.shiftKey
) {
const {$first, $last} = this.$boundaryFocusable;
if (this.hasFocusLoop) return handleFocusChain(e, $first, $last) as void;
if (!$last || e.target === (e.shiftKey ? $first : $last)) {
this.activator.focus();
e.stopPropagation();
e.preventDefault();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/esl-utils/dom/focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {TAB} from './keys';
* Chain focus order between passed elements.
* Passive (should be called inside keyboard event handler)
*/
export const handleFocusChain = (e: KeyboardEvent, first: HTMLElement, last: HTMLElement): boolean | undefined => {
export const handleFocusChain = (e: KeyboardEvent, first: HTMLElement | undefined, last: HTMLElement | undefined): boolean | undefined => {
if (e.key !== TAB) return;
if (last && e.target === first && e.shiftKey) {
last.focus();
Expand Down

0 comments on commit e3393fe

Please sign in to comment.