feat(renderer): add support for danger styling in MessageBox

Signed-off-by: Dias Tursynbayev <original.justmello1337@gmail.com>
This commit is contained in:
Dias Tursynbayev 2026-03-03 16:50:38 +01:00 committed by Simon Rey
parent a165724486
commit f125bc1ef5
3 changed files with 172 additions and 3 deletions

View file

@ -55,7 +55,7 @@ export interface MessageBoxOptions {
*/
buttons?: ButtonsType[];
/**
* The (optional) type, one of 'none' | 'info' | 'error' | 'question' | 'warning'.
* The (optional) type, one of 'none' | 'info' | 'error' | 'question' | 'warning' | 'danger'.
*/
type?: string;
/**

View file

@ -194,4 +194,169 @@ describe('MessageBox', () => {
expect(title2).toBeInTheDocument();
await fireEvent.click(ok2);
});
test('Expect danger type to keep default button first in layout', async () => {
const idRequest = 700;
const messageBoxOptions: MessageBoxOptions = {
id: idRequest,
title: 'Danger',
message: 'Danger Message',
type: 'danger',
buttons: ['Delete', 'Cancel'],
};
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
callback(messageBoxOptions);
}
});
render(MessageBox, {});
const allButtons = screen.getAllByRole('button');
expect(allButtons[1]).toHaveTextContent('Cancel');
expect(allButtons[2]).toHaveTextContent('Delete');
await fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
expect(window.sendShowMessageBoxOnSelect).toBeCalledWith(idRequest, 1, undefined);
});
test('Expect explicit default and cancel ids to be honored', async () => {
const idRequest = 701;
const messageBoxOptions: MessageBoxOptions = {
id: idRequest,
title: 'Explicit ids',
message: 'Message',
type: 'warning',
buttons: ['Ignore', 'Abort'],
cancelId: 1,
defaultId: 1,
};
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
callback(messageBoxOptions);
}
});
render(MessageBox, {});
const allButtons = screen.getAllByRole('button');
expect(allButtons[1]).toHaveTextContent('Cancel');
expect(allButtons[2]).toHaveTextContent('Ignore');
await userEvent.keyboard('{Escape}');
expect(window.sendShowMessageBoxOnSelect).toBeCalledWith(idRequest, 1);
});
test('Expect cancel at index 0 to move default to index 1', async () => {
const idRequest = 702;
const messageBoxOptions: MessageBoxOptions = {
id: idRequest,
title: 'Cancel first',
message: 'Message',
buttons: ['Cancel', 'Proceed'],
};
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
callback(messageBoxOptions);
}
});
render(MessageBox, {});
const allButtons = screen.getAllByRole('button');
expect(allButtons[1]).toHaveTextContent('Cancel');
expect(allButtons[2]).toHaveTextContent('Proceed');
await fireEvent.click(screen.getByRole('button', { name: 'Proceed' }));
expect(window.sendShowMessageBoxOnSelect).toBeCalledWith(idRequest, 1, undefined);
});
test('Expect danger default button to use danger styling', async () => {
const messageBoxOptions: MessageBoxOptions = {
id: 703,
title: 'Danger default',
message: 'Message',
type: 'danger',
buttons: ['Cancel', 'Delete'],
defaultId: 1,
};
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
callback(messageBoxOptions);
}
});
render(MessageBox, {});
const deleteButton = screen.getByRole('button', { name: 'Delete' });
expect(deleteButton.className).toContain('pd-button-danger-bg');
});
test.each([
{ id: 704, title: 'Warn', message: 'Message', type: 'warning' as const },
{ id: 705, title: 'Info', message: 'Message', type: 'info' as const },
{ id: 706, title: 'Question', message: 'Message', type: 'question' as const },
])('Expect $type icon to render', async messageBoxOptions => {
let eventCallback: ((options: MessageBoxOptions) => void) | undefined;
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
eventCallback = callback;
}
});
render(MessageBox, {});
await vi.waitFor(() => eventCallback !== undefined);
eventCallback?.(messageBoxOptions);
expect(await screen.findByText(messageBoxOptions.title)).toBeInTheDocument();
if (messageBoxOptions.type === 'info') {
expect(document.querySelectorAll('.place-content-center').length).toBeGreaterThanOrEqual(2);
}
});
test('Expect dropdown button to call selection with dropdown index', async () => {
const idRequest = 707;
const messageBoxOptions: MessageBoxOptions = {
id: idRequest,
title: 'Actions',
message: 'Message',
buttons: [{ type: 'dropdownButton', heading: 'More', buttons: ['A', 'B'] }],
};
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
callback(messageBoxOptions);
}
});
render(MessageBox, {});
await fireEvent.click(screen.getByRole('button', { name: 'More' }));
await fireEvent.click(await screen.findByText('B'));
expect(window.sendShowMessageBoxOnSelect).toBeCalledWith(idRequest, 0, 1);
});
test('Expect icon button to call selection', async () => {
const idRequest = 708;
const messageBoxOptions: MessageBoxOptions = {
id: idRequest,
title: 'Actions',
message: 'Message',
buttons: [{ type: 'iconButton', label: 'Run', icon: 'fas fa-play' }],
};
receiveFunctionMock.mockImplementation((message: string, callback: (options: MessageBoxOptions) => void) => {
if (message === 'showMessageBox:open') {
callback(messageBoxOptions);
}
});
render(MessageBox, {});
await vi.waitFor(() => expect(screen.getByRole('button', { name: 'Run' })).toBeInTheDocument());
await fireEvent.click(screen.getByRole('button', { name: 'Run' }));
expect(window.sendShowMessageBoxOnSelect).toBeCalledWith(idRequest, 0, undefined);
});
});

View file

@ -24,6 +24,8 @@ let footerMarkdownDescription: string | undefined = $state();
let display = $state(false);
const DANGER_TYPE = 'danger';
const showMessageBoxCallback = (messageBoxParameter: unknown): void => {
const options: MessageBoxOptions | undefined = messageBoxParameter as MessageBoxOptions;
currentId = options?.id || 0;
@ -107,7 +109,9 @@ async function onClose(): Promise<void> {
function getButtonType(b: boolean): ButtonType {
// eslint-disable-next-line sonarjs/no-selector-parameter
if (b) {
if (b && type === DANGER_TYPE) {
return 'danger';
} else if (b) {
return 'primary';
} else {
return 'secondary';
@ -119,7 +123,7 @@ function getButtonType(b: boolean): ButtonType {
<Dialog title={title} onclose={onClose}>
{#snippet icon()}
{#if type === 'error'}
{#if type === 'error' || type === DANGER_TYPE}
<Icon class="h-4 w-4 text-[var(--pd-state-error)]" icon={faCircleExclamation} />
{:else if type === 'warning'}
<Icon class="h-4 w-4 text-[var(--pd-state-warning)]" icon={faTriangleExclamation} />