Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
621dc13
feat(input): add new js and css props
liamdebeasi Oct 31, 2022
d24428c
chore(): copy updates
liamdebeasi Oct 31, 2022
0402ae8
test(input): move old tests to legacy directory
liamdebeasi Oct 31, 2022
7643d2d
feat(input): add legacy vs modern template rendering
liamdebeasi Oct 31, 2022
259a3a2
refactor(form): add form controller
liamdebeasi Oct 31, 2022
a989e94
chore(): update comments
liamdebeasi Oct 31, 2022
2a80a5f
chore(): update tests
liamdebeasi Oct 31, 2022
08ea3a0
chore(): add moved screenshots
liamdebeasi Oct 31, 2022
bd80054
chore(): add moved screenshots
liamdebeasi Oct 31, 2022
19f4994
chore(): fix test file
liamdebeasi Oct 31, 2022
d02c144
chore(): prettier
liamdebeasi Oct 31, 2022
8d7f0c4
feat(input): add missing fixed label
liamdebeasi Oct 31, 2022
fff8ed1
Merge remote-tracking branch 'origin/FW-2591' into 2591-props
liamdebeasi Nov 1, 2022
563f177
Update core/src/components/input/input.tsx
liamdebeasi Nov 1, 2022
e1696af
chore(): run build
liamdebeasi Nov 1, 2022
a17a9ae
Merge remote-tracking branch 'origin/2591-props' into 2591-template
liamdebeasi Nov 1, 2022
f556aee
chore(input): remove justify property
liamdebeasi Nov 1, 2022
6b8131a
Update core/src/components/input/input.tsx
liamdebeasi Nov 2, 2022
e0b5cd6
feat(input): add missing fill property
liamdebeasi Nov 2, 2022
5a0f812
refactor(): explicitly add element type
liamdebeasi Nov 2, 2022
4272366
chore(): fix el type
liamdebeasi Nov 2, 2022
6d09ad6
Merge remote-tracking branch 'origin/2591-props' into 2591-template
liamdebeasi Nov 2, 2022
127d791
Merge remote-tracking branch 'origin/FW-2591' into 2591-template
liamdebeasi Nov 7, 2022
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
57 changes: 46 additions & 11 deletions core/src/components/input/input.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core';
import { createLegacyFormController } from '@utils/forms';
import type { LegacyFormController } from '@utils/forms';
import { printIonWarning } from '@utils/logging';

import { getIonMode } from '../../global/ionic-global';
import type {
Expand Down Expand Up @@ -30,6 +33,12 @@ export class Input implements ComponentInterface {
private inputId = `ion-input-${inputIds++}`;
private inheritedAttributes: Attributes = {};
private isComposing = false;

private legacyFormController!: LegacyFormController;

// This flag ensures we log the deprecation warning at most once.
private hasLoggedDeprecationWarning = false;

/**
* `true` if the input was cleared as a result of the user typing
* with `clearOnEdit` enabled.
Expand All @@ -44,7 +53,7 @@ export class Input implements ComponentInterface {

@State() hasFocus = false;

@Element() el!: HTMLElement;
@Element() el!: HTMLIonInputElement;

/**
* The color to use from your application's color palette.
Expand Down Expand Up @@ -328,6 +337,10 @@ export class Input implements ComponentInterface {
}

connectedCallback() {
const { el } = this;

this.legacyFormController = createLegacyFormController(el);

this.emitStyle();
this.debounceChanged();
if (Build.isBrowser) {
Expand Down Expand Up @@ -408,14 +421,16 @@ export class Input implements ComponentInterface {
}

private emitStyle() {
this.ionStyle.emit({
interactive: true,
input: true,
'has-placeholder': this.placeholder !== undefined,
'has-value': this.hasValue(),
'has-focus': this.hasFocus,
'interactive-disabled': this.disabled,
});
if (this.legacyFormController.hasLegacyControl()) {
this.ionStyle.emit({
interactive: true,
input: true,
'has-placeholder': this.placeholder !== undefined,
'has-value': this.hasValue(),
'has-focus': this.hasFocus,
'interactive-disabled': this.disabled,
});
}
}

private onInput = (ev: InputEvent | Event) => {
Expand Down Expand Up @@ -497,7 +512,21 @@ export class Input implements ComponentInterface {
return this.getValue().length > 0;
}

render() {
private renderInput() {
return <Host>Stubbed input</Host>;
}

private renderLegacyInput() {
if (!this.hasLoggedDeprecationWarning) {
printIonWarning(
`Using ion-input with an ion-label has been deprecated. To migrate, remove the ion-label and use the "label" property on ion-input instead.

For inputs that do not have a visible label, developers should use "aria-label" so screen readers can announce the purpose of the input.`,
this.el
);
this.hasLoggedDeprecationWarning = true;
}

const mode = getIonMode(this);
const value = this.getValue();
const labelId = this.inputId + '-lbl';
Expand All @@ -518,7 +547,7 @@ export class Input implements ComponentInterface {
<input
class="native-input"
ref={(input) => (this.nativeInput = input)}
aria-labelledby={label ? labelId : null}
aria-labelledby={label ? label.id : null}
disabled={this.disabled}
accept={this.accept}
autoCapitalize={this.autocapitalize}
Expand Down Expand Up @@ -568,6 +597,12 @@ export class Input implements ComponentInterface {
</Host>
);
}

render() {
const { legacyFormController } = this;

return legacyFormController.hasLegacyControl() ? this.renderLegacyInput() : this.renderInput();
}
}

let inputIds = 0;
21 changes: 13 additions & 8 deletions core/src/components/input/test/basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
<ion-content id="content">
<ion-list>
<ion-item>
<ion-input value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"></ion-input>
<ion-input
aria-label="my long text"
value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"
></ion-input>
</ion-item>

<ion-item>
<!--<ion-item>
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>

Expand All @@ -43,12 +46,14 @@
<ion-item lines="none" id="noneItem">
<ion-input id="noneInput" placeholder="None"></ion-input>
</ion-item>

-->
<ion-item>
<ion-label>Default Label</ion-label>
<ion-input value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"></ion-input>
<ion-input
label="Default value"
value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"
></ion-input>
</ion-item>

<!--
<ion-item>
<ion-label>Clear Input</ion-label>
<ion-input
Expand Down Expand Up @@ -100,7 +105,6 @@
<ion-item>
<ion-label position="fixed">Type #</ion-label>
<div type="number" value="333" class="input input-md hydrated">
<!---->
<input
aria-disabled="false"
autocapitalize="none"
Expand Down Expand Up @@ -148,7 +152,8 @@
<ion-item style="max-width: 250px">
<ion-label>Right</ion-label>
<ion-input class="ion-text-right" value="Narrow input"></ion-input>
</ion-item>
</ion-item> -->
</ion-list>
</ion-content>

<script>
Expand Down
169 changes: 169 additions & 0 deletions core/src/components/input/test/legacy/basic/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Input - Basic</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>
</head>

<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Input - Basic</ion-title>
</ion-toolbar>
</ion-header>

<ion-content id="content">
<ion-list>
<ion-item>
<ion-input value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"></ion-input>
</ion-item>

<ion-item>
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>

<ion-item lines="full" id="fullItem">
<ion-input id="fullInput" placeholder="Full"></ion-input>
</ion-item>

<ion-item lines="inset" id="insetItem">
<ion-input id="insetInput" placeholder="Inset"></ion-input>
</ion-item>

<ion-item lines="none" id="noneItem">
<ion-input id="noneInput" placeholder="None"></ion-input>
</ion-item>

<ion-item>
<ion-label>Default Label</ion-label>
<ion-input value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"></ion-input>
</ion-item>

<ion-item>
<ion-label>Clear Input</ion-label>
<ion-input
clear-input
value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"
></ion-input>
</ion-item>

<ion-item color="dark">
<ion-label position="floating">Floating</ion-label>
<ion-input checked></ion-input>
</ion-item>

<ion-item>
<ion-label position="fixed">Type #</ion-label>
<ion-input type="number" value="333"></ion-input>
</ion-item>

<ion-item>
<ion-label position="stacked">Password</ion-label>
<ion-input type="password"></ion-input>
</ion-item>

<ion-item>
<ion-label position="stacked">Placeholder</ion-label>
<ion-input placeholder="Enter Something"></ion-input>
</ion-item>

<ion-item>
<ion-label>Disabled</ion-label>
<ion-input id="dynamicDisabled" value="Disabled" disabled></ion-input>
</ion-item>

<ion-item>
<ion-label>Readonly</ion-label>
<ion-input id="dynamicReadonly" value="Readonly" readonly></ion-input>
</ion-item>

<ion-item>
<ion-label>Slot</ion-label>
<ion-input slot="start" value="Start"></ion-input>
</ion-item>

<ion-item>
<ion-label>Toggle</ion-label>
<ion-toggle checked slot="end"></ion-toggle>
</ion-item>

<ion-item>
<ion-label position="fixed">Type #</ion-label>
<div type="number" value="333" class="input input-md hydrated">
<!---->
<input
aria-disabled="false"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
autofocus="false"
class="native-input native-input-md"
spellcheck="false"
type="number"
/>
<button type="button" class="input-clear-icon" hidden=""></button>
</div>
</ion-item>
</ion-list>

<div class="ion-text-center">
<ion-button onclick="toggleBoolean('dynamicDisabled', 'disabled')"> Toggle Disabled </ion-button>

<ion-button color="secondary" onclick="toggleBoolean('dynamicReadonly', 'readonly')">
Toggle Readonly
</ion-button>
</div>

<ion-item>
<ion-label>Clear Input</ion-label>
<ion-input
clear-input
value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"
></ion-input>
</ion-item>

<ion-item>
<ion-label>Clear On Edit</ion-label>
<ion-input
clear-on-edit
value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"
></ion-input>
</ion-item>

<ion-item style="max-width: 250px">
<ion-input value="Narrow input"></ion-input>
<ion-label class="ion-text-right">Left</ion-label>
</ion-item>

<ion-item style="max-width: 250px">
<ion-label>Right</ion-label>
<ion-input class="ion-text-right" value="Narrow input"></ion-input>
</ion-item>
</ion-content>

<script>
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => {
console.log(ev);
});

function toggleBoolean(id, prop) {
var el = document.getElementById(id);

var isTrue = el[prop] ? false : true;
el[prop] = isTrue;
console.log('in toggleBoolean, setting', prop, 'to', isTrue);
}
</script>
</ion-app>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { test } from '@utils/test/playwright';

test.describe('input: masking', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/src/components/input/test/masking');
await page.goto('/src/components/input/test/legacy/masking');
});

test('should filter out spaces', async ({ page, skip }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { test } from '@utils/test/playwright';

test.describe('input: spec', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/src/components/input/test/spec');
await page.goto('/src/components/input/test/legacy/spec');
});

test('should not have visual regressions', async ({ page }) => {
Expand Down
31 changes: 31 additions & 0 deletions core/src/utils/forms/form-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Creates a controller that tracks whether a form control is using the legacy or modern syntax. This should be removed when the legacy form control syntax is removed.
*
* @internal
* @prop el: The Ionic form component to reference
*/
export const createLegacyFormController = (el: HTMLIonInputElement): LegacyFormController => {
const controlEl: HTMLIonInputElement = el;
let legacyControl = true;

/**
* Detect if developers are using the legacy form control syntax
* so a deprecation warning is logged. This warning can be disabled
* by either using the new `label` property or setting `aria-label`
* on the control.
*/
const hasLabelProp = controlEl.label !== undefined;
const hasAriaLabelAttribute = controlEl.hasAttribute('aria-label');

legacyControl = !hasLabelProp && !hasAriaLabelAttribute;

const hasLegacyControl = () => {
return legacyControl;
};

return { hasLegacyControl };
};

export type LegacyFormController = {
hasLegacyControl: () => boolean;
};
1 change: 1 addition & 0 deletions core/src/utils/forms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './form-controller';