fix(input): check for tabindex and pass it properly to native input (#21170)

* fix(input): check for tabindex and pass it properly to native input

references #17515

* style(input): fix lint error

* test(input): update test for more use cases (inside item)

* fix(item): adds delegatesFocus to shadow

* style(input): add comment block on what the code does
This commit is contained in:
Brandy Carney
2020-05-13 12:18:03 -04:00
committed by GitHub
parent 50678c03c9
commit dd4cb706ff
3 changed files with 99 additions and 8 deletions

View File

@ -21,6 +21,7 @@ export class Input implements ComponentInterface {
private nativeInput?: HTMLInputElement; private nativeInput?: HTMLInputElement;
private inputId = `ion-input-${inputIds++}`; private inputId = `ion-input-${inputIds++}`;
private didBlurAfterEdit = false; private didBlurAfterEdit = false;
private tabindex?: string | number;
@State() hasFocus = false; @State() hasFocus = false;
@ -88,13 +89,6 @@ export class Input implements ComponentInterface {
this.emitStyle(); this.emitStyle();
} }
/**
* A hint to the browser for which keyboard to display.
* Possible values: `"none"`, `"text"`, `"tel"`, `"url"`,
* `"email"`, `"numeric"`, `"decimal"`, and `"search"`.
*/
@Prop() inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
/** /**
* A hint to the browser for which enter key to display. * A hint to the browser for which enter key to display.
* Possible values: `"enter"`, `"done"`, `"go"`, `"next"`, * Possible values: `"enter"`, `"done"`, `"go"`, `"next"`,
@ -102,6 +96,13 @@ export class Input implements ComponentInterface {
*/ */
@Prop() enterkeyhint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; @Prop() enterkeyhint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send';
/**
* A hint to the browser for which keyboard to display.
* Possible values: `"none"`, `"text"`, `"tel"`, `"url"`,
* `"email"`, `"numeric"`, `"decimal"`, and `"search"`.
*/
@Prop() inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
/** /**
* The maximum value, which must not be less than its minimum (min attribute) value. * The maximum value, which must not be less than its minimum (min attribute) value.
*/ */
@ -213,6 +214,17 @@ export class Input implements ComponentInterface {
*/ */
@Event() ionStyle!: EventEmitter<StyleEventDetail>; @Event() ionStyle!: EventEmitter<StyleEventDetail>;
componentWillLoad() {
// If the ion-input has a tabindex attribute we get the value
// and pass it down to the native input, then remove it from the
// ion-input to avoid causing tabbing twice on the same element
if (this.el.hasAttribute('tabindex')) {
const tabindex = this.el.getAttribute('tabindex');
this.tabindex = tabindex !== null ? tabindex : undefined;
this.el.removeAttribute('tabindex');
}
}
connectedCallback() { connectedCallback() {
this.emitStyle(); this.emitStyle();
this.debounceChanged(); this.debounceChanged();
@ -384,6 +396,7 @@ export class Input implements ComponentInterface {
spellCheck={this.spellcheck} spellCheck={this.spellcheck}
step={this.step} step={this.step}
size={this.size} size={this.size}
tabindex={this.tabindex}
type={this.type} type={this.type}
value={value} value={value}
onInput={this.onInput} onInput={this.onInput}

View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Input - Tabindex</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Input - Tabindex</ion-title>
<ion-buttons slot="primary">
<ion-button>
<ion-icon slot="icon-only" name="menu"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-input tabindex="0" placeholder="Tab 9th"></ion-input>
<ion-input placeholder="Tab 10th"></ion-input>
<hr>
<ion-input placeholder="Tab 8th" tabindex="8"></ion-input>
<ion-input placeholder="Skip" tabindex="-1"></ion-input>
<ion-input placeholder="Tab 7th" tabindex="7"></ion-input>
<div tabindex="0">Tab 11th</div>
<ion-input placeholder="Tab 6th" tabindex="6"></ion-input>
<ion-input placeholder="Tab 5th" tabindex="5"></ion-input>
<ion-item tabindex="4">
<ion-label position="stacked">Tab 4th</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item tabindex="3">
<ion-label position="stacked">Tab 3rd</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item tabindex="-1">
<ion-label position="stacked">Skip</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item tabindex="2">
<ion-label position="stacked">Tab 2nd</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item tabindex="1">
<ion-label position="stacked">Tab 1st</ion-label>
<ion-input></ion-input>
</ion-item>
</ion-content>
<style>
ion-input,
div {
margin-bottom: 10px;
}
.md div {
padding-left: 8px;
}
</style>
</ion-app>
</body>
</html>

View File

@ -20,7 +20,9 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
ios: 'item.ios.scss', ios: 'item.ios.scss',
md: 'item.md.scss' md: 'item.md.scss'
}, },
shadow: true shadow: {
delegatesFocus: true
}
}) })
export class Item implements ComponentInterface, AnchorInterface, ButtonInterface { export class Item implements ComponentInterface, AnchorInterface, ButtonInterface {