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
This commit is contained in:
Brandy Carney
2024-04-03 11:19:09 -04:00
committed by GitHub
parent 4fc3bbbe5f
commit e9654436a6
20 changed files with 192 additions and 2 deletions

View File

@ -623,6 +623,7 @@ ion-input,prop,placeholder,string | undefined,undefined,false,false
ion-input,prop,readonly,boolean,false,false,true ion-input,prop,readonly,boolean,false,false,true
ion-input,prop,required,boolean,false,false,false ion-input,prop,required,boolean,false,false,false
ion-input,prop,shape,"round" | undefined,undefined,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,spellcheck,boolean,false,false,false
ion-input,prop,step,string | undefined,undefined,false,false ion-input,prop,step,string | undefined,undefined,false,false
ion-input,prop,theme,"ios" | "md" | "ionic",undefined,false,false ion-input,prop,theme,"ios" | "md" | "ionic",undefined,false,false

View File

@ -1450,6 +1450,10 @@ export namespace Components {
* The shape of the input. If "round" it will have an increased border radius. * The shape of the input. If "round" it will have an increased border radius.
*/ */
"shape"?: 'round'; "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. * 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. * The shape of the input. If "round" it will have an increased border radius.
*/ */
"shape"?: 'round'; "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. * If `true`, the element will have its spelling and grammar checked.
*/ */

View File

@ -3,3 +3,12 @@
// Ionic Input // Ionic Input
// -------------------------------------------------- // --------------------------------------------------
// Ionic Input Sizes
// --------------------------------------------------
:host(.input-size-large) {
min-height: 48px;
}

View File

@ -4,6 +4,7 @@ import type { NotchController } from '@utils/forms';
import { createNotchController } from '@utils/forms'; import { createNotchController } from '@utils/forms';
import type { Attributes } from '@utils/helpers'; import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from '@utils/helpers'; import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from '@utils/helpers';
import { printIonWarning } from '@utils/logging';
import { createSlotMutationController } from '@utils/slot-mutation-controller'; import { createSlotMutationController } from '@utils/slot-mutation-controller';
import type { SlotMutationController } from '@utils/slot-mutation-controller'; import type { SlotMutationController } from '@utils/slot-mutation-controller';
import { createColorClasses, hostContext } from '@utils/theme'; import { createColorClasses, hostContext } from '@utils/theme';
@ -251,6 +252,12 @@ export class Input implements ComponentInterface {
*/ */
@Prop() step?: string; @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. * 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(); 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) => { private onInput = (ev: InputEvent | Event) => {
const input = ev.target as HTMLInputElement | null; const input = ev.target as HTMLInputElement | null;
if (input) { if (input) {
@ -686,6 +703,7 @@ export class Input implements ComponentInterface {
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this; const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this;
const theme = getIonTheme(this); const theme = getIonTheme(this);
const value = this.getValue(); const value = this.getValue();
const size = this.getSize();
const inItem = hostContext('ion-item', this.el); const inItem = hostContext('ion-item', this.el);
const shouldRenderHighlight = theme === 'md' && fill !== 'outline' && !inItem; const shouldRenderHighlight = theme === 'md' && fill !== 'outline' && !inItem;
@ -721,6 +739,7 @@ export class Input implements ComponentInterface {
'label-floating': labelShouldFloat, 'label-floating': labelShouldFloat,
[`input-fill-${fill}`]: fill !== undefined, [`input-fill-${fill}`]: fill !== undefined,
[`input-shape-${shape}`]: shape !== undefined, [`input-shape-${shape}`]: shape !== undefined,
[`input-size-${size}`]: size !== undefined,
[`input-label-placement-${labelPlacement}`]: true, [`input-label-placement-${labelPlacement}`]: true,
'in-item': inItem, 'in-item': inItem,
'in-item-color': hostContext('ion-item.ion-color', this.el), 'in-item-color': hostContext('ion-item.ion-color', this.el),

View 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>

View 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`));
});
});
});
});

View File

@ -957,7 +957,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite
@ProxyCmp({ @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'] methods: ['setFocus', 'getInputElement']
}) })
@Component({ @Component({
@ -965,7 +965,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>', template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property // 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 { export class IonInput {
protected el: HTMLElement; protected el: HTMLElement;

View File

@ -427,6 +427,7 @@ export const IonInput = /*@__PURE__*/ defineContainer<JSX.IonInput, JSX.IonInput
'shape', 'shape',
'spellcheck', 'spellcheck',
'step', 'step',
'size',
'type', 'type',
'value', 'value',
'ionInput', 'ionInput',