Skip to content

Commit

Permalink
fix(input): title attribute is automatically inherited (#22493)
Browse files Browse the repository at this point in the history
resolves #22055
  • Loading branch information
liamdebeasi committed Nov 13, 2020
1 parent d77a9d5 commit abad12f
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 12 deletions.
15 changes: 4 additions & 11 deletions core/src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Hos

import { getIonMode } from '../../global/ionic-global';
import { AutocompleteTypes, Color, InputChangeEventDetail, StyleEventDetail, TextFieldTypes } from '../../interface';
import { debounceEvent, findItemLabel } from '../../utils/helpers';
import { debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';

/**
Expand All @@ -21,7 +21,7 @@ export class Input implements ComponentInterface {
private nativeInput?: HTMLInputElement;
private inputId = `ion-input-${inputIds++}`;
private didBlurAfterEdit = false;
private tabindex?: string | number;
private inheritedAttributes: { [k: string]: any } = {};

/**
* This is required for a WebKit bug which requires us to
Expand Down Expand Up @@ -225,14 +225,7 @@ export class Input implements ComponentInterface {
@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');
}
this.inheritedAttributes = inheritAttributes(this.el, ['tabindex', 'title']);
}

connectedCallback() {
Expand Down Expand Up @@ -428,13 +421,13 @@ export class Input implements ComponentInterface {
spellcheck={this.spellcheck}
step={this.step}
size={this.size}
tabindex={this.tabindex}
type={this.type}
value={value}
onInput={this.onInput}
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyDown={this.onKeydown}
{...this.inheritedAttributes}
/>
{(this.clearInput && !this.readonly && !this.disabled) && <button
aria-label="reset"
Expand Down
8 changes: 7 additions & 1 deletion core/src/components/textarea/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Hos

import { getIonMode } from '../../global/ionic-global';
import { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface';
import { debounceEvent, findItemLabel, raf } from '../../utils/helpers';
import { debounceEvent, findItemLabel, inheritAttributes, raf } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';

/**
Expand All @@ -22,6 +22,7 @@ export class Textarea implements ComponentInterface {
private inputId = `ion-textarea-${textareaIds++}`;
private didBlurAfterEdit = false;
private textareaWrapper?: HTMLElement;
private inheritedAttributes: { [k: string]: any } = {};

/**
* This is required for a WebKit bug which requires us to
Expand Down Expand Up @@ -212,6 +213,10 @@ export class Textarea implements ComponentInterface {
}
}

componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['title']);
}

componentDidLoad() {
raf(() => this.runAutoGrow());
}
Expand Down Expand Up @@ -379,6 +384,7 @@ export class Textarea implements ComponentInterface {
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyDown={this.onKeyDown}
{...this.inheritedAttributes}
>
{value}
</textarea>
Expand Down
26 changes: 26 additions & 0 deletions core/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ import { Side } from '../interface';
declare const __zone_symbol__requestAnimationFrame: any;
declare const requestAnimationFrame: any;

/**
* Elements inside of web components sometimes need to inherit global attributes
* set on the host. For example, the inner input in `ion-input` should inherit
* the `title` attribute that developers set directly on `ion-input`. This
* helper function should be called in componentWillLoad and assigned to a variable
* that is later used in the render function.
*
* This does not need to be reactive as changing attributes on the host element
* does not trigger a re-render.
*/
export const inheritAttributes = (el: HTMLElement, attributes: string[] = []) => {
const attributeObject: { [k: string]: any } = {};

attributes.forEach(attr => {
if (el.hasAttribute(attr)) {
const value = el.getAttribute(attr);
if (value !== null) {
attributeObject[attr] = el.getAttribute(attr);
}
el.removeAttribute(attr);
}
});

return attributeObject;
}

export const addEventListener = (el: any, eventName: string, callback: any, opts?: any) => {
if (typeof (window as any) !== 'undefined') {
const win = window as any;
Expand Down
39 changes: 39 additions & 0 deletions core/src/utils/test/attributes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { inheritAttributes } from '../helpers';

describe('inheritAttributes()', () => {
it('should create an attribute inheritance object', () => {
const el = document.createElement('div');
el.setAttribute('tabindex', '20');
el.setAttribute('title', 'myTitle');

const attributeObject = inheritAttributes(el, ['tabindex', 'title']);

expect(attributeObject).toEqual({
tabindex: '20',
title: 'myTitle'
});
});

it('should not inherit attributes that are not defined on the element', () => {
const el = document.createElement('div');
el.setAttribute('tabindex', '20');

const attributeObject = inheritAttributes(el, ['tabindex', 'title']);

expect(attributeObject).toEqual({
tabindex: '20'
});
});

it('should not inherit attributes that are not defined on the input array', () => {
const el = document.createElement('div');
el.setAttribute('tabindex', '20');
el.setAttribute('title', 'myTitle');

const attributeObject = inheritAttributes(el, ['title']);

expect(attributeObject).toEqual({
title: 'myTitle'
});
});
});

0 comments on commit abad12f

Please sign in to comment.