feat(input): add styles for clear action to ionic theme (#29337)
Issue number: Internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> For the `ionic` theme: Adjusted the size of the clear button to match design specs, and updated the behavior so the button additionally only shows if the native input *or* any other actions within the `ion-input` are focused. (See code comments for details.) The actual icon used has not been modified. The ticket says to "use the material design clear icon as the reference icon for this feature," and we've already added a feature allowing the icon to be customized at the app level. Let me know if the intention was to use a different default icon with the `ionic` theme. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> --------- Co-authored-by: ionitron <hi@ionicframework.com>
@ -21,6 +21,44 @@
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
// Input Clear Button
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
.input-clear-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
color: #{$ionic-color-neutral-500};
|
||||
}
|
||||
|
||||
.input-clear-icon.ion-focused {
|
||||
border-radius: #{$ionic-border-radius-rounded-small};
|
||||
|
||||
outline: #{$ionic-border-size-medium} solid #{$ionic-color-primary-100};
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.input-clear-icon ion-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* The clear button should be visible if the native input
|
||||
* OR any other interactive elements in the component (the
|
||||
* clear button, slotted buttons, etc.) are focused. If we
|
||||
* only looked at the native input, tabbing to the clear
|
||||
* button would immediately hide it.
|
||||
*
|
||||
* Note that the clear button also requires the native input
|
||||
* to have any value, but this is not specific to the ionic
|
||||
* theme, so it is handled elsewhere.
|
||||
*/
|
||||
:host(:not(:focus-within)) .input-clear-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Ionic Input Shapes
|
||||
// --------------------------------------------------
|
||||
|
||||
@ -69,7 +107,7 @@
|
||||
// color for the text within the input
|
||||
--color: #{tokens.$ionic-color-neutral-400};
|
||||
--background: #{tokens.$ionic-color-neutral-10};
|
||||
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@ -729,7 +729,7 @@ export class Input implements ComponentInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { disabled, fill, readonly, shape, inputId, el, hasFocus, clearInputIcon } = this;
|
||||
const { disabled, fill, readonly, shape, inputId, el, hasFocus, clearInput, clearInputIcon } = this;
|
||||
const theme = getIonTheme(this);
|
||||
const value = this.getValue();
|
||||
const size = this.getSize();
|
||||
@ -835,11 +835,11 @@ export class Input implements ComponentInterface {
|
||||
onCompositionend={this.onCompositionEnd}
|
||||
{...this.inheritedAttributes}
|
||||
/>
|
||||
{this.clearInput && !readonly && !disabled && (
|
||||
{clearInput && !readonly && !disabled && (
|
||||
<button
|
||||
aria-label="reset"
|
||||
type="button"
|
||||
class="input-clear-icon"
|
||||
class="input-clear-icon ion-focusable"
|
||||
onPointerDown={(ev) => {
|
||||
/**
|
||||
* This prevents mobile browsers from
|
||||
|
||||
@ -131,3 +131,92 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['ionic-md'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('input: clear button in ionic theme, visual checks'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
label="Label"
|
||||
label-placement="stacked"
|
||||
clear-input="true"
|
||||
value="Text"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await input.evaluate((el: HTMLIonInputElement) => el.setFocus());
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-with-clear-button`));
|
||||
});
|
||||
|
||||
test('should not have visual regressions when clear button is focused', async ({ page }) => {
|
||||
// extra padding around input ensures focus ring doesn't get cut off at screenshot edges
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
#container {
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="container">
|
||||
<ion-input
|
||||
label="Label"
|
||||
label-placement="stacked"
|
||||
clear-input="true"
|
||||
value="Text"
|
||||
></ion-input>
|
||||
</div>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await input.evaluate((el: HTMLIonInputElement) => el.setFocus());
|
||||
await page.waitForChanges();
|
||||
|
||||
const clearButton = input.locator('.input-clear-icon');
|
||||
clearButton.evaluate((el: HTMLElement) => el.classList.add('ion-focused'));
|
||||
await page.waitForChanges();
|
||||
|
||||
const container = page.locator('#container');
|
||||
await expect(container).toHaveScreenshot(screenshot(`input-clear-button-focused`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['ionic-md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('input: clear button in ionic theme, functionality checks'), () => {
|
||||
test('should show clear button when any part of input is focused', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
label="Label"
|
||||
label-placement="stacked"
|
||||
clear-input="true"
|
||||
value="Text"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
const clearButton = input.locator('.input-clear-icon');
|
||||
|
||||
await expect(clearButton).not.toBeVisible();
|
||||
|
||||
await input.evaluate((el: HTMLIonInputElement) => el.setFocus());
|
||||
await expect(clearButton).toBeVisible();
|
||||
|
||||
// ensure blurring native input doesn't immediately hide clear button
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(clearButton).toBeFocused();
|
||||
await expect(clearButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.5 KiB |