Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,41 @@ describe(
_.deployMode.NavigateBacktoEditor();
});

it("6a. Regression: programmatic defaultSwitchState changes sync isSwitchedOn for dependents (#41566)", function () {
// Bind dependent text widget to Switch1.isSwitchedOn
cy.openPropertyPane("textwidget");
cy.updateCodeInput(
".t--property-control-text",
`{{Toggler.isSwitchedOn}}`,
);

// Seed default to true and assert dependent reflects it
cy.openPropertyPane("switchwidget");
cy.get(".t--property-control-defaultstate input").last().then(($el) => {
if (!$el.is(":checked")) cy.wrap($el).click({ force: true });
});
cy.get(".t--widget-textwidget")
.scrollIntoView()
.should("contain", "true");

// User toggles the Switch once so meta.isSwitchedOn starts overriding the default
cy.get(`${formWidgetsPage.switchWidget} label`).first().click();
cy.get(".t--widget-textwidget")
.scrollIntoView()
.should("contain", "false");

// Programmatic change to defaultSwitchState should now propagate to dependents
cy.openPropertyPane("switchwidget");
cy.get(".t--property-control-defaultstate input").last().click();
cy.get(".t--widget-textwidget")
.scrollIntoView()
.should("contain", "true");

// Restore binding used by the next test
cy.openPropertyPane("textwidget");
cy.updateCodeInput(".t--property-control-text", `{{Toggler.isDirty}}`);
});

it("6. Check isDirty meta property", function () {
cy.openPropertyPane("textwidget");
cy.updateCodeInput(".t--property-control-text", `{{Toggler.isDirty}}`);
Expand Down
8 changes: 8 additions & 0 deletions app/client/src/widgets/SwitchWidget/widget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,14 @@ class SwitchWidget extends BaseWidget<SwitchWidgetProps, WidgetState> {
) {
this.props.updateWidgetMetaProperty("isDirty", false);
}

// Sync meta when isSwitchedOn changes programmatically so dependents don't see a stale value.
if (this.props.isSwitchedOn !== prevProps.isSwitchedOn) {
this.props.updateWidgetMetaProperty(
"isSwitchedOn",
this.props.isSwitchedOn,
);
Comment on lines +436 to +441
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

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

The sync block is gated by this.props.onChange, but onChange is optional and often unset in the repro (programmatic setValue/bindings can update defaultSwitchState without configuring the Switch’s own onChange). In that case this code never runs and isSwitchedOn meta can still go stale. Remove the && this.props.onChange guard (and, if you want to avoid extra dispatches, consider keying the sync specifically off defaultSwitchState changes instead of isSwitchedOn changes).

Copilot uses AI. Check for mistakes.
Comment on lines +436 to +441
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

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

This change fixes a regression scenario (programmatic updates after a manual toggle) but there doesn’t appear to be an automated test covering it. Please add a Cypress regression that: toggles the Switch once directly (to set meta), then changes it via Switch1.setValue(...)/another widget, and asserts dependents bound to {{Switch1.isSwitchedOn}} update correctly.

Copilot uses AI. Check for mistakes.
}
}

onChange = (isSwitchedOn: boolean) => {
Expand Down