@descope-ui/descope-tooltip 3.6.0 → 3.7.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descope-ui/descope-tooltip",
3
- "version": "3.6.0",
3
+ "version": "3.7.1",
4
4
  "files": [
5
5
  "src",
6
6
  "stories"
@@ -18,13 +18,14 @@
18
18
  },
19
19
  "devDependencies": {
20
20
  "@playwright/test": "1.58.2",
21
- "e2e-utils": "3.6.0"
21
+ "e2e-utils": "3.7.1"
22
22
  },
23
23
  "dependencies": {
24
24
  "@vaadin/tooltip": "24.3.4",
25
- "@descope-ui/common": "3.6.0",
26
- "@descope-ui/theme-globals": "3.6.0",
27
- "@descope-ui/descope-enriched-text": "3.6.0"
25
+ "@descope-ui/common": "3.7.1",
26
+ "@descope-ui/descope-enriched-text": "3.7.1",
27
+ "@descope-ui/theme-globals": "3.7.1",
28
+ "@descope-ui/descope-anchored": "3.7.1"
28
29
  },
29
30
  "publishConfig": {
30
31
  "link-workspace-packages": false
@@ -6,6 +6,7 @@ import { compose } from '@descope-ui/common/utils';
6
6
  import {
7
7
  forwardAttrs,
8
8
  getComponentName,
9
+ injectStyle,
9
10
  } from '@descope-ui/common/components-helpers';
10
11
  import { createBaseClass } from '@descope-ui/common/base-classes';
11
12
  import { EnrichedTextClass } from '@descope-ui/descope-enriched-text/class';
@@ -20,14 +21,26 @@ const tooltipAttrs = [
20
21
  'opened',
21
22
  ];
22
23
 
23
- const BaseClass = createBaseClass({
24
+ class RawTooltip extends createBaseClass({
24
25
  componentName,
25
26
  baseSelector: 'vaadin-tooltip',
26
- });
27
+ }) {
28
+ constructor() {
29
+ super();
30
+
31
+ this.attachShadow({ mode: 'open' }).innerHTML = `
32
+ <descope-anchored>
33
+ <slot></slot>
34
+ <vaadin-tooltip slot="anchored"></vaadin-tooltip>
35
+ </descope-anchored>
36
+ `;
37
+
38
+ this.defaultSlot = this.shadowRoot.querySelector('slot:not([name])');
39
+ this.tooltip = this.shadowRoot.querySelector('vaadin-tooltip');
40
+ }
27
41
 
28
- class RawTooltip extends BaseClass {
29
42
  static get observedAttributes() {
30
- return tooltipAttrs.concat(BaseClass.observedAttributes || []);
43
+ return tooltipAttrs.concat(super.observedAttributes || []);
31
44
  }
32
45
 
33
46
  get isOpened() {
@@ -50,51 +63,81 @@ class RawTooltip extends BaseClass {
50
63
  return this.getAttribute('static-display') === 'true';
51
64
  }
52
65
 
66
+ get srLabel() {
67
+ return this.tooltip?.querySelector('[slot="sr-label"]');
68
+ }
69
+
53
70
  // We use `static-display` for presentation purposes, to show the tooltip content.
54
71
  // This should be used only when `opened` is `true`. Once `static-display` is set,
55
- // the overlay would become a `static` element, and will have layout in the presenting page.
56
- // This is mainly aimed to solve the presentation problem on our Styles Page in the Console.
72
+ // the overlay flows in-line and has layout in the presenting page. Mainly aimed
73
+ // to solve the presentation problem on our Styles Page in the Console.
57
74
  #handleStaticDisplay() {
58
75
  if (this.isStaticDisplay) {
59
- this.#revealWrappedParts();
76
+ this.#revealOverlay();
60
77
  this.setAttribute('inert', 'true');
61
78
  } else {
62
- this.#hideWrappedParts();
63
79
  this.removeAttribute('inert');
64
80
  }
65
81
  }
66
82
 
67
- init() {
68
- super.init();
83
+ #revealOverlay() {
84
+ if (!this.overlay) return;
85
+ // Keep the overlay in vaadin-tooltip.shadowRoot so adopted stylesheets from
86
+ // portalMixin continue to apply (they are scoped to that shadow root).
87
+ // Layout is handled via CSS: anchored-root becomes a column, vaadin-tooltip
88
+ // becomes a visible block below the anchor, and the sr-label is hidden.
89
+ this.overlay.style.display = 'block';
90
+ this.overlay.style.position = 'static';
91
+ }
69
92
 
70
- // Create the vaadin-tooltip here instead of constructor (for React compatibility)
71
- this.style.display = 'contents';
72
- this.insertAdjacentHTML('beforeend', '<vaadin-tooltip></vaadin-tooltip>');
73
- this.tooltip = this.querySelector('vaadin-tooltip');
93
+ init() {
94
+ super.init?.();
74
95
 
75
- this.#hideWrappedParts();
96
+ injectStyle(
97
+ `
98
+ :host {
99
+ display: inline-block;
100
+ }
101
+ vaadin-tooltip {
102
+ display: block;
103
+ position: absolute;
104
+ width: 0;
105
+ height: 0;
106
+ overflow: hidden;
107
+ }
108
+ /* Stack anchor above the anchored element so the tooltip flows below it inline. */
109
+ :host([static-display="true"]) descope-anchored::part(root) {
110
+ flex-direction: column;
111
+ }
112
+ :host([static-display="true"]) descope-anchored::part(anchored) {
113
+ position: static;
114
+ inset: unset;
115
+ }
116
+ :host([static-display="true"]) vaadin-tooltip {
117
+ position: static;
118
+ width: auto;
119
+ height: auto;
120
+ overflow: visible;
121
+ }
122
+ :host([static-display="true"]) vaadin-tooltip [slot="sr-label"] {
123
+ display: none;
124
+ }
125
+ `,
126
+ this,
127
+ );
76
128
 
129
+ this.defaultSlot.addEventListener('slotchange', () =>
130
+ this.#setTooltipTarget(),
131
+ );
77
132
  this.#setTooltipTarget();
78
133
 
79
134
  setTimeout(() => this.#onOverlayReady());
80
135
  }
81
136
 
82
- #hideWrappedParts() {
83
- this.tooltip.style.width = '0';
84
- this.tooltip.style.height = '0';
85
- this.tooltip.style.display = 'block';
86
- this.tooltip.style.overflow = 'hidden';
87
- this.tooltip.style.position = 'absolute';
88
- }
89
-
90
- #revealWrappedParts() {
91
- this.tooltip.style.width = '100%';
92
- this.tooltip.style.height = '100%';
93
- this.tooltip.style.position = 'static';
94
- this.tooltip.style.overflow = 'visible';
95
- this.tooltip.textContent = '';
96
- this.overlay.style.display = 'block';
97
- this.overlay.style.position = 'static';
137
+ #setTooltipTarget() {
138
+ const target = this.defaultSlot?.assignedElements()?.[0];
139
+ if (!target) return;
140
+ this.tooltip.target = target;
98
141
  }
99
142
 
100
143
  #onOverlayReady() {
@@ -108,16 +151,6 @@ class RawTooltip extends BaseClass {
108
151
  this.#handleTooltipVisibility();
109
152
  }
110
153
 
111
- #setTooltipTarget() {
112
- if (!this.children?.length) return;
113
-
114
- let ele = Array.from(this.children).find((child) => child !== this.tooltip);
115
-
116
- if (!ele) return;
117
-
118
- this.tooltip.target = ele;
119
- }
120
-
121
154
  #clearOverlayContentNode() {
122
155
  this.overlayContentNode.innerHTML = '';
123
156
  }
@@ -131,10 +164,6 @@ class RawTooltip extends BaseClass {
131
164
  return enrichedText;
132
165
  }
133
166
 
134
- get srLabel() {
135
- return this.tooltip?.querySelector('[slot="sr-label"]');
136
- }
137
-
138
167
  #initTooltipTextComponent() {
139
168
  if (!this.overlayContentNode) return;
140
169
 
@@ -157,25 +186,35 @@ class RawTooltip extends BaseClass {
157
186
  });
158
187
  }
159
188
 
160
- // the default vaadin behavior is to attach the overlay to the body when opened
161
- // we do not want that because it's difficult to style the overlay in this way
162
- // so we override it to open inside the shadow DOM
189
+ // The default vaadin behavior is to attach the overlay to the body when opened,
190
+ // which makes it hard to style. We move the overlay into our shadow root instead.
191
+ // Critical: vaadin computes position against the overlay's offsetParent moving
192
+ // it changes that context, so we must call `_updatePosition()` afterwards.
193
+ // That's vaadin's own position-recompute method (the same one its resize/scroll
194
+ // listeners use internally), so we get the right reposition without dispatching
195
+ // global events.
163
196
  #overrideAttachOverlay() {
164
197
  if (!this.overlay) return;
165
198
 
166
- if (this.isOpened) {
167
- // When `opened` attr is used, vaadin doesn't execute `_attachOverlay`,
168
- // and the overlay element is rendered outside the component, on the top
169
- // level. We need to move it back to the local component's DOM.
199
+ const originalAttach = this.overlay._attachOverlay?.bind(this.overlay);
200
+
201
+ this.overlay._detachOverlay = () => {};
202
+ this.overlay._attachOverlay = () => {
203
+ originalAttach?.();
170
204
  setTimeout(() => {
171
205
  this.tooltip.shadowRoot.appendChild(this.overlay);
172
- this.#handleStaticDisplay();
206
+ this.overlay._updatePosition?.();
173
207
  });
174
- } else {
175
- this.overlay._detachOverlay = () => {};
208
+ };
176
209
 
177
- this.overlay._attachOverlay = () =>
210
+ if (this.isOpened) {
211
+ // When `opened` attr is set at init, vaadin renders the overlay at the top
212
+ // level without calling `_attachOverlay`. Move it back into our shadow root.
213
+ setTimeout(() => {
178
214
  this.tooltip.shadowRoot.appendChild(this.overlay);
215
+ this.#handleStaticDisplay();
216
+ this.overlay._updatePosition?.();
217
+ });
179
218
  }
180
219
  }
181
220
 
@@ -217,11 +256,6 @@ const { overlay, content } = {
217
256
  content: { selector: () => 'vaadin-tooltip-overlay::part(content)' },
218
257
  };
219
258
 
220
- /**
221
- * This component has no Shadow DOM of its own, so we can't add styles to it
222
- * (otherwise it would affect the rest of the DOM).
223
- * Note that all styles are within PortalMixin.
224
- */
225
259
  export const TooltipClass = compose(
226
260
  componentNameValidationMixin,
227
261
  portalMixin({
@@ -1,3 +1,4 @@
1
+ import '@descope-ui/descope-anchored';
1
2
  import '@descope-ui/descope-enriched-text';
2
3
  import '@vaadin/tooltip';
3
4
  import { componentName, TooltipClass } from './TooltipClass';