diff --git a/packages/@glimmer-workspace/integration-tests/test/helpers/if-inline-test.ts b/packages/@glimmer-workspace/integration-tests/test/helpers/if-inline-test.ts
new file mode 100644
index 000000000..0d48fd49c
--- /dev/null
+++ b/packages/@glimmer-workspace/integration-tests/test/helpers/if-inline-test.ts
@@ -0,0 +1,197 @@
+import {
+ defineComponent,
+ jitSuite,
+ RenderTest,
+ strip,
+ test,
+ tracked,
+} from '@glimmer-workspace/integration-tests';
+
+class IfInlineTest extends RenderTest {
+ static suiteName = 'Helpers test: inline {{if}}';
+
+ @test
+ 'inline if helper updates when tracked property changes'() {
+ class Person {
+ @tracked isActive = false;
+
+ toggle() {
+ this.isActive = !this.isActive;
+ }
+ }
+
+ const person = new Person();
+
+ this.render(strip`
{{if this.person.isActive "Active" "Inactive"}}
`, { person });
+
+ this.assertHTML('Inactive
', 'Initial render shows inactive state');
+ this.assertStableRerender();
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML('Active
', 'Updates when tracked property changes to true');
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML('Inactive
', 'Updates when tracked property changes to false');
+ }
+
+ @test
+ 'inline if helper with only truthy value updates when tracked property changes'() {
+ class Person {
+ @tracked isActive = false;
+
+ toggle() {
+ this.isActive = !this.isActive;
+ }
+ }
+
+ const person = new Person();
+
+ this.render(strip`{{if this.person.isActive "Active"}}
`, { person });
+
+ this.assertHTML('', 'Initial render shows empty when false');
+ this.assertStableRerender();
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML('Active
', 'Updates when tracked property changes to true');
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML('', 'Updates when tracked property changes to false');
+ }
+
+ @test
+ 'inline if helper updates when component argument changes'() {
+ const TestComponent = defineComponent({}, '{{if @isActive "Active" "Inactive"}}');
+
+ this.render('', {
+ isActive: false,
+ TestComponent,
+ });
+
+ this.assertHTML('Inactive', 'Initial render shows inactive state');
+ this.assertStableRerender();
+
+ this.rerender({ isActive: true, TestComponent });
+ this.assertHTML('Active', 'Updates when argument changes to true');
+
+ this.rerender({ isActive: false, TestComponent });
+ this.assertHTML('Inactive', 'Updates when argument changes to false');
+ }
+
+ @test
+ 'inline if helper with components updates when tracked property changes'() {
+ class Person {
+ @tracked isActive = false;
+
+ toggle() {
+ this.isActive = !this.isActive;
+ }
+ }
+
+ const person = new Person();
+
+ const Ok = defineComponent({}, 'OK Component
');
+ const Ko = defineComponent({}, 'KO Component
');
+
+ // Create a component with Ok and Ko in scope
+ const TestContainer = defineComponent({ Ok, Ko }, '{{if @isActive Ok Ko}}
');
+
+ this.render('', { person, TestContainer });
+
+ this.assertHTML('', 'Initial render shows KO component');
+ this.assertStableRerender();
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML(
+ '',
+ 'Updates to OK component when tracked property changes to true'
+ );
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML(
+ '',
+ 'Updates to KO component when tracked property changes to false'
+ );
+ }
+
+ @test
+ 'inline if helper with components updates when component argument changes'() {
+ const Ok = defineComponent({}, 'OK Component
');
+ const Ko = defineComponent({}, 'KO Component
');
+
+ const TestComponent = defineComponent({ Ok, Ko }, '{{if @isOk Ok Ko}}');
+
+ this.render('', { isOk: false, TestComponent });
+
+ this.assertHTML('KO Component
', 'Initial render shows KO component');
+ this.assertStableRerender();
+
+ this.rerender({ isOk: true, TestComponent });
+ this.assertHTML(
+ 'OK Component
',
+ 'Updates to OK component when argument changes to true'
+ );
+
+ this.rerender({ isOk: false, TestComponent });
+ this.assertHTML(
+ 'KO Component
',
+ 'Updates to KO component when argument changes to false'
+ );
+ }
+
+ @test
+ 'comparison with block form if helper using components'() {
+ class Person {
+ @tracked isActive = false;
+
+ toggle() {
+ this.isActive = !this.isActive;
+ }
+ }
+
+ const person = new Person();
+
+ const Ok = defineComponent({}, 'OK Component
');
+ const Ko = defineComponent({}, 'KO Component
');
+
+ // Create a component with Ok and Ko in scope for both inline and block forms
+ const TestContainer = defineComponent(
+ { Ok, Ko },
+ `
+
+ Inline: {{if @isActive Ok Ko}}
+ Block: {{#if @isActive}}{{else}}{{/if}}
+
+ `
+ );
+
+ this.render('', { person, TestContainer });
+
+ this.assertHTML(
+ 'Inline:
KO Component
Block:
KO Component
',
+ 'Initial render both show KO component'
+ );
+ this.assertStableRerender();
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML(
+ 'Inline:
OK Component
Block:
OK Component
',
+ 'Both update to OK component when tracked property changes to true'
+ );
+
+ person.toggle();
+ this.rerender();
+ this.assertHTML(
+ 'Inline:
KO Component
Block:
KO Component
',
+ 'Both update to KO component when tracked property changes to false'
+ );
+ }
+}
+
+jitSuite(IfInlineTest);
diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts
index c27b0ca51..0e6b7aa48 100644
--- a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts
+++ b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts
@@ -285,10 +285,18 @@ APPEND_OPCODES.add(VM_IF_INLINE_OP, (vm) => {
vm.stack.push(
createComputeRef(() => {
- if (toBool(valueForRef(condition))) {
- return valueForRef(truthy);
+ // Explicitly consume the condition reference to ensure tracking
+ let conditionValue = valueForRef(condition);
+
+ // Also explicitly consume truthy and falsy references to ensure proper tracking
+ // when they are component references
+ let truthyValue = valueForRef(truthy);
+ let falsyValue = valueForRef(falsy);
+
+ if (toBool(conditionValue)) {
+ return truthyValue;
} else {
- return valueForRef(falsy);
+ return falsyValue;
}
})
);