@lightdash/cli 2.261.0 → 2.263.0

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.
@@ -232,6 +232,90 @@ exactly ONE source row and ONE metric-slot field, and only when
232
232
  show each action on its own flag (a viewer may have one permission but not
233
233
  the other).
234
234
 
235
+ ### Open the menu at the data point
236
+
237
+ Anchor this menu at the activated mark, not at an edge of the iframe or chart.
238
+ If neither action is enabled, do not open or render an empty menu and do not
239
+ give marks action-button semantics.
240
+ Keep the clicked datum's `sourceRow` and declared metric name (and `fieldId` for
241
+ a multiple input) in the menu state; the menu items must act on that exact
242
+ identity. Pass the helper a library-neutral anchor: the activating DOM mark,
243
+ whether activation was by pointer or keyboard, and pointer viewport coordinates
244
+ when available. For a pointer activation, use native `clientX` and `clientY`,
245
+ never chart-relative coordinates such as `chartX` or `chartY`. For keyboard
246
+ activation, anchor at the centre of the activating mark's
247
+ `getBoundingClientRect()` instead.
248
+
249
+ Adapt each chart library before calling the helper. Recharts' React click
250
+ handler supplies `event.currentTarget`, `event.clientX`, `event.clientY`, and
251
+ `event.detail === 0` for keyboard-originated clicks. For ECharts, extract the
252
+ native pointer event from its click parameters before reading `clientX` and
253
+ `clientY`; do not assume React fields exist. D3 already passes the native event.
254
+
255
+ **Make each eligible SVG mark keyboard reachable while either action is enabled.**
256
+ When neither is enabled, omit the button role, tab stop, and action handlers.
257
+ Otherwise give the mark `tabIndex={0}`,
258
+ `role="button"`, and an accessible name from its category and formatted value.
259
+ In Recharts, use a custom `shape` to wire `onClick` and `onKeyDown` on the rendered
260
+ mark; the chart-wide `accessibilityLayer` alone does not open per-mark menus.
261
+ On Enter or Space, prevent the default key action and call the same helper with
262
+ `keyboard: true` and the mark as `activator`. Verify Tab → mark → Enter/Space →
263
+ menu → Escape returns focus to that mark.
264
+ Register rendered marks in a ref map keyed by stable datum key, using a ref callback
265
+ on the SVG element: `ref={(node) => registerMark(datum.key, node)}`.
266
+ Resolve the current mark after the menu unmounts when restoring
267
+ focus: a saved DOM element can become detached during chart rerenders.
268
+
269
+ Use the existing shadcn/Radix `DropdownMenu`, rendered with
270
+ `createPortal(..., document.body)`. Its 1px fixed trigger is positioned at the
271
+ stored viewport coordinates; this avoids transformed iframe ancestors changing
272
+ the containing block for `position: fixed`. Let `DropdownMenuContent` use its
273
+ normal Radix collision handling so a menu near a viewport edge flips or shifts
274
+ onscreen. The placement state is small; the existing `underlyingData.open` and
275
+ `drillDown.open` calls remain the menu items' host actions:
276
+
277
+ ```tsx
278
+ const markElements = useRef(new Map());
279
+ const registerMark = (key, node) => {
280
+ if (node) markElements.current.set(key, node);
281
+ else markElements.current.delete(key);
282
+ };
283
+ const openPointMenu = (datum, anchor) => {
284
+ if (!underlyingData.enabled && !drillDown.enabled) return;
285
+ const rect = anchor.activator.getBoundingClientRect();
286
+ setTooltipVisible(false);
287
+ setMenu({
288
+ sourceRow: datum.sourceRow,
289
+ metric: datum.metric,
290
+ fieldId: datum.fieldId,
291
+ pointKey: datum.key,
292
+ keyboard: anchor.keyboard,
293
+ x: anchor.keyboard ? rect.left + rect.width / 2 : anchor.clientX,
294
+ y: anchor.keyboard ? rect.top + rect.height / 2 : anchor.clientY,
295
+ });
296
+ if (!anchor.keyboard) anchor.activator.blur();
297
+ };
298
+
299
+ {menu && (underlyingData.enabled || drillDown.enabled) && createPortal(
300
+ <DropdownMenu open onOpenChange={(open) => !open && setMenu(null)}>
301
+ <DropdownMenuTrigger asChild>
302
+ <span aria-hidden style={{ position: 'fixed', left: menu.x, top: menu.y, width: 1, height: 1 }} />
303
+ </DropdownMenuTrigger>
304
+ <DropdownMenuContent
305
+ onCloseAutoFocus={(event) => {
306
+ event.preventDefault();
307
+ if (menu.keyboard) {
308
+ requestAnimationFrame(() => markElements.current.get(menu.pointKey)?.focus());
309
+ }
310
+ }}
311
+ >
312
+ {/* independently gated action items */}
313
+ </DropdownMenuContent>
314
+ </DropdownMenu>,
315
+ document.body,
316
+ )}
317
+ ```
318
+
235
319
  ### Interaction hygiene
236
320
 
237
321
  One floating surface at a time, and no leftover emphasis — native Lightdash
@@ -245,8 +329,9 @@ highlighted:
245
329
  reappear until the pointer moves again after the menu closes.
246
330
  - **No persistent focus or active styling on a clicked mark.** Disable click
247
331
  emphasis (recharts: no `activeShape` on click state; echarts: turn off
248
- lingering `emphasis`/`select`) and blur any focused SVG node after opening
249
- the menu. Only hover may emphasise, and only while hovering.
332
+ lingering `emphasis`/`select`). A keyboard activator keeps its normal focus
333
+ affordance and receives focus again when the menu closes; it must not look
334
+ like a selected data point. Only hover may emphasise, and only while hovering.
250
335
  - **Subtle hover, no cursor band.** The library-default full-height band
251
336
  behind the hovered mark (recharts `<Tooltip cursor>`) is not native
252
337
  behaviour — use `cursor={false}` or a faint theme-token fill.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "2.261.0",
3
+ "version": "2.263.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  "@actions/core": "1.11.1",
23
23
  "@aws-sdk/credential-providers": "3.995.0",
24
24
  "@casl/ability": "6.8.0",
25
- "@lightdash/common": "2.261.0",
26
- "@lightdash/warehouses": "2.261.0",
25
+ "@lightdash/common": "2.263.0",
26
+ "@lightdash/warehouses": "2.263.0",
27
27
  "@sidvind/better-ajv-errors": "7.0.0",
28
28
  "@types/columnify": "1.5.1",
29
29
  "ajv": "8.18.0",