Skip to content

Commit 1d99685

Browse files
authored
refactor!: update dialog to use native popover (#9807)
* use native popover for dialog overlay * use dialog as content root * update content root related tests * export parts * update snapshots * add host snapshot * move role to dialog * move aria-label to dialog * use ready instead of firstUpdated * reflect dialog-specific state attributes * move header title logic to dialog * remove obsolete setting CRUD dialog role * update integration tests * simplify header title aria-label logic * JSDoc fixes
1 parent 469ac0e commit 1d99685

23 files changed

+368
-221
lines changed

packages/crud/src/vaadin-crud-dialog.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,6 @@ class CrudDialog extends DialogBaseMixin(OverlayClassMixin(ThemePropertyMixin(Po
167167
`;
168168
}
169169

170-
/** @protected */
171-
firstUpdated(props) {
172-
super.firstUpdated(props);
173-
174-
this.role = 'dialog';
175-
}
176-
177170
/** @private **/
178171
__cancel() {
179172
this.dispatchEvent(new CustomEvent('cancel'));

packages/dialog/src/vaadin-dialog-base-mixin.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export declare class DialogBaseMixinClass {
3333
modeless: boolean;
3434

3535
/**
36-
* The `role` attribute value to be set on the overlay. Defaults to "dialog".
36+
* The `role` attribute value to be set on the dialog. Defaults to "dialog".
3737
*
3838
* @attr {string} overlay-role
3939
*/

packages/dialog/src/vaadin-dialog-base-mixin.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,12 @@ export const DialogBaseMixin = (superClass) =>
7777
},
7878

7979
/**
80-
* The `role` attribute value to be set on the overlay. Defaults to "dialog".
80+
* The `role` attribute value to be set on the dialog. Defaults to "dialog".
8181
*
8282
* @attr {string} overlay-role
8383
*/
8484
overlayRole: {
8585
type: String,
86-
value: 'dialog',
8786
},
8887
};
8988
}
@@ -103,6 +102,19 @@ export const DialogBaseMixin = (superClass) =>
103102
overlay.addEventListener('vaadin-overlay-closed', this.__handleOverlayClosed.bind(this));
104103

105104
this._overlayElement = overlay;
105+
106+
if (!this.hasAttribute('role')) {
107+
this.role = 'dialog';
108+
}
109+
}
110+
111+
/** @protected */
112+
updated(props) {
113+
super.updated(props);
114+
115+
if (props.has('overlayRole')) {
116+
this.role = this.overlayRole || 'dialog';
117+
}
106118
}
107119

108120
/** @private */

packages/dialog/src/vaadin-dialog-overlay-mixin.js

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
66
import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
7+
import { setOverlayStateAttribute } from '@vaadin/overlay/src/vaadin-overlay-utils.js';
78

89
/**
910
* @polymerMixin
@@ -43,6 +44,24 @@ export const DialogOverlayMixin = (superClass) =>
4344
];
4445
}
4546

47+
/**
48+
* Override method from OverlayFocusMixin to use owner as content root
49+
* @protected
50+
* @override
51+
*/
52+
get _contentRoot() {
53+
return this.owner;
54+
}
55+
56+
/**
57+
* Override method from OverlayFocusMixin to use owner as modal root
58+
* @protected
59+
* @override
60+
*/
61+
get _modalRoot() {
62+
return this.owner;
63+
}
64+
4665
/** @protected */
4766
ready() {
4867
super.ready();
@@ -59,6 +78,22 @@ export const DialogOverlayMixin = (superClass) =>
5978
});
6079
}
6180

81+
/**
82+
* @protected
83+
* @override
84+
*/
85+
_attachOverlay() {
86+
this.showPopover();
87+
}
88+
89+
/**
90+
* @protected
91+
* @override
92+
*/
93+
_detachOverlay() {
94+
this.hidePopover();
95+
}
96+
6297
/** @private */
6398
__createContainer(slot) {
6499
const container = document.createElement('div');
@@ -99,8 +134,8 @@ export const DialogOverlayMixin = (superClass) =>
99134
this._oldOpenedFooterHeader = opened;
100135

101136
// Set attributes here to update styles before detecting content overflow
102-
this.toggleAttribute('has-header', !!headerRenderer);
103-
this.toggleAttribute('has-footer', !!footerRenderer);
137+
setOverlayStateAttribute(this, 'has-header', !!headerRenderer);
138+
setOverlayStateAttribute(this, 'has-footer', !!footerRenderer);
104139

105140
if (headerRendererChanged) {
106141
if (headerRenderer) {
@@ -134,7 +169,7 @@ export const DialogOverlayMixin = (superClass) =>
134169

135170
/** @private */
136171
_headerTitleChanged(headerTitle, opened) {
137-
this.toggleAttribute('has-title', !!headerTitle);
172+
setOverlayStateAttribute(this, 'has-title', !!headerTitle);
138173

139174
if (opened && (headerTitle || this._oldHeaderTitle)) {
140175
this.requestContentUpdate();
@@ -150,7 +185,7 @@ export const DialogOverlayMixin = (superClass) =>
150185
this.headerTitleElement.setAttribute('slot', 'title');
151186
this.headerTitleElement.classList.add('draggable');
152187
}
153-
this.appendChild(this.headerTitleElement);
188+
this.owner.appendChild(this.headerTitleElement);
154189
this.headerTitleElement.textContent = this.headerTitle;
155190
} else if (this.headerTitleElement) {
156191
this.headerTitleElement.remove();
@@ -167,7 +202,7 @@ export const DialogOverlayMixin = (superClass) =>
167202
if (this.headerContainer) {
168203
// If a new renderer has been set, make sure to reattach the container
169204
if (!this.headerContainer.parentElement) {
170-
this.appendChild(this.headerContainer);
205+
this.owner.appendChild(this.headerContainer);
171206
}
172207

173208
if (this.headerRenderer) {
@@ -179,7 +214,7 @@ export const DialogOverlayMixin = (superClass) =>
179214
if (this.footerContainer) {
180215
// If a new renderer has been set, make sure to reattach the container
181216
if (!this.footerContainer.parentElement) {
182-
this.appendChild(this.footerContainer);
217+
this.owner.appendChild(this.footerContainer);
183218
}
184219

185220
if (this.footerRenderer) {
@@ -227,9 +262,9 @@ export const DialogOverlayMixin = (superClass) =>
227262

228263
const value = overflow.trim();
229264
if (value.length > 0 && this.getAttribute('overflow') !== value) {
230-
this.setAttribute('overflow', value);
265+
setOverlayStateAttribute(this, 'overflow', value);
231266
} else if (value.length === 0 && this.hasAttribute('overflow')) {
232-
this.removeAttribute('overflow');
267+
setOverlayStateAttribute(this, 'overflow', null);
233268
}
234269
}
235270
};

packages/dialog/src/vaadin-dialog.d.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,13 @@ export type DialogEventMap = DialogCustomEventMap & HTMLElementEventMap;
9191
*
9292
* ### Styling
9393
*
94-
* `<vaadin-dialog>` uses `<vaadin-dialog-overlay>` internal
95-
* themable component as the actual visible dialog overlay.
96-
*
97-
* See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation.
98-
* for `<vaadin-dialog-overlay>` parts.
99-
*
100-
* In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
94+
* The following shadow DOM parts are available for styling:
10195
*
10296
* Part name | Description
10397
* -----------------|-------------------------------------------
98+
* `backdrop` | Backdrop of the overlay
99+
* `overlay` | The overlay container
100+
* `content` | The overlay content
104101
* `header` | Element wrapping title and header content
105102
* `header-content` | Element wrapping the header content slot
106103
* `title` | Element wrapping the title slot
@@ -115,9 +112,6 @@ export type DialogEventMap = DialogCustomEventMap & HTMLElementEventMap;
115112
* `has-footer` | Set when the element has footer renderer
116113
* `overflow` | Set to `top`, `bottom`, none or both
117114
*
118-
* Note: the `theme` attribute value set on `<vaadin-dialog>` is
119-
* propagated to the internal `<vaadin-dialog-overlay>` component.
120-
*
121115
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
122116
*
123117
* @fires {CustomEvent} resize - Fired when the dialog resize is finished.

packages/dialog/src/vaadin-dialog.js

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,13 @@ export { DialogOverlay } from './vaadin-dialog-overlay.js';
4848
*
4949
* ### Styling
5050
*
51-
* `<vaadin-dialog>` uses `<vaadin-dialog-overlay>` internal
52-
* themable component as the actual visible dialog overlay.
53-
*
54-
* See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation.
55-
* for `<vaadin-dialog-overlay>` parts.
56-
*
57-
* In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
51+
* The following shadow DOM parts are available for styling:
5852
*
5953
* Part name | Description
6054
* -----------------|-------------------------------------------
55+
* `backdrop` | Backdrop of the overlay
56+
* `overlay` | The overlay container
57+
* `content` | The overlay content
6158
* `header` | Element wrapping title and header content
6259
* `header-content` | Element wrapping the header content slot
6360
* `title` | Element wrapping the title slot
@@ -72,9 +69,6 @@ export { DialogOverlay } from './vaadin-dialog-overlay.js';
7269
* `has-footer` | Set when the element has footer renderer
7370
* `overflow` | Set to `top`, `bottom`, none or both
7471
*
75-
* Note: the `theme` attribute value set on `<vaadin-dialog>` is
76-
* propagated to the internal `<vaadin-dialog-overlay>` component.
77-
*
7872
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
7973
*
8074
* @fires {CustomEvent} resize - Fired when the dialog resize is finished.
@@ -108,32 +102,25 @@ class Dialog extends DialogSizeMixin(
108102

109103
static get styles() {
110104
return css`
111-
:host {
105+
:host,
106+
[hidden] {
112107
display: none !important;
113108
}
114-
`;
115-
}
116109
117-
static get properties() {
118-
return {
119-
/**
120-
* Set the `aria-label` attribute for assistive technologies like
121-
* screen readers. An empty string value for this property (the
122-
* default) means that the `aria-label` attribute is not present.
123-
*/
124-
ariaLabel: {
125-
type: String,
126-
value: '',
127-
},
128-
};
110+
:host([opened]),
111+
:host([opening]),
112+
:host([closing]) {
113+
display: contents !important;
114+
}
115+
`;
129116
}
130117

131118
/** @protected */
132119
render() {
133120
return html`
134121
<vaadin-dialog-overlay
135122
id="overlay"
136-
role="${this.overlayRole}"
123+
popover="manual"
137124
.owner="${this}"
138125
.opened="${this.opened}"
139126
.headerTitle="${this.headerTitle}"
@@ -144,15 +131,29 @@ class Dialog extends DialogSizeMixin(
144131
@mousedown="${this._bringOverlayToFront}"
145132
@touchstart="${this._bringOverlayToFront}"
146133
theme="${ifDefined(this._theme)}"
147-
aria-label="${ifDefined(this.ariaLabel || this.headerTitle)}"
148134
.modeless="${this.modeless}"
149135
.withBackdrop="${!this.modeless}"
150136
?resizable="${this.resizable}"
151137
restore-focus-on-close
152138
focus-trap
153-
></vaadin-dialog-overlay>
139+
exportparts="backdrop, overlay, header, title, header-content, content, footer"
140+
>
141+
<slot name="title" slot="title"></slot>
142+
<slot name="header-content" slot="header-content"></slot>
143+
<slot name="footer" slot="footer"></slot>
144+
<slot></slot>
145+
</vaadin-dialog-overlay>
154146
`;
155147
}
148+
149+
/** @protected */
150+
updated(props) {
151+
super.updated(props);
152+
153+
if (props.has('headerTitle')) {
154+
this.ariaLabel = this.headerTitle;
155+
}
156+
}
156157
}
157158

158159
defineCustomElement(Dialog);

0 commit comments

Comments
 (0)