Skip to content

Commit 90df172

Browse files
authored
refactor!: update rich text editor color popup to use native popover (#9777)
1 parent b48f412 commit 90df172

File tree

4 files changed

+101
-46
lines changed

4 files changed

+101
-46
lines changed

packages/rich-text-editor/src/vaadin-rich-text-editor-mixin.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ export const RichTextEditorMixin = (superClass) =>
176176
'#ffffff', '#facccc', '#ffebcc', '#ffffcc', '#cce8cc', '#cce0f5', '#ebd6ff',
177177
'#bbbbbb', '#f06666', '#ffc266', '#ffff66', '#66b966', '#66a3e0', '#c285ff',
178178
'#888888', '#a10000', '#b26b00', '#b2b200', '#006100', '#0047b2', '#6b24b2',
179-
'#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466'
179+
'#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466',
180180
];
181181
},
182182
},
@@ -416,8 +416,8 @@ export const RichTextEditorMixin = (superClass) =>
416416
// Flush pending htmlValue only once the editor is fully initialized
417417
this.__flushPendingHtmlValue();
418418

419-
this.$.backgroundPopup.target = this.shadowRoot.querySelector('#btn-background');
420-
this.$.colorPopup.target = this.shadowRoot.querySelector('#btn-color');
419+
this.querySelector('[slot="color-popup"]').target = this.shadowRoot.querySelector('#btn-color');
420+
this.querySelector('[slot="background-popup"]').target = this.shadowRoot.querySelector('#btn-background');
421421

422422
// Set up tooltip to show when hovering or focusing toolbar buttons
423423
this._tooltip = document.createElement('vaadin-tooltip');

packages/rich-text-editor/src/vaadin-rich-text-editor-popup.js

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
3333
static get styles() {
3434
return css`
3535
:host {
36-
display: none;
36+
display: none !important;
37+
}
38+
39+
:host([opened]),
40+
:host([opening]),
41+
:host([closing]) {
42+
display: contents !important;
3743
}
3844
`;
3945
}
@@ -46,16 +52,13 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
4652

4753
opened: {
4854
type: Boolean,
55+
reflectToAttribute: true,
4956
notify: true,
5057
},
5158

5259
colors: {
5360
type: Array,
5461
},
55-
56-
renderer: {
57-
type: Object,
58-
},
5962
};
6063
}
6164

@@ -67,16 +70,20 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
6770
render() {
6871
return html`
6972
<vaadin-rich-text-editor-popup-overlay
70-
.renderer="${this.renderer}"
73+
popover="manual"
74+
.owner="${this}"
7175
.opened="${this.opened}"
7276
.positionTarget="${this.target}"
7377
no-vertical-overlap
7478
horizontal-align="start"
7579
vertical-align="top"
7680
focus-trap
81+
exportparts="overlay, content"
7782
@opened-changed="${this._onOpenedChanged}"
7883
@vaadin-overlay-escape-press="${this._onOverlayEscapePress}"
79-
></vaadin-rich-text-editor-popup-overlay>
84+
>
85+
<slot></slot>
86+
</vaadin-rich-text-editor-popup-overlay>
8087
`;
8188
}
8289

@@ -98,19 +105,17 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
98105

99106
/** @private */
100107
__colorsChanged(colors) {
101-
this.renderer = (root) => {
102-
render(
103-
html`
104-
${colors.map(
105-
(color) => html`
106-
<button data-color="${color}" style="background: ${color}" @click="${this._onColorClick}"></button>
107-
`,
108-
)}
109-
`,
110-
root,
111-
{ host: this },
112-
);
113-
};
108+
render(
109+
html`
110+
${colors.map(
111+
(color) => html`
112+
<button data-color="${color}" style="background: ${color}" @click="${this._onColorClick}"></button>
113+
`,
114+
)}
115+
`,
116+
this,
117+
{ host: this },
118+
);
114119
}
115120

116121
/** @private */
@@ -152,10 +157,46 @@ class RichTextEditorPopupOverlay extends PositionMixin(
152157
return html`
153158
<div id="backdrop" part="backdrop" hidden></div>
154159
<div part="overlay" id="overlay">
155-
<div part="content" id="content"><slot></slot></div>
160+
<div part="content" id="content">
161+
<slot></slot>
162+
</div>
156163
</div>
157164
`;
158165
}
166+
167+
/**
168+
* @protected
169+
* @override
170+
*/
171+
_attachOverlay() {
172+
this.showPopover();
173+
}
174+
175+
/**
176+
* @protected
177+
* @override
178+
*/
179+
_detachOverlay() {
180+
this.hidePopover();
181+
}
182+
183+
/**
184+
* Override method from OverlayFocusMixin to use owner as content root
185+
* @protected
186+
* @override
187+
*/
188+
get _contentRoot() {
189+
return this.owner;
190+
}
191+
192+
/**
193+
* Override method from OverlayFocusMixin to use owner as modal root
194+
* @protected
195+
* @override
196+
*/
197+
get _modalRoot() {
198+
return this.owner;
199+
}
159200
}
160201

161202
defineCustomElement(RichTextEditorPopupOverlay);

packages/rich-text-editor/src/vaadin-rich-text-editor.js

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -350,25 +350,13 @@ class RichTextEditor extends RichTextEditorMixin(
350350
<div class="announcer" aria-live="polite"></div>
351351
</div>
352352
353-
<vaadin-rich-text-editor-popup
354-
id="colorPopup"
355-
.colors="${this.colorOptions}"
356-
.opened="${this._colorEditing}"
357-
@color-selected="${this.__onColorSelected}"
358-
@opened-changed="${this.__onColorEditingChanged}"
359-
></vaadin-rich-text-editor-popup>
360-
361-
<vaadin-rich-text-editor-popup
362-
id="backgroundPopup"
363-
.colors="${this.colorOptions}"
364-
.opened="${this._backgroundEditing}"
365-
@color-selected="${this.__onBackgroundSelected}"
366-
@opened-changed="${this.__onBackgroundEditingChanged}"
367-
></vaadin-rich-text-editor-popup>
368-
369353
<slot name="tooltip"></slot>
370354
371355
<slot name="link-dialog"></slot>
356+
357+
<slot name="color-popup"></slot>
358+
359+
<slot name="background-popup"></slot>
372360
`;
373361
}
374362

@@ -380,11 +368,11 @@ class RichTextEditor extends RichTextEditorMixin(
380368
update(changedProperties) {
381369
super.update(changedProperties);
382370

383-
this.__renderLinkDialog();
371+
this.__renderSlottedOverlays();
384372
}
385373

386374
/** @private */
387-
__renderLinkDialog() {
375+
__renderSlottedOverlays() {
388376
render(
389377
html`
390378
<vaadin-confirm-dialog
@@ -409,6 +397,22 @@ class RichTextEditor extends RichTextEditorMixin(
409397
@value-changed="${this._onLinkUrlChanged}"
410398
></vaadin-text-field>
411399
</vaadin-confirm-dialog>
400+
401+
<vaadin-rich-text-editor-popup
402+
slot="color-popup"
403+
.colors="${this.colorOptions}"
404+
.opened="${this._colorEditing}"
405+
@color-selected="${this.__onColorSelected}"
406+
@opened-changed="${this.__onColorEditingChanged}"
407+
></vaadin-rich-text-editor-popup>
408+
409+
<vaadin-rich-text-editor-popup
410+
slot="background-popup"
411+
.colors="${this.colorOptions}"
412+
.opened="${this._backgroundEditing}"
413+
@color-selected="${this.__onBackgroundSelected}"
414+
@opened-changed="${this.__onBackgroundEditingChanged}"
415+
></vaadin-rich-text-editor-popup>
412416
`,
413417
this,
414418
{ host: this },

packages/rich-text-editor/test/toolbar.test.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,14 @@ describe('toolbar controls', () => {
223223
let popup, overlay;
224224

225225
beforeEach(() => {
226-
popup = rte.shadowRoot.querySelector(`#${style}Popup`);
226+
popup = rte.querySelector(`[slot="${style}-popup"]`);
227227
overlay = popup.shadowRoot.querySelector('vaadin-rich-text-editor-popup-overlay');
228228
});
229229

230230
it(`should apply ${style} when clicking the "toolbar-button-${style}" and selecting value`, async () => {
231231
getButton(style).click();
232232
await oneEvent(overlay, 'vaadin-overlay-open');
233-
const button = overlay.querySelectorAll('button')[1];
233+
const button = popup.querySelectorAll('button')[1];
234234
button.click();
235235
editor.insertText(0, 'Foo', 'user');
236236
expect(editor.getFormat(0, 3)[style]).to.equal(button.dataset.color);
@@ -247,7 +247,7 @@ describe('toolbar controls', () => {
247247

248248
// Default color (black) or background (white)
249249
const value = style === 'color' ? '#000000' : '#ffffff';
250-
const button = overlay.querySelector(`[data-color="${value}"]`);
250+
const button = popup.querySelector(`[data-color="${value}"]`);
251251
button.click();
252252
expect(editor.getFormat(0, 3)[style]).to.be.not.ok;
253253
});
@@ -280,10 +280,20 @@ describe('toolbar controls', () => {
280280
getButton(style).click();
281281
await oneEvent(overlay, 'vaadin-overlay-open');
282282

283-
const popup = document.querySelector('vaadin-rich-text-editor-popup-overlay');
284283
const button = popup.querySelectorAll('button')[1];
285284
expect(button.dataset.color).to.equal(rte.colorOptions[1]);
286285
});
286+
287+
it('should export all overlay parts for styling', () => {
288+
const parts = [...overlay.shadowRoot.querySelectorAll('[part]')]
289+
.map((el) => el.getAttribute('part'))
290+
.filter((part) => part !== 'backdrop');
291+
const exportParts = overlay.getAttribute('exportparts').split(', ');
292+
293+
parts.forEach((part) => {
294+
expect(exportParts).to.include(part);
295+
});
296+
});
287297
});
288298
});
289299
});

0 commit comments

Comments
 (0)