Skip to content

feat: add support for dynamic theme in iframes#150

Draft
javier-godoy wants to merge 3 commits intomasterfrom
dynamic-theme-iframe
Draft

feat: add support for dynamic theme in iframes#150
javier-godoy wants to merge 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 color-scheme plus an optional dynamic theme object for richer updates.
    • Observers receive event-based theme updates across component trees.
  • Bug Fixes

    • Dynamic theme application now correctly handles content hosted inside iframes.
  • Refactor

    • Legacy string-based theme callback deprecated; event-based observer API added while preserving backward compatibility.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Walkthrough

Restructures theme-change propagation to publish a new ThemeChangeEvent and notify observers; updates TabbedDemo to emit that event after theme changes; modifies DynamicTheme.apply to resolve the correct document at runtime (iframe host vs top document) and use a local _document in the injected JS.

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 (encapsulates ColorScheme and optional DynamicTheme). Removes @FunctionalInterface, deprecates old onThemeChange(String), and adds a new default onThemeChange(ThemeChangeEvent) for event-based notifications while preserving backward compatibility.
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 that constructs and dispatches ThemeChangeEvent to ThemeChangeObservers (in addition to existing string-based notifications).
Client-side theme application
src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java
At runtime determines the appropriate document reference (iframe host vs top document), injects a local _document binding into the JS template, and replaces document usages with _document (including startViewTransition 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 accurately describes the main change: adding iframe support for dynamic themes. The changeset includes iframe-specific document resolution logic in DynamicTheme, theme event propagation updates, and backward compatibility changes—all centered on enabling dynamic themes in iframe contexts.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dynamic-theme-iframe

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

Comment on lines +34 to +35
public ThemeChangeEvent(Component source, ColorScheme colorScheme, DynamicTheme dynamicTheme) {
super(source, true);
Copy link

@coderabbitai coderabbitai bot Mar 5, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
fd 'ThemeChangeEvent.java$' --exec sed -n '31,38p' {}
fd 'TabbedDemo.java$' --exec sed -n '475,482p' {}

Repository: FlowingCode/CommonsDemo

Length of output: 697


Set fromClient to false for this event.

Line 35 marks the event as client-originated, but ThemeChangeEvent is instantiated from server-side code in TabbedDemo.observeThemeChange() and dispatched to observers without any client interaction. This breaks the semantic contract of ComponentEvent and can cause listener logic relying on isFromClient() to behave incorrectly.

🔧 Proposed fix
-    super(source, true);
+    super(source, false);
🤖 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/ThemeChangeEvent.java`
around lines 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.

Copy link
Member Author

Choose a reason for hiding this comment

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

@coderabbitai The event change is usually initiated from the client, but by the time the event is dispatched there is no clear indication of the actual source.

Copy link

Choose a reason for hiding this comment

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

@javier-godoy, that's a fair point — since theme changes are typically user-driven (client-initiated), defaulting to true is a reasonable approximation. The limitation is that if the theme is ever changed programmatically from server-side code, isFromClient() would return true incorrectly.

If you anticipate mixing both client- and server-initiated changes in the future, an alternative would be to thread fromClient as a constructor parameter:

public ThemeChangeEvent(Component source, ColorScheme colorScheme, DynamicTheme dynamicTheme, boolean fromClient) {
    super(source, fromClient);
    ...
}

That way, callers like observeThemeChange() can pass the appropriate value when the origin is known. That said, if theme changes are exclusively user-driven in practice, the current approach is acceptable. I'll leave it to your judgment.


🧠 Learnings used
Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 138
File: src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java:22-36
Timestamp: 2026-01-29T18:39:49.374Z
Learning: The file `src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java` contains both Flowing Code's license header and Vaadin's copyright notice because the ColorScheme code is copied from Vaadin's open source framework. Both headers are intentional and required for proper attribution.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java:1-38
Timestamp: 2026-02-04T19:18:59.956Z
Learning: For DynamicThemeInitializer.java (src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java), when loading the theme property from dynamic-theme.properties, use the value exactly as written (case-sensitive) after trimming whitespace. Do not apply any automatic case normalization (e.g., toUpperCase/toLowerCase). The value should be used to match the corresponding enum constant name precisely.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java:1-38
Timestamp: 2026-02-04T18:39:52.796Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeServiceInitListener.java`, configuration validation for the theme property should use a fail-fast approach. Invalid theme values should propagate IllegalArgumentException to cause service initialization to fail immediately, allowing developers to catch configuration errors during the build/test phase rather than masking them with defaults or logging.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 140
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java:70-73
Timestamp: 2026-02-02T19:45:32.988Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`, the methods `isFeatureInitialized()`, `getCurrent()`, and `initialize()` that call `VaadinSession.getCurrent()` are designed to be invoked only from within a valid Vaadin request context, where the session is guaranteed to be non-null. Null checks for the session are not required.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 149
File: src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java:205-214
Timestamp: 2026-03-02T18:40:21.008Z
Learning: Routing convention guideline: In FlowingCode/CommonsDemo demos (e.g., TabbedDemo.java), the first segment of routes must be a static, alphanumeric component or module name (e.g., /twincolgrid). Routes starting with pure symbols or dynamic numbers violate the convention. Apply this rule to all demo Java files under src/main/java/com/flowingcode/vaadin/addons/demo/. During reviews, verify that the first path segment in route mappings is ASCII alphanumeric and does not start with symbols or digits. If a route violates this, propose refactoring to a valid static segment (e.g., /moduleName) and update any related navigation or permissions logic accordingly.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll leave it to the reviewer's judgment.

Copy link

Choose a reason for hiding this comment

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

@javier-godoy, understood — I'll leave the note for the human reviewer to weigh in. The current true value is a reasonable approximation given that theme changes are typically user-driven; the alternative of threading fromClient through the constructor would offer more precision if mixed origins ever become a concern.


🧠 Learnings used
Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 138
File: src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java:22-36
Timestamp: 2026-01-29T18:39:49.374Z
Learning: The file `src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java` contains both Flowing Code's license header and Vaadin's copyright notice because the ColorScheme code is copied from Vaadin's open source framework. Both headers are intentional and required for proper attribution.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java:1-38
Timestamp: 2026-02-04T19:18:59.956Z
Learning: For DynamicThemeInitializer.java (src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java), when loading the theme property from dynamic-theme.properties, use the value exactly as written (case-sensitive) after trimming whitespace. Do not apply any automatic case normalization (e.g., toUpperCase/toLowerCase). The value should be used to match the corresponding enum constant name precisely.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java:1-38
Timestamp: 2026-02-04T18:39:52.796Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeServiceInitListener.java`, configuration validation for the theme property should use a fail-fast approach. Invalid theme values should propagate IllegalArgumentException to cause service initialization to fail immediately, allowing developers to catch configuration errors during the build/test phase rather than masking them with defaults or logging.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 140
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java:70-73
Timestamp: 2026-02-02T19:45:32.988Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`, the methods `isFeatureInitialized()`, `getCurrent()`, and `initialize()` that call `VaadinSession.getCurrent()` are designed to be invoked only from within a valid Vaadin request context, where the session is guaranteed to be non-null. Null checks for the session are not required.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 149
File: src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java:205-214
Timestamp: 2026-03-02T18:40:21.008Z
Learning: Routing convention guideline: In FlowingCode/CommonsDemo demos (e.g., TabbedDemo.java), the first segment of routes must be a static, alphanumeric component or module name (e.g., /twincolgrid). Routes starting with pure symbols or dynamic numbers violate the convention. Apply this rule to all demo Java files under src/main/java/com/flowingcode/vaadin/addons/demo/. During reviews, verify that the first path segment in route mappings is ASCII alphanumeric and does not start with symbols or digits. If a route violates this, propose refactoring to a valid static segment (e.g., /moduleName) and update any related navigation or permissions logic accordingly.

Copy link
Member

Choose a reason for hiding this comment

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

I think CodeRabbit raises a fair point here. The justification given in it's first message should be taken in consideration. Hardcoding the value to true might not accurately reflect the origin and could affect listeners relying on isFromClient(). But this is my opinion. On the other hand, we could leave the current implementation and go back and fix it if some of the concerns come true.

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

Choose a reason for hiding this comment

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

@javier-godoy as discussed in meeting, the parameter should be set as false when the event is initialized in observeThemeChange method.

@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

Comment on lines +475 to +482
private static void observeThemeChange(Component source) {
DynamicTheme dynamicTheme = null;
if (DynamicTheme.isFeatureSupported()) {
dynamicTheme = DynamicTheme.getCurrent();
}
ThemeChangeEvent event = new ThemeChangeEvent(source, false, getColorScheme(), dynamicTheme);
collectThemeChangeObservers(source).forEach(observer -> observer.onThemeChange(event));
}
Copy link

@coderabbitai coderabbitai bot Mar 6, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd 'DynamicTheme.java$' --exec cat -n {}

Repository: FlowingCode/CommonsDemo

Length of output: 10202


🏁 Script executed:

fd 'TabbedDemo.java$' --exec cat -n {} | sed -n '130,140p'

Repository: FlowingCode/CommonsDemo

Length of output: 558


Use isFeatureInitialized() instead of isFeatureSupported() for consistency.

Line 477 checks DynamicTheme.isFeatureSupported() before calling getCurrent(), while line 134 correctly uses DynamicTheme.isFeatureInitialized(). These are distinct methods:

  • isFeatureSupported() - checks Vaadin version >= 25
  • isFeatureInitialized() - checks both version support and session initialization

Since getCurrent() returns null if the feature has not been initialized (per its documentation), the check at line 477 should use isFeatureInitialized() to match line 134 and ensure the theme is actually available before use.

🤖 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 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.

Copy link
Member Author

Choose a reason for hiding this comment

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

@coderabbitai I disagree. Since getCurrent() returns null if the feature has not been initialized, I only have to check if the feature is supported (in order to avoid the exception). Note that isFeatureInitialized already checks getCurrent()!=null, thus modifying this block to call isFeatureInitialized would be equivalent to the following redundant code:

    DynamicTheme dynamicTheme = null;
    if (DynamicTheme.isFeatureSupported() && DynamicTheme.getCurrent()!=null) {
      dynamicTheme = DynamicTheme.getCurrent();
    }

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd 'DynamicTheme.java$' --exec grep -n 'isFeatureSupported\|isFeatureInitialized\|getCurrent' {}

Length of output: 804


@javier-godoy, you're right. Looking at the implementation, getCurrent() (line 97–99) does not guard on isFeatureSupported() — it directly returns VaadinSession.getCurrent().getAttribute(DynamicTheme.class), which safely returns null when the feature hasn't been initialized. The isFeatureSupported() check in observeThemeChange is only needed to guard methods that do throw when unsupported (like apply()).

As you noted, substituting isFeatureInitialized() — which is defined as isFeatureSupported() && getCurrent() != null — would introduce a redundant implicit call to getCurrent(), making the block equivalent to calling it twice. The current code is correct and non-redundant. I withdraw my suggestion.


✏️ Learnings added
Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 150
File: src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java:475-482
Timestamp: 2026-03-06T19:42:42.656Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`, `getCurrent()` (line 97-99) does NOT check `isFeatureSupported()` internally; it directly returns the session attribute and safely returns null when the feature is not initialized. Only methods like `apply()` throw when `isFeatureSupported()` is false. Therefore, checking `isFeatureSupported()` before calling `getCurrent()` is sufficient and correct — using `isFeatureInitialized()` instead would be redundant, as it internally calls `getCurrent() != null`, causing an implicit double invocation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 140
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java:70-73
Timestamp: 2026-02-02T19:45:32.988Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java`, the methods `isFeatureInitialized()`, `getCurrent()`, and `initialize()` that call `VaadinSession.getCurrent()` are designed to be invoked only from within a valid Vaadin request context, where the session is guaranteed to be non-null. Null checks for the session are not required.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 138
File: src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java:22-36
Timestamp: 2026-01-29T18:39:49.374Z
Learning: The file `src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java` contains both Flowing Code's license header and Vaadin's copyright notice because the ColorScheme code is copied from Vaadin's open source framework. Both headers are intentional and required for proper attribution.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java:1-38
Timestamp: 2026-02-04T18:39:52.796Z
Learning: In `src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeServiceInitListener.java`, configuration validation for the theme property should use a fail-fast approach. Invalid theme values should propagate IllegalArgumentException to cause service initialization to fail immediately, allowing developers to catch configuration errors during the build/test phase rather than masking them with defaults or logging.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java:1-38
Timestamp: 2026-02-04T19:18:59.956Z
Learning: For DynamicThemeInitializer.java (src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java), when loading the theme property from dynamic-theme.properties, use the value exactly as written (case-sensitive) after trimming whitespace. Do not apply any automatic case normalization (e.g., toUpperCase/toLowerCase). The value should be used to match the corresponding enum constant name precisely.

Learnt from: javier-godoy
Repo: FlowingCode/CommonsDemo PR: 149
File: src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java:205-214
Timestamp: 2026-03-02T18:40:21.008Z
Learning: Routing convention guideline: In FlowingCode/CommonsDemo demos (e.g., TabbedDemo.java), the first segment of routes must be a static, alphanumeric component or module name (e.g., /twincolgrid). Routes starting with pure symbols or dynamic numbers violate the convention. Apply this rule to all demo Java files under src/main/java/com/flowingcode/vaadin/addons/demo/. During reviews, verify that the first path segment in route mappings is ASCII alphanumeric and does not start with symbols or digits. If a route violates this, propose refactoring to a valid static segment (e.g., /moduleName) and update any related navigation or permissions logic accordingly.

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

sonarqubecloud bot commented Mar 6, 2026

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants