Update custom components#171
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the panel-custom-components skill documentation to better guide users building custom Panel components, with added guidance around CDN/import-map choices and JS↔Python communication patterns.
Changes:
- Added a CDN selection guide and expanded guidance for plugin-based JS libraries (e.g., FullCalendar-style ecosystems).
- Clarified JS↔Python communication patterns and emphasized correct usage of
_send_msgvs nonexistentsend_msg. - Slimmed down the Playwright testing section in favor of referencing a dedicated Playwright skill; bumped skill version to
1.1.0.
| --- | ||
| name: panel-custom-components | ||
| description: Build custom Panel components using JSComponent (vanilla JS, web components), ReactComponent (React/JSX), AnyWidgetComponent (AnyWidget spec for cross-platform), or MaterialUIComponent (Material UI themed). Use when wrapping JS libraries, creating interactive widgets, or building themed components. Includes decision guide, best practices, DOs/DON'Ts, and Playwright UI testing patterns. | ||
| description: Build custom Panel components using JSComponent (vanilla JS, web components), ReactComponent (React/JSX), AnyWidgetComponent (AnyWidget spec for cross-platform), or MaterialUIComponent (Material UI themed). Use when wrapping JS libraries, creating interactive widgets, or building themed components. Includes decision guide, CDN selection guide, best practices, and DOs/DON'Ts. For Playwright UI testing patterns, see the `panel-pytest-playwright` skill. |
There was a problem hiding this comment.
panel-pytest-playwright is referenced as an existing skill, but there is no corresponding skills/panel-pytest-playwright/ in this repo. Either add that skill (so cross-references resolve) or change these references to an existing skill/path.
| description: Build custom Panel components using JSComponent (vanilla JS, web components), ReactComponent (React/JSX), AnyWidgetComponent (AnyWidget spec for cross-platform), or MaterialUIComponent (Material UI themed). Use when wrapping JS libraries, creating interactive widgets, or building themed components. Includes decision guide, CDN selection guide, best practices, and DOs/DON'Ts. For Playwright UI testing patterns, see the `panel-pytest-playwright` skill. | |
| description: Build custom Panel components using JSComponent (vanilla JS, web components), ReactComponent (React/JSX), AnyWidgetComponent (AnyWidget spec for cross-platform), or MaterialUIComponent (Material UI themed). Use when wrapping JS libraries, creating interactive widgets, or building themed components. Includes decision guide, CDN selection guide, best practices, and DOs/DON'Ts. For Playwright UI testing patterns, refer to the project's Playwright testing documentation and existing testing skills. |
| # In Python -- _handle_msg receives the dict from JS | ||
| import json | ||
|
|
||
| class MyCalendar(JSComponent): | ||
| current_date = param.String(default="") | ||
| current_view = param.String(default="") | ||
| clicked_event = param.Dict(default={}) |
There was a problem hiding this comment.
The Python example for _handle_msg uses param.String / param.Dict but the snippet only imports json. Add import param (or make the snippet explicitly rely on an earlier import) so it’s copy/paste runnable.
| }); | ||
| }, | ||
| eventClick(info) { | ||
| model.send_msg({ clicked_event: JSON.stringify(serializeEvent(info.event)) }); |
There was a problem hiding this comment.
This snippet calls serializeEvent(info.event) but serializeEvent is not defined anywhere else in the document. Define it (even minimally) or replace with an explicit field extraction so readers can run the example without hitting a ReferenceError.
| model.send_msg({ clicked_event: JSON.stringify(serializeEvent(info.event)) }); | |
| model.send_msg({ | |
| clicked_event: JSON.stringify({ | |
| id: info.event.id, | |
| title: info.event.title, | |
| start: info.event.start ? info.event.start.toISOString() : null, | |
| end: info.event.end ? info.event.end.toISOString() : null, | |
| allDay: info.event.allDay, | |
| }) | |
| }); |
| function loadPluginIfNeeded(viewName, pluginName) { | ||
| const toolbar = model.header_toolbar || {}; | ||
| const inToolbar = Object.values(toolbar).some(v => v.includes(viewName)); | ||
| if (model.initial_view.startsWith(viewName) || inToolbar) { | ||
| return import(`@fullcalendar/${pluginName}`).then(m => m.default); | ||
| } | ||
| return Promise.resolve(null); |
There was a problem hiding this comment.
The plugin-loading example uses bare specifiers in dynamic imports (e.g. import(@fullcalendar/${pluginName}) and import("@fullcalendar/interaction")). In a browser this will fail unless the Python _importmap includes mappings for every plugin you might import (or a namespace mapping like "@fullcalendar/": "https://.../"). Consider adding the required _importmap snippet alongside this example.
Separate out testing from developing custom components. Also, improve custom components repo selection.