Skip to content

Commit 1fd95f8

Browse files
Unwrap signals returned from Show's when callback
When when is a function like () => someSignal, Show used the returned Signal object directly in its truthiness check. Signal objects are always truthy, so the fallback branch was never shown — even when the signal's value was falsy. Show now unwraps the return value when it is a Signal, reading .value before the truthiness check. This matches the existing behavior for the non-callback form (when={someSignal}), which already reads .value. Assisted-By: devx/dca78f42-f55f-4065-b66c-fc3731bd0420
1 parent 1e3ab34 commit 1fd95f8

4 files changed

Lines changed: 52 additions & 9 deletions

File tree

.changeset/show-unwrap-callback.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@preact/signals-utils": patch
3+
---
4+
5+
Unwrap signals returned from `Show`'s `when` callback
6+
7+
When `when` is a function like `() => someSignal`, `Show` previously used the
8+
returned Signal object directly in its truthiness check. Since Signal objects
9+
are always truthy, this meant the fallback branch was never shown — even when
10+
the signal's value was `false`, `0`, `""`, or `null`.
11+
12+
`Show` now unwraps the return value when it's a Signal, reading `.value` before
13+
the truthiness check. This matches the existing behavior for the non-callback
14+
form (`when={someSignal}`), which already reads `.value`.

packages/devtools-ui/README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,18 @@ function MyCustomDevTools() {
106106

107107
### `mount(options)`
108108

109-
| Option | Type | Required | Description |
110-
| ------------ | ------------------------------------------------- | -------- | -------------------------------- |
111-
| `adapter` | `DevToolsAdapter` | Yes | The communication adapter to use |
112-
| `container` | `HTMLElement` | Yes | The DOM element to render into |
113-
| `hideHeader` | `boolean` | No | Hide the header bar |
109+
| Option | Type | Required | Description |
110+
| ------------ | ----------------------------------------------------- | -------- | -------------------------------- |
111+
| `adapter` | `DevToolsAdapter` | Yes | The communication adapter to use |
112+
| `container` | `HTMLElement` | Yes | The DOM element to render into |
113+
| `hideHeader` | `boolean` | No | Hide the header bar |
114114
| `initialTab` | `"updates" \| "performance" \| "timeline" \| "graph"` | No | Which tab to show initially |
115115

116116
### `DevToolsPanel`
117117

118-
| Prop | Type | Default | Description |
119-
| ------------ | ------------------------------------------------- | ----------- | ---------------------- |
120-
| `hideHeader` | `boolean` | `false` | Hide the header bar |
118+
| Prop | Type | Default | Description |
119+
| ------------ | ----------------------------------------------------- | ----------- | ---------------------- |
120+
| `hideHeader` | `boolean` | `false` | Hide the header bar |
121121
| `initialTab` | `"updates" \| "performance" \| "timeline" \| "graph"` | `"updates"` | Initial tab to display |
122122

123123
## Styling

packages/preact/utils/src/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ Item.displayName = "Item";
2020
export function Show<T = boolean>(
2121
props: ShowProps<T>
2222
): ComponentChildren | null {
23-
const value =
23+
const raw =
2424
typeof props.when === "function" ? props.when() : props.when.value;
25+
// Unwrap a signal returned by a `when` callback so `when={() => someSignal}`
26+
// checks the signal's value, not the signal object (which is always truthy).
27+
const value =
28+
raw instanceof Signal ? (raw as unknown as Signal<T>).value : raw;
2529
if (!value) {
2630
const fallback = props.fallback;
2731
return typeof fallback === "function" ? fallback() : fallback || null;

packages/preact/utils/test/browser/index.test.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,31 @@ describe("@preact/signals-utils", () => {
163163
// counter=2, class should update after remount
164164
expect(scratch.innerHTML).to.eq('<div class="val-2">content</div>');
165165
});
166+
167+
it("Should unwrap a signal returned from a when callback", () => {
168+
const toggle = signal(false);
169+
const Paragraph = (props: any) => <p>{props.children}</p>;
170+
act(() => {
171+
render(
172+
<Show when={() => toggle} fallback={<Paragraph>Hiding</Paragraph>}>
173+
<Paragraph>Showing</Paragraph>
174+
</Show>,
175+
scratch
176+
);
177+
});
178+
// Signal object is always truthy, but Show should unwrap and check .value
179+
expect(scratch.innerHTML).to.eq("<p>Hiding</p>");
180+
181+
act(() => {
182+
toggle.value = true;
183+
});
184+
expect(scratch.innerHTML).to.eq("<p>Showing</p>");
185+
186+
act(() => {
187+
toggle.value = false;
188+
});
189+
expect(scratch.innerHTML).to.eq("<p>Hiding</p>");
190+
});
166191
});
167192

168193
describe("<For />", () => {

0 commit comments

Comments
 (0)