Skip to content

Commit 573d951

Browse files
committed
docs(select): add helperText and errorText section
1 parent 6e27ebe commit 573d951

File tree

8 files changed

+295
-0
lines changed

8 files changed

+295
-0
lines changed

docs/api/select.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,16 @@ import TypeaheadExample from '@site/static/usage/v8/select/typeahead/index.md';
253253

254254
<TypeaheadExample />
255255

256+
## Helper & Error Text
257+
258+
Helper and error text can be used inside of a select with the `helperText` and `errorText` property. The error text will not be displayed unless the `ion-invalid` and `ion-touched` classes are added to the `ion-select`. This ensures errors are not shown before the user has a chance to enter data.
259+
260+
In Angular, this is done automatically through form validation. In JavaScript, React and Vue, the class needs to be manually added based on your own validation.
261+
262+
import HelperError from '@site/static/usage/v8/select/helper-error/index.md';
263+
264+
<HelperError />
265+
256266
## Interfaces
257267

258268
### SelectChangeEventDetail
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
```html
2+
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
3+
<ion-select
4+
formControlName="favFruit"
5+
label="Default label"
6+
placeholder="Favorite Fruit"
7+
helperText="Select your favorite fruit"
8+
errorText="This field is required"
9+
>
10+
<ion-select-option value="apple">Apple</ion-select-option>
11+
<ion-select-option value="banana">Banana</ion-select-option>
12+
<ion-select-option value="orange">Orange</ion-select-option>
13+
</ion-select>
14+
15+
<br />
16+
17+
<ion-button type="submit" size="small">Submit</ion-button>
18+
</form>
19+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
```ts
2+
import { Component } from '@angular/core';
3+
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
4+
import { IonSelect, IonButton } from '@ionic/angular/standalone';
5+
6+
@Component({
7+
selector: 'app-example',
8+
standalone: true,
9+
imports: [IonSelect, IonButton, ReactiveFormsModule],
10+
templateUrl: './example.component.html',
11+
styleUrl: './example.component.css',
12+
})
13+
export class ExampleComponent {
14+
myForm: FormGroup;
15+
16+
constructor(private fb: FormBuilder) {
17+
this.myForm = this.fb.group({
18+
favFruit: [false, Validators.required],
19+
});
20+
}
21+
22+
onSubmit() {
23+
// Mark the control as touched to trigger the error message.
24+
// This is needed if the user submits the form without interacting
25+
// with the checkbox.
26+
this.myForm.get('favFruit')!.markAsTouched();
27+
}
28+
}
29+
```
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Input</title>
7+
<link rel="stylesheet" href="../../common.css" />
8+
<script src="../../common.js"></script>
9+
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@8/dist/ionic/ionic.esm.js"></script>
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@8/css/ionic.bundle.css" />
11+
</head>
12+
13+
<body>
14+
<div class="container">
15+
<form id="my-form">
16+
<ion-select
17+
label="Default label"
18+
placeholder="Favorite Fruit"
19+
helper-text="Select your favorite fruit"
20+
error-text="This field is required"
21+
>
22+
<ion-select-option value="apple">Apple</ion-select-option>
23+
<ion-select-option value="banana">Banana</ion-select-option>
24+
<ion-select-option value="orange">Orange</ion-select-option>
25+
</ion-select>
26+
27+
<br />
28+
29+
<ion-button type="submit" size="small">Submit</ion-button>
30+
</form>
31+
</div>
32+
33+
<script>
34+
const form = document.getElementById('my-form');
35+
const favFruit = form.querySelector('ion-select');
36+
37+
form.addEventListener('submit', (event) => submit(event));
38+
favFruit.addEventListener('ionChange', (event) => validateSelect(event));
39+
40+
const validateSelect = (event) => {
41+
favFruit.classList.add('ion-touched');
42+
43+
if (!event.detail.value) {
44+
favFruit.classList.add('ion-invalid');
45+
favFruit.classList.remove('ion-valid');
46+
} else {
47+
favFruit.classList.remove('ion-invalid');
48+
favFruit.classList.add('ion-valid');
49+
}
50+
};
51+
52+
const submit = (event) => {
53+
event.preventDefault();
54+
55+
validateSelect({ detail: { value: favFruit.value } });
56+
};
57+
</script>
58+
</body>
59+
</html>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Playground from '@site/src/components/global/Playground';
2+
3+
import javascript from './javascript.md';
4+
import react from './react.md';
5+
import vue from './vue.md';
6+
7+
import angular_example_component_html from './angular/example_component_html.md';
8+
import angular_example_component_ts from './angular/example_component_ts.md';
9+
10+
<Playground
11+
version="8"
12+
code={{
13+
javascript,
14+
react,
15+
vue,
16+
angular: {
17+
files: {
18+
'src/app/example.component.html': angular_example_component_html,
19+
'src/app/example.component.ts': angular_example_component_ts,
20+
},
21+
},
22+
}}
23+
src="usage/v8/select/helper-error/demo.html"
24+
/>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
```html
2+
<form id="my-form">
3+
<ion-select
4+
label="Default label"
5+
placeholder="Favorite Fruit"
6+
helper-text="Select your favorite fruit"
7+
error-text="This field is required"
8+
>
9+
<ion-select-option value="apple">Apple</ion-select-option>
10+
<ion-select-option value="banana">Banana</ion-select-option>
11+
<ion-select-option value="orange">Orange</ion-select-option>
12+
</ion-select>
13+
14+
<br />
15+
16+
<ion-button type="submit" size="small">Submit</ion-button>
17+
</form>
18+
19+
<script>
20+
const form = document.getElementById('my-form');
21+
const favFruit = form.querySelector('ion-select');
22+
23+
form.addEventListener('submit', (event) => submit(event));
24+
favFruit.addEventListener('ionChange', (event) => validateSelect(event));
25+
26+
const validateSelect = (event) => {
27+
favFruit.classList.add('ion-touched');
28+
29+
if (!event.detail.value) {
30+
favFruit.classList.add('ion-invalid');
31+
favFruit.classList.remove('ion-valid');
32+
} else {
33+
favFruit.classList.remove('ion-invalid');
34+
favFruit.classList.add('ion-valid');
35+
}
36+
};
37+
38+
const submit = (event) => {
39+
event.preventDefault();
40+
41+
validateSelect({ detail: { value: favFruit.value } });
42+
};
43+
</script>
44+
```
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
```tsx
2+
import React, { useRef, useState } from 'react';
3+
import { IonSelect, IonSelectOption, IonButton, SelectCustomEvent } from '@ionic/react';
4+
5+
function Example() {
6+
const [isTouched, setIsTouched] = useState<boolean>(false);
7+
const [isValid, setIsValid] = useState<boolean | undefined>();
8+
9+
const favFruitRef = useRef<HTMLIonSelectElement>(null);
10+
11+
const validateSelect = (event: SelectCustomEvent<{ value: string }>) => {
12+
setIsTouched(true);
13+
setIsValid(event.detail.value);
14+
};
15+
16+
const submit = (event: React.FormEvent<HTMLFormElement>) => {
17+
event.preventDefault();
18+
19+
if (favFruitRef.current) {
20+
validateSelect({ detail: { value: favFruitRef.current.value } } as SelectCustomEvent<{
21+
value: string;
22+
}>);
23+
}
24+
};
25+
26+
return (
27+
<>
28+
<form onSubmit={submit}>
29+
<IonSelect
30+
ref={favFruitRef}
31+
className={`${isValid ? 'ion-valid' : ''} ${isValid === false ? 'ion-invalid' : ''} ${
32+
isTouched ? 'ion-touched' : ''
33+
}`}
34+
helperText="Select your favorite fruit"
35+
errorText="This field is required"
36+
onIonChange={(event) => validateSelect(event)}
37+
>
38+
<IonSelectOption value="apple">Apple</IonSelectOption>
39+
<IonSelectOption value="banana">Banana</IonSelectOption>
40+
<IonSelectOption value="orange">Orange</IonSelectOption>
41+
</IonSelect>
42+
43+
<br />
44+
45+
<IonButton type="submit" size="small">
46+
Submit
47+
</IonButton>
48+
</form>
49+
</>
50+
);
51+
}
52+
53+
export default Example;
54+
```
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
```html
2+
<template>
3+
<form @submit.prevent="submit">
4+
<ion-select
5+
v-model="favFruit"
6+
helper-text="Select your favorite frui"
7+
error-text="This field is required"
8+
@ionChange="validateSelect"
9+
:class="{ 'ion-valid': isValid, 'ion-invalid': isValid === false, 'ion-touched': isTouched }"
10+
>
11+
<ion-select-option value="apple">Apple</ion-select-option>
12+
<ion-select-option value="banana">Banana</ion-select-option>
13+
<ion-select-option value="orange">Orange</ion-select-option>
14+
</ion-select>
15+
16+
<br />
17+
18+
<ion-button type="submit" size="small">Submit</ion-button>
19+
</form>
20+
</template>
21+
22+
<script lang="ts">
23+
import { defineComponent, ref } from 'vue';
24+
import { IonSelect, IonSelectOption, IonButton, SelectCustomEvent } from '@ionic/vue';
25+
26+
export default defineComponent({
27+
components: {
28+
IonSelect,
29+
IonSelectOption,
30+
IonButton,
31+
},
32+
setup() {
33+
const favFruit = ref(false);
34+
const isTouched = ref(false);
35+
const isValid = ref<boolean | undefined>();
36+
37+
const validateSelect = (event: SelectCustomEvent<{ value: string }>) => {
38+
isTouched.value = true;
39+
isValid.value = event.detail.value;
40+
};
41+
42+
const submit = () => {
43+
validateSelect({ detail: { value: favFruit.value } } as SelectCustomEvent<{ value: string }>);
44+
};
45+
46+
return {
47+
favFruit,
48+
isTouched,
49+
isValid,
50+
validateSelect,
51+
submit,
52+
};
53+
},
54+
});
55+
</script>
56+
```

0 commit comments

Comments
 (0)