Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .changeset/clean-cameras-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
"@cube-dev/ui-kit": minor
---

**Breaking Change:** AlertDialog API cancel button behavior changed

The `cancel` button in AlertDialog now rejects the promise instead of resolving with `'cancel'` status, aligning it with the dismiss (Escape key) behavior.

**Migration Guide:**

**Before:**
```typescript
alertDialogAPI.open({...})
.then((status) => {
if (status === 'cancel') {
// Handle cancel
} else if (status === 'confirm') {
// Handle confirm
}
})
```

**After:**
```typescript
alertDialogAPI.open({...})
.then((status) => {
if (status === 'confirm') {
// Handle confirm
} else if (status === 'secondary') {
// Handle secondary action
}
})
.catch(() => {
// Handle cancel or dismiss
})
```

**Note:** `AlertDialogResolveStatus` type no longer includes `'cancel'` - it now only contains `'confirm' | 'secondary'`.
22 changes: 21 additions & 1 deletion src/components/overlays/AlertDialog/AlertDialogApiProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,19 @@ export function AlertDialogApiProvider(props) {
* cancelToken: abortDialog.signal
* });
*
* openedDialog.then(() => console.log('closed'))
* openedDialog
* .then((status) => {
* // User confirmed or used secondary action
* if (status === 'confirm') {
* // Handle confirm
* } else if (status === 'secondary') {
* // Handle secondary action
* }
* })
* .catch(() => {
* // User cancelled or dismissed the dialog
* // Handle cancel/dismiss
* })
*
* return () => {
* abortDialog.abort();
Expand All @@ -141,6 +153,14 @@ export function AlertDialogApiProvider(props) {
*
* const onPress = useCallback(() => {
* alertDialogAPI.open({...})
* .then((status) => {
* if (status === 'confirm') {
* // Handle confirm
* }
* })
* .catch(() => {
* // Handle cancel/dismiss
* })
* }, [])
*
* return <Button onPress={onPress}>New issue</Button>
Expand Down
15 changes: 14 additions & 1 deletion src/components/overlays/AlertDialog/AlertDialogZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,20 @@ export function AlertDialogZone(props: DialogZoneProps) {
return {
confirm: mergeActionProps(actions.confirm, 'confirm'),
secondary: mergeActionProps(actions.secondary, 'secondary'),
cancel: mergeActionProps(actions.cancel, 'cancel'),
cancel:
typeof actions.cancel === 'undefined'
? undefined
: typeof actions.cancel === 'boolean'
? actions.cancel
? { onPress: () => reject(undefined) }
: false
: {
...(actions.cancel as CubeButtonProps),
onPress: (e) => {
(actions.cancel as CubeButtonProps).onPress?.(e);
reject(undefined);
},
},
};
})();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,70 @@ describe('useAlertDialogApi()', () => {
expect(onReject).toHaveBeenCalled();
await expect(dialogPromise).rejects.toEqual(undefined);
});

it('should reject when cancel button (boolean) is clicked', async () => {
const { getByRole } = renderWithRoot(
<TestComponent actions={{ cancel: true }} />,
);
const showDialogButton = getByRole('button', { name: 'Open Dialog' });

await userEvent.click(showDialogButton);

const cancelButton = getByRole('button', { name: 'Cancel' });

await userEvent.click(cancelButton);

expect(onReject).toHaveBeenCalled();
await expect(dialogPromise).rejects.toEqual(undefined);
});

it('should reject when cancel button (object) is clicked', async () => {
const { getByRole } = renderWithRoot(
<TestComponent actions={{ cancel: { label: 'No thanks' } }} />,
);
const showDialogButton = getByRole('button', { name: 'Open Dialog' });

await userEvent.click(showDialogButton);

const cancelButton = getByRole('button', { name: 'No thanks' });

await userEvent.click(cancelButton);

expect(onReject).toHaveBeenCalled();
await expect(dialogPromise).rejects.toEqual(undefined);
});

it('should resolve when confirm button is clicked', async () => {
const { getByRole } = renderWithRoot(
<TestComponent actions={{ confirm: true }} />,
);
const showDialogButton = getByRole('button', { name: 'Open Dialog' });

await userEvent.click(showDialogButton);

const confirmButton = getByRole('button', { name: 'Ok' });

await userEvent.click(confirmButton);

expect(onResolve).toHaveBeenCalledWith('confirm');
await expect(dialogPromise).resolves.toEqual('confirm');
});

it('should resolve when secondary button is clicked', async () => {
const { getByRole } = renderWithRoot(
<TestComponent
actions={{ confirm: true, secondary: { label: 'Later' } }}
/>,
);
const showDialogButton = getByRole('button', { name: 'Open Dialog' });

await userEvent.click(showDialogButton);

const secondaryButton = getByRole('button', { name: 'Later' });

await userEvent.click(secondaryButton);

expect(onResolve).toHaveBeenCalledWith('secondary');
await expect(dialogPromise).resolves.toEqual('secondary');
});
});
2 changes: 1 addition & 1 deletion src/components/overlays/AlertDialog/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface DialogProps
content: ReactNode | (({ resolve, reject }) => ReactNode);
}

export type AlertDialogResolveStatus = 'confirm' | 'cancel' | 'secondary';
export type AlertDialogResolveStatus = 'confirm' | 'secondary';

interface AlertDialogMeta {
id: number;
Expand Down
Loading