Skip to content

Commit 8f83dac

Browse files
authored
refactor!: update rich-text-editor to render tooltip in light DOM (#9757)
1 parent f82f873 commit 8f83dac

File tree

4 files changed

+167
-38
lines changed

4 files changed

+167
-38
lines changed

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,31 @@ export const RichTextEditorMixin = (superClass) =>
424424
this.$.linkUrl.focus();
425425
});
426426
});
427+
428+
// Set up tooltip to show when hovering or focusing toolbar buttons
429+
this._tooltip = document.createElement('vaadin-tooltip');
430+
this._tooltip.slot = 'tooltip';
431+
// Create dummy aria target, as toolbar buttons already have aria-label, and also cannot be linked with the
432+
// tooltip being in the light DOM
433+
this._tooltip.ariaTarget = document.createElement('div');
434+
this.append(this._tooltip);
435+
436+
const buttons = this.shadowRoot.querySelectorAll('[part~="toolbar-button"]');
437+
buttons.forEach((button) => {
438+
button.addEventListener('mouseenter', this.__showTooltip.bind(this));
439+
button.addEventListener('focusin', this.__showTooltip.bind(this));
440+
});
441+
}
442+
443+
/** @private */
444+
__showTooltip(event) {
445+
const target = event.target;
446+
this._tooltip.target = target;
447+
this._tooltip.text = target.ariaLabel;
448+
this._tooltip._stateController.open({
449+
focus: event.type === 'focusin',
450+
hover: event.type === 'mouseenter',
451+
});
427452
}
428453

429454
/** @private */
@@ -799,10 +824,7 @@ export const RichTextEditorMixin = (superClass) =>
799824
timeOut.after(timeout),
800825
() => {
801826
const formatting = Array.from(this.shadowRoot.querySelectorAll('[part="toolbar"] .ql-active'))
802-
.map((button) => {
803-
const tooltip = this.shadowRoot.querySelector(`[for="${button.id}"]`);
804-
return tooltip.text;
805-
})
827+
.map((button) => button.getAttribute('aria-label'))
806828
.join(', ');
807829
announcer.textContent = formatting;
808830
},

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

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -127,35 +127,51 @@ class RichTextEditor extends RichTextEditorMixin(
127127
id="btn-undo"
128128
type="button"
129129
part="toolbar-button toolbar-button-undo"
130+
aria-label="${this.__effectiveI18n.undo}"
130131
@click="${this._undo}"
131132
></button>
132-
<vaadin-tooltip for="btn-undo" .text="${this.__effectiveI18n.undo}"></vaadin-tooltip>
133133
134134
<button
135135
id="btn-redo"
136136
type="button"
137137
part="toolbar-button toolbar-button-redo"
138+
aria-label="${this.__effectiveI18n.redo}"
138139
@click="${this._redo}"
139140
></button>
140-
<vaadin-tooltip for="btn-redo" .text="${this.__effectiveI18n.redo}"></vaadin-tooltip>
141141
</span>
142142
143143
<span part="toolbar-group toolbar-group-emphasis">
144144
<!-- Bold -->
145-
<button id="btn-bold" class="ql-bold" part="toolbar-button toolbar-button-bold"></button>
146-
<vaadin-tooltip for="btn-bold" .text="${this.__effectiveI18n.bold}"></vaadin-tooltip>
145+
<button
146+
id="btn-bold"
147+
class="ql-bold"
148+
part="toolbar-button toolbar-button-bold"
149+
aria-label="${this.__effectiveI18n.bold}"
150+
></button>
147151
148152
<!-- Italic -->
149-
<button id="btn-italic" class="ql-italic" part="toolbar-button toolbar-button-italic"></button>
150-
<vaadin-tooltip for="btn-italic" .text="${this.__effectiveI18n.italic}"></vaadin-tooltip>
153+
<button
154+
id="btn-italic"
155+
class="ql-italic"
156+
part="toolbar-button toolbar-button-italic"
157+
aria-label="${this.__effectiveI18n.italic}"
158+
></button>
151159
152160
<!-- Underline -->
153-
<button id="btn-underline" class="ql-underline" part="toolbar-button toolbar-button-underline"></button>
154-
<vaadin-tooltip for="btn-underline" .text="${this.__effectiveI18n.underline}"></vaadin-tooltip>
161+
<button
162+
id="btn-underline"
163+
class="ql-underline"
164+
part="toolbar-button toolbar-button-underline"
165+
aria-label="${this.__effectiveI18n.underline}"
166+
></button>
155167
156168
<!-- Strike -->
157-
<button id="btn-strike" class="ql-strike" part="toolbar-button toolbar-button-strike"></button>
158-
<vaadin-tooltip for="btn-strike" .text="${this.__effectiveI18n.strike}"></vaadin-tooltip>
169+
<button
170+
id="btn-strike"
171+
class="ql-strike"
172+
part="toolbar-button toolbar-button-strike"
173+
aria-label="${this.__effectiveI18n.strike}"
174+
></button>
159175
</span>
160176
161177
<span part="toolbar-group toolbar-group-style">
@@ -164,17 +180,17 @@ class RichTextEditor extends RichTextEditorMixin(
164180
id="btn-color"
165181
type="button"
166182
part="toolbar-button toolbar-button-color"
183+
aria-label="${this.__effectiveI18n.color}"
167184
@click="${this.__onColorClick}"
168185
></button>
169-
<vaadin-tooltip for="btn-color" .text="${this.__effectiveI18n.color}"></vaadin-tooltip>
170186
<!-- Background -->
171187
<button
172188
id="btn-background"
173189
type="button"
174190
part="toolbar-button toolbar-button-background"
191+
aria-label="${this.__effectiveI18n.background}"
175192
@click="${this.__onBackgroundClick}"
176193
></button>
177-
<vaadin-tooltip for="btn-background" .text="${this.__effectiveI18n.background}"></vaadin-tooltip>
178194
</span>
179195
180196
<span part="toolbar-group toolbar-group-heading">
@@ -185,24 +201,24 @@ class RichTextEditor extends RichTextEditorMixin(
185201
class="ql-header"
186202
value="1"
187203
part="toolbar-button toolbar-button-h1"
204+
aria-label="${this.__effectiveI18n.h1}"
188205
></button>
189-
<vaadin-tooltip for="btn-h1" .text="${this.__effectiveI18n.h1}"></vaadin-tooltip>
190206
<button
191207
id="btn-h2"
192208
type="button"
193209
class="ql-header"
194210
value="2"
195211
part="toolbar-button toolbar-button-h2"
212+
aria-label="${this.__effectiveI18n.h2}"
196213
></button>
197-
<vaadin-tooltip for="btn-h2" .text="${this.__effectiveI18n.h2}"></vaadin-tooltip>
198214
<button
199215
id="btn-h3"
200216
type="button"
201217
class="ql-header"
202218
value="3"
203219
part="toolbar-button toolbar-button-h3"
220+
aria-label="${this.__effectiveI18n.h3}"
204221
></button>
205-
<vaadin-tooltip for="btn-h3" .text="${this.__effectiveI18n.h3}"></vaadin-tooltip>
206222
</span>
207223
208224
<span part="toolbar-group toolbar-group-glyph-transformation">
@@ -212,15 +228,15 @@ class RichTextEditor extends RichTextEditorMixin(
212228
class="ql-script"
213229
value="sub"
214230
part="toolbar-button toolbar-button-subscript"
231+
aria-label="${this.__effectiveI18n.subscript}"
215232
></button>
216-
<vaadin-tooltip for="btn-subscript" .text="${this.__effectiveI18n.subscript}"></vaadin-tooltip>
217233
<button
218234
id="btn-superscript"
219235
class="ql-script"
220236
value="super"
221237
part="toolbar-button toolbar-button-superscript"
238+
aria-label="${this.__effectiveI18n.superscript}"
222239
></button>
223-
<vaadin-tooltip for="btn-superscript" text="${this.__effectiveI18n.superscript}"></vaadin-tooltip>
224240
</span>
225241
226242
<span part="toolbar-group toolbar-group-list">
@@ -231,16 +247,16 @@ class RichTextEditor extends RichTextEditorMixin(
231247
class="ql-list"
232248
value="ordered"
233249
part="toolbar-button toolbar-button-list-ordered"
250+
aria-label="${this.__effectiveI18n.listOrdered}"
234251
></button>
235-
<vaadin-tooltip for="btn-ol" text="${this.__effectiveI18n.listOrdered}"></vaadin-tooltip>
236252
<button
237253
id="btn-ul"
238254
type="button"
239255
class="ql-list"
240256
value="bullet"
241257
part="toolbar-button toolbar-button-list-bullet"
258+
aria-label="${this.__effectiveI18n.listBullet}"
242259
></button>
243-
<vaadin-tooltip for="btn-ul" text="${this.__effectiveI18n.listBullet}"></vaadin-tooltip>
244260
</span>
245261
246262
<span part="toolbar-group toolbar-group-alignment">
@@ -251,24 +267,24 @@ class RichTextEditor extends RichTextEditorMixin(
251267
class="ql-align"
252268
value=""
253269
part="toolbar-button toolbar-button-align-left"
270+
aria-label="${this.__effectiveI18n.alignLeft}"
254271
></button>
255-
<vaadin-tooltip for="btn-left" .text="${this.__effectiveI18n.alignLeft}"></vaadin-tooltip>
256272
<button
257273
id="btn-center"
258274
type="button"
259275
class="ql-align"
260276
value="center"
261277
part="toolbar-button toolbar-button-align-center"
278+
aria-label="${this.__effectiveI18n.alignCenter}"
262279
></button>
263-
<vaadin-tooltip for="btn-center" .text="${this.__effectiveI18n.alignCenter}"></vaadin-tooltip>
264280
<button
265281
id="btn-right"
266282
type="button"
267283
class="ql-align"
268284
value="right"
269285
part="toolbar-button toolbar-button-align-right"
286+
aria-label="${this.__effectiveI18n.alignRight}"
270287
></button>
271-
<vaadin-tooltip for="btn-right" .text="${this.__effectiveI18n.alignRight}"></vaadin-tooltip>
272288
</span>
273289
274290
<span part="toolbar-group toolbar-group-rich-text">
@@ -277,18 +293,18 @@ class RichTextEditor extends RichTextEditorMixin(
277293
id="btn-image"
278294
type="button"
279295
part="toolbar-button toolbar-button-image"
296+
aria-label="${this.__effectiveI18n.image}"
280297
@touchend="${this._onImageTouchEnd}"
281298
@click="${this._onImageClick}"
282299
></button>
283-
<vaadin-tooltip for="btn-image" .text="${this.__effectiveI18n.image}"></vaadin-tooltip>
284300
<!-- Link -->
285301
<button
286302
id="btn-link"
287303
type="button"
288304
part="toolbar-button toolbar-button-link"
305+
aria-label="${this.__effectiveI18n.link}"
289306
@click="${this._onLinkClick}"
290307
></button>
291-
<vaadin-tooltip for="btn-link" .text="${this.__effectiveI18n.link}"></vaadin-tooltip>
292308
</span>
293309
294310
<span part="toolbar-group toolbar-group-block">
@@ -298,22 +314,27 @@ class RichTextEditor extends RichTextEditorMixin(
298314
type="button"
299315
class="ql-blockquote"
300316
part="toolbar-button toolbar-button-blockquote"
317+
aria-label="${this.__effectiveI18n.blockquote}"
301318
></button>
302-
<vaadin-tooltip for="btn-blockquote" .text="${this.__effectiveI18n.blockquote}"></vaadin-tooltip>
303319
<!-- Code block -->
304320
<button
305321
id="btn-code"
306322
type="button"
307323
class="ql-code-block"
308324
part="toolbar-button toolbar-button-code-block"
325+
aria-label="${this.__effectiveI18n.codeBlock}"
309326
></button>
310-
<vaadin-tooltip for="btn-code" .text="${this.__effectiveI18n.codeBlock}"></vaadin-tooltip>
311327
</span>
312328
313329
<span part="toolbar-group toolbar-group-format">
314330
<!-- Clean -->
315-
<button id="btn-clean" type="button" class="ql-clean" part="toolbar-button toolbar-button-clean"></button>
316-
<vaadin-tooltip for="btn-clean" .text="${this.__effectiveI18n.clean}"></vaadin-tooltip>
331+
<button
332+
id="btn-clean"
333+
type="button"
334+
class="ql-clean"
335+
part="toolbar-button toolbar-button-clean"
336+
aria-label="${this.__effectiveI18n.clean}"
337+
></button>
317338
</span>
318339
319340
<input
@@ -374,6 +395,8 @@ class RichTextEditor extends RichTextEditorMixin(
374395
@color-selected="${this.__onBackgroundSelected}"
375396
@opened-changed="${this.__onBackgroundEditingChanged}"
376397
></vaadin-rich-text-editor-popup>
398+
399+
<slot name="tooltip"></slot>
377400
`;
378401
}
379402

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,14 @@ describe('accessibility', () => {
2424
announcer = rte.shadowRoot.querySelector('[aria-live=polite]');
2525
});
2626

27-
it('should have default tooltips for the buttons', () => {
27+
it('should have aria-label for the buttons', () => {
2828
buttons.forEach((button, index) => {
2929
const expectedLabel = rte.i18n[Object.keys(rte.i18n)[index]];
30-
const tooltip = rte.shadowRoot.querySelector(`[for="${button.id}"]`);
31-
expect(tooltip.text).to.equal(expectedLabel);
30+
expect(button.ariaLabel).to.equal(expectedLabel);
3231
});
3332
});
3433

35-
it('should localize tooltips for the buttons', async () => {
34+
it('should localize aria-label for the buttons', async () => {
3635
const defaultI18n = rte.i18n;
3736

3837
const localized = {};
@@ -44,8 +43,7 @@ describe('accessibility', () => {
4443

4544
buttons.forEach((button, index) => {
4645
const expectedLabel = `${defaultI18n[Object.keys(defaultI18n)[index]]} localized`;
47-
const tooltip = rte.shadowRoot.querySelector(`[for="${button.id}"]`);
48-
expect(tooltip.text).to.equal(expectedLabel);
46+
expect(button.ariaLabel).to.equal(expectedLabel);
4947
});
5048
});
5149

0 commit comments

Comments
 (0)