Skip to content

Commit ea8c1e2

Browse files
feat(input, textarea, select): add section for start and end slots (#3271)
1 parent 2c86917 commit ea8c1e2

File tree

21 files changed

+452
-0
lines changed

21 files changed

+452
-0
lines changed

docs/api/input.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,22 @@ Please submit bug reports with Maskito to the [Maskito Github repository](https:
148148

149149
:::
150150

151+
## Start and End Slots (experimental)
152+
153+
The `start` and `end` slots can be used to place icons, buttons, or prefix/suffix text on either side of the input.
154+
155+
Note that this feature is considered experimental because it relies on a simulated version of [Web Component slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots). As a result, the simulated behavior may not exactly match the native slot behavior.
156+
157+
:::note
158+
In most cases, [Icon](./icon.md) components placed in these slots should have `aria-hidden="true"`. See the [Icon accessibility docs](https://ionicframework.com/docs/api/icon#accessibility) for more information.
159+
160+
If slot content is meant to be interacted with, it should be wrapped in an interactive element such as a [Button](./button.md). This ensures that the content can be tabbed to.
161+
:::
162+
163+
import StartEndSlots from '@site/static/usage/v7/input/start-end-slots/index.md';
164+
165+
<StartEndSlots />
166+
151167
## Theming
152168

153169
### Colors

docs/api/select.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,20 @@ import InterfaceOptionsExample from '@site/static/usage/v7/select/customization/
173173

174174
<InterfaceOptionsExample />
175175

176+
## Start and End Slots
177+
178+
The `start` and `end` slots can be used to place icons, buttons, or prefix/suffix text on either side of the select. If the slot content is clicked, the select will not open.
179+
180+
:::note
181+
In most cases, [Icon](./icon.md) components placed in these slots should have `aria-hidden="true"`. See the [Icon accessibility docs](https://ionicframework.com/docs/api/icon#accessibility) for more information.
182+
183+
If slot content is meant to be interacted with, it should be wrapped in an interactive element such as a [Button](./button.md). This ensures that the content can be tabbed to.
184+
:::
185+
186+
import StartEndSlots from '@site/static/usage/v7/select/start-end-slots/index.md';
187+
188+
<StartEndSlots />
189+
176190
## Customization
177191

178192
There are two units that make up the Select component and each need to be styled separately. The `ion-select` element is represented on the view by the selected value(s), or placeholder if there is none, and dropdown icon. The interface, which is defined in the [Interfaces](#interfaces) section above, is the dialog that opens when clicking on the `ion-select`. The interface contains all of the options defined by adding `ion-select-option` elements. The following sections will go over the differences between styling these.

docs/api/textarea.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ import ClearOnEditPlayground from '@site/static/usage/v7/textarea/clear-on-edit/
109109

110110
<ClearOnEditPlayground />
111111

112+
## Start and End Slots (experimental)
113+
114+
The `start` and `end` slots can be used to place icons, buttons, or prefix/suffix text on either side of the textarea.
115+
116+
Note that this feature is considered experimental because it relies on a simulated version of [Web Component slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots). As a result, the simulated behavior may not exactly match the native slot behavior.
117+
118+
:::note
119+
In most cases, [Icon](./icon.md) components placed in these slots should have `aria-hidden="true"`. See the [Icon accessibility docs](https://ionicframework.com/docs/api/icon#accessibility) for more information.
120+
121+
If slot content is meant to be interacted with, it should be wrapped in an interactive element such as a [Button](./button.md). This ensures that the content can be tabbed to.
122+
:::
123+
124+
import StartEndSlots from '@site/static/usage/v7/textarea/start-end-slots/index.md';
125+
126+
<StartEndSlots />
127+
112128
## Migrating from Legacy Textarea Syntax
113129

114130
A simpler textarea syntax was introduced in Ionic 7.0. This new syntax reduces the boilerplate required to setup an textarea, resolves accessibility issues, and improves the developer experience.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```html
2+
<ion-list>
3+
<ion-item>
4+
<ion-input labelPlacement="stacked" label="Email" placeholder="[email protected]">
5+
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
6+
<ion-button fill="clear" slot="end" aria-label="Show/hide">
7+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
8+
</ion-button>
9+
</ion-input>
10+
</ion-item>
11+
</ion-list>
12+
```
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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@7/dist/ionic/ionic.esm.js"></script>
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" />
11+
</head>
12+
13+
<body>
14+
<ion-app>
15+
<ion-content>
16+
<div class="container">
17+
<ion-list>
18+
<ion-item>
19+
<ion-input label-placement="stacked" label="Email" placeholder="[email protected]">
20+
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
21+
<ion-button fill="clear" slot="end" aria-label="Show/hide">
22+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
23+
</ion-button>
24+
</ion-input>
25+
</ion-item>
26+
</ion-list>
27+
</div>
28+
</ion-content>
29+
</ion-app>
30+
</body>
31+
</html>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
import angular from './angular.md';
7+
8+
<Playground
9+
version="7"
10+
code={{
11+
javascript,
12+
react,
13+
vue,
14+
angular,
15+
}}
16+
src="usage/v7/input/start-end-slots/demo.html"
17+
/>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```html
2+
<ion-list>
3+
<ion-item>
4+
<ion-input label-placement="stacked" label="Email" placeholder="[email protected]">
5+
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
6+
<ion-button fill="clear" slot="end" aria-label="Show/hide">
7+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
8+
</ion-button>
9+
</ion-input>
10+
</ion-item>
11+
</ion-list>
12+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
```tsx
2+
import React from 'react';
3+
import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/react';
4+
import { eye, lockClosed } from 'ionicons/icons';
5+
6+
function Example() {
7+
return (
8+
<IonList>
9+
<IonItem>
10+
<IonInput labelPlacement="stacked" label="Email" placeholder="[email protected]">
11+
<IonIcon slot="start" icon={lockClosed} aria-hidden="true"></IonIcon>
12+
<IonButton fill="clear" slot="end" aria-label="Show/hide">
13+
<IonIcon slot="icon-only" name={eye} aria-hidden="true"></IonIcon>
14+
</IonButton>
15+
</IonInput>
16+
</IonItem>
17+
</IonList>
18+
);
19+
}
20+
export default Example;
21+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
```html
2+
<template>
3+
<ion-list>
4+
<ion-item>
5+
<ion-input label-placement="stacked" label="Email" placeholder="[email protected]">
6+
<ion-icon slot="start" :icon="lockClosed" aria-hidden="true"></ion-icon>
7+
<ion-button fill="clear" slot="end" aria-label="Show/hide">
8+
<ion-icon slot="icon-only" :icon="eye" aria-hidden="true"></ion-icon>
9+
</ion-button>
10+
</ion-input>
11+
</ion-item>
12+
</ion-list>
13+
</template>
14+
15+
<script lang="ts">
16+
import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/vue';
17+
import { eye, lockClosed } from 'ionicons/icons';
18+
import { defineComponent } from 'vue';
19+
20+
export default defineComponent({
21+
components: {
22+
IonButton,
23+
IonIcon,
24+
IonInput,
25+
IonItem,
26+
IonList,
27+
},
28+
setup() {
29+
return { eye, lockClosed };
30+
},
31+
});
32+
</script>
33+
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
```html
2+
<ion-list>
3+
<ion-item>
4+
<ion-select labelPlacement="stacked" label="Favorite fruit" value="apple">
5+
<ion-icon slot="start" name="leaf" aria-hidden="true"></ion-icon>
6+
<ion-select-option value="apple">Apple</ion-select-option>
7+
<ion-select-option value="banana">Banana</ion-select-option>
8+
<ion-select-option value="orange">Orange</ion-select-option>
9+
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
10+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
11+
</ion-button>
12+
</ion-select>
13+
</ion-item>
14+
</ion-list>
15+
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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>Select</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@7/dist/ionic/ionic.esm.js"></script>
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" />
11+
12+
<style>
13+
/**
14+
* This is to deal with a bug in 7.6 where a select in a list/item
15+
* wrapped in a flex container will shrink to 0 width. When the bug
16+
* is fixed, we can remove this.
17+
*/
18+
ion-list {
19+
min-width: 400px;
20+
}
21+
</style>
22+
</head>
23+
24+
<body>
25+
<ion-app>
26+
<ion-content>
27+
<div class="container">
28+
<ion-list>
29+
<ion-item>
30+
<ion-select label-placement="stacked" label="Favorite fruit" value="apple">
31+
<ion-icon slot="start" name="leaf" aria-hidden="true"></ion-icon>
32+
<ion-select-option value="apple">Apple</ion-select-option>
33+
<ion-select-option value="banana">Banana</ion-select-option>
34+
<ion-select-option value="orange">Orange</ion-select-option>
35+
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
36+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
37+
</ion-button>
38+
</ion-select>
39+
</ion-item>
40+
</ion-list>
41+
</div>
42+
</ion-content>
43+
</ion-app>
44+
</body>
45+
</html>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
import angular from './angular.md';
7+
8+
<Playground
9+
version="7"
10+
code={{
11+
javascript,
12+
react,
13+
vue,
14+
angular,
15+
}}
16+
src="usage/v7/select/start-end-slots/demo.html"
17+
size="300px"
18+
/>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
```html
2+
<ion-list>
3+
<ion-item>
4+
<ion-select label-placement="stacked" label="Favorite fruit" value="apple">
5+
<ion-icon slot="start" name="leaf" aria-hidden="true"></ion-icon>
6+
<ion-select-option value="apple">Apple</ion-select-option>
7+
<ion-select-option value="banana">Banana</ion-select-option>
8+
<ion-select-option value="orange">Orange</ion-select-option>
9+
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
10+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
11+
</ion-button>
12+
</ion-select>
13+
</ion-item>
14+
</ion-list>
15+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
```tsx
2+
import React from 'react';
3+
import { IonButton, IonIcon, IonItem, IonList, IonSelect, IonSelectOption } from '@ionic/react';
4+
import { eye, leaf } from 'ionicons/icons';
5+
6+
function Example() {
7+
return (
8+
<IonList>
9+
<IonItem>
10+
<IonSelect labelPlacement="stacked" label="Favorite fruit" value="apple">
11+
<IonIcon slot="start" icon={leaf} aria-hidden="true"></IonIcon>
12+
<IonSelectOption value="apple">Apple</IonSelectOption>
13+
<IonSelectOption value="banana">Banana</IonSelectOption>
14+
<IonSelectOption value="orange">Orange</IonSelectOption>
15+
<IonButton fill="clear" slot="end" aria-label="Show/hide password">
16+
<IonIcon slot="icon-only" icon={eye} aria-hidden="true"></IonIcon>
17+
</IonButton>
18+
</IonSelect>
19+
</IonItem>
20+
</IonList>
21+
);
22+
}
23+
export default Example;
24+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
```html
2+
<template>
3+
<ion-list>
4+
<ion-item>
5+
<ion-select label-placement="stacked" label="Favorite fruit" value="apple">
6+
<ion-icon slot="start" :icon="leaf" aria-hidden="true"></ion-icon>
7+
<ion-select-option value="apple">Apple</ion-select-option>
8+
<ion-select-option value="banana">Banana</ion-select-option>
9+
<ion-select-option value="orange">Orange</ion-select-option>
10+
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
11+
<ion-icon slot="icon-only" :icon="eye" aria-hidden="true"></ion-icon>
12+
</ion-button>
13+
</ion-select>
14+
</ion-item>
15+
</ion-list>
16+
</template>
17+
18+
<script lang="ts">
19+
import { IonButton, IonIcon, IonItem, IonList, IonSelect, IonSelectOption } from '@ionic/vue';
20+
import { eye, leaf } from 'ionicons/icons';
21+
import { defineComponent } from 'vue';
22+
23+
export default defineComponent({
24+
components: {
25+
IonButton,
26+
IonIcon,
27+
IonItem,
28+
IonList,
29+
IonSelect,
30+
IonSelectOption,
31+
},
32+
setup() {
33+
return { eye, leaf };
34+
},
35+
});
36+
</script>
37+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```html
2+
<ion-list>
3+
<ion-item>
4+
<ion-textarea labelPlacement="stacked" label="Comments" placeholder="Enter your comments">
5+
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
6+
<ion-button fill="clear" slot="end" aria-label="Show/hide">
7+
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
8+
</ion-button>
9+
</ion-textarea>
10+
</ion-item>
11+
</ion-list>
12+
```

0 commit comments

Comments
 (0)