Skip to content

feat: add support for dynamic theme in iframes#150

Merged
paodb merged 3 commits intomasterfrom
dynamic-theme-iframe
Mar 16, 2026
Merged

feat: add support for dynamic theme in iframes#150
paodb merged 3 commits intomasterfrom
dynamic-theme-iframe

Conversation

@javier-godoy
Copy link
Member

@javier-godoy javier-godoy commented Mar 5, 2026

Summary by CodeRabbit

  • New Features

    • Theme change notifications now include a ColorScheme and an optional dynamic theme object via a new event, improving the detail delivered to observers.
    • Observers are notified with an event-based API in addition to the legacy string notification.
  • Bug Fixes

    • Theme application reliably handles iframe-hosted content and uses view-transition when available for smoother updates.
  • Refactor

    • Legacy string callback deprecated; observer interface updated to preserve backward compatibility.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a ThemeChangeEvent and updates observer API; TabbedDemo now emits event-based theme changes; DynamicTheme.apply resolves the correct document at runtime (iframe vs top) and injects a local _document binding into the client JS, replacing direct document usages.

Changes

Cohort / File(s) Summary
Theme event types & observer
src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java, src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java
Adds ThemeChangeEvent carrying ColorScheme and optional DynamicTheme. Converts ThemeChangeObserver to provide default onThemeChange(String) (deprecated) and new onThemeChange(ThemeChangeEvent) methods; removes @FunctionalInterface.
Theme propagation in UI
src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java
After applying themes/color schemes, calls a new observeThemeChange(Component) helper to construct and dispatch ThemeChangeEvent to observers while retaining existing string-based notifications.
Client-side theme application
src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java
Resolves whether to use this.contentWindow.document (iframe) or top document at runtime, injects a _document binding into the injected JS, replaces document usages with _document, handles missing nodes, and uses _document.startViewTransition when available with fallback to immediate apply.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • mlopezFC
  • paodb
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add support for dynamic theme in iframes' is directly aligned with the PR's core objective and the primary changes made, which focus on enabling dynamic theme support within iframe contexts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dynamic-theme-iframe
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java (1)

142-145: Guard against null selection before applying dynamic theme.

Line 143 dereferences ev.getValue() directly. A defensive null guard avoids a potential NPE if the value is ever cleared/reset.

💡 Suggested change
       themeSelect.addValueChangeListener(ev -> {
-        ev.getValue().apply(this);
+        DynamicTheme selected = ev.getValue();
+        if (selected == null) {
+          return;
+        }
+        selected.apply(this);
         observeThemeChange(this);
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java` around
lines 142 - 145, The value-change listener on themeSelect dereferences
ev.getValue() without a null check, risking an NPE if the selection is cleared;
update the listener in TabbedDemo to first check that ev.getValue() != null
before calling ev.getValue().apply(this) and observeThemeChange(this), and skip
(or handle) the theme application when the new value is null so the code only
applies themes for non-null selections.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`:
- Around line 239-240: Remove the accidental debug logging in the theme
application code: locate the console.error(_document) call inside the
DynamicTheme implementation (the theme application method that also queries link
elements with querySelector(`link[href='${href}']`)) and delete that
console.error invocation so the document object is not logged on every theme
switch; leave the subsequent logic (link lookup and theme switching) untouched.

In `@src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java`:
- Around line 34-35: The ThemeChangeEvent constructor currently calls
super(source, true) marking the event as client-originated; change it to call
super(source, false) so the event is marked server-originated. Update the
ThemeChangeEvent(Component source, ColorScheme colorScheme, DynamicTheme
dynamicTheme) constructor to pass false to the ComponentEvent superclass (so
isFromClient() returns false) — this aligns with how
TabbedDemo.observeThemeChange() creates and dispatches the event from
server-side code.

---

Nitpick comments:
In `@src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java`:
- Around line 142-145: The value-change listener on themeSelect dereferences
ev.getValue() without a null check, risking an NPE if the selection is cleared;
update the listener in TabbedDemo to first check that ev.getValue() != null
before calling ev.getValue().apply(this) and observeThemeChange(this), and skip
(or handle) the theme application when the new value is null so the code only
applies themes for non-null selections.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 131e6910-66ff-47d7-ad78-9d3dbc6914ad

📥 Commits

Reviewing files that changed from the base of the PR and between 2d8a8ee and 34079ad.

📒 Files selected for processing (4)
  • src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java

@javier-godoy javier-godoy force-pushed the dynamic-theme-iframe branch from 34079ad to afc7306 Compare March 5, 2026 19:03
@javier-godoy javier-godoy requested review from mlopezFC and paodb March 5, 2026 19:10
@javier-godoy javier-godoy marked this pull request as ready for review March 5, 2026 19:10
Copy link
Member

@paodb paodb left a comment

Choose a reason for hiding this comment

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

Sonar reports a few warnings. I think we should consider them. In particular, the Deprecated annotation should include the since and/or forRemoval attributes, and the corresponding Deprecated Javadoc tag should be added.

@github-project-automation github-project-automation bot moved this from To Do to In Progress in Flowing Code Addons Mar 6, 2026
@javier-godoy javier-godoy force-pushed the dynamic-theme-iframe branch from afc7306 to eab7f53 Compare March 6, 2026 16:40
@javier-godoy javier-godoy requested a review from paodb March 6, 2026 16:44
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java (1)

483-484: Minor: Extra blank line.

There are two consecutive blank lines here. Consider removing one for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java` around
lines 483 - 484, Remove the extra blank line in TabbedDemo.java so there aren't
two consecutive empty lines; locate the consecutive blank lines inside the
TabbedDemo class (near the existing methods/fields around the 480s) and delete
one to keep a single blank line for consistent formatting.
src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java (1)

34-36: Minor: Trailing semicolon after method body.

The semicolon after the closing brace is syntactically valid but unconventional. Consider removing it for consistency.

   `@Deprecated`(forRemoval = true, since = "5.3.0")
   default void onThemeChange(String themeName) {
-  };
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java`
around lines 34 - 36, Remove the stray trailing semicolon after the default
method body in ThemeChangeObserver.onThemeChange; in the default method
declaration (default void onThemeChange(String themeName) { }) delete the
semicolon following the closing brace so the method ends with just the closing
brace, keeping the `@Deprecated` annotation and method signature intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java`:
- Around line 475-482: In observeThemeChange, replace the
DynamicTheme.isFeatureSupported() check with DynamicTheme.isFeatureInitialized()
before calling DynamicTheme.getCurrent() so you only call getCurrent() when the
feature is actually initialized; update the conditional in the
observeThemeChange method (which creates the ThemeChangeEvent and calls
collectThemeChangeObservers(...).forEach(...)) to use isFeatureInitialized() and
keep dynamicTheme null when not initialized.
- Around line 471-472: In setColorScheme, add a brief inline comment above the
two notification calls (collectThemeChangeObservers(...).forEach(observer ->
observer.onThemeChange(theme)); and observeThemeChange(component);) explaining
that observers are deliberately notified twice to support backward
compatibility: first via the deprecated onThemeChange(String theme) and then via
the new onThemeChange(ThemeChangeEvent) delivery; reference ThemeChangeObserver
and note both methods are default methods so implementers overriding both may
receive both notifications during migration and the first call can be removed
once the deprecated API is dropped.

---

Nitpick comments:
In `@src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java`:
- Around line 483-484: Remove the extra blank line in TabbedDemo.java so there
aren't two consecutive empty lines; locate the consecutive blank lines inside
the TabbedDemo class (near the existing methods/fields around the 480s) and
delete one to keep a single blank line for consistent formatting.

In `@src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java`:
- Around line 34-36: Remove the stray trailing semicolon after the default
method body in ThemeChangeObserver.onThemeChange; in the default method
declaration (default void onThemeChange(String themeName) { }) delete the
semicolon following the closing brace so the method ends with just the closing
brace, keeping the `@Deprecated` annotation and method signature intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fab3fd98-b705-4daf-b55e-c3c0b4bae1bc

📥 Commits

Reviewing files that changed from the base of the PR and between afc7306 and eab7f53.

📒 Files selected for processing (4)
  • src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java

@javier-godoy javier-godoy force-pushed the dynamic-theme-iframe branch from eab7f53 to 37f64d7 Compare March 6, 2026 19:38
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java (1)

236-246: ⚠️ Potential issue | 🟠 Major

The iframe path can blank the target document on the first switch.

If _document resolves to an iframe document, Line 240 skips missing links, but initialize(...) only injects the active stylesheet and doPrepare(...) still populates the outer document. That lets Lines 244-245 disable the current iframe stylesheet before the new one exists, leaving the iframe unthemed after the first switch. Please create/preload the selected href in _document before disabling the old theme, or make doPrepare() resolve the same document.

🔧 Minimal guard to avoid dropping all iframe theme styles
         const applyTheme = () => {
           ["lumo/lumo.css", "aura/aura.css"].forEach(href=> {
             let link = _document.querySelector(`link[href='${href}']`);
-            if (!link) return;
+            if (!link) {
+              if (href !== $0) return;
+              link = _document.createElement("link");
+              link.rel = "stylesheet";
+              link.href = href;
+              _document.head.prepend(link);
+            }
             if (href === $0) {
                if (link.rel === 'preload') link.rel = 'stylesheet';
                if (link.disabled) link.disabled = false;
             } else if (link.rel === 'stylesheet' && !link.disabled) {
                link.disabled = true;
             }
           });
         };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java` around
lines 236 - 246, applyTheme currently disables the old stylesheet in _document
before ensuring the new one exists, which can leave an iframe unthemed; update
the logic in applyTheme (and/or adjust doPrepare/initialize) so that the
selected href is created or preloaded in the same _document before disabling
other styles: specifically, when iterating in applyTheme for the target href
($0) ensure you create or insert a preload/link element into _document (or have
doPrepare return the iframe document rather than the outer document) and wait
for it to be available (or convert preload to stylesheet) before altering
link.rel or link.disabled on existing links to avoid temporarily removing all
iframe styles.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`:
- Around line 236-246: applyTheme currently disables the old stylesheet in
_document before ensuring the new one exists, which can leave an iframe
unthemed; update the logic in applyTheme (and/or adjust doPrepare/initialize) so
that the selected href is created or preloaded in the same _document before
disabling other styles: specifically, when iterating in applyTheme for the
target href ($0) ensure you create or insert a preload/link element into
_document (or have doPrepare return the iframe document rather than the outer
document) and wait for it to be available (or convert preload to stylesheet)
before altering link.rel or link.disabled on existing links to avoid temporarily
removing all iframe styles.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1bf228f7-9dd1-40b3-99be-8399e782b649

📥 Commits

Reviewing files that changed from the base of the PR and between eab7f53 and 37f64d7.

📒 Files selected for processing (4)
  • src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java
  • src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java

@javier-godoy
Copy link
Member Author

If _document resolves to an iframe document, Line 240 skips missing links, but initialize(...) only injects the active stylesheet and doPrepare(...) still populates the outer document. That lets Lines 244-245 disable the current iframe stylesheet before the new one exists, leaving the iframe unthemed after the first switch. Please create/preload the selected href in _document before disabling the old theme, or make doPrepare() resolve the same document.

Yes... I tested it quickly and assumed it was Aura theme, but now I realized that it was switching to the base theme instead.

@javier-godoy javier-godoy marked this pull request as draft March 6, 2026 19:52
@javier-godoy javier-godoy force-pushed the dynamic-theme-iframe branch from 37f64d7 to 602bf21 Compare March 12, 2026 20:00
@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`:
- Around line 228-258: The current apply() implementation (inside
component.getElement().executeJs where applyTheme is defined) can disable the
active theme before the new theme is created in an iframe; change applyTheme to
run in two passes against the iframe-aware _document: first ensure the selected
theme ($0) exists and is enabled in _document (create a <link> if missing or
convert preload→stylesheet and enable it), then in a second pass disable other
themes only after the chosen theme is present; also update doPrepare() to
preload into the same iframe-aware document (use the same iframe detection logic
that sets document/_document) so preloads happen inside the iframe rather than
only on the outer document.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cef1be75-1241-4a7c-9b52-828d422521a2

📥 Commits

Reviewing files that changed from the base of the PR and between 37f64d7 and 602bf21.

📒 Files selected for processing (1)
  • src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java

@javier-godoy javier-godoy marked this pull request as ready for review March 13, 2026 19:36
@paodb paodb merged commit 23cd4d0 into master Mar 16, 2026
3 checks passed
@paodb paodb deleted the dynamic-theme-iframe branch March 16, 2026 15:03
@github-project-automation github-project-automation bot moved this from In Progress to Pending release in Flowing Code Addons Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pending release

Development

Successfully merging this pull request may close these issues.

2 participants