diff --git a/core/api.txt b/core/api.txt
index 745d82786af..a43e79a733b 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -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
@@ -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
diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx
index 89646f6a247..ea5fa464186 100644
--- a/core/src/components/textarea/textarea.tsx
+++ b/core/src/components/textarea/textarea.tsx
@@ -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
@@ -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.
diff --git a/packages/angular/test/base/e2e/src/lazy/inputs.spec.ts b/packages/angular/test/base/e2e/src/lazy/inputs.spec.ts
index fdf2b38adc4..d8b8b4919bd 100644
--- a/packages/angular/test/base/e2e/src/lazy/inputs.spec.ts
+++ b/packages/angular/test/base/e2e/src/lazy/inputs.spec.ts
@@ -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 }) => {
+ // 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 }) => {
+ // 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', () => {
diff --git a/packages/angular/test/base/e2e/src/standalone/inputs.spec.ts b/packages/angular/test/base/e2e/src/standalone/inputs.spec.ts
new file mode 100644
index 00000000000..b5b17496ee2
--- /dev/null
+++ b/packages/angular/test/base/e2e/src/standalone/inputs.spec.ts
@@ -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', '');
+ });
+ });
+});
diff --git a/packages/angular/test/base/src/app/lazy/inputs/inputs.component.html b/packages/angular/test/base/src/app/lazy/inputs/inputs.component.html
index c63e1c2792e..28381edc619 100644
--- a/packages/angular/test/base/src/app/lazy/inputs/inputs.component.html
+++ b/packages/angular/test/base/src/app/lazy/inputs/inputs.component.html
@@ -11,18 +11,18 @@
DateTime
-
+
{{datetime}}
DateTime Mirror
-
+
{{datetime}}
-
+
No Game Console
NES
Nintendo64
@@ -48,7 +48,7 @@
-
+
Toggle
{{toggle}}
@@ -62,27 +62,27 @@
-
+
{{input}}
-
+
{{input}}
- Input OTP
+ Input OTP
{{inputOtp}}
- Input OTP Mirror
+ Input OTP Mirror
{{inputOtp}}
-
+
Checkbox
{{checkbox}}
@@ -97,7 +97,7 @@
- Radio
+ Radio
{{radio}}
@@ -110,14 +110,20 @@
-
+
Range
+
+
+
+
Set values
Reset values
+ Toggle Disabled
+ Toggle Readonly
diff --git a/packages/angular/test/base/src/app/lazy/inputs/inputs.component.ts b/packages/angular/test/base/src/app/lazy/inputs/inputs.component.ts
index e53f73948c3..fb04b7d1467 100644
--- a/packages/angular/test/base/src/app/lazy/inputs/inputs.component.ts
+++ b/packages/angular/test/base/src/app/lazy/inputs/inputs.component.ts
@@ -16,6 +16,11 @@ export class InputsComponent {
select? = 'nes';
changes = 0;
range? = 50;
+ textarea? = 'some text';
+
+ // States
+ isDisabled = false;
+ isReadonly = false;
setValues() {
console.log('set values');
@@ -27,6 +32,7 @@ export class InputsComponent {
this.toggle = true;
this.select = 'nes';
this.range = 50;
+ this.textarea = 'some text';
}
resetValues() {
@@ -39,6 +45,17 @@ export class InputsComponent {
this.toggle = false;
this.select = undefined;
this.range = undefined;
+ this.textarea = undefined;
+ }
+
+ toggleDisable() {
+ console.log(`toggle disable to ${!this.isDisabled}`);
+ this.isDisabled = !this.isDisabled;
+ }
+
+ toggleReadonly() {
+ console.log(`toggle readonly to ${!this.isReadonly}`);
+ this.isReadonly = !this.isReadonly;
}
counter() {
diff --git a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts
index 667ef672e8b..46508b144ce 100644
--- a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts
+++ b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts
@@ -7,6 +7,7 @@ export const routes: Routes = [
component: AppComponent,
children: [
{ path: '', loadComponent: () => import('../home-page/home-page.component').then(c => c.HomePageComponent) },
+ { path: 'inputs', loadComponent: () => import('../inputs/inputs.component').then(c => c.InputsComponent) },
{ path: 'menu-controller', loadComponent: () => import('../menu-controller/menu-controller.component').then(c => c.MenuControllerComponent) },
{ path: 'action-sheet-controller', loadComponent: () => import('../action-sheet-controller/action-sheet-controller.component').then(c => c.ActionSheetControllerComponent) },
{ path: 'popover', loadComponent: () => import('../popover/popover.component').then(c => c.PopoverComponent) },
diff --git a/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html b/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html
index 6dbad643eb2..3aa3062f9ba 100644
--- a/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html
+++ b/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html
@@ -28,6 +28,11 @@
Icon Test
+
+
+ Inputs Test
+
+
Reorder Group Test
diff --git a/packages/angular/test/base/src/app/standalone/inputs/inputs.component.html b/packages/angular/test/base/src/app/standalone/inputs/inputs.component.html
new file mode 100644
index 00000000000..d1334c479c2
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/inputs/inputs.component.html
@@ -0,0 +1,79 @@
+
+
+
+ Inputs test
+
+
+
+
+
+
+ Set values
+ Reset values
+ Toggle Disabled
+ Toggle Readonly
+
+
diff --git a/packages/angular/test/base/src/app/standalone/inputs/inputs.component.ts b/packages/angular/test/base/src/app/standalone/inputs/inputs.component.ts
new file mode 100644
index 00000000000..7273fa5926a
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/inputs/inputs.component.ts
@@ -0,0 +1,80 @@
+import { Component } from '@angular/core';
+import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
+import {
+ IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem,
+ IonLabel, IonDatetime, IonNote, IonSelect, IonSelectOption,
+ IonToggle, IonInput, IonInputOtp, IonCheckbox, IonRadioGroup,
+ IonRadio, IonRange, IonTextarea, IonButton
+} from '@ionic/angular/standalone';
+
+@Component({
+ selector: 'app-inputs',
+ templateUrl: './inputs.component.html',
+ standalone: true,
+ imports: [
+ ReactiveFormsModule,
+ IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem,
+ IonLabel, IonDatetime, IonNote, IonSelect, IonSelectOption,
+ IonToggle, IonInput, IonInputOtp, IonCheckbox, IonRadioGroup,
+ IonRadio, IonRange, IonTextarea, IonButton
+ ],
+})
+export class InputsComponent {
+ // Create the FormGroup
+ form = new FormGroup({
+ datetime: new FormControl('1994-03-15'),
+ input: new FormControl('some text'),
+ inputOtp: new FormControl('1234'),
+ checkbox: new FormControl(true),
+ radio: new FormControl('nes'),
+ toggle: new FormControl(true),
+ select: new FormControl('nes'),
+ range: new FormControl(50),
+ textarea: new FormControl('some text'),
+ });
+
+ // States
+ isDisabled = false;
+ isReadonly = false;
+
+ setValues() {
+ console.log('set values');
+ this.form.patchValue({
+ datetime: '1994-03-15',
+ input: 'some text',
+ inputOtp: '1234',
+ checkbox: true,
+ radio: 'nes',
+ toggle: true,
+ select: 'nes',
+ range: 50,
+ textarea: 'some text',
+ });
+ }
+
+ resetValues() {
+ console.log('reset values');
+ // reset them each
+ this.form.patchValue({
+ datetime: undefined,
+ input: undefined,
+ inputOtp: undefined,
+ checkbox: false,
+ radio: undefined,
+ toggle: false,
+ select: undefined,
+ range: undefined,
+ textarea: undefined,
+ });
+ }
+
+ toggleDisable() {
+ console.log(`toggle disable to ${!this.isDisabled}`);
+ this.isDisabled = !this.isDisabled;
+ }
+
+ toggleReadonly() {
+ console.log(`toggle readonly to ${!this.isReadonly}`);
+ this.isReadonly = !this.isReadonly;
+ }
+}
diff --git a/packages/react/test/base/src/pages/Inputs.tsx b/packages/react/test/base/src/pages/Inputs.tsx
index 7e5db11b5de..47ac3ac5328 100644
--- a/packages/react/test/base/src/pages/Inputs.tsx
+++ b/packages/react/test/base/src/pages/Inputs.tsx
@@ -66,6 +66,10 @@ const Inputs: React.FC = () => {
const [segment, setSegment] = useState('dogs');
const [select, setSelect] = useState('apples');
+ // States
+ const [isDisabled, setIsDisabled] = useState(false);
+ const [isReadonly, setIsReadonly] = useState(false);
+
const reset = () => {
setCheckbox(false);
setToggle(false);
@@ -94,6 +98,14 @@ const Inputs: React.FC = () => {
setSelect('bananas');
};
+ const toggleDisable = () => {
+ setIsDisabled(!isDisabled);
+ };
+
+ const toggleReadonly = () => {
+ setIsReadonly(!isReadonly);
+ };
+
return (
@@ -109,6 +121,7 @@ const Inputs: React.FC = () => {
onIonChange={(e: IonSegmentCustomEvent) => {
if (typeof e.detail.value === 'string') setSegment(e.detail.value);
}}
+ disabled={isDisabled}
>
Dogs
@@ -122,6 +135,7 @@ const Inputs: React.FC = () => {
) => setSearchbar(e.detail.value!)}
+ disabled={isDisabled}
>
@@ -137,6 +151,7 @@ const Inputs: React.FC = () => {
) => setCheckbox(e.detail.checked)}
+ disabled={isDisabled}
>
Checkbox
@@ -146,6 +161,7 @@ const Inputs: React.FC = () => {
) => setToggle(e.detail.checked)}
+ disabled={isDisabled}
>
Toggle
@@ -156,6 +172,8 @@ const Inputs: React.FC = () => {
value={input}
onIonInput={(e: IonInputCustomEvent) => setInput(e.detail.value!)}
label="Input"
+ disabled={isDisabled}
+ readonly={isReadonly}
>
@@ -163,6 +181,8 @@ const Inputs: React.FC = () => {
) => setInputOtp(e.detail.value ?? '')}
+ disabled={isDisabled}
+ readonly={isReadonly}
>
@@ -174,6 +194,7 @@ const Inputs: React.FC = () => {
max={100}
value={range}
onIonChange={(e: IonRangeCustomEvent) => setRange(e.detail.value as { lower: number; upper: number })}
+ disabled={isDisabled}
>
@@ -182,6 +203,8 @@ const Inputs: React.FC = () => {
value={textarea}
onIonInput={(e: IonTextareaCustomEvent) => setTextarea(e.detail.value!)}
label="Textarea"
+ disabled={isDisabled}
+ readonly={isReadonly}
>
@@ -195,6 +218,8 @@ const Inputs: React.FC = () => {
setDatetime(value);
}
}}
+ disabled={isDisabled}
+ readonly={isReadonly}
>
@@ -203,13 +228,13 @@ const Inputs: React.FC = () => {
onIonChange={(e: IonRadioGroupCustomEvent) => setRadio(e.detail.value)}
>
- Red
+ Red
- Green
+ Green
- Blue
+ Blue
@@ -218,6 +243,7 @@ const Inputs: React.FC = () => {
value={select}
onIonChange={(e: IonSelectCustomEvent>) => setSelect(e.detail.value)}
label="Select"
+ disabled={isDisabled}
>
Apples
Bananas
@@ -241,6 +267,8 @@ const Inputs: React.FC = () => {
Reset Values
Set Values
+ Toggle Disabled
+ Toggle Readonly
diff --git a/packages/react/test/base/tests/e2e/specs/components/inputs.cy.ts b/packages/react/test/base/tests/e2e/specs/components/inputs.cy.ts
index b32468bd96e..55732f89cae 100644
--- a/packages/react/test/base/tests/e2e/specs/components/inputs.cy.ts
+++ b/packages/react/test/base/tests/e2e/specs/components/inputs.cy.ts
@@ -3,48 +3,69 @@ describe('Inputs', () => {
cy.visit('/inputs')
})
- it('should have default value', () => {
- cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-input').should('have.prop', 'value').and('eq', '');
- cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
- cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
- cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
- cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
- cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
- cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
- cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
- cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
- });
+ describe('basic functionality', () => {
+ it('should have default value', () => {
+ cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-input').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
+ cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
+ cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
+ cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
+ });
+
+ it('should set/reset values', () => {
+ cy.get('ion-button#set').click();
- it('should set/reset values', () => {
- cy.get('ion-button#set').click();
-
- cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', true);
- cy.get('ion-toggle').should('have.prop', 'checked').and('eq', true);
- cy.get('ion-input').should('have.prop', 'value').and('eq', 'Hello World');
- cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '1234');
- cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 10, upper: 90 });
- cy.get('ion-textarea').should('have.prop', 'value').and('eq', 'Lorem Ipsum');
- cy.get('ion-searchbar').should('have.prop', 'value').and('eq', 'Search Query');
- cy.get('ion-datetime').should('have.prop', 'value').and('eq', '2019-01-31');
- cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'blue');
- cy.get('ion-segment').should('have.prop', 'value').and('eq', 'cats');
- cy.get('ion-select').should('have.prop', 'value').and('eq', 'bananas');
-
- cy.get('ion-button#reset').click();
-
- cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-input').should('have.prop', 'value').and('eq', '');
- cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
- cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
- cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
- cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
- cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
- cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
- cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
- cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
+ cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', true);
+ cy.get('ion-toggle').should('have.prop', 'checked').and('eq', true);
+ cy.get('ion-input').should('have.prop', 'value').and('eq', 'Hello World');
+ cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '1234');
+ cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 10, upper: 90 });
+ cy.get('ion-textarea').should('have.prop', 'value').and('eq', 'Lorem Ipsum');
+ cy.get('ion-searchbar').should('have.prop', 'value').and('eq', 'Search Query');
+ cy.get('ion-datetime').should('have.prop', 'value').and('eq', '2019-01-31');
+ cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'blue');
+ cy.get('ion-segment').should('have.prop', 'value').and('eq', 'cats');
+ cy.get('ion-select').should('have.prop', 'value').and('eq', 'bananas');
+
+ cy.get('ion-button#reset').click();
+
+ cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-input').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
+ cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
+ cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
+ cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
+ });
+
+ it('should reflect props when component has a default value', () => {
+ // Disable inputs
+ cy.get('ion-button#disable').click();
+
+ // Disabled prop
+ cy.get('ion-input').should('have.attr', 'disabled');
+ cy.get('ion-input-otp').should('have.attr', 'disabled');
+ cy.get('ion-textarea').should('have.attr', 'disabled');
+
+ // Reset disabled state and set readonly state
+ cy.get('ion-button#disable').click();
+ cy.get('ion-button#readonly').click();
+
+ // Readonly prop
+ cy.get('ion-input').should('have.attr', 'readonly');
+ cy.get('ion-input-otp').should('have.attr', 'readonly');
+ cy.get('ion-textarea').should('have.attr', 'readonly');
+ });
});
describe('updating text input refs', () => {
diff --git a/packages/vue/test/base/src/views/Inputs.vue b/packages/vue/test/base/src/views/Inputs.vue
index 28108f4f0dd..6bfe69984b2 100644
--- a/packages/vue/test/base/src/views/Inputs.vue
+++ b/packages/vue/test/base/src/views/Inputs.vue
@@ -8,7 +8,7 @@
Inputs
-
+
Dogs
@@ -18,7 +18,7 @@
-
+
@@ -30,48 +30,48 @@
- Checkbox
+ Checkbox
- Toggle
+ Toggle
-
+
-
+
-
+
-
+
Datetime
-
+
- Red
+ Red
- Green
+ Green
- Blue
+ Blue
-
+
Apples
Bananas
@@ -94,6 +94,8 @@
Reset Values
Set Values
+ Toggle Disabled
+ Toggle Readonly
@@ -171,6 +173,10 @@ export default defineComponent({
const segment = ref('dogs');
const select = ref('apples');
+ // States
+ const isDisabled = ref(false);
+ const isReadonly = ref(false);
+
const reset = () => {
checkbox.value = false;
toggle.value = false;
@@ -205,6 +211,14 @@ export default defineComponent({
select.value = 'bananas';
}
+ const toggleDisable = () => {
+ isDisabled.value = !isDisabled.value;
+ };
+
+ const toggleReadonly = () => {
+ isReadonly.value = !isReadonly.value;
+ };
+
return {
checkbox,
toggle,
@@ -217,9 +231,13 @@ export default defineComponent({
radio,
segment,
select,
+ isDisabled,
+ isReadonly,
reset,
- set
+ set,
+ toggleDisable,
+ toggleReadonly
}
}
});
diff --git a/packages/vue/test/base/tests/e2e/specs/inputs.cy.js b/packages/vue/test/base/tests/e2e/specs/inputs.cy.js
index 26c26f8ef95..f836496a5d0 100644
--- a/packages/vue/test/base/tests/e2e/specs/inputs.cy.js
+++ b/packages/vue/test/base/tests/e2e/specs/inputs.cy.js
@@ -2,48 +2,70 @@ describe('Inputs', () => {
beforeEach(() => {
cy.visit('/inputs')
})
- it('should have default value', () => {
- cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-input').should('have.prop', 'value').and('eq', '');
- cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
- cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
- cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
- cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
- cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
- cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
- cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
- cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
- });
+
+ describe('basic functionality', () => {
+ it('should have default value', () => {
+ cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-input').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
+ cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
+ cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
+ cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
+ });
+
+ it('should set/reset values', () => {
+ cy.get('ion-button#set').click();
+
+ cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', true);
+ cy.get('ion-toggle').should('have.prop', 'checked').and('eq', true);
+ cy.get('ion-input').should('have.prop', 'value').and('eq', 'Hello World');
+ cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '1234');
+ cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 10, upper: 90 });
+ cy.get('ion-textarea').should('have.prop', 'value').and('eq', 'Lorem Ipsum');
+ cy.get('ion-searchbar').should('have.prop', 'value').and('eq', 'Search Query');
+ cy.get('ion-datetime').should('have.prop', 'value').and('eq', '2019-01-31');
+ cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'blue');
+ cy.get('ion-segment').should('have.prop', 'value').and('eq', 'cats');
+ cy.get('ion-select').should('have.prop', 'value').and('eq', 'bananas');
- it('should set/reset values', () => {
- cy.get('ion-button#set').click();
+ cy.get('ion-button#reset').click();
- cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', true);
- cy.get('ion-toggle').should('have.prop', 'checked').and('eq', true);
- cy.get('ion-input').should('have.prop', 'value').and('eq', 'Hello World');
- cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '1234');
- cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 10, upper: 90 });
- cy.get('ion-textarea').should('have.prop', 'value').and('eq', 'Lorem Ipsum');
- cy.get('ion-searchbar').should('have.prop', 'value').and('eq', 'Search Query');
- cy.get('ion-datetime').should('have.prop', 'value').and('eq', '2019-01-31');
- cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'blue');
- cy.get('ion-segment').should('have.prop', 'value').and('eq', 'cats');
- cy.get('ion-select').should('have.prop', 'value').and('eq', 'bananas');
+ cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
+ cy.get('ion-input').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
+ cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
+ cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
+ cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
+ cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
+ });
+
+ it('should reflect props when component has a default value', () => {
+ // Disable inputs
+ cy.get('ion-button#disable').click();
- cy.get('ion-button#reset').click();
+ // Disabled prop
+ cy.get('ion-input').should('have.attr', 'disabled');
+ cy.get('ion-input-otp').should('have.attr', 'disabled');
+ cy.get('ion-textarea').should('have.attr', 'disabled');
- cy.get('ion-checkbox').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-toggle').should('have.prop', 'checked').and('eq', false);
- cy.get('ion-input').should('have.prop', 'value').and('eq', '');
- cy.get('ion-input-otp').should('have.prop', 'value').and('eq', '');
- cy.get('ion-range').should('have.prop', 'value').and('deep.eq', { lower: 30, upper: 70 });
- cy.get('ion-textarea').should('have.prop', 'value').and('eq', '');
- cy.get('ion-searchbar').should('have.prop', 'value').and('eq', '');
- cy.get('ion-datetime').should('have.prop', 'value').and('eq', '');
- cy.get('ion-radio-group').should('have.prop', 'value').and('eq', 'red');
- cy.get('ion-segment').should('have.prop', 'value').and('eq', 'dogs');
- cy.get('ion-select').should('have.prop', 'value').and('eq', 'apples');
+ // Reset disabled state and set readonly state
+ cy.get('ion-button#disable').click();
+ cy.get('ion-button#readonly').click();
+
+ // Readonly prop
+ cy.get('ion-input').should('have.attr', 'readonly');
+ cy.get('ion-input-otp').should('have.attr', 'readonly');
+ cy.get('ion-textarea').should('have.attr', 'readonly');
+ });
});
describe('updating text input refs', () => {