@api-client/ui 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/build/src/elements/highlight/MarkdownStyles.d.ts.map +1 -1
  2. package/build/src/elements/highlight/MarkdownStyles.js +7 -11
  3. package/build/src/elements/highlight/MarkdownStyles.js.map +1 -1
  4. package/build/src/md/dialog/internals/ConfirmDialog.d.ts +39 -0
  5. package/build/src/md/dialog/internals/ConfirmDialog.d.ts.map +1 -0
  6. package/build/src/md/dialog/internals/ConfirmDialog.js +75 -0
  7. package/build/src/md/dialog/internals/ConfirmDialog.js.map +1 -0
  8. package/build/src/md/dialog/internals/Dialog.d.ts +6 -0
  9. package/build/src/md/dialog/internals/Dialog.d.ts.map +1 -1
  10. package/build/src/md/dialog/internals/Dialog.js +43 -5
  11. package/build/src/md/dialog/internals/Dialog.js.map +1 -1
  12. package/build/src/md/dialog/internals/Dialog.styles.d.ts.map +1 -1
  13. package/build/src/md/dialog/internals/Dialog.styles.js +39 -1
  14. package/build/src/md/dialog/internals/Dialog.styles.js.map +1 -1
  15. package/build/src/md/dialog/ui-confirm-dialog.d.ts +48 -0
  16. package/build/src/md/dialog/ui-confirm-dialog.d.ts.map +1 -0
  17. package/build/src/md/dialog/ui-confirm-dialog.js +64 -0
  18. package/build/src/md/dialog/ui-confirm-dialog.js.map +1 -0
  19. package/demo/md/dialog/confirm-dialog.html +49 -0
  20. package/demo/md/dialog/confirm-dialog.ts +121 -0
  21. package/demo/md/dialog/dialog.ts +76 -1
  22. package/demo/md/index.html +2 -0
  23. package/eslint.config.js +6 -6
  24. package/package.json +7 -2
  25. package/scripts/release.js +66 -0
  26. package/src/elements/highlight/MarkdownStyles.ts +7 -11
  27. package/src/md/dialog/README.md +212 -0
  28. package/src/md/dialog/internals/ConfirmDialog.ts +79 -0
  29. package/src/md/dialog/internals/Dialog.styles.ts +39 -1
  30. package/src/md/dialog/internals/Dialog.ts +17 -4
  31. package/src/md/dialog/ui-confirm-dialog.ts +52 -0
  32. package/test/README.md +2 -0
  33. package/test/md/dialog/UiConfirmDialog.test.ts +131 -0
  34. package/test/md/dialog/UiDialog.test.ts +42 -0
@@ -0,0 +1,212 @@
1
+ # UI Confirm Dialog
2
+
3
+ A simple Material Design 3 styled | Property | Type | Default | Description |
4
+ |----------|------|---------|-------------|
5
+ | `open` | `boolean` | `false` | Controls dialog visibility |
6
+ | `modal` | `boolean` | `true` | Whether dialog is modal (set by default) |
7
+ | `confirmLabel` | `string` | `'Confirm'` | Text for the confirm button |
8
+ | `dismissLabel` | `string` | `'Cancel'` | Text for the dismiss button |
9
+ | `destructive` | `boolean` | `false` | Styles confirm button with error colors for destructive actions |m dialog component for confirming user actions.
10
+
11
+ ## Features
12
+
13
+ - **Material Design 3 Styling**: Follows MD3 design tokens and patterns
14
+ - **Customizable Button Labels**: Configure confirm and dismiss button text
15
+ - **Slot-based Content**: Flexible content structure with title and body slots
16
+ - **Modal by Default**: Designed for confirmation workflows
17
+ - **Accessible**: Supports keyboard navigation and screen readers
18
+ - **Built-in Event Handling**: Dispatches close events with detailed reason information
19
+
20
+ ## Usage
21
+
22
+ ### Basic Usage
23
+
24
+ ```html
25
+ <ui-confirm-dialog .open="${showDialog}" @close="${handleClose}">
26
+ <span slot="title">Confirm Action</span>
27
+ <p>Are you sure you want to proceed with this action?</p>
28
+ </ui-confirm-dialog>
29
+ ```
30
+
31
+ ### Custom Button Labels
32
+
33
+ ```html
34
+ <ui-confirm-dialog
35
+ confirmLabel="Delete"
36
+ dismissLabel="Keep"
37
+ destructive
38
+ .open="${showDialog}"
39
+ @close="${handleClose}"
40
+ >
41
+ <span slot="title">Delete Item</span>
42
+ <p>Are you sure you want to delete this item? This action cannot be undone.</p>
43
+ </ui-confirm-dialog>
44
+ ```
45
+
46
+ ### Event Handling
47
+
48
+ ```javascript
49
+ function handleClose(e) {
50
+ const { cancelled, value } = e.detail;
51
+ if (!cancelled) {
52
+ // User confirmed the action
53
+ performAction();
54
+ } else {
55
+ // User dismissed the dialog
56
+ console.log('Action cancelled');
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Properties
62
+
63
+ | Property | Type | Default | Description |
64
+ |----------|------|---------|-------------|
65
+ | `open` | `boolean` | `false` | Controls dialog visibility |
66
+ | `modal` | `boolean` | `true` | Whether dialog is modal (set by default) |
67
+ | `confirmLabel` | `string` | `'Confirm'` | Text for the confirm button |
68
+ | `dismissLabel` | `string` | `'Cancel'` | Text for the dismiss button |
69
+
70
+ ## Slots
71
+
72
+ | Slot | Description |
73
+ |------|-------------|
74
+ | `title` | Dialog title content |
75
+ | (default) | Main body content |
76
+
77
+ ## Events
78
+
79
+ | Event | Detail Type | Description |
80
+ |-------|-------------|-------------|
81
+ | `close` | `UiDialogClosingReason` | Fired when dialog is closed |
82
+
83
+ ### UiDialogClosingReason
84
+
85
+ ```typescript
86
+ interface UiDialogClosingReason {
87
+ cancelled: boolean; // true if dismissed, false if confirmed
88
+ value?: unknown; // optional value associated with the action
89
+ }
90
+ ```
91
+
92
+ ## Styling
93
+
94
+ The component uses Material Design 3 design tokens and can be styled using CSS custom properties:
95
+
96
+ ```css
97
+ ui-confirm-dialog {
98
+ --ui-dialog-max-width: 400px;
99
+ --ui-dialog-max-height: 300px;
100
+ }
101
+ ```
102
+
103
+ ## Design Patterns
104
+
105
+ ### Confirmation Workflows
106
+
107
+ Use this dialog when you need users to explicitly confirm an action:
108
+
109
+ - Deleting items
110
+ - Saving changes
111
+ - Leaving unsaved work
112
+ - Performing destructive actions
113
+
114
+ ### Button Styling
115
+
116
+ The component follows Material Design patterns:
117
+
118
+ - **Dismiss button**: Text style (lower emphasis)
119
+ - **Confirm button**: Filled style (higher emphasis)
120
+
121
+ ### Content Guidelines
122
+
123
+ - **Title**: Keep concise, use action-oriented language
124
+ - **Body**: Explain the consequence of the action
125
+ - **Button Labels**: Use specific, clear language
126
+
127
+ ## Examples
128
+
129
+ ### Delete Confirmation
130
+
131
+ ```html
132
+ <ui-confirm-dialog
133
+ confirmLabel="Delete"
134
+ dismissLabel="Keep"
135
+ destructive
136
+ .open="${showDelete}"
137
+ >
138
+ <span slot="title">Delete Item</span>
139
+ <p>Are you sure you want to delete this item?</p>
140
+ <p><strong>This action cannot be undone.</strong></p>
141
+ </ui-confirm-dialog>
142
+ ```
143
+
144
+ ### Save Changes
145
+
146
+ ```html
147
+ <ui-confirm-dialog
148
+ confirmLabel="Save & Exit"
149
+ dismissLabel="Continue Editing"
150
+ .open="${showSave}"
151
+ >
152
+ <span slot="title">Save Changes</span>
153
+ <p>You have unsaved changes in your document.</p>
154
+ <p>Would you like to save your changes before exiting?</p>
155
+ </ui-confirm-dialog>
156
+ ```
157
+
158
+ ### Logout Confirmation
159
+
160
+ ```html
161
+ <ui-confirm-dialog
162
+ confirmLabel="Logout"
163
+ dismissLabel="Stay Logged In"
164
+ .open="${showLogout}"
165
+ >
166
+ <span slot="title">Logout</span>
167
+ <p>Are you sure you want to logout?</p>
168
+ </ui-confirm-dialog>
169
+ ```
170
+
171
+ ### Destructive Actions
172
+
173
+ Use the `destructive` attribute for actions that are permanent or could cause data loss:
174
+
175
+ - Deleting items
176
+ - Removing users
177
+ - Clearing data
178
+ - Resetting settings
179
+
180
+ The destructive styling uses Material Design error colors to provide clear visual feedback that the action is potentially harmful.
181
+
182
+ ```html
183
+ <ui-confirm-dialog
184
+ confirmLabel="Delete Account"
185
+ dismissLabel="Cancel"
186
+ destructive
187
+ .open="${showDeleteAccount}"
188
+ >
189
+ <span slot="title">Delete Account</span>
190
+ <p>This will permanently delete your account and all associated data.</p>
191
+ <p><strong>This action cannot be undone.</strong></p>
192
+ </ui-confirm-dialog>
193
+ ```
194
+
195
+ ## Accessibility
196
+
197
+ The component includes:
198
+
199
+ - Proper ARIA attributes
200
+ - Keyboard navigation support (ESC to dismiss)
201
+ - Focus management
202
+ - Screen reader compatibility
203
+
204
+ ## Integration
205
+
206
+ To use in your project:
207
+
208
+ ```typescript
209
+ import '../path/to/ui-confirm-dialog.js';
210
+ ```
211
+
212
+ The component is available as `<ui-confirm-dialog>` in your HTML templates.
@@ -0,0 +1,79 @@
1
+ import { TemplateResult, html } from 'lit'
2
+ import { ClassInfo, classMap } from 'lit/directives/class-map.js'
3
+ import UiDialog from './Dialog.js'
4
+
5
+ import '../../button/ui-button.js'
6
+
7
+ /**
8
+ * A simple Material Design 3 styled confirm dialog for confirming user actions.
9
+ *
10
+ * This dialog provides a clean way to ask users to confirm or dismiss an action.
11
+ * It supports customizable button labels and content through slots.
12
+ *
13
+ * **Usage Example:**
14
+ * ```html
15
+ * <ui-confirm-dialog modal .open="${this.showConfirm}" @close="${this.handleConfirmClose}">
16
+ * <span slot="title">Delete Item</span>
17
+ * <p>Are you sure you want to delete this item? This action cannot be undone.</p>
18
+ * </ui-confirm-dialog>
19
+ * ```
20
+ *
21
+ * **Event Handling:**
22
+ * Listen for the `close` event to handle user interaction:
23
+ * ```javascript
24
+ * dialog.addEventListener('close', (e) => {
25
+ * const { cancelled } = e.detail;
26
+ * if (!cancelled) {
27
+ * // User confirmed the action
28
+ * performAction();
29
+ * }
30
+ * });
31
+ * ```
32
+ *
33
+ * @slot title - The dialog title content
34
+ * @slot - The main body content of the dialog
35
+ * @fires close - Dispatched when the dialog is closed with closing reason details
36
+ */
37
+ export default class ConfirmDialog extends UiDialog {
38
+ constructor() {
39
+ super()
40
+ // Set modal by default for confirm dialogs
41
+ this.modal = true
42
+ this.confirmLabel = 'Confirm'
43
+ this.dismissLabel = 'Cancel'
44
+ }
45
+
46
+ protected override renderContent(): TemplateResult[] {
47
+ return [this.renderTitle(), this.renderBody(), this.renderButtons()]
48
+ }
49
+
50
+ protected override renderButtons(): TemplateResult {
51
+ const classes: ClassInfo = {
52
+ 'buttons': true,
53
+ 'with-buttons': true,
54
+ }
55
+
56
+ return html`
57
+ <div class="${classMap(classes)}" part="buttons">
58
+ <ui-button
59
+ color="text"
60
+ value="dismiss"
61
+ class="internal-button"
62
+ @click="${this.handleDismiss}"
63
+ part="dismiss-button"
64
+ >
65
+ ${this.dismissLabel}
66
+ </ui-button>
67
+ <ui-button
68
+ color="filled"
69
+ value="confirm"
70
+ class="internal-button ${this.destructive ? 'destructive' : ''}"
71
+ @click="${this.handleConfirm}"
72
+ part="confirm-button"
73
+ >
74
+ ${this.confirmLabel}
75
+ </ui-button>
76
+ </div>
77
+ `
78
+ }
79
+ }
@@ -28,6 +28,20 @@ export default css`
28
28
  flex-direction: column;
29
29
  }
30
30
 
31
+ /* Positioning for non-modal dialogs */
32
+ dialog.non-modal {
33
+ position: fixed;
34
+ top: 50%;
35
+ left: 50%;
36
+ transform: translate(-50%, -50%);
37
+ margin: 0;
38
+ }
39
+
40
+ dialog.non-modal:open {
41
+ /* Override the animation transform for non-modal dialogs to account for centering */
42
+ animation: 250ms cubic-bezier(0.2, 0, 0, 1) show-non-modal-dialog;
43
+ }
44
+
31
45
  dialog:open::backdrop {
32
46
  animation: 250ms cubic-bezier(0.2, 0, 0, 1) show-backdrop;
33
47
  }
@@ -35,7 +49,6 @@ export default css`
35
49
  .container {
36
50
  display: flex;
37
51
  flex-direction: column;
38
- overflow: hidden;
39
52
  flex: 1;
40
53
  }
41
54
 
@@ -57,6 +70,11 @@ export default css`
57
70
  height: 24px;
58
71
  }
59
72
 
73
+ .icon.destructive ::slotted(*) {
74
+ color: var(--md-sys-color-error);
75
+ fill: var(--md-sys-color-error);
76
+ }
77
+
60
78
  .title {
61
79
  display: none;
62
80
  color: var(--md-sys-color-on-surface);
@@ -114,6 +132,15 @@ export default css`
114
132
  }
115
133
  }
116
134
 
135
+ @keyframes show-non-modal-dialog {
136
+ from {
137
+ transform: translate(-50%, -50%) translateY(-110%) scaleY(0);
138
+ }
139
+ to {
140
+ transform: translate(-50%, -50%) translateY(0%) scaleY(1);
141
+ }
142
+ }
143
+
117
144
  @keyframes show-backdrop {
118
145
  from {
119
146
  opacity: 0;
@@ -122,4 +149,15 @@ export default css`
122
149
  opacity: 1;
123
150
  }
124
151
  }
152
+
153
+ /* Destructive button styling for dangerous actions */
154
+ .internal-button.destructive {
155
+ --_background-color: var(--md-sys-color-error);
156
+ --_color: var(--md-sys-color-on-error);
157
+
158
+ /* Override ripple colors for better interaction feedback */
159
+ --md-ripple-hover-state-layer-color: var(--md-sys-color-on-error);
160
+ --md-ripple-focus-state-layer-color: var(--md-sys-color-on-error);
161
+ --md-ripple-pressed-state-layer-color: var(--md-sys-color-on-error);
162
+ }
125
163
  `
@@ -123,6 +123,13 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
123
123
  */
124
124
  @property({ type: String }) accessor confirmLabel: string | undefined
125
125
 
126
+ /**
127
+ * When true, styles the confirm button with error colors to indicate
128
+ * a destructive action (e.g., delete, remove, etc.).
129
+ * @attribute
130
+ */
131
+ @property({ type: Boolean }) accessor destructive = false
132
+
126
133
  /**
127
134
  * Part of the imperative access to the element.
128
135
  * When set, it wraps the content in a `<form>` element.
@@ -300,6 +307,9 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
300
307
 
301
308
  override handleKeyDown(e: KeyboardEvent): void {
302
309
  super.handleKeyDown(e)
310
+ if (e.defaultPrevented) {
311
+ return
312
+ }
303
313
  if (e.key === 'Escape') {
304
314
  this.handleInteraction('dismiss')
305
315
  }
@@ -389,9 +399,10 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
389
399
  }
390
400
 
391
401
  override render(): TemplateResult {
392
- const { useForm } = this
402
+ const { useForm, modal } = this
403
+ const dialogClass = modal ? 'modal' : 'non-modal'
393
404
  return html`
394
- <dialog @close="${this.handleDialogClose}" part="dialog">
405
+ <dialog @close="${this.handleDialogClose}" part="dialog" class="${dialogClass}">
395
406
  <div class="container">${useForm ? this.#renderFormContent() : this.renderContent()}</div>
396
407
  </dialog>
397
408
  `
@@ -413,6 +424,7 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
413
424
  const classes: ClassInfo = {
414
425
  'icon': true,
415
426
  'with-icon': this.hasIcon,
427
+ 'destructive': this.destructive,
416
428
  }
417
429
  return html`
418
430
  <div class="${classMap(classes)}" part="icon">
@@ -473,17 +485,18 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
473
485
  }
474
486
 
475
487
  #renderConfirmButton(): TemplateResult | typeof nothing {
476
- const { confirmLabel, confirmValue = 'confirm', useForm } = this
488
+ const { confirmLabel, confirmValue = 'confirm', useForm, destructive } = this
477
489
  if (!confirmLabel) {
478
490
  return nothing
479
491
  }
480
492
  const type = useForm ? 'submit' : 'button'
493
+ const buttonClass = destructive ? 'internal-button destructive' : 'internal-button'
481
494
  return html`
482
495
  <ui-button
483
496
  color="text"
484
497
  type="${type}"
485
498
  value="${confirmValue}"
486
- class="internal-button"
499
+ class="${buttonClass}"
487
500
  @click="${this.handleConfirm}"
488
501
  part="positive-action"
489
502
  >${confirmLabel}</ui-button
@@ -0,0 +1,52 @@
1
+ import type { CSSResultOrNative } from 'lit'
2
+ import { customElement } from 'lit/decorators.js'
3
+ import Element from './internals/ConfirmDialog.js'
4
+ import dialogStyles from './internals/Dialog.styles.js'
5
+
6
+ /**
7
+ * A simple Material Design 3 styled confirm dialog component.
8
+ *
9
+ * This dialog is designed specifically for confirmation workflows where users
10
+ * need to confirm or dismiss an action. It provides a clean, accessible interface
11
+ * with customizable button labels and content.
12
+ *
13
+ * **Features:**
14
+ * - Material Design 3 styling
15
+ * - Customizable confirm and dismiss button labels
16
+ * - Modal by default
17
+ * - Accessible keyboard navigation
18
+ * - Slot-based content structure
19
+ *
20
+ * **Usage:**
21
+ * ```html
22
+ * <ui-confirm-dialog .open="${showDialog}" @close="${handleClose}">
23
+ * <span slot="title">Confirm Action</span>
24
+ * <p>Are you sure you want to proceed with this action?</p>
25
+ * </ui-confirm-dialog>
26
+ * ```
27
+ *
28
+ * **Customizing Button Labels:**
29
+ * ```html
30
+ * <ui-confirm-dialog
31
+ * confirmLabel="Delete"
32
+ * dismissLabel="Keep"
33
+ * .open="${showDialog}"
34
+ * >
35
+ * <span slot="title">Delete Item</span>
36
+ * <p>This action cannot be undone.</p>
37
+ * </ui-confirm-dialog>
38
+ * ```
39
+ *
40
+ * @slot title - The dialog title
41
+ * @slot - The main content body
42
+ */
43
+ @customElement('ui-confirm-dialog')
44
+ export class UiConfirmDialogElement extends Element {
45
+ static override styles: CSSResultOrNative[] = [dialogStyles]
46
+ }
47
+
48
+ declare global {
49
+ interface HTMLElementTagNameMap {
50
+ 'ui-confirm-dialog': UiConfirmDialogElement
51
+ }
52
+ }
package/test/README.md CHANGED
@@ -49,6 +49,8 @@ npm run test:watch
49
49
  npm run test:coverage
50
50
  ```
51
51
 
52
+ > ⚠️ **Note**: This project uses Web Test Runner, not Jest. Test filtering options like `--testPathPattern` from Jest are not available. To run specific tests, you can temporarily modify the `files` pattern in `web-test-runner.config.js` or use pattern matching if supported by your Web Test Runner version. For most cases, running all tests is recommended as the test suite is optimized for speed.
53
+
52
54
  ## Global Test Utilities
53
55
 
54
56
  The setup provides global utilities accessible via `window.testUtils`:
@@ -0,0 +1,131 @@
1
+ import { fixture, html, assert } from '@open-wc/testing'
2
+ import '../../../src/md/dialog/ui-confirm-dialog.js'
3
+ import type UiConfirmDialog from '../../../src/md/dialog/internals/ConfirmDialog.js'
4
+
5
+ describe('UiConfirmDialog', () => {
6
+ async function basicFixture(): Promise<UiConfirmDialog> {
7
+ return fixture(html`<ui-confirm-dialog></ui-confirm-dialog>`)
8
+ }
9
+
10
+ async function customLabelsFixture(): Promise<UiConfirmDialog> {
11
+ return fixture(html`<ui-confirm-dialog confirmLabel="Delete" dismissLabel="Keep"></ui-confirm-dialog>`)
12
+ }
13
+
14
+ async function withContentFixture(): Promise<UiConfirmDialog> {
15
+ return fixture(html`
16
+ <ui-confirm-dialog>
17
+ <span slot="title">Confirm Action</span>
18
+ <p>Are you sure you want to proceed?</p>
19
+ </ui-confirm-dialog>
20
+ `)
21
+ }
22
+
23
+ async function destructiveFixture(): Promise<UiConfirmDialog> {
24
+ return fixture(html`<ui-confirm-dialog destructive></ui-confirm-dialog>`)
25
+ }
26
+
27
+ it('renders with default labels', async () => {
28
+ const element = await basicFixture()
29
+ assert.equal(element.confirmLabel, 'Confirm')
30
+ assert.equal(element.dismissLabel, 'Cancel')
31
+ })
32
+
33
+ it('renders with custom labels', async () => {
34
+ const element = await customLabelsFixture()
35
+ assert.equal(element.confirmLabel, 'Delete')
36
+ assert.equal(element.dismissLabel, 'Keep')
37
+ })
38
+
39
+ it('is modal by default', async () => {
40
+ const element = await basicFixture()
41
+ assert.isTrue(element.modal)
42
+ })
43
+
44
+ it('renders buttons with correct values', async () => {
45
+ const element = await basicFixture()
46
+ await element.updateComplete
47
+
48
+ const dismissButton = element.shadowRoot!.querySelector('ui-button[value="dismiss"]')
49
+ const confirmButton = element.shadowRoot!.querySelector('ui-button[value="confirm"]')
50
+
51
+ assert.ok(dismissButton, 'has dismiss button')
52
+ assert.ok(confirmButton, 'has confirm button')
53
+ assert.equal(dismissButton!.textContent!.trim(), 'Cancel')
54
+ assert.equal(confirmButton!.textContent!.trim(), 'Confirm')
55
+ })
56
+
57
+ it('renders title slot', async () => {
58
+ const element = await withContentFixture()
59
+ await element.updateComplete
60
+
61
+ const titleSlot = element.shadowRoot!.querySelector('slot[name="title"]')
62
+ assert.ok(titleSlot, 'has title slot')
63
+ })
64
+
65
+ it('renders body slot', async () => {
66
+ const element = await withContentFixture()
67
+ await element.updateComplete
68
+
69
+ const bodySlot = element.shadowRoot!.querySelector('slot:not([name])')
70
+ assert.ok(bodySlot, 'has body slot')
71
+ })
72
+
73
+ it('uses filled button for confirm action', async () => {
74
+ const element = await basicFixture()
75
+ await element.updateComplete
76
+
77
+ const confirmButton = element.shadowRoot!.querySelector('ui-button[value="confirm"]')
78
+ assert.equal(confirmButton!.getAttribute('color'), 'filled')
79
+ })
80
+
81
+ it('uses text button for dismiss action', async () => {
82
+ const element = await basicFixture()
83
+ await element.updateComplete
84
+
85
+ const dismissButton = element.shadowRoot!.querySelector('ui-button[value="dismiss"]')
86
+ assert.equal(dismissButton!.getAttribute('color'), 'text')
87
+ })
88
+
89
+ it('renders as destructive dialog', async () => {
90
+ const element = await destructiveFixture()
91
+ await element.updateComplete
92
+
93
+ const confirmButton = element.shadowRoot!.querySelector('ui-button[value="confirm"]')
94
+ assert.ok(confirmButton?.classList.contains('destructive'), 'confirm button has destructive class')
95
+ })
96
+
97
+ it('renders confirm button with destructive styling', async () => {
98
+ const element = await destructiveFixture()
99
+ await element.updateComplete
100
+
101
+ const confirmButton = element.shadowRoot!.querySelector('ui-button[value="confirm"]')
102
+ assert.equal(confirmButton!.getAttribute('color'), 'filled')
103
+ assert.ok(confirmButton!.classList.contains('destructive'), 'has destructive class for styling')
104
+ })
105
+
106
+ it('is not destructive by default', async () => {
107
+ const element = await basicFixture()
108
+ assert.isFalse(element.destructive)
109
+ })
110
+
111
+ it('can be set to destructive', async () => {
112
+ const element = await destructiveFixture()
113
+ assert.isTrue(element.destructive)
114
+ })
115
+
116
+ it('applies destructive class to confirm button when destructive is true', async () => {
117
+ const element = await destructiveFixture()
118
+ await element.updateComplete
119
+
120
+ const confirmButton = element.shadowRoot!.querySelector('ui-button[value="confirm"]')
121
+ assert.isTrue(confirmButton!.classList.contains('destructive'))
122
+ })
123
+
124
+ it('does not apply destructive class when destructive is false', async () => {
125
+ const element = await basicFixture()
126
+ await element.updateComplete
127
+
128
+ const confirmButton = element.shadowRoot!.querySelector('ui-button[value="confirm"]')
129
+ assert.isFalse(confirmButton!.classList.contains('destructive'))
130
+ })
131
+ })
@@ -145,6 +145,26 @@ describe('md', () => {
145
145
  await element.updateComplete
146
146
  assert.isTrue(spy.calledOnce)
147
147
  })
148
+
149
+ it('applies modal class to dialog when modal is true', async () => {
150
+ const element = await basicFixture()
151
+ element.modal = true
152
+ await element.updateComplete
153
+
154
+ const dialog = element.shadowRoot!.querySelector('dialog') as HTMLDialogElement
155
+ assert.ok(dialog, 'has the dialog element')
156
+ assert.isTrue(dialog.classList.contains('modal'), 'has modal class')
157
+ })
158
+
159
+ it('applies non-modal class to dialog when modal is false', async () => {
160
+ const element = await basicFixture()
161
+ element.modal = false
162
+ await element.updateComplete
163
+
164
+ const dialog = element.shadowRoot!.querySelector('dialog') as HTMLDialogElement
165
+ assert.ok(dialog, 'has the dialog element')
166
+ assert.isTrue(dialog.classList.contains('non-modal'), 'has non-modal class')
167
+ })
148
168
  })
149
169
 
150
170
  describe('icon', () => {
@@ -229,6 +249,28 @@ describe('md', () => {
229
249
  const container = element.shadowRoot!.querySelector('.buttons') as HTMLElement
230
250
  assert.isTrue(container.classList.contains('with-buttons'), 'has the with-buttons class')
231
251
  })
252
+
253
+ it('applies destructive class to confirm button when destructive is true', async () => {
254
+ const element = await basicFixture()
255
+ element.confirmLabel = 'Delete'
256
+ element.destructive = true
257
+ await element.updateComplete
258
+
259
+ const button = element.shadowRoot!.querySelector('.internal-button[value="confirm"]') as UiButton
260
+ assert.ok(button, 'has the confirm button')
261
+ assert.isTrue(button.classList.contains('destructive'), 'has destructive class')
262
+ })
263
+
264
+ it('does not apply destructive class when destructive is false', async () => {
265
+ const element = await basicFixture()
266
+ element.confirmLabel = 'Confirm'
267
+ element.destructive = false
268
+ await element.updateComplete
269
+
270
+ const button = element.shadowRoot!.querySelector('.internal-button[value="confirm"]') as UiButton
271
+ assert.ok(button, 'has the confirm button')
272
+ assert.isFalse(button.classList.contains('destructive'), 'does not have destructive class')
273
+ })
232
274
  })
233
275
 
234
276
  describe('form handling', () => {