Skip to content

Commit 73a1daf

Browse files
authored
feat(alert, toast): pass arbitrary HTML attributes to host element (#23891)
resolves #23825
1 parent 1e13429 commit 73a1daf

File tree

13 files changed

+108
-9
lines changed

13 files changed

+108
-9
lines changed

core/api.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ ion-alert,prop,buttons,(string | AlertButton)[],[],false,false
5050
ion-alert,prop,cssClass,string | string[] | undefined,undefined,false,false
5151
ion-alert,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
5252
ion-alert,prop,header,string | undefined,undefined,false,false
53+
ion-alert,prop,htmlAttributes,AlertAttributes | undefined,undefined,false,false
5354
ion-alert,prop,inputs,AlertInput[],[],false,false
5455
ion-alert,prop,keyboardClose,boolean,true,false,false
5556
ion-alert,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
@@ -1268,6 +1269,7 @@ ion-toast,prop,cssClass,string | string[] | undefined,undefined,false,false
12681269
ion-toast,prop,duration,number,0,false,false
12691270
ion-toast,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
12701271
ion-toast,prop,header,string | undefined,undefined,false,false
1272+
ion-toast,prop,htmlAttributes,ToastAttributes | undefined,undefined,false,false
12711273
ion-toast,prop,keyboardClose,boolean,false,false,false
12721274
ion-toast,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
12731275
ion-toast,prop,message,IonicSafeString | string | undefined,undefined,false,false

core/src/components.d.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
88
import { ActionSheetButton, AlertButton, AlertInput, AnimationBuilder, AutocompleteTypes, CheckboxChangeEventDetail, Color, ComponentProps, ComponentRef, DatetimeChangeEventDetail, DatetimeOptions, DomRenderFn, FooterHeightFn, FrameworkDelegate, HeaderFn, HeaderHeightFn, InputChangeEventDetail, ItemHeightFn, ItemRenderFn, ItemReorderEventDetail, MenuChangeEventDetail, NavComponent, NavComponentWithProps, NavOptions, OverlayEventDetail, PickerButton, PickerColumn, RadioGroupChangeEventDetail, RangeChangeEventDetail, RangeValue, RefresherEventDetail, RouteID, RouterDirection, RouterEventDetail, RouterOutletOptions, RouteWrite, ScrollBaseDetail, ScrollDetail, SearchbarChangeEventDetail, SegmentButtonLayout, SegmentChangeEventDetail, SelectChangeEventDetail, SelectInterface, SelectPopoverOption, Side, SpinnerTypes, StyleEventDetail, SwipeGestureHandler, TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout, TextareaChangeEventDetail, TextFieldTypes, ToastButton, ToggleChangeEventDetail, TransitionDoneFn, TransitionInstruction, ViewController } from "./interface";
99
import { IonicSafeString } from "./utils/sanitization";
10+
import { AlertAttributes } from "./components/alert/alert-interface";
1011
import { NavigationHookCallback } from "./components/route/route-interface";
1112
import { SelectCompareFn } from "./components/select/select-interface";
13+
import { ToastAttributes } from "./components/toast/toast-interface";
1214
export namespace Components {
1315
interface IonActionSheet {
1416
/**
@@ -106,6 +108,10 @@ export namespace Components {
106108
* The main title in the heading of the alert.
107109
*/
108110
"header"?: string;
111+
/**
112+
* Additional attributes to pass to the alert.
113+
*/
114+
"htmlAttributes"?: AlertAttributes;
109115
/**
110116
* Array of input to show in the alert.
111117
*/
@@ -2565,6 +2571,10 @@ export namespace Components {
25652571
* Header to be shown in the toast.
25662572
*/
25672573
"header"?: string;
2574+
/**
2575+
* Additional attributes to pass to the toast.
2576+
*/
2577+
"htmlAttributes"?: ToastAttributes;
25682578
/**
25692579
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
25702580
*/
@@ -3409,6 +3419,10 @@ declare namespace LocalJSX {
34093419
* The main title in the heading of the alert.
34103420
*/
34113421
"header"?: string;
3422+
/**
3423+
* Additional attributes to pass to the alert.
3424+
*/
3425+
"htmlAttributes"?: AlertAttributes;
34123426
/**
34133427
* Array of input to show in the alert.
34143428
*/
@@ -5894,6 +5908,10 @@ declare namespace LocalJSX {
58945908
* Header to be shown in the toast.
58955909
*/
58965910
"header"?: string;
5911+
/**
5912+
* Additional attributes to pass to the toast.
5913+
*/
5914+
"htmlAttributes"?: ToastAttributes;
58975915
/**
58985916
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
58995917
*/

core/src/components/alert/alert-interface.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface AlertOptions {
1313
backdropDismiss?: boolean;
1414
translucent?: boolean;
1515
animated?: boolean;
16+
htmlAttributes?: AlertAttributes;
1617

1718
mode?: Mode;
1819
keyboardClose?: boolean;
@@ -22,6 +23,8 @@ export interface AlertOptions {
2223
leaveAnimation?: AnimationBuilder;
2324
}
2425

26+
export interface AlertAttributes extends JSXBase.HTMLAttributes<HTMLElement> {}
27+
2528
export interface AlertInput {
2629
type?: TextFieldTypes | 'checkbox' | 'radio' | 'textarea';
2730
name?: string;

core/src/components/alert/alert.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { BACKDROP, dismiss, eventMethod, isCancel, prepareOverlay, present, safe
88
import { IonicSafeString, sanitizeDOMString } from '../../utils/sanitization';
99
import { getClassMap } from '../../utils/theme';
1010

11+
import { AlertAttributes } from './alert-interface';
1112
import { iosEnterAnimation } from './animations/ios.enter';
1213
import { iosLeaveAnimation } from './animations/ios.leave';
1314
import { mdEnterAnimation } from './animations/md.enter';
@@ -110,6 +111,11 @@ export class Alert implements ComponentInterface, OverlayInterface {
110111
*/
111112
@Prop() animated = true;
112113

114+
/**
115+
* Additional attributes to pass to the alert.
116+
*/
117+
@Prop() htmlAttributes?: AlertAttributes;
118+
113119
/**
114120
* Emitted after the alert has presented.
115121
*/
@@ -550,15 +556,16 @@ export class Alert implements ComponentInterface, OverlayInterface {
550556
}
551557

552558
render() {
553-
const { overlayIndex, header, subHeader } = this;
559+
const { overlayIndex, header, subHeader, htmlAttributes } = this;
554560
const mode = getIonMode(this);
555561
const hdrId = `alert-${overlayIndex}-hdr`;
556562
const subHdrId = `alert-${overlayIndex}-sub-hdr`;
557563
const msgId = `alert-${overlayIndex}-msg`;
564+
const role = htmlAttributes?.role || (this.inputs.length > 0 || this.buttons.length > 0 ? 'alertdialog' : 'alert');
558565

559566
return (
560567
<Host
561-
role="dialog"
568+
role={role}
562569
aria-modal="true"
563570
tabindex="-1"
564571
style={{
@@ -571,6 +578,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
571578
}}
572579
onIonAlertWillDismiss={this.dispatchCancelHandler}
573580
onIonBackdropTap={this.onBackdropTap}
581+
{...htmlAttributes as any}
574582
>
575583

576584
<ion-backdrop tappable={this.backdropDismiss}/>

core/src/components/alert/readme.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,22 @@ interface AlertOptions {
9595
backdropDismiss?: boolean;
9696
translucent?: boolean;
9797
animated?: boolean;
98-
98+
htmlAttributes?: AlertAttributes;
99+
99100
mode?: Mode;
100101
keyboardClose?: boolean;
101102
id?: string;
102-
103+
103104
enterAnimation?: AnimationBuilder;
104105
leaveAnimation?: AnimationBuilder;
105106
}
106107
```
107108

109+
### AlertAttributes
110+
```typescript
111+
interface AlertAttributes extends JSXBase.HTMLAttributes<HTMLElement> {}
112+
```
113+
108114
### AlertTextareaAttributes
109115
```typescript
110116
interface AlertTextareaAttributes extends JSXBase.TextareaHTMLAttributes<HTMLTextAreaElement> {}
@@ -1768,6 +1774,7 @@ export default defineComponent({
17681774
| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` |
17691775
| `enterAnimation` | -- | Animation to use when the alert is presented. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
17701776
| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` |
1777+
| `htmlAttributes` | -- | Additional attributes to pass to the alert. | `AlertAttributes \| undefined` | `undefined` |
17711778
| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` |
17721779
| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` |
17731780
| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |

core/src/components/alert/test/basic/e2e.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,21 @@ test(`alert:rtl: basic, radio`, async () => {
102102
test(`alert:rtl: basic, checkbox`, async () => {
103103
await testAlert(DIRECTORY, '#checkbox', true);
104104
});
105+
106+
// Attributes
107+
108+
test('alert: htmlAttributes', async () => {
109+
const page = await newE2EPage({ url: '/src/components/alert/test/basic?ionic:_testing=true' });
110+
111+
await page.click('#basic');
112+
await page.waitForSelector('#basic');
113+
114+
let alert = await page.find('ion-alert');
115+
116+
expect(alert).not.toBe(null);
117+
await alert.waitForVisible();
118+
119+
const attribute = await page.evaluate((el) => document.querySelector('ion-alert').getAttribute('data-testid'));
120+
121+
expect(attribute).toEqual('basic-alert');
122+
});

core/src/components/alert/test/basic/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@
5858
header: 'Alert',
5959
subHeader: 'Subtitle',
6060
message: 'This is an alert message.',
61-
buttons: ['OK']
61+
buttons: ['OK'],
62+
htmlAttributes: {
63+
'data-testid': 'basic-alert'
64+
}
6265
});
6366
}
6467

core/src/components/toast/readme.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface ToastOptions {
3737
position?: 'top' | 'bottom' | 'middle';
3838
translucent?: boolean;
3939
animated?: boolean;
40+
htmlAttributes?: ToastAttributes;
4041

4142
color?: Color;
4243
mode?: Mode;
@@ -48,6 +49,11 @@ interface ToastOptions {
4849
}
4950
```
5051

52+
### ToastAttributes
53+
```typescript
54+
interface ToastAttributes extends JSXBase.HTMLAttributes<HTMLElement> {}
55+
```
56+
5157
<!-- Auto Generated Below -->
5258

5359

@@ -405,6 +411,7 @@ export default defineComponent({
405411
| `duration` | `duration` | How many milliseconds to wait before hiding the toast. By default, it will show until `dismiss()` is called. | `number` | `0` |
406412
| `enterAnimation` | -- | Animation to use when the toast is presented. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
407413
| `header` | `header` | Header to be shown in the toast. | `string \| undefined` | `undefined` |
414+
| `htmlAttributes` | -- | Additional attributes to pass to the toast. | `ToastAttributes \| undefined` | `undefined` |
408415
| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `false` |
409416
| `leaveAnimation` | -- | Animation to use when the toast is dismissed. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
410417
| `message` | `message` | Message to be shown in the toast. | `IonicSafeString \| string \| undefined` | `undefined` |

core/src/components/toast/test/basic/e2e.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { newE2EPage } from '@stencil/core/testing';
12
import { testToast } from '../test.utils';
23

34
const DIRECTORY = 'basic';
@@ -101,3 +102,21 @@ test('toast:rtl: start end position', async () => {
101102
test('toast:rtl: html', async () => {
102103
await testToast(DIRECTORY, '#toast-html', true);
103104
});
105+
106+
// Attributes
107+
108+
test('toast: htmlAttributes', async () => {
109+
const page = await newE2EPage({ url: '/src/components/toast/test/basic?ionic:_testing=true' });
110+
111+
await page.click('#show-bottom-toast');
112+
await page.waitForSelector('#show-bottom-toast');
113+
114+
let toast = await page.find('ion-toast');
115+
116+
expect(toast).not.toBe(null);
117+
await toast.waitForVisible();
118+
119+
const attribute = await page.evaluate((el) => document.querySelector('ion-toast').getAttribute('data-testid'));
120+
121+
expect(attribute).toEqual('basic-toast');
122+
});

core/src/components/toast/test/basic/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
</ion-header>
2424

2525
<ion-content class="ion-padding" id="content">
26-
<ion-button expand="block" id="show-bottom-toast" onclick="openToast({ message: 'Hellooo', position: 'bottom', duration: 2000 })">
26+
<ion-button expand="block" id="show-bottom-toast" onclick="openToast({ message: 'Hellooo', position: 'bottom', duration: 2000, htmlAttributes: { 'data-testid': 'basic-toast' } })">
2727
Position Bottom
2828
</ion-button>
2929

@@ -211,4 +211,4 @@
211211
</style>
212212
</body>
213213

214-
</html>
214+
</html>

0 commit comments

Comments
 (0)