feat(input): add the large size for the ionic theme (#29249)
Issue number: internal --------- ## What is the current behavior? Input does not have a size property. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - Adds the `size` property with support for the `"large"` size on the `"ionic"` theme only - Adds tests for the size property - Note: the screenshots will not look right until the fill styles are added ## Does this introduce a breaking change? - [ ] Yes - [x] No
@@ -623,6 +623,7 @@ ion-input,prop,placeholder,string | undefined,undefined,false,false
|
||||
ion-input,prop,readonly,boolean,false,false,true
|
||||
ion-input,prop,required,boolean,false,false,false
|
||||
ion-input,prop,shape,"round" | undefined,undefined,false,false
|
||||
ion-input,prop,size,"large" | undefined,undefined,false,false
|
||||
ion-input,prop,spellcheck,boolean,false,false,false
|
||||
ion-input,prop,step,string | undefined,undefined,false,false
|
||||
ion-input,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
|
||||
8
core/src/components.d.ts
vendored
@@ -1450,6 +1450,10 @@ export namespace Components {
|
||||
* The shape of the input. If "round" it will have an increased border radius.
|
||||
*/
|
||||
"shape"?: 'round';
|
||||
/**
|
||||
* The size of the input. If "large", it will have an increased height. By default the size is unset. This property only applies to the `"ionic"` theme.
|
||||
*/
|
||||
"size"?: 'large';
|
||||
/**
|
||||
* If `true`, the element will have its spelling and grammar checked.
|
||||
*/
|
||||
@@ -6694,6 +6698,10 @@ declare namespace LocalJSX {
|
||||
* The shape of the input. If "round" it will have an increased border radius.
|
||||
*/
|
||||
"shape"?: 'round';
|
||||
/**
|
||||
* The size of the input. If "large", it will have an increased height. By default the size is unset. This property only applies to the `"ionic"` theme.
|
||||
*/
|
||||
"size"?: 'large';
|
||||
/**
|
||||
* If `true`, the element will have its spelling and grammar checked.
|
||||
*/
|
||||
|
||||
@@ -3,3 +3,12 @@
|
||||
|
||||
// Ionic Input
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
|
||||
// Ionic Input Sizes
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.input-size-large) {
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { NotchController } from '@utils/forms';
|
||||
import { createNotchController } from '@utils/forms';
|
||||
import type { Attributes } from '@utils/helpers';
|
||||
import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from '@utils/helpers';
|
||||
import { printIonWarning } from '@utils/logging';
|
||||
import { createSlotMutationController } from '@utils/slot-mutation-controller';
|
||||
import type { SlotMutationController } from '@utils/slot-mutation-controller';
|
||||
import { createColorClasses, hostContext } from '@utils/theme';
|
||||
@@ -251,6 +252,12 @@ export class Input implements ComponentInterface {
|
||||
*/
|
||||
@Prop() step?: string;
|
||||
|
||||
/**
|
||||
* The size of the input. If "large", it will have an increased height. By default the
|
||||
* size is unset. This property only applies to the `"ionic"` theme.
|
||||
*/
|
||||
@Prop() size?: 'large';
|
||||
|
||||
/**
|
||||
* The type of control to display. The default type is text.
|
||||
*/
|
||||
@@ -464,6 +471,16 @@ export class Input implements ComponentInterface {
|
||||
return typeof this.value === 'number' ? this.value.toString() : (this.value || '').toString();
|
||||
}
|
||||
|
||||
private getSize() {
|
||||
const theme = getIonTheme(this);
|
||||
const { size } = this;
|
||||
if (theme !== 'ionic' && size === 'large') {
|
||||
printIonWarning(`The "${size}" size is not supported in the ${theme} theme.`);
|
||||
return undefined;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private onInput = (ev: InputEvent | Event) => {
|
||||
const input = ev.target as HTMLInputElement | null;
|
||||
if (input) {
|
||||
@@ -686,6 +703,7 @@ export class Input implements ComponentInterface {
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this;
|
||||
const theme = getIonTheme(this);
|
||||
const value = this.getValue();
|
||||
const size = this.getSize();
|
||||
const inItem = hostContext('ion-item', this.el);
|
||||
const shouldRenderHighlight = theme === 'md' && fill !== 'outline' && !inItem;
|
||||
|
||||
@@ -721,6 +739,7 @@ export class Input implements ComponentInterface {
|
||||
'label-floating': labelShouldFloat,
|
||||
[`input-fill-${fill}`]: fill !== undefined,
|
||||
[`input-shape-${shape}`]: shape !== undefined,
|
||||
[`input-size-${size}`]: size !== undefined,
|
||||
[`input-label-placement-${labelPlacement}`]: true,
|
||||
'in-item': inItem,
|
||||
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
||||
|
||||
74
core/src/components/input/test/size/index.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Input - Size</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>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Input - Size</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content" class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>No Fill: No Size</h2>
|
||||
<ion-input label="Email"></ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline: No Size</h2>
|
||||
<ion-input fill="outline" label="Email"></ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>No Fill: Large Size</h2>
|
||||
<ion-input size="large" label="Email"></ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline: Large Size</h2>
|
||||
<ion-input size="large" fill="outline" label="Email"></ion-input>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
78
core/src/components/input/test/size/input.e2e.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* Size is only available in the Ionic theme
|
||||
*/
|
||||
configs({ modes: ['ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('input: size'), () => {
|
||||
test.describe('input: size large', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
size="large"
|
||||
label="Email"
|
||||
value="hi@ionic.io"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-size-large`));
|
||||
});
|
||||
test('should render correctly with floating label', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
size="large"
|
||||
label="Email"
|
||||
label-placement="floating"
|
||||
value="hi@ionic.io"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-size-large-label-floating`));
|
||||
});
|
||||
test('should not have visual regressions with fill outline', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
fill="outline"
|
||||
size="large"
|
||||
label="Email"
|
||||
label-placement="floating"
|
||||
value="hi@ionic.io"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-size-large-outline`));
|
||||
});
|
||||
test('should not have visual regressions with fill outline and round shape', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
fill="outline"
|
||||
shape="round"
|
||||
size="large"
|
||||
label="Email"
|
||||
label-placement="floating"
|
||||
value="hi@ionic.io"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-size-large-outline-round`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -957,7 +957,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', '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', 'theme', 'type', 'value'],
|
||||
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', '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', 'size', 'spellcheck', 'step', 'theme', 'type', 'value'],
|
||||
methods: ['setFocus', 'getInputElement']
|
||||
})
|
||||
@Component({
|
||||
@@ -965,7 +965,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', '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', 'theme', 'type', 'value'],
|
||||
inputs: ['autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', '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', 'size', 'spellcheck', 'step', 'theme', 'type', 'value'],
|
||||
})
|
||||
export class IonInput {
|
||||
protected el: HTMLElement;
|
||||
|
||||
@@ -427,6 +427,7 @@ export const IonInput = /*@__PURE__*/ defineContainer<JSX.IonInput, JSX.IonInput
|
||||
'shape',
|
||||
'spellcheck',
|
||||
'step',
|
||||
'size',
|
||||
'type',
|
||||
'value',
|
||||
'ionInput',
|
||||
|
||||