Skip to content

Commit b9065f2

Browse files
committed
feat(dialogs): allow to quickly show a dialog
Signed-off-by: Ferdinand Thiessen <[email protected]>
1 parent 12ccd02 commit b9065f2

File tree

4 files changed

+621
-1
lines changed

4 files changed

+621
-1
lines changed

lib/dialogs.spec.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { cleanup, findByRole, fireEvent, getByLabelText, getByRole } from '@testing-library/vue'
7+
import { afterEach, expect, test } from 'vitest'
8+
import { showConfirmation } from './dialogs.ts'
9+
10+
afterEach(cleanup)
11+
12+
test('Show confirmation dialog', async () => {
13+
const promise = showConfirmation({
14+
name: 'Dialog name',
15+
text: 'Dialog text',
16+
})
17+
18+
const dialog = await findByRole(document.documentElement, 'dialog')
19+
expect(dialog).toBeInstanceOf(HTMLElement)
20+
21+
expect(getByLabelText(document.documentElement, 'Dialog name')).toBe(dialog)
22+
expect(dialog.textContent).toContain('Dialog text')
23+
24+
const close = getByRole(dialog, 'button', { name: 'Close' })
25+
expect(close).toBeInstanceOf(HTMLElement)
26+
const confirm = getByRole(dialog, 'button', { name: 'Confirm' })
27+
expect(confirm).toBeInstanceOf(HTMLElement)
28+
await fireEvent(confirm, new MouseEvent('click', { bubbles: true }))
29+
expect(promise).resolves.toBe(true)
30+
})
31+
32+
test('show confirmation dialog with reject', async () => {
33+
const promise = showConfirmation({
34+
name: 'Dialog name',
35+
text: 'Dialog text',
36+
labelConfirm: 'My confirm',
37+
labelReject: 'My reject',
38+
})
39+
40+
const dialog = await findByRole(document.documentElement, 'dialog')
41+
const confirm = getByRole(dialog, 'button', { name: 'My confirm' })
42+
const reject = getByRole(dialog, 'button', { name: 'My reject' })
43+
expect(confirm).toBeInstanceOf(HTMLElement)
44+
expect(reject).toBeInstanceOf(HTMLElement)
45+
await fireEvent(reject, new MouseEvent('click', { bubbles: true }))
46+
expect(promise).resolves.toBe(false)
47+
})
48+
49+
test('show confirmation dialog and close', async () => {
50+
const promise = showConfirmation({
51+
name: 'Dialog name',
52+
text: 'Dialog text',
53+
})
54+
55+
const dialog = await findByRole(document.documentElement, 'dialog')
56+
const close = getByRole(dialog, 'button', { name: 'Close' })
57+
expect(close).toBeInstanceOf(HTMLElement)
58+
await fireEvent(close, new MouseEvent('click', { bubbles: true }))
59+
expect(promise).rejects.toThrowError('Dialog closed')
60+
})

lib/dialogs.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { IDialogButton, IDialogSeverity } from './components/types.ts'
77

88
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
99
import GenericDialog from './components/GenericDialog.vue'
10+
import { t } from './utils/l10n.ts'
1011
import { logger } from './utils/logger.ts'
1112

1213
export type * from './components/types.ts'
@@ -172,3 +173,57 @@ export class DialogBuilder {
172173
export function getDialogBuilder(name: string) {
173174
return new DialogBuilder(name)
174175
}
176+
177+
export interface ConfirmationDialogOptions {
178+
/** The name of the dialog (heading) */
179+
name: string
180+
/** The text of the dialog */
181+
text: string
182+
/** The text of the confirmation button */
183+
labelConfirm?: string
184+
/** The text of the reject button */
185+
labelReject?: string
186+
/** The severity */
187+
severity?: IDialogSeverity
188+
}
189+
190+
/**
191+
* Open a new confirmation dialog.
192+
* The dialog either resolves to true when the confirm-button was pressed,
193+
* or to false if the reject-button was pressed.
194+
*
195+
* @param options - Dialog options see `ConfirmationDialogOptions`
196+
*/
197+
export async function showConfirmation(options: ConfirmationDialogOptions): Promise<boolean> {
198+
options = {
199+
labelConfirm: t('Confirm'),
200+
...options,
201+
}
202+
203+
const { promise, resolve } = Promise.withResolvers<boolean>()
204+
const buttons: IDialogButton[] = [{
205+
label: options.labelConfirm!,
206+
variant: 'primary',
207+
callback() {
208+
resolve(true)
209+
},
210+
}]
211+
212+
if (options.labelReject) {
213+
buttons.unshift({
214+
label: options.labelReject,
215+
callback() {
216+
resolve(false)
217+
},
218+
})
219+
}
220+
221+
const dialog = new Dialog(
222+
options.name,
223+
options.text,
224+
buttons,
225+
options.severity,
226+
)
227+
await dialog.show()
228+
return promise
229+
}

0 commit comments

Comments
 (0)