Skip to content

Commit

Permalink
feat(action-sheet): add data property to ActionSheetButton (#23744)
Browse files Browse the repository at this point in the history
resolves #23700

Co-authored-by: Liam DeBeasi <[email protected]>
  • Loading branch information
EinfachHans and liamdebeasi authored Aug 9, 2021
1 parent fbd32ff commit 30f8508
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 25 deletions.
2 changes: 1 addition & 1 deletion core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ion-accordion-group,event,ionChange,AccordionGroupChangeEventDetail<any>,true
ion-action-sheet,scoped
ion-action-sheet,prop,animated,boolean,true,false,false
ion-action-sheet,prop,backdropDismiss,boolean,true,false,false
ion-action-sheet,prop,buttons,(string | ActionSheetButton)[],[],false,false
ion-action-sheet,prop,buttons,(string | ActionSheetButton<any>)[],[],false,false
ion-action-sheet,prop,cssClass,string | string[] | undefined,undefined,false,false
ion-action-sheet,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-action-sheet,prop,header,string | undefined,undefined,false,false
Expand Down
3 changes: 2 additions & 1 deletion core/src/components/action-sheet/action-sheet-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ export interface ActionSheetOptions {
leaveAnimation?: AnimationBuilder;
}

export interface ActionSheetButton {
export interface ActionSheetButton<T = any> {
text?: string;
role?: 'cancel' | 'destructive' | 'selected' | string;
icon?: string;
cssClass?: string | string[];
id?: string;
handler?: () => boolean | void | Promise<boolean | void>;
data?: T;
}
4 changes: 2 additions & 2 deletions core/src/components/action-sheet/action-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
private async buttonClick(button: ActionSheetButton) {
const role = button.role;
if (isCancel(role)) {
return this.dismiss(undefined, role);
return this.dismiss(button.data, role);
}
const shouldDismiss = await this.callButtonHandler(button);
if (shouldDismiss) {
return this.dismiss(undefined, button.role);
return this.dismiss(button.data, button.role);
}
return Promise.resolve();
}
Expand Down
55 changes: 44 additions & 11 deletions core/src/components/action-sheet/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ An Action Sheet is a dialog that displays a set of options. It appears on top of

A button's `role` property can either be `destructive` or `cancel`. Buttons without a role property will have the default look for the platform. Buttons with the `cancel` role will always load as the bottom button, no matter where they are in the array. All other buttons will be displayed in the order they have been added to the `buttons` array. Note: We recommend that `destructive` buttons are always the first button in the array, making them the top button. Additionally, if the action sheet is dismissed by tapping the backdrop, then it will fire the handler from the button with the cancel role.

A button can also be passed data via the `data` property on `ActionSheetButton`. This will populate the `data` field in the return value of the `onDidDismiss` method.

## Customization

Action Sheet uses scoped encapsulation, which means it will automatically scope its CSS by appending each of the styles with an additional class at runtime. Overriding scoped selectors in CSS requires a [higher specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) selector.
Expand Down Expand Up @@ -39,12 +41,13 @@ Any of the defined [CSS Custom Properties](#css-custom-properties) can be used t
### ActionSheetButton

```typescript
interface ActionSheetButton {
interface ActionSheetButton<T = any> {
text?: string;
role?: 'cancel' | 'destructive' | 'selected' | string;
icon?: string;
cssClass?: string | string[];
handler?: () => boolean | void | Promise<boolean | void>;
data?: T;
}
```

Expand Down Expand Up @@ -97,18 +100,23 @@ export class ActionSheetExample {
role: 'destructive',
icon: 'trash',
id: 'delete-button',
data: {
type: 'delete'
},
handler: () => {
console.log('Delete clicked');
}
}, {
text: 'Share',
icon: 'share',
data: 10,
handler: () => {
console.log('Share clicked');
}
}, {
text: 'Play (open modal)',
icon: 'caret-forward-circle',
data: 'Data value',
handler: () => {
console.log('Play clicked');
}
Expand All @@ -129,8 +137,8 @@ export class ActionSheetExample {
});
await actionSheet.present();

const { role } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role', role);
const { role, data } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role and data', role, data);
}

}
Expand All @@ -155,18 +163,23 @@ async function presentActionSheet() {
role: 'destructive',
icon: 'trash',
id: 'delete-button',
data: {
type: 'delete'
},
handler: () => {
console.log('Delete clicked');
}
}, {
text: 'Share',
icon: 'share',
data: 10,
handler: () => {
console.log('Share clicked');
}
}, {
text: 'Play (open modal)',
icon: 'caret-forward-circle',
data: 'Data value',
handler: () => {
console.log('Play clicked');
}
Expand All @@ -187,8 +200,8 @@ async function presentActionSheet() {
document.body.appendChild(actionSheet);
await actionSheet.present();

const { role } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role', role);
const { role, data } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role and data', role, data);
}
```

Expand Down Expand Up @@ -270,18 +283,23 @@ export const ActionSheetExample: React.FC = () => {
role: 'destructive',
icon: trash,
id: 'delete-button',
data: {
type: 'delete'
},
handler: () => {
console.log('Delete clicked');
}
}, {
text: 'Share',
icon: share,
data: 10,
handler: () => {
console.log('Share clicked');
}
}, {
text: 'Play (open modal)',
icon: caretForwardCircle,
data: 'Data value',
handler: () => {
console.log('Play clicked');
}
Expand Down Expand Up @@ -328,18 +346,23 @@ export class ActionSheetExample {
role: 'destructive',
icon: 'trash',
id: 'delete-button',
data: {
type: 'delete'
},
handler: () => {
console.log('Delete clicked');
}
}, {
text: 'Share',
icon: 'share',
data: 10,
handler: () => {
console.log('Share clicked');
}
}, {
text: 'Play (open modal)',
icon: 'caret-forward-circle',
data: 'Data value',
handler: () => {
console.log('Play clicked');
}
Expand All @@ -360,8 +383,8 @@ export class ActionSheetExample {
});
await actionSheet.present();

const { role } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role', role);
const { role, data } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role and data', role, data);
}

render() {
Expand Down Expand Up @@ -400,21 +423,26 @@ export default defineComponent({
text: 'Delete',
role: 'destructive',
icon: trash,
id: 'delete-button',
id: 'delete-button',
data: {
type: 'delete'
},
handler: () => {
console.log('Delete clicked')
},
},
{
text: 'Share',
icon: share,
data: 10,
handler: () => {
console.log('Share clicked')
},
},
{
text: 'Play (open modal)',
icon: caretForwardCircle,
data: 'Data value',
handler: () => {
console.log('Play clicked')
},
Expand All @@ -438,8 +466,8 @@ export default defineComponent({
});
await actionSheet.present();
const { role } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role', role);
const { role, data } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role and data', role, data);
},
},
});
Expand Down Expand Up @@ -476,20 +504,25 @@ export default defineComponent({
text: 'Delete',
role: 'destructive',
icon: trash,
data: {
type: 'delete'
}
handler: () => {
console.log('Delete clicked')
},
},
{
text: 'Share',
icon: share,
data: 10,
handler: () => {
console.log('Share clicked')
},
},
{
text: 'Play (open modal)',
icon: caretForwardCircle,
data: 'Data value',
handler: () => {
console.log('Play clicked')
},
Expand Down Expand Up @@ -525,7 +558,7 @@ export default defineComponent({
| ----------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------- |
| `animated` | `animated` | If `true`, the action sheet will animate. | `boolean` | `true` |
| `backdropDismiss` | `backdrop-dismiss` | If `true`, the action sheet will be dismissed when the backdrop is clicked. | `boolean` | `true` |
| `buttons` | -- | An array of buttons for the action sheet. | `(string \| ActionSheetButton)[]` | `[]` |
| `buttons` | -- | An array of buttons for the action sheet. | `(string \| ActionSheetButton<any>)[]` | `[]` |
| `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` |
| `enterAnimation` | -- | Animation to use when the action sheet is presented. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
| `header` | `header` | Title for the action sheet. | `string \| undefined` | `undefined` |
Expand Down
32 changes: 32 additions & 0 deletions core/src/components/action-sheet/test/basic/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ const getActiveElementText = async (page) => {
return await page.evaluate(el => el && el.textContent, activeElement);
}

test('action-sheet: data', async () => {
const page = await newE2EPage({ url: '/src/components/action-sheet/test/basic?ionic:_testing=true' });
const didDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');

await page.click('#buttonData');
await page.waitForSelector('#buttonData');

const actionSheet = await page.find('ion-action-sheet');
await actionSheet.waitForVisible();

const button = await actionSheet.find('button#option');
await button.click();

expect(didDismiss).toHaveReceivedEventDetail({ data: { type: '1' } });
});

test('action-sheet: data cancel', async () => {
const page = await newE2EPage({ url: '/src/components/action-sheet/test/basic?ionic:_testing=true' });
const didDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');

await page.click('#buttonData');
await page.waitForSelector('#buttonData');

const actionSheet = await page.find('ion-action-sheet');
await actionSheet.waitForVisible();

const button = await actionSheet.find('button.action-sheet-cancel');
await button.click();

expect(didDismiss).toHaveReceivedEventDetail({ data: { type: 'cancel' }, role: 'cancel' });
})

test('action-sheet: focus trap', async () => {
const page = await newE2EPage({ url: '/src/components/action-sheet/test/basic?ionic:_testing=true' });

Expand Down
23 changes: 23 additions & 0 deletions core/src/components/action-sheet/test/basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<ion-button expand="block" id="scrollableOptions" onclick="presentScroll()">Scrollable Options</ion-button>
<ion-button expand="block" id="scrollWithoutCancel" onclick="presentScrollNoCancel()">Scroll Without Cancel</ion-button>
<ion-button expand="block" id="customBackdrop" onclick="presentWithCssClass('custom-backdrop')">Custom Backdrop Opacity</ion-button>
<ion-button expand="block" id="buttonData" onclick="presentWithButtonData()">Button data</ion-button>
</ion-content>

</ion-app>
Expand Down Expand Up @@ -427,6 +428,28 @@
]
});
}

async function presentWithButtonData() {
await openActionSheet({
buttons: [
{
text: 'Option 1',
id: 'option',
data: {
type: '1'
}
},
{
text: 'Cancel',
role: 'cancel',
id: 'cancel',
data: {
type: 'cancel'
}
}
]
});
}
</script>
</body>

Expand Down
9 changes: 7 additions & 2 deletions core/src/components/action-sheet/usage/angular.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,23 @@ export class ActionSheetExample {
role: 'destructive',
icon: 'trash',
id: 'delete-button',
data: {
type: 'delete'
},
handler: () => {
console.log('Delete clicked');
}
}, {
text: 'Share',
icon: 'share',
data: 10,
handler: () => {
console.log('Share clicked');
}
}, {
text: 'Play (open modal)',
icon: 'caret-forward-circle',
data: 'Data value',
handler: () => {
console.log('Play clicked');
}
Expand All @@ -52,8 +57,8 @@ export class ActionSheetExample {
});
await actionSheet.present();

const { role } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role', role);
const { role, data } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role and data', role, data);
}

}
Expand Down
Loading

0 comments on commit 30f8508

Please sign in to comment.