@@ -3,10 +3,15 @@ import { render, screen, fireEvent } from '@testing-library/react';
33import { DashboardRenderer } from '../DashboardRenderer' ;
44import type { DashboardSchema } from '@object-ui/types' ;
55
6- // Mock SchemaRenderer to avoid pulling in the full renderer tree
6+ // Mock SchemaRenderer to avoid pulling in the full renderer tree.
7+ // Forwards className and includes an interactive child to simulate real chart content.
78vi . mock ( '@object-ui/react' , ( ) => ( {
8- SchemaRenderer : ( { schema } : { schema : any } ) => (
9- < div data-testid = "schema-renderer" > { schema ?. type ?? 'unknown' } </ div >
9+ SchemaRenderer : ( { schema, className } : { schema : any ; className ?: string } ) => (
10+ < div data-testid = "schema-renderer" className = { className } >
11+ < button data-testid = { `interactive-child-${ schema ?. type ?? 'unknown' } ` } >
12+ { schema ?. type ?? 'unknown' }
13+ </ button >
14+ </ div >
1015 ) ,
1116} ) ) ;
1217
@@ -229,6 +234,144 @@ describe('DashboardRenderer design mode', () => {
229234 } ) ;
230235 } ) ;
231236
237+ describe ( 'Content pointer-events in design mode' , ( ) => {
238+ it ( 'should apply pointer-events-none to widget content in design mode' , ( ) => {
239+ render (
240+ < DashboardRenderer
241+ schema = { DASHBOARD_WITH_WIDGETS }
242+ designMode
243+ selectedWidgetId = { null }
244+ onWidgetClick = { vi . fn ( ) }
245+ /> ,
246+ ) ;
247+
248+ // Card widget (bar chart) — content wrapper should have pointer-events-none
249+ const barWidget = screen . getByTestId ( 'dashboard-preview-widget-w2' ) ;
250+ const contentWrapper = barWidget . querySelector ( '.pointer-events-none' ) ;
251+ expect ( contentWrapper ) . toBeInTheDocument ( ) ;
252+ } ) ;
253+
254+ it ( 'should apply pointer-events-none to self-contained (metric) widget content' , ( ) => {
255+ render (
256+ < DashboardRenderer
257+ schema = { DASHBOARD_WITH_WIDGETS }
258+ designMode
259+ selectedWidgetId = { null }
260+ onWidgetClick = { vi . fn ( ) }
261+ /> ,
262+ ) ;
263+
264+ // Metric widget — SchemaRenderer receives pointer-events-none className
265+ const metricWidget = screen . getByTestId ( 'dashboard-preview-widget-w1' ) ;
266+ const contentWrapper = metricWidget . querySelector ( '.pointer-events-none' ) ;
267+ expect ( contentWrapper ) . toBeInTheDocument ( ) ;
268+ } ) ;
269+
270+ it ( 'should NOT apply pointer-events-none when not in design mode' , ( ) => {
271+ const { container } = render ( < DashboardRenderer schema = { DASHBOARD_WITH_WIDGETS } /> ) ;
272+
273+ // No element should have pointer-events-none class
274+ expect ( container . querySelector ( '.pointer-events-none' ) ) . not . toBeInTheDocument ( ) ;
275+ } ) ;
276+
277+ it ( 'should still call onWidgetClick when clicking on Card-based widget content area' , ( ) => {
278+ const onWidgetClick = vi . fn ( ) ;
279+ render (
280+ < DashboardRenderer
281+ schema = { DASHBOARD_WITH_WIDGETS }
282+ designMode
283+ selectedWidgetId = { null }
284+ onWidgetClick = { onWidgetClick }
285+ /> ,
286+ ) ;
287+
288+ // Click on the bar chart widget (Card-based)
289+ fireEvent . click ( screen . getByTestId ( 'dashboard-preview-widget-w2' ) ) ;
290+ expect ( onWidgetClick ) . toHaveBeenCalledWith ( 'w2' ) ;
291+ } ) ;
292+
293+ it ( 'should still call onWidgetClick when clicking on table widget' , ( ) => {
294+ const onWidgetClick = vi . fn ( ) ;
295+ render (
296+ < DashboardRenderer
297+ schema = { DASHBOARD_WITH_WIDGETS }
298+ designMode
299+ selectedWidgetId = { null }
300+ onWidgetClick = { onWidgetClick }
301+ /> ,
302+ ) ;
303+
304+ // Click on the table widget (Card-based)
305+ fireEvent . click ( screen . getByTestId ( 'dashboard-preview-widget-w3' ) ) ;
306+ expect ( onWidgetClick ) . toHaveBeenCalledWith ( 'w3' ) ;
307+ } ) ;
308+ } ) ;
309+
310+ describe ( 'Click-capture overlay in design mode' , ( ) => {
311+ it ( 'should render a click-capture overlay on Card-based widgets in design mode' , ( ) => {
312+ render (
313+ < DashboardRenderer
314+ schema = { DASHBOARD_WITH_WIDGETS }
315+ designMode
316+ selectedWidgetId = { null }
317+ onWidgetClick = { vi . fn ( ) }
318+ /> ,
319+ ) ;
320+
321+ // Card widget (bar chart) — should have an absolute overlay div
322+ const barWidget = screen . getByTestId ( 'dashboard-preview-widget-w2' ) ;
323+ const overlay = barWidget . querySelector ( '[data-testid="widget-click-overlay"]' ) ;
324+ expect ( overlay ) . toBeInTheDocument ( ) ;
325+ expect ( overlay ?. className ) . toContain ( 'absolute' ) ;
326+ expect ( overlay ?. className ) . toContain ( 'inset-0' ) ;
327+ expect ( overlay ?. className ) . toContain ( 'z-10' ) ;
328+ } ) ;
329+
330+ it ( 'should render a click-capture overlay on self-contained widgets in design mode' , ( ) => {
331+ render (
332+ < DashboardRenderer
333+ schema = { DASHBOARD_WITH_WIDGETS }
334+ designMode
335+ selectedWidgetId = { null }
336+ onWidgetClick = { vi . fn ( ) }
337+ /> ,
338+ ) ;
339+
340+ // Metric widget — should have an absolute overlay div
341+ const metricWidget = screen . getByTestId ( 'dashboard-preview-widget-w1' ) ;
342+ const overlay = metricWidget . querySelector ( '[data-testid="widget-click-overlay"]' ) ;
343+ expect ( overlay ) . toBeInTheDocument ( ) ;
344+ expect ( overlay ?. className ) . toContain ( 'absolute' ) ;
345+ expect ( overlay ?. className ) . toContain ( 'inset-0' ) ;
346+ expect ( overlay ?. className ) . toContain ( 'z-10' ) ;
347+ } ) ;
348+
349+ it ( 'should NOT render overlays when not in design mode' , ( ) => {
350+ const { container } = render ( < DashboardRenderer schema = { DASHBOARD_WITH_WIDGETS } /> ) ;
351+
352+ expect ( container . querySelector ( '[data-testid="widget-click-overlay"]' ) ) . not . toBeInTheDocument ( ) ;
353+ } ) ;
354+
355+ it ( 'should apply relative positioning to widget container in design mode' , ( ) => {
356+ render (
357+ < DashboardRenderer
358+ schema = { DASHBOARD_WITH_WIDGETS }
359+ designMode
360+ selectedWidgetId = { null }
361+ onWidgetClick = { vi . fn ( ) }
362+ /> ,
363+ ) ;
364+
365+ // Card widget should have relative for overlay positioning
366+ const barWidget = screen . getByTestId ( 'dashboard-preview-widget-w2' ) ;
367+ expect ( barWidget . className ) . toContain ( 'relative' ) ;
368+
369+ // Metric widget should have relative for overlay positioning
370+ const metricWidget = screen . getByTestId ( 'dashboard-preview-widget-w1' ) ;
371+ expect ( metricWidget . className ) . toContain ( 'relative' ) ;
372+ } ) ;
373+ } ) ;
374+
232375 describe ( 'Non-design mode behavior' , ( ) => {
233376 it ( 'should not add design mode attributes when designMode is off' , ( ) => {
234377 const { container } = render ( < DashboardRenderer schema = { DASHBOARD_WITH_WIDGETS } /> ) ;
0 commit comments