Skip to content

Commit

Permalink
fix(picker): fix focus handling of picker in edge
Browse files Browse the repository at this point in the history
  • Loading branch information
jgroth authored and adrianschmidt committed Sep 10, 2019
1 parent 37b5d66 commit 24bbe71
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 0 deletions.
18 changes: 18 additions & 0 deletions src/components/chip-set/chip-set.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ export class ChipSet {
@Event()
private change: EventEmitter;

/**
* Emitted when an input chip set has received focus and editing in the text field has started
*/
@Event()
private startEdit: EventEmitter;

/**
* Emitted when an input chip set has lost focus and editing in the text field has ended
*/
@Event()
private stopEdit: EventEmitter;

/**
* Dispatched when the input is changed for type `input`
*/
Expand Down Expand Up @@ -271,6 +283,7 @@ export class ChipSet {
*/
private handleTextFieldFocus() {
this.editMode = true;
this.startEdit.emit();
}

/**
Expand All @@ -281,6 +294,11 @@ export class ChipSet {
private handleInputBlur() {
this.editMode = false;
this.textValue = ' ';

// This timeout is needed in order to let a new element receive focus
setTimeout(() => {
this.stopEdit.emit();
}, 0);
}

private handleTextInput(event) {
Expand Down
21 changes: 21 additions & 0 deletions src/components/picker/picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@stencil/core';
import AwesomeDebouncePromise from 'awesome-debounce-promise';
import { ListItem, Searcher } from '../../interface';
import { isDescendant } from '../../util/dom';
import {
ARROW_DOWN,
ARROW_DOWN_KEY_CODE,
Expand Down Expand Up @@ -109,6 +110,7 @@ export class Picker {
this.handleChange = this.handleChange.bind(this);
this.handleInteract = this.handleInteract.bind(this);
this.handleListChange = this.handleListChange.bind(this);
this.handleStopEdit = this.handleStopEdit.bind(this);
}

@Watch('value')
Expand Down Expand Up @@ -171,6 +173,8 @@ export class Picker {
onFocus={this.handleInputFieldFocus}
onChange={this.handleChange}
onInteract={this.handleInteract}
onStartEdit={this.handleInputFieldFocus}
onStopEdit={this.handleStopEdit}
/>,
<div class="mdc-menu-surface--anchor">{this.renderDropdown()}</div>,
];
Expand Down Expand Up @@ -260,6 +264,23 @@ export class Picker {
);
}

/**
* Check if a descendant still has focus, if not reset text value and search result
*
* @returns {void}
*/
private handleStopEdit() {
// In browsers where shadow DOM is not supported activeElement on shadowRoot will return null
// However, document.activeElement will return the actual focused element instead of the outermost shadow host
const element =
this.element.shadowRoot.activeElement || document.activeElement;
if (isDescendant(element as HTMLElement, this.element)) {
return;
}

this.handleElementBlur();
}

/**
* Reset the value of the input field when the control loses focus
*
Expand Down
30 changes: 30 additions & 0 deletions src/util/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Check if an element is a descendant of another, even if it is located within a shadow root
*
* @param {Node} element the element to check
* @param {Node} parent the parent element
*
* @returns {boolean} true if the element is a descendant of the parent element, false otherwise
*/
export function isDescendant(element: Node, parent: Node) {
if (parent.contains(element)) {
return true;
}

let currentNode: Node = element;
let i = 0; // Just in case something weird happens, let's not crash the browser…
const DEPTH = 1000; // Max depth to search.

while (
i < DEPTH &&
currentNode &&
currentNode.getRootNode().nodeName === '#document-fragment'
) {
currentNode = (currentNode.getRootNode() as any).host;
if (parent.contains(currentNode)) {
return true;
}
i += 1;
}
return parent.contains(currentNode);
}

0 comments on commit 24bbe71

Please sign in to comment.