Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@ ion-input,prop,autocapitalize,string,'off',false,false
ion-input,prop,autocomplete,"additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday" | "bday-day" | "bday-month" | "bday-year" | "cc-additional-name" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "email" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "impp" | "language" | "name" | "new-password" | "nickname" | "off" | "on" | "one-time-code" | "organization" | "organization-title" | "photo" | "postal-code" | "sex" | "street-address" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "transaction-amount" | "transaction-currency" | "url" | "username",'off',false,false
ion-input,prop,autocorrect,boolean,false,false,false
ion-input,prop,autofocus,boolean,false,false,false
ion-input,prop,clearButtonText,string,'reset',false,false
ion-input,prop,clearInput,boolean,false,false,false
ion-input,prop,clearInputIcon,string | undefined,undefined,false,false
ion-input,prop,clearOnEdit,boolean | undefined,undefined,false,false
Expand Down
11 changes: 11 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,11 @@ export namespace Components {
* @default false
*/
"autofocus": boolean;
/**
* The accessible label for the clear button. Only applies when `clearInput` is set to `true`.
* @default 'reset'
*/
"clearButtonText": string;
/**
* If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input.
* @default false
Expand Down Expand Up @@ -6462,6 +6467,11 @@ declare namespace LocalJSX {
* @default false
*/
"autofocus"?: boolean;
/**
* The accessible label for the clear button. Only applies when `clearInput` is set to `true`.
* @default 'reset'
*/
"clearButtonText"?: string;
/**
* If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input.
* @default false
Expand Down Expand Up @@ -9205,6 +9215,7 @@ declare namespace LocalJSX {
"autofocus": boolean;
"clearInput": boolean;
"clearInputIcon": string;
"clearButtonText": string;
"clearOnEdit": boolean;
"counter": boolean;
"debounce": number;
Expand Down
11 changes: 10 additions & 1 deletion core/src/components/input/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,16 @@
* being activated, so we never get to that state.
*/
.input-clear-icon:focus {
opacity: 0.5;
opacity: 1;
}

.input-clear-icon:focus-visible {
@include border-radius(4px);

outline: 2px solid #000;
outline-offset: 1px;

box-shadow: 0 0 0 1px #fff;
}

:host(.has-value) .input-clear-icon {
Expand Down
9 changes: 7 additions & 2 deletions core/src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ export class Input implements ComponentInterface {
*/
@Prop() clearInputIcon?: string;

/**
* The accessible label for the clear button. Only applies when `clearInput` is set to `true`.
*/
@Prop() clearButtonText = 'reset';

/**
* If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
*/
Expand Down Expand Up @@ -856,7 +861,7 @@ export class Input implements ComponentInterface {
}

render() {
const { disabled, fill, readonly, shape, inputId, labelPlacement, hasFocus, clearInputIcon } = this;
const { disabled, fill, readonly, shape, inputId, labelPlacement, hasFocus, clearInputIcon, clearButtonText } = this;
const mode = getIonMode(this);
const value = this.getValue();
const inItem = hostContext('ion-item', this.el);
Expand Down Expand Up @@ -945,7 +950,7 @@ export class Input implements ComponentInterface {
<div class="input-end">
{this.clearInput && !readonly && !disabled && (
<button
aria-label="reset"
aria-label={clearButtonText}
type="button"
class="input-clear-icon"
onPointerDown={(ev) => {
Expand Down
47 changes: 47 additions & 0 deletions core/src/components/input/test/basic/input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,53 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c
const item = page.locator('ion-item');
await expect(item).toHaveScreenshot(screenshot(`input-with-clear-button-item-color`));
});

test('should retain opacity: 1 and render a visible focus indicator when the clear button receives keyboard focus', async ({ page, browserName }) => {
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';

await page.setContent(
`
<ion-input label="my label" value="abc" clear-input="true"></ion-input>
`,
config
);

const input = page.locator('ion-input');
const nativeInput = input.locator('input');
const clearButton = input.locator('.input-clear-icon');

await nativeInput.focus();
await page.keyboard.press(tabKey);

await expect(clearButton).toBeFocused();
await expect(clearButton).toHaveCSS('opacity', '1');
await expect(clearButton).toHaveCSS('outline-style', 'solid');
await expect(clearButton).toHaveCSS('outline-width', '2px');
});

test('should have default aria-label "reset" on clear button', async ({ page }) => {
await page.setContent(
`
<ion-input label="my label" value="abc" clear-input="true"></ion-input>
`,
config
);

const clearButton = page.locator('ion-input .input-clear-icon');
await expect(clearButton).toHaveAttribute('aria-label', 'reset');
});

test('should reflect custom clearButtonText on clear button aria-label', async ({ page }) => {
await page.setContent(
`
<ion-input label="my label" value="abc" clear-input="true" clear-button-text="Clear text"></ion-input>
`,
config
);

const clearButton = page.locator('ion-input .input-clear-icon');
await expect(clearButton).toHaveAttribute('aria-label', 'Clear text');
});
});

test.describe(title('input: click'), () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/src/lazy/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1047,15 +1047,15 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite


@ProxyCmp({
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearInputIcon', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'spellcheck', 'step', 'type', 'value'],
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearButtonText', 'clearInput', 'clearInputIcon', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'spellcheck', 'step', 'type', 'value'],
methods: ['setFocus', 'getInputElement']
})
@Component({
selector: 'ion-input',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearInputIcon', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'spellcheck', 'step', 'type', 'value'],
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearButtonText', 'clearInput', 'clearInputIcon', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'spellcheck', 'step', 'type', 'value'],
outputs: ['ionInput', 'ionChange', 'ionBlur', 'ionFocus'],
standalone: false
})
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ export const IonInput: StencilVueComponent<JSX.IonInput, JSX.IonInput["value"]>
'autofocus',
'clearInput',
'clearInputIcon',
'clearButtonText',
'clearOnEdit',
'counter',
'counterFormatter',
Expand Down
Loading