Skip to content

feat: integrate shiny-react module namespace support - #11

Merged
schloerke merged 15 commits into
mainfrom
schloerke/git-pull
Mar 18, 2026
Merged

feat: integrate shiny-react module namespace support#11
schloerke merged 15 commits into
mainfrom
schloerke/git-pull

Conversation

@schloerke

Copy link
Copy Markdown
Collaborator

Integrates module namespace support from wch/shiny-react#3 into shinyjson's vendored shiny-react copy, and copies the upstream examples as reference material.

Summary

  • ShinyModuleContext — New React context provider (ShinyModuleProvider), useShinyModuleNamespace() hook, and applyNamespace() utility for prefixing IDs with module namespaces
  • ShinyReactComponentElement — Base HTMLElement subclass for custom web elements that render React components with automatic namespace wrapping, slot preservation, data-* config parsing, and Shiny binding lifecycle
  • Namespace support in hooksuseShinyInput, useShinyOutput, useShinyMessageHandler, and ImageOutput all accept an optional namespace parameter and auto-read from ShinyModuleProvider context
  • Python post_message fix — Wraps message type in resolve_id() so messages inside Shiny modules are correctly namespaced
  • JS test infrastructure — Vitest + jsdom + @testing-library/react with 24 tests covering namespace logic, hook integration, and custom element behavior
  • Upstream examples — All 9 shiny-react examples copied to examples/shiny-react-upstream/ as reference material for future adaptation

Verification

After make js-build && make update-dist:

# In a Shiny module, post_message now correctly namespaces:
from shinyjson import post_message
# Inside @module.server with id="mymod", sends type "mymod-notify"
await post_message(session, "notify", {"text": "hello"})
// JS: Downstream packages can use namespace support
const { ShinyModuleProvider, ShinyReactComponentElement } = window.shinyjson;

// Wrap components in a namespace
<ShinyModuleProvider namespace="counter1">
  <CounterWidget />  // useShinyInput("count") becomes "counter1-count"
</ShinyModuleProvider>

// Or extend the base custom element class
class MyWidget extends ShinyReactComponentElement {
  static component = CounterWidget;
}

All checks pass: 24 JS tests, 20 Python tests, TypeScript compiles, pyright clean.

schloerke and others added 15 commits March 18, 2026 15:06
Covers vendored source changes from wch/shiny-react#3, Python
post_message namespace fix, upstream examples as reference material,
build/export wiring, and JavaScript unit test plan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10-task plan covering: Vitest infrastructure, ShinyModuleContext,
use-shiny namespace support, ImageOutput update, ShinyReactComponentElement,
global API wiring, Python post_message fix, upstream examples, and final build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…yMessageHandler

Each hook now accepts an optional `namespace` parameter and also reads
from ShinyModuleProvider context. Explicit namespace takes precedence
over context. IDs are prefixed via applyNamespace() before registry
lookups. Includes tests covering plain, explicit, context, and
override scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add namespace prop and context-based namespace resolution to ImageOutput.
The component now applies namespaces to clientdata input IDs and the
output binding, enabling proper Shiny module support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ShinyReactComponentElement, a base HTMLElement subclass for creating
custom elements that render React components with automatic Shiny
integration. Features include namespace support via ShinyModuleProvider,
slot preservation for blended content, data-* config parsing, and proper
Shiny binding lifecycle management.

Update shiny-react index.ts to export ShinyReactComponentElement,
ShinyModuleProvider, and useShinyModuleNamespace.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ndow.shinyjson

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verbatim copies from wch/shiny-react#3 (gadenbuie:feat/multiple-react-roots).
These use the @posit/shiny-react copy-paste pattern and won't run as-is
within shinyjson. They serve as reference for future adaptation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ImageOutput was applying namespace to IDs then passing them to
useShinyInput/useShinyOutput, which also applied namespace from
context — resulting in IDs like "mod1-mod1-plot".

The namespace option on hooks now accepts `string | null`:
- undefined (omitted): use context namespace
- null: explicitly skip namespacing
- string: use this specific namespace

ImageOutput passes `{ namespace: null }` to suppress the hooks'
own namespacing since it already embeds the namespace in IDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously captureSlots used either/or logic: if any [data-slot]
elements existed, non-slotted children were silently dropped. Now
named slots and __children__ coexist, matching native web component
<slot> semantics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pre-commit trailing-whitespace hook was modifying Vite's minified
output, causing verify-js-built CI to fail because a clean rebuild
produces different whitespace than the committed files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@schloerke
schloerke marked this pull request as ready for review March 18, 2026 20:09
@schloerke
schloerke requested a review from Copilot March 18, 2026 20:10
@schloerke

Copy link
Copy Markdown
Collaborator Author

Going to merge. Copilot can still post it's review and be fixed in a followup

@schloerke
schloerke merged commit d03be9f into main Mar 18, 2026
5 checks passed
@schloerke
schloerke deleted the schloerke/git-pull branch March 18, 2026 20:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR vendors upstream shiny-react module namespace support into shinyjson’s JS bundle, fixes Python-side post_message() so custom message types are correctly namespaced inside Shiny modules, and adds a comprehensive JS test setup plus upstream examples for reference.

Changes:

  • Add module namespace primitives (ShinyModuleProvider, useShinyModuleNamespace, applyNamespace) and export them for downstream use.
  • Extend namespace support into ImageOutput and align Python post_message() with Shiny module namespacing via resolve_id(), with a regression test.
  • Add Vitest/jsdom test infrastructure and copy upstream shiny-react examples into examples/shiny-react-upstream/ (plus tooling excludes/ignores).

Reviewed changes

Copilot reviewed 185 out of 192 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
pyproject.toml Excludes upstream example directory from Ruff checks.
pkg-r/inst/lib/shiny/GIT_VERSION Updates vendored JS asset version for the R package.
pkg-py/tests/test_post_message.py Adds coverage for namespaced message types via resolve_id().
pkg-py/src/shinyjson/www/GIT_VERSION Updates vendored JS asset version for the Python package.
pkg-py/src/shinyjson/_post_message.py Namespaces message type using resolve_id() before sending.
js/vitest.config.ts Adds Vitest configuration (jsdom env) for JS unit tests.
js/src/shiny-react/index.ts Exposes new namespace provider/hook and custom element base class.
js/src/shiny-react/tests/ShinyModuleContext.test.tsx Tests namespacing utility and provider behavior.
js/src/shiny-react/ShinyModuleContext.tsx Implements React context + helper for module namespaces.
js/src/shiny-react/ImageOutput.tsx Adds optional namespace prop + context-based namespacing integration.
js/src/index.ts Publishes new exports on window.shinyjson.
js/package.json Adds vitest + testing dependencies and a npm test script.
examples/shiny-react-upstream/9-blended/tsconfig.json Upstream example (blended) TS config.
examples/shiny-react-upstream/9-blended/srcts/main.tsx Upstream example (blended) custom element usage.
examples/shiny-react-upstream/9-blended/r/app.R Upstream example (blended) R app.
examples/shiny-react-upstream/9-blended/package.json Upstream example (blended) build scripts/deps.
examples/shiny-react-upstream/8-modules/tsconfig.json Upstream example (modules) TS config.
examples/shiny-react-upstream/8-modules/srcts/main.tsx Upstream example (modules) React entrypoint.
examples/shiny-react-upstream/8-modules/srcts/CounterWidget.tsx Upstream example (modules) widget using shiny-react hooks.
examples/shiny-react-upstream/8-modules/srcts/App.tsx Upstream example (modules) provider-wrapped instances.
examples/shiny-react-upstream/8-modules/srcts-standard/styles.css Upstream example (modules) standard/custom-element styling.
examples/shiny-react-upstream/8-modules/srcts-standard/main.tsx Upstream example (modules) custom element definition entrypoint.
examples/shiny-react-upstream/8-modules/srcts-standard/CounterWidget.tsx Upstream example (modules) widget used by custom element.
examples/shiny-react-upstream/8-modules/r/shinyreact.R Upstream example (modules) R helpers.
examples/shiny-react-upstream/8-modules/r/app.R Upstream example (modules) R app.
examples/shiny-react-upstream/8-modules/py/shinyreact.py Upstream example (modules) Python helpers (async post_message).
examples/shiny-react-upstream/8-modules/py/app.py Upstream example (modules) Python app.
examples/shiny-react-upstream/8-modules/package.json Upstream example (modules) build scripts/deps.
examples/shiny-react-upstream/7-chat/tsconfig.json Upstream example (chat) TS config.
examples/shiny-react-upstream/7-chat/srcts/main.tsx Upstream example (chat) React entrypoint.
examples/shiny-react-upstream/7-chat/srcts/hooks/useImageUpload.ts Upstream example (chat) image upload hook.
examples/shiny-react-upstream/7-chat/srcts/hooks/useDragAndDrop.ts Upstream example (chat) DnD hook.
examples/shiny-react-upstream/7-chat/srcts/contexts/ThemeContext.tsx Upstream example (chat) theme context.
examples/shiny-react-upstream/7-chat/srcts/components/ui/scroll-area.tsx Upstream example (chat) UI component.
examples/shiny-react-upstream/7-chat/srcts/components/ui/input.tsx Upstream example (chat) UI component.
examples/shiny-react-upstream/7-chat/srcts/components/ui/card.tsx Upstream example (chat) UI component.
examples/shiny-react-upstream/7-chat/srcts/components/ui/button.tsx Upstream example (chat) UI component.
examples/shiny-react-upstream/7-chat/srcts/components/ui/avatar.tsx Upstream example (chat) UI component.
examples/shiny-react-upstream/7-chat/srcts/components/ImagePreview.tsx Upstream example (chat) attachment preview UI.
examples/shiny-react-upstream/7-chat/r/shinyreact.R Upstream example (chat) R helpers.
examples/shiny-react-upstream/7-chat/r/app.R Upstream example (chat) R app.
examples/shiny-react-upstream/7-chat/py/shinyreact.py Upstream example (chat) Python helpers.
examples/shiny-react-upstream/7-chat/py/requirements.txt Upstream example (chat) Python requirements.
examples/shiny-react-upstream/7-chat/py/app.py Upstream example (chat) Python app.
examples/shiny-react-upstream/7-chat/package.json Upstream example (chat) build scripts/deps.
examples/shiny-react-upstream/7-chat/components.json Upstream example (chat) shadcn/ui config.
examples/shiny-react-upstream/7-chat/build.ts Upstream example (chat) build tooling.
examples/shiny-react-upstream/7-chat/.gitignore Upstream example (chat) local ignore rules.
examples/shiny-react-upstream/6-dashboard/tsconfig.json Upstream example (dashboard) TS config.
examples/shiny-react-upstream/6-dashboard/srcts/main.tsx Upstream example (dashboard) React entrypoint.
examples/shiny-react-upstream/6-dashboard/srcts/css.d.ts Upstream example (dashboard) CSS module typing.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/table.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/skeleton.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/separator.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/input.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/card.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/button.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/ui/badge.tsx Upstream example (dashboard) UI component.
examples/shiny-react-upstream/6-dashboard/srcts/components/Sidebar.tsx Upstream example (dashboard) sidebar UI.
examples/shiny-react-upstream/6-dashboard/srcts/components/MetricsCards.tsx Upstream example (dashboard) metrics UI.
examples/shiny-react-upstream/6-dashboard/srcts/components/Dashboard.tsx Upstream example (dashboard) main UI.
examples/shiny-react-upstream/6-dashboard/r/shinyreact.R Upstream example (dashboard) R helpers.
examples/shiny-react-upstream/6-dashboard/py/shinyreact.py Upstream example (dashboard) Python helpers.
examples/shiny-react-upstream/6-dashboard/py/app.py Upstream example (dashboard) Python app.
examples/shiny-react-upstream/6-dashboard/package.json Upstream example (dashboard) build scripts/deps.
examples/shiny-react-upstream/6-dashboard/components.json Upstream example (dashboard) shadcn/ui config.
examples/shiny-react-upstream/6-dashboard/build.ts Upstream example (dashboard) build tooling.
examples/shiny-react-upstream/6-dashboard/.gitignore Upstream example (dashboard) local ignore rules.
examples/shiny-react-upstream/5-shadcn/tsconfig.json Upstream example (shadcn) TS config.
examples/shiny-react-upstream/5-shadcn/srcts/main.tsx Upstream example (shadcn) React entrypoint.
examples/shiny-react-upstream/5-shadcn/srcts/css.d.ts Upstream example (shadcn) CSS module typing.
examples/shiny-react-upstream/5-shadcn/srcts/components/ui/separator.tsx Upstream example (shadcn) UI component.
examples/shiny-react-upstream/5-shadcn/srcts/components/ui/input.tsx Upstream example (shadcn) UI component.
examples/shiny-react-upstream/5-shadcn/srcts/components/ui/card.tsx Upstream example (shadcn) UI component.
examples/shiny-react-upstream/5-shadcn/srcts/components/ui/button.tsx Upstream example (shadcn) UI component.
examples/shiny-react-upstream/5-shadcn/srcts/components/ui/badge.tsx Upstream example (shadcn) UI component.
examples/shiny-react-upstream/5-shadcn/srcts/components/TextInputCard.tsx Upstream example (shadcn) input/output card.
examples/shiny-react-upstream/5-shadcn/srcts/components/PlotCard.tsx Upstream example (shadcn) ImageOutput usage.
examples/shiny-react-upstream/5-shadcn/srcts/components/ButtonEventCard.tsx Upstream example (shadcn) event-priority input usage.
examples/shiny-react-upstream/5-shadcn/srcts/components/App.tsx Upstream example (shadcn) app composition.
examples/shiny-react-upstream/5-shadcn/r/shinyreact.R Upstream example (shadcn) R helpers.
examples/shiny-react-upstream/5-shadcn/r/app.R Upstream example (shadcn) R app.
examples/shiny-react-upstream/5-shadcn/py/shinyreact.py Upstream example (shadcn) Python helpers.
examples/shiny-react-upstream/5-shadcn/py/app.py Upstream example (shadcn) Python app.
examples/shiny-react-upstream/5-shadcn/package.json Upstream example (shadcn) build scripts/deps.
examples/shiny-react-upstream/5-shadcn/components.json Upstream example (shadcn) shadcn/ui config.
examples/shiny-react-upstream/5-shadcn/build.ts Upstream example (shadcn) build tooling.
examples/shiny-react-upstream/5-shadcn/.gitignore Upstream example (shadcn) local ignore rules.
examples/shiny-react-upstream/4-messages/tsconfig.json Upstream example (messages) TS config.
examples/shiny-react-upstream/4-messages/srcts/main.tsx Upstream example (messages) React entrypoint.
examples/shiny-react-upstream/4-messages/srcts/App.tsx Upstream example (messages) message handler demo.
examples/shiny-react-upstream/4-messages/r/shinyreact.R Upstream example (messages) R helpers.
examples/shiny-react-upstream/4-messages/r/app.R Upstream example (messages) R app.
examples/shiny-react-upstream/4-messages/py/shinyreact.py Upstream example (messages) Python helpers.
examples/shiny-react-upstream/4-messages/py/app.py Upstream example (messages) Python app.
examples/shiny-react-upstream/4-messages/package.json Upstream example (messages) build scripts/deps.
examples/shiny-react-upstream/4-messages/README.md Upstream example (messages) documentation.
examples/shiny-react-upstream/4-messages/.gitignore Upstream example (messages) local ignore rules.
examples/shiny-react-upstream/3-outputs/tsconfig.json Upstream example (outputs) TS config.
examples/shiny-react-upstream/3-outputs/srcts/main.tsx Upstream example (outputs) React entrypoint.
examples/shiny-react-upstream/3-outputs/srcts/StatisticsCard.tsx Upstream example (outputs) output rendering.
examples/shiny-react-upstream/3-outputs/srcts/SliderCard.tsx Upstream example (outputs) input control.
examples/shiny-react-upstream/3-outputs/srcts/PlotCard.tsx Upstream example (outputs) ImageOutput usage.
examples/shiny-react-upstream/3-outputs/srcts/InputOutputCard.tsx Upstream example (outputs) shared UI wrapper.
examples/shiny-react-upstream/3-outputs/srcts/DataTableCard.tsx Upstream example (outputs) table output rendering.
examples/shiny-react-upstream/3-outputs/srcts/Card.tsx Upstream example (outputs) shared UI wrapper.
examples/shiny-react-upstream/3-outputs/srcts/App.tsx Upstream example (outputs) app composition.
examples/shiny-react-upstream/3-outputs/r/shinyreact.R Upstream example (outputs) R helpers.
examples/shiny-react-upstream/3-outputs/r/mtcars.csv Upstream example (outputs) sample data.
examples/shiny-react-upstream/3-outputs/r/app.R Upstream example (outputs) R app.
examples/shiny-react-upstream/3-outputs/py/shinyreact.py Upstream example (outputs) Python helpers.
examples/shiny-react-upstream/3-outputs/py/mtcars.csv Upstream example (outputs) sample data.
examples/shiny-react-upstream/3-outputs/py/app.py Upstream example (outputs) Python app.
examples/shiny-react-upstream/3-outputs/package.json Upstream example (outputs) build scripts/deps.
examples/shiny-react-upstream/3-outputs/.gitignore Upstream example (outputs) local ignore rules.
examples/shiny-react-upstream/2-inputs/tsconfig.json Upstream example (inputs) TS config.
examples/shiny-react-upstream/2-inputs/srcts/main.tsx Upstream example (inputs) React entrypoint.
examples/shiny-react-upstream/2-inputs/srcts/TextInputCard.tsx Upstream example (inputs) input/output demo.
examples/shiny-react-upstream/2-inputs/srcts/SliderInputCard.tsx Upstream example (inputs) input/output demo.
examples/shiny-react-upstream/2-inputs/srcts/SelectInputCard.tsx Upstream example (inputs) input/output demo.
examples/shiny-react-upstream/2-inputs/srcts/RadioInputCard.tsx Upstream example (inputs) input/output demo.
examples/shiny-react-upstream/2-inputs/srcts/NumberInputCard.tsx Upstream example (inputs) input/output demo.
examples/shiny-react-upstream/2-inputs/srcts/InputOutputCard.tsx Upstream example (inputs) shared UI wrapper.
examples/shiny-react-upstream/2-inputs/srcts/FileInputCard.tsx Upstream example (inputs) file input demo.
examples/shiny-react-upstream/2-inputs/srcts/DateInputCard.tsx Upstream example (inputs) date input demo.
examples/shiny-react-upstream/2-inputs/srcts/CheckboxInputCard.tsx Upstream example (inputs) checkbox demo.
examples/shiny-react-upstream/2-inputs/srcts/Card.tsx Upstream example (inputs) shared UI wrapper.
examples/shiny-react-upstream/2-inputs/srcts/ButtonInputCard.tsx Upstream example (inputs) event-priority demo.
examples/shiny-react-upstream/2-inputs/srcts/BatchFormCard.tsx Upstream example (inputs) batch submission demo.
examples/shiny-react-upstream/2-inputs/srcts/App.tsx Upstream example (inputs) app composition.
examples/shiny-react-upstream/2-inputs/r/shinyreact.R Upstream example (inputs) R helpers.
examples/shiny-react-upstream/2-inputs/r/app.R Upstream example (inputs) R app.
examples/shiny-react-upstream/2-inputs/py/shinyreact.py Upstream example (inputs) Python helpers.
examples/shiny-react-upstream/2-inputs/py/app.py Upstream example (inputs) Python app.
examples/shiny-react-upstream/2-inputs/package.json Upstream example (inputs) build scripts/deps.
examples/shiny-react-upstream/2-inputs/README.md Upstream example (inputs) documentation.
examples/shiny-react-upstream/2-inputs/.gitignore Upstream example (inputs) local ignore rules.
examples/shiny-react-upstream/1-hello-world/tsconfig.json Upstream example (hello world) TS config.
examples/shiny-react-upstream/1-hello-world/srcts/main.tsx Upstream example (hello world) React entrypoint.
examples/shiny-react-upstream/1-hello-world/srcts/HelloWorldComponent.tsx Upstream example (hello world) basic hook usage.
examples/shiny-react-upstream/1-hello-world/r/shinyreact.R Upstream example (hello world) R helpers.
examples/shiny-react-upstream/1-hello-world/r/app.R Upstream example (hello world) R app.
examples/shiny-react-upstream/1-hello-world/py/shinyreact.py Upstream example (hello world) Python helpers.
examples/shiny-react-upstream/1-hello-world/py/app.py Upstream example (hello world) Python app.
examples/shiny-react-upstream/1-hello-world/package.json Upstream example (hello world) build scripts/deps.
examples/shiny-react-upstream/1-hello-world/README.md Upstream example (hello world) documentation.
examples/shiny-react-upstream/1-hello-world/.gitignore Upstream example (hello world) local ignore rules.
Makefile Adds js-test target to run Vitest.
.pre-commit-config.yaml Excludes generated dist/vendor dirs from whitespace/EOL hooks.
.gitignore Ignores built assets for upstream examples (pattern added).
Files not reviewed (1)
  • js/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread examples/shiny-react-upstream/8-modules/py/app.py
Comment thread js/src/shiny-react/ShinyModuleContext.tsx
Comment thread js/src/shiny-react/ShinyModuleContext.tsx
Comment thread js/src/shiny-react/ImageOutput.tsx
Comment thread examples/shiny-react-upstream/7-chat/py/app.py
Comment thread examples/shiny-react-upstream/5-shadcn/components.json
Comment thread examples/shiny-react-upstream/5-shadcn/package.json
Comment thread examples/shiny-react-upstream/8-modules/srcts-standard/main.tsx
schloerke added a commit that referenced this pull request Mar 18, 2026
- Await async post_message() in 8-modules example
- Update ShinyModuleContext JSDoc to reflect nesting behavior
- Clarify applyNamespace docs for empty string/null/undefined
- Add namespace prop JSDoc to ImageOutput component
- Remove debug print(env_file) in 7-chat example
- Fix tailwind.css path in 5-shadcn components.json
- Fix 5-shadcn package.json name/description, add private:true
- Fix indentation in 8-modules srcts-standard/main.tsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
schloerke added a commit that referenced this pull request Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants