Feedback
Dialog
Usage#
import { WdsDialogComponent } from '@wds/angular/dialog';Examples#
Default#
show() and close() open and close the dialog; heading and description name and describe it. Buttons go to the footer slot. wds-close fires when the dialog starts closing, wds-after-close after the exit animation.
import { Component } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsDialogComponent } from '@wds/angular/dialog';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-dialog-default-demo',
imports: [...WDS_FIELD, WdsButtonComponent, WdsDialogComponent, WdsInputComponent],
template: `
<wds-button variant="outline" (click)="dialog.show()">Edit profile</wds-button>
<wds-dialog #dialog heading="Edit profile" description="Make changes to your profile here. Click save when you're done.">
<div class="grid gap-4">
<wds-field>
<wds-field-label>Name</wds-field-label>
<wds-input value="Pedro Duarte" />
</wds-field>
<wds-field>
<wds-field-label>Username</wds-field-label>
<wds-input value="@peduarte" />
</wds-field>
</div>
<wds-button slot="footer" variant="outline" (click)="dialog.close()">Cancel</wds-button>
<wds-button slot="footer" (click)="dialog.close()">Save changes</wds-button>
</wds-dialog>
`,
})
export class DialogDefaultDemo {}Scrollable content#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
import { Component } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsDialogComponent } from '@wds/angular/dialog';
@Component({
selector: 'app-dialog-scrollable-demo',
imports: [WdsButtonComponent, WdsDialogComponent],
template: `
<wds-button variant="outline" (click)="dialog.show()">Scrollable content</wds-button>
<wds-dialog #dialog heading="Terms of service" description="The content scrolls; the header and the footer stay in place.">
@for (paragraph of paragraphs; track $index) {
<p class="mt-0 mb-4 text-sm leading-relaxed">{{ paragraph }}</p>
}
<wds-button slot="footer" (click)="dialog.close()">Accept</wds-button>
</wds-dialog>
`,
})
export class DialogScrollableDemo {
protected readonly paragraphs = Array.from({ length: 10 }, () => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.');
}Width#
--wds-dialog-width sets the width (default 32rem); on a narrow screen the dialog keeps a margin from the edges.
import { Component } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsDialogComponent } from '@wds/angular/dialog';
@Component({
selector: 'app-dialog-width-demo',
imports: [WdsButtonComponent, WdsDialogComponent],
template: `
<div class="flex flex-wrap justify-center gap-2">
<wds-button variant="outline" (click)="narrow.show()">Narrow</wds-button>
<wds-button variant="outline" (click)="wide.show()">Wide</wds-button>
</div>
<wds-dialog #narrow class="[--wds-dialog-width:24rem]" heading="Narrow dialog" description="--wds-dialog-width: 24rem">
<wds-button slot="footer" (click)="narrow.close()">Close</wds-button>
</wds-dialog>
<wds-dialog #wide class="[--wds-dialog-width:48rem]" heading="Wide dialog" description="--wds-dialog-width: 48rem">
<wds-button slot="footer" (click)="wide.close()">Close</wds-button>
</wds-dialog>
`,
})
export class DialogWidthDemo {}Static backdrop#
static-backdrop a click on the backdrop does not close the dialog. dismissible set to false (a property) also blocks Escape; with hide-close the dialog then closes only from your own button.
import { Component } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsDialogComponent } from '@wds/angular/dialog';
@Component({
selector: 'app-dialog-static-backdrop-demo',
imports: [WdsButtonComponent, WdsDialogComponent],
template: `
<div class="flex flex-wrap justify-center gap-2">
<wds-button variant="outline" (click)="backdrop.show()">Static backdrop</wds-button>
<wds-button variant="outline" (click)="locked.show()">Not dismissible</wds-button>
</div>
<wds-dialog #backdrop static-backdrop heading="Static backdrop" description="A click on the backdrop does not close this dialog; Escape does.">
<wds-button slot="footer" (click)="backdrop.close()">Close</wds-button>
</wds-dialog>
<wds-dialog #locked [dismissible]="false" hide-close heading="Not dismissible" description="Neither Escape nor the backdrop closes this dialog. Use the button.">
<wds-button slot="footer" (click)="locked.close()">I understand</wds-button>
</wds-dialog>
`,
})
export class DialogStaticBackdropDemo {}Confirm#
WdsDialogService.open() builds a confirmation from the config alone: title, description, okText / cancelText (footer buttons), okDestructive and width. closed emits true after OK and undefined after any other way of closing. HTML and React have no service, so their tabs show the same dialog declared in the markup.
import { Component, inject, signal } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsDialogService } from '@wds/angular/dialog';
@Component({
selector: 'app-dialog-confirm-demo',
imports: [WdsButtonComponent],
template: `
<div class="flex flex-wrap items-center justify-center gap-3">
<wds-button variant="destructive" (click)="confirm()">Delete project</wds-button>
<span class="text-sm text-muted-foreground">Result: {{ result() }}</span>
</div>
`,
})
export class DialogConfirmDemo {
private readonly dialogs = inject(WdsDialogService);
protected readonly result = signal('none');
protected confirm(): void {
this.dialogs
.open<unknown, boolean>({
title: 'Delete project?',
description: 'This action cannot be undone. The project and its data will be deleted.',
okText: 'Delete',
cancelText: 'Cancel',
okDestructive: true,
width: '28rem',
})
// The OK button closes with true (or with the value returned by onOk).
.closed.subscribe((ok) => {
// undefined: closed with Cancel, Escape, the backdrop or the close button.
this.result.set(ok ? 'deleted' : 'cancelled');
});
}
}Service#
WdsDialogService.open(Component, config) renders a component in the dialog: data goes to the component through WDS_OVERLAY_DATA, and the component closes the dialog with WdsOverlayRef.close(value); the value comes out of closed. HTML and React have no service, so their tabs show the same dialog declared in the markup.
import { Component, inject, signal } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_OVERLAY_DATA, WdsDialogService, WdsOverlayRef } from '@wds/angular/dialog';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsInputComponent } from '@wds/angular/input';
/** Content of the dialog: any component. It closes the dialog itself through WdsOverlayRef. */
@Component({
selector: 'app-rename-dialog',
imports: [...WDS_FIELD, WdsButtonComponent, WdsInputComponent],
template: `
<wds-field>
<wds-field-label>Project name</wds-field-label>
<wds-input [value]="name()" (wdsInput)="name.set($event.value)" />
</wds-field>
<div class="mt-6 flex justify-end gap-2">
<wds-button variant="outline" (click)="ref.close()">Cancel</wds-button>
<wds-button [disabled]="!name().trim()" (click)="ref.close(name().trim())">Rename</wds-button>
</div>
`,
})
export class RenameDialog {
protected readonly ref = inject<WdsOverlayRef<RenameDialog, string>>(WdsOverlayRef);
// config.data of open()
protected readonly name = signal(inject<{ name: string }>(WDS_OVERLAY_DATA).name);
}
@Component({
selector: 'app-dialog-service-demo',
imports: [WdsButtonComponent],
template: `
<div class="flex flex-wrap items-center justify-center gap-3">
<wds-button variant="outline" (click)="rename()">Rename</wds-button>
<span class="text-sm text-muted-foreground">Project: {{ project() }}</span>
</div>
`,
})
export class DialogServiceDemo {
private readonly dialogs = inject(WdsDialogService);
protected readonly project = signal('My project');
protected rename(): void {
this.dialogs
.open<RenameDialog, string>(RenameDialog, {
title: 'Rename project',
description: 'The new name is visible to everyone on the team.',
data: { name: this.project() },
})
.closed.subscribe((name) => {
// undefined: closed with Cancel, Escape, the backdrop or the close button.
if (name) this.project.set(name);
});
}
}API References#
wds-dialog#
close().
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| open | boolean | false | show() and close(). |
| heading | string | '' | heading slot). |
| description | string | '' | description slot), linked as aria-describedby. |
| hide-close.hideClose | boolean | false | |
| static-backdrop.staticBackdrop | boolean | false | |
| dismissible | boolean | true | false blocks closing with Escape, the backdrop (and dragging in a drawer). |
| close-label.closeLabel | string | 'Zamknij' |
Events
| Event | Description |
|---|---|
| wds-closeCustomEvent<void> | close()), before the exit animation. |
| wds-after-closeCustomEvent<void> |
Methods
| Method | Description |
|---|---|
| show() | |
| close() |
Slots
| Slot | Description |
|---|---|
| (default) | autofocus gets focus when it opens (otherwise the first focusable element). |
| heading | heading attribute). |
| description | description attribute). |
| footer |
CSS parts (::part)
| Part | Description |
|---|---|
| dialog | <dialog>. |
| panel | |
| header | |
| title | |
| description | |
| close | |
| body | |
| footer |
CSS custom properties
| Property | Description |
|---|---|
| --wds-dialog-width |
Accessibility#
Screen reader#
<dialog> named by heading and described by description (aria-labelledby, aria-describedby). Always give it a heading; hide it visually if the design has no title.
close-label (default Zamknij, in Polish). With hide-close and dismissible set to false, keep a button in the dialog that closes it.
Keyboard support#
| Key | Function |
|---|---|
dismissible is false). |