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
4 changes: 2 additions & 2 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1873,7 +1873,7 @@ ion-textarea,prop,cols,number | undefined,undefined,false,true
ion-textarea,prop,counter,boolean,false,false,false
ion-textarea,prop,counterFormatter,((inputLength: number, maxLength: number) => string) | undefined,undefined,false,false
ion-textarea,prop,debounce,number | undefined,undefined,false,false
ion-textarea,prop,disabled,boolean,false,false,false
ion-textarea,prop,disabled,boolean,false,false,true
ion-textarea,prop,enterkeyhint,"done" | "enter" | "go" | "next" | "previous" | "search" | "send" | undefined,undefined,false,false
ion-textarea,prop,errorText,string | undefined,undefined,false,false
ion-textarea,prop,fill,"outline" | "solid" | undefined,undefined,false,false
Expand All @@ -1886,7 +1886,7 @@ ion-textarea,prop,minlength,number | undefined,undefined,false,false
ion-textarea,prop,mode,"ios" | "md",undefined,false,false
ion-textarea,prop,name,string,this.inputId,false,false
ion-textarea,prop,placeholder,string | undefined,undefined,false,false
ion-textarea,prop,readonly,boolean,false,false,false
ion-textarea,prop,readonly,boolean,false,false,true
ion-textarea,prop,required,boolean,false,false,false
ion-textarea,prop,rows,number | undefined,undefined,false,false
ion-textarea,prop,shape,"round" | undefined,undefined,false,false
Expand Down
4 changes: 2 additions & 2 deletions core/src/components/textarea/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export class Textarea implements ComponentInterface {
/**
* If `true`, the user cannot interact with the textarea.
*/
@Prop() disabled = false;
@Prop({ reflect: true }) disabled = false;

/**
* The fill for the item. If `"solid"` the item will have a background. If
Expand Down Expand Up @@ -177,7 +177,7 @@ export class Textarea implements ComponentInterface {
/**
* If `true`, the user cannot modify the value.
*/
@Prop() readonly = false;
@Prop({ reflect: true }) readonly = false;

/**
* If `true`, the user must fill in a value before submitting a form.
Expand Down
211 changes: 116 additions & 95 deletions packages/angular/test/base/e2e/src/lazy/inputs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,105 +5,126 @@ test.describe('Inputs', () => {
await page.goto('/lazy/inputs');
});

test('should have default values', async ({ page }) => {
// Check primary elements for default values
await expect(page.locator('ion-checkbox').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-radio-group').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-toggle').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-input').first()).toHaveJSProperty('value', 'some text');
await expect(page.locator('ion-input-otp').first()).toHaveJSProperty('value', '1234');
await expect(page.locator('ion-datetime').first()).toHaveJSProperty('value', '1994-03-15');
await expect(page.locator('ion-select').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-range').first()).toHaveJSProperty('value', 50);
});
test.describe('basic functionality', () => {
test('should have default values', async ({ page }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a lot of changes but I only moved the tests under a describe.

// Check primary elements for default values
await expect(page.locator('ion-checkbox').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-radio-group').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-toggle').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-input').first()).toHaveJSProperty('value', 'some text');
await expect(page.locator('ion-input-otp').first()).toHaveJSProperty('value', '1234');
await expect(page.locator('ion-datetime').first()).toHaveJSProperty('value', '1994-03-15');
await expect(page.locator('ion-select').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-range').first()).toHaveJSProperty('value', 50);
});

test('should reset values', async ({ page }) => {
await page.locator('#reset-button').click();

// Check primary elements after reset
await expect(page.locator('ion-checkbox').first()).toHaveJSProperty('checked', false);
await expect(page.locator('ion-radio-group').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-toggle').first()).toHaveJSProperty('checked', false);
/**
* The `value` property gets set to undefined
* for these components, so we need to check
* that the value property is undefined.
*/
await expect(page.locator('ion-input').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-input-otp').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-datetime').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-select').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-range').first()).toHaveJSProperty('value', undefined);
});
test('should reset values', async ({ page }) => {
await page.locator('#reset-button').click();

// Check primary elements after reset
await expect(page.locator('ion-checkbox').first()).toHaveJSProperty('checked', false);
await expect(page.locator('ion-radio-group').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-toggle').first()).toHaveJSProperty('checked', false);
/**
* The `value` property gets set to undefined
* for these components, so we need to check
* that the value property is undefined.
*/
await expect(page.locator('ion-input').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-input-otp').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-datetime').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-select').first()).toHaveJSProperty('value', undefined);
await expect(page.locator('ion-range').first()).toHaveJSProperty('value', undefined);
});

test('should set values', async ({ page }) => {
await page.locator('#reset-button').click();
await page.locator('#set-button').click();

// Check primary elements after setting values
await expect(page.locator('ion-checkbox').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-radio-group').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-toggle').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-input').first()).toHaveJSProperty('value', 'some text');
await expect(page.locator('ion-input-otp').first()).toHaveJSProperty('value', '1234');
await expect(page.locator('ion-datetime').first()).toHaveJSProperty('value', '1994-03-15');
await expect(page.locator('ion-select').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-range').first()).toHaveJSProperty('value', 50);
});
test('should set values', async ({ page }) => {
await page.locator('#reset-button').click();
await page.locator('#set-button').click();

// Check primary elements after setting values
await expect(page.locator('ion-checkbox').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-radio-group').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-toggle').first()).toHaveJSProperty('checked', true);
await expect(page.locator('ion-input').first()).toHaveJSProperty('value', 'some text');
await expect(page.locator('ion-input-otp').first()).toHaveJSProperty('value', '1234');
await expect(page.locator('ion-datetime').first()).toHaveJSProperty('value', '1994-03-15');
await expect(page.locator('ion-select').first()).toHaveJSProperty('value', 'nes');
await expect(page.locator('ion-range').first()).toHaveJSProperty('value', 50);
});

test('should update angular when values change', async ({ page }) => {
await page.locator('#reset-button').click();

await page.locator('ion-checkbox#first-checkbox').click();
await page.locator('ion-radio').first().click();
await page.locator('ion-toggle').first().click();

await page.locator('ion-input').nth(0).locator('input').fill('hola');
await page.locator('ion-input').nth(0).locator('input').blur();

await page.locator('ion-input-otp input').nth(0).fill('1');
await page.locator('ion-input-otp input').nth(1).fill('2');
await page.locator('ion-input-otp input').nth(2).fill('3');
await page.locator('ion-input-otp input').nth(3).fill('4');
await page.locator('ion-input-otp input').nth(3).blur();

// Set date to 1994-03-14
await page.locator('ion-datetime').first().click();
await page.locator('ion-datetime').first().locator('.calendar-day:not([disabled])').first().click();

await page.locator('ion-select#game-console').click();
await expect(page.locator('ion-alert')).toBeVisible();
// Playstation option
await page.locator('ion-alert .alert-radio-button').nth(3).click();
// Click confirm button
await page.locator('ion-alert .alert-button:not(.alert-button-role-cancel)').click();

// Check note text (Angular binding updates)
await expect(page.locator('#checkbox-note')).toHaveText('true');
await expect(page.locator('#radio-note')).toHaveText('nes');
await expect(page.locator('#toggle-note')).toHaveText('true');
await expect(page.locator('#input-note')).toHaveText('hola');
await expect(page.locator('#input-otp-note')).toHaveText('1234');
await expect(page.locator('#datetime-note')).toHaveText('1994-03-14');
await expect(page.locator('#select-note')).toHaveText('ps');
});
test('should update angular when values change', async ({ page }) => {
await page.locator('#reset-button').click();

test('should update values when erasing input', async ({ page }) => {
// Focus the input and press backspace to remove last character
await page.locator('ion-input').nth(0).locator('input').click();
await page.locator('ion-input').nth(0).locator('input').press('Backspace');
// Check mirror element reflects the change
await expect(page.locator('ion-input').nth(1)).toHaveJSProperty('value', 'some tex');
// Check note text (Angular binding)
await expect(page.locator('#input-note')).toHaveText('some tex');

// Focus the last OTP input and press backspace
await page.locator('ion-input-otp input').last().click();
await page.locator('ion-input-otp input').last().press('Backspace');
// Check mirror element reflects the change
await expect(page.locator('ion-input-otp').nth(1)).toHaveJSProperty('value', '123');
// Check note text (Angular binding)
await expect(page.locator('#input-otp-note')).toHaveText('123');
await page.locator('ion-checkbox#first-checkbox').click();
await page.locator('ion-radio').first().click();
await page.locator('ion-toggle').first().click();

await page.locator('ion-input').nth(0).locator('input').fill('hola');
await page.locator('ion-input').nth(0).locator('input').blur();

await page.locator('ion-input-otp input').nth(0).fill('1');
await page.locator('ion-input-otp input').nth(1).fill('2');
await page.locator('ion-input-otp input').nth(2).fill('3');
await page.locator('ion-input-otp input').nth(3).fill('4');
await page.locator('ion-input-otp input').nth(3).blur();

// Set date to 1994-03-14
await page.locator('ion-datetime').first().click();
await page.locator('ion-datetime').first().locator('.calendar-day:not([disabled])').first().click();

await page.locator('ion-select#game-console').click();
await expect(page.locator('ion-alert')).toBeVisible();
// Playstation option
await page.locator('ion-alert .alert-radio-button').nth(3).click();
// Click confirm button
await page.locator('ion-alert .alert-button:not(.alert-button-role-cancel)').click();

// Check note text (Angular binding updates)
await expect(page.locator('#checkbox-note')).toHaveText('true');
await expect(page.locator('#radio-note')).toHaveText('nes');
await expect(page.locator('#toggle-note')).toHaveText('true');
await expect(page.locator('#input-note')).toHaveText('hola');
await expect(page.locator('#input-otp-note')).toHaveText('1234');
await expect(page.locator('#datetime-note')).toHaveText('1994-03-14');
await expect(page.locator('#select-note')).toHaveText('ps');
});

test('should update values when erasing input', async ({ page }) => {
// Focus the input and press backspace to remove last character
await page.locator('ion-input').nth(0).locator('input').click();
await page.locator('ion-input').nth(0).locator('input').press('Backspace');
// Check mirror element reflects the change
await expect(page.locator('ion-input').nth(1)).toHaveJSProperty('value', 'some tex');
// Check note text (Angular binding)
await expect(page.locator('#input-note')).toHaveText('some tex');

// Focus the last OTP input and press backspace
await page.locator('ion-input-otp input').last().click();
await page.locator('ion-input-otp input').last().press('Backspace');
// Check mirror element reflects the change
await expect(page.locator('ion-input-otp').nth(1)).toHaveJSProperty('value', '123');
// Check note text (Angular binding)
await expect(page.locator('#input-otp-note')).toHaveText('123');
});

test('should reflect props when component has a default value', async ({ page }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new test added.

// Disable inputs
await page.locator('#disable-button').click();

// Disabled prop
await expect(page.locator('ion-input').first()).toHaveAttribute('disabled', '');
await expect(page.locator('ion-input-otp').first()).toHaveAttribute('disabled', '');
await expect(page.locator('ion-textarea').first()).toHaveAttribute('disabled', '');

// Reset disabled state and set readonly state
await page.locator('#disable-button').click();
await page.locator('#readonly-button').click();

// Readonly prop
await expect(page.locator('ion-input').first()).toHaveAttribute('readonly', '');
await expect(page.locator('ion-input-otp').first()).toHaveAttribute('readonly', '');
await expect(page.locator('ion-textarea').first()).toHaveAttribute('readonly', '');
});
});

test.describe('updating text input refs', () => {
Expand Down
28 changes: 28 additions & 0 deletions packages/angular/test/base/e2e/src/standalone/inputs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { test, expect } from '@playwright/test';

test.describe('Inputs', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/standalone/inputs');
});

test.describe('basic functionality', () => {
test('should reflect props when component has a default value', async ({ page }) => {
// Disable inputs
await page.locator('#disable-button').click();

// Disabled prop
await expect(page.locator('ion-input')).toHaveAttribute('disabled', '');
await expect(page.locator('ion-input-otp')).toHaveAttribute('disabled', '');
await expect(page.locator('ion-textarea')).toHaveAttribute('disabled', '');

// Reset disabled state and set readonly state
await page.locator('#disable-button').click();
await page.locator('#readonly-button').click();

// Readonly prop
await expect(page.locator('ion-input')).toHaveAttribute('readonly', '');
await expect(page.locator('ion-input-otp')).toHaveAttribute('readonly', '');
await expect(page.locator('ion-textarea')).toHaveAttribute('readonly', '');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@

<ion-item>
<ion-label>DateTime</ion-label>
<ion-datetime [(ngModel)]="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY"></ion-datetime>
<ion-datetime [(ngModel)]="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY" [disabled]="isDisabled" [readonly]="isReadonly"></ion-datetime>
<ion-note slot="end" id="datetime-note">{{datetime}}</ion-note>
</ion-item>

<ion-item color="dark">
<ion-label>DateTime Mirror</ion-label>
<ion-datetime [(ngModel)]="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY"></ion-datetime>
<ion-datetime [(ngModel)]="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY" [disabled]="isDisabled" [readonly]="isReadonly"></ion-datetime>
<ion-note slot="end">{{datetime}}</ion-note>
</ion-item>

<ion-item>
<ion-select label="Select" [(ngModel)]="select" id="game-console">
<ion-select label="Select" [(ngModel)]="select" id="game-console" [disabled]="isDisabled">
<ion-select-option value="">No Game Console</ion-select-option>
<ion-select-option value="nes">NES</ion-select-option>
<ion-select-option value="n64" selected>Nintendo64</ion-select-option>
Expand All @@ -48,7 +48,7 @@
</ion-item>

<ion-item>
<ion-toggle [(ngModel)]="toggle" slot="end">
<ion-toggle [(ngModel)]="toggle" slot="end" [disabled]="isDisabled">
Toggle
</ion-toggle>
<ion-note slot="end" id="toggle-note">{{toggle}}</ion-note>
Expand All @@ -62,27 +62,27 @@
</ion-item>

<ion-item>
<ion-input label="Input" [(ngModel)]="input"></ion-input>
<ion-input label="Input" [(ngModel)]="input" [disabled]="isDisabled" [readonly]="isReadonly"></ion-input >
<ion-note slot="end" id="input-note">{{input}}</ion-note>
</ion-item>

<ion-item color="dark">
<ion-input label="Input Mirror" [(ngModel)]="input"></ion-input>
<ion-input label="Input Mirror" [(ngModel)]="input" [disabled]="isDisabled" [readonly]="isReadonly"></ion-input>
<ion-note slot="end">{{input}}</ion-note>
</ion-item>

<ion-item>
<ion-input-otp [(ngModel)]="inputOtp">Input OTP</ion-input-otp>
<ion-input-otp [(ngModel)]="inputOtp" [disabled]="isDisabled" [readonly]="isReadonly">Input OTP</ion-input-otp>
<ion-note slot="end" id="input-otp-note">{{inputOtp}}</ion-note>
</ion-item>

<ion-item color="dark">
<ion-input-otp [(ngModel)]="inputOtp">Input OTP Mirror</ion-input-otp>
<ion-input-otp [(ngModel)]="inputOtp" [disabled]="isDisabled" [readonly]="isReadonly">Input OTP Mirror</ion-input-otp>
<ion-note slot="end">{{inputOtp}}</ion-note>
</ion-item>

<ion-item>
<ion-checkbox [(ngModel)]="checkbox" slot="start" id="first-checkbox">
<ion-checkbox [(ngModel)]="checkbox" slot="start" id="first-checkbox" [disabled]="isDisabled">
Checkbox
</ion-checkbox>
<ion-note slot="end" id="checkbox-note">{{checkbox}}</ion-note>
Expand All @@ -97,7 +97,7 @@

<ion-item>
<ion-radio-group value="nes" [(ngModel)]="radio" id="first-radio">
<ion-radio value="nes">Radio</ion-radio>
<ion-radio value="nes" [disabled]="isDisabled">Radio</ion-radio>
</ion-radio-group>
<ion-note slot="end" id="radio-note">{{radio}}</ion-note>
</ion-item>
Expand All @@ -110,14 +110,20 @@
</ion-item>

<ion-item>
<ion-range [(ngModel)]="range" min="0" max="100" id="first-range">
<ion-range [(ngModel)]="range" min="0" max="100" id="first-range" [disabled]="isDisabled">
<ion-label slot="start">Range</ion-label>
</ion-range>
</ion-item>

<ion-item>
<ion-textarea [(ngModel)]="textarea" minLength="0" maxLength="100" [disabled]="isDisabled" [readonly]="isReadonly" label="Textarea"></ion-textarea>
</ion-item>

</ion-list>
<p>
<ion-button (click)="setValues()" id="set-button">Set values</ion-button>
<ion-button (click)="resetValues()" id="reset-button">Reset values</ion-button>
<ion-button (click)="toggleDisable()" id="disable-button">Toggle Disabled</ion-button>
<ion-button (click)="toggleReadonly()" id="readonly-button">Toggle Readonly</ion-button>
</p>
</ion-content>
Loading
Loading