Skip to content

Commit 2fe2d18

Browse files
Copilothotlong
andcommitted
fix: add pointer-events-none to widget content in design mode to fix click-to-select
In dashboard edit mode, SchemaRenderer content (charts, tables, interactive SVGs) was intercepting click events before they could bubble to the widget Card/div selection handler. This prevented users from selecting widgets in the left/main dashboard area. Fix: Apply pointer-events-none to SchemaRenderer content wrappers when designMode is active, allowing clicks to pass through to the parent element's onClick handler. This enables proper widget selection while maintaining full interactivity in non-edit mode. Added 4 new tests verifying the fix. Updated ROADMAP.md with Phase 9 entry. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 77261ce commit 2fe2d18

3 files changed

Lines changed: 67 additions & 2 deletions

File tree

ROADMAP.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,14 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
389389
- [x] `WidgetConfigPanel` — added `headerExtra` prop for custom header actions
390390
- [x] Update 21 integration tests (10 DashboardDesignInteraction + 11 DashboardViewSelection) to verify inline config panel pattern, widget deletion, live preview sync
391391

392+
**Phase 9 — Design Mode Widget Selection Click-Through Fix:**
393+
- [x] Fix: Widget content (charts, tables via `SchemaRenderer`) intercepted click events, preventing selection in edit mode
394+
- [x] Add `pointer-events-none` to `SchemaRenderer` content wrappers in `DashboardRenderer` design mode so clicks pass through to the widget Card/div selection handler
395+
- [x] Self-contained (metric) widgets: `pointer-events-none` applied to `SchemaRenderer` className in design mode
396+
- [x] Card-based (chart/table) widgets: `pointer-events-none` applied to `CardContent` inner wrapper in design mode
397+
- [x] No impact on non-design mode — widgets remain fully interactive when not editing
398+
- [x] Add 4 new Vitest tests: pointer-events-none presence in design mode, absence in normal mode, click-to-select on Card-based widgets
399+
392400
### P1.11 Console — Schema-Driven View Config Panel Migration
393401

394402
> Migrated the Console ViewConfigPanel from imperative implementation (~1655 lines) to Schema-Driven architecture using `ConfigPanelRenderer` + `useConfigDraft` + `ConfigPanelSchema`, reducing to ~170 lines declarative wrapper + schema factory.

packages/plugin-dashboard/src/DashboardRenderer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export const DashboardRenderer = forwardRef<HTMLDivElement, DashboardRendererPro
194194
}: undefined}
195195
{...designModeProps}
196196
>
197-
<SchemaRenderer schema={componentSchema} className="h-full w-full" />
197+
<SchemaRenderer schema={componentSchema} className={cn("h-full w-full", designMode && "pointer-events-none")} />
198198
</div>
199199
);
200200
}
@@ -222,7 +222,7 @@ export const DashboardRenderer = forwardRef<HTMLDivElement, DashboardRendererPro
222222
</CardHeader>
223223
)}
224224
<CardContent className="p-0">
225-
<div className={cn("h-full w-full", "p-3 sm:p-4 md:p-6")}>
225+
<div className={cn("h-full w-full", "p-3 sm:p-4 md:p-6", designMode && "pointer-events-none")}>
226226
<SchemaRenderer schema={componentSchema} />
227227
</div>
228228
</CardContent>

packages/plugin-dashboard/src/__tests__/DashboardRenderer.designMode.test.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,63 @@ describe('DashboardRenderer design mode', () => {
229229
});
230230
});
231231

232+
describe('Content pointer-events in design mode', () => {
233+
it('should apply pointer-events-none to widget content in design mode', () => {
234+
render(
235+
<DashboardRenderer
236+
schema={DASHBOARD_WITH_WIDGETS}
237+
designMode
238+
selectedWidgetId={null}
239+
onWidgetClick={vi.fn()}
240+
/>,
241+
);
242+
243+
// Card widget (bar chart) — content wrapper should have pointer-events-none
244+
const barWidget = screen.getByTestId('dashboard-preview-widget-w2');
245+
const contentWrapper = barWidget.querySelector('.pointer-events-none');
246+
expect(contentWrapper).toBeInTheDocument();
247+
});
248+
249+
it('should NOT apply pointer-events-none when not in design mode', () => {
250+
const { container } = render(<DashboardRenderer schema={DASHBOARD_WITH_WIDGETS} />);
251+
252+
// No element should have pointer-events-none class
253+
expect(container.querySelector('.pointer-events-none')).not.toBeInTheDocument();
254+
});
255+
256+
it('should still call onWidgetClick when clicking on Card-based widget content area', () => {
257+
const onWidgetClick = vi.fn();
258+
render(
259+
<DashboardRenderer
260+
schema={DASHBOARD_WITH_WIDGETS}
261+
designMode
262+
selectedWidgetId={null}
263+
onWidgetClick={onWidgetClick}
264+
/>,
265+
);
266+
267+
// Click on the bar chart widget (Card-based)
268+
fireEvent.click(screen.getByTestId('dashboard-preview-widget-w2'));
269+
expect(onWidgetClick).toHaveBeenCalledWith('w2');
270+
});
271+
272+
it('should still call onWidgetClick when clicking on table widget', () => {
273+
const onWidgetClick = vi.fn();
274+
render(
275+
<DashboardRenderer
276+
schema={DASHBOARD_WITH_WIDGETS}
277+
designMode
278+
selectedWidgetId={null}
279+
onWidgetClick={onWidgetClick}
280+
/>,
281+
);
282+
283+
// Click on the table widget (Card-based)
284+
fireEvent.click(screen.getByTestId('dashboard-preview-widget-w3'));
285+
expect(onWidgetClick).toHaveBeenCalledWith('w3');
286+
});
287+
});
288+
232289
describe('Non-design mode behavior', () => {
233290
it('should not add design mode attributes when designMode is off', () => {
234291
const { container } = render(<DashboardRenderer schema={DASHBOARD_WITH_WIDGETS} />);

0 commit comments

Comments
 (0)