Skip to content

Commit

Permalink
Ensure dynamic component invocations follow splattribute rules.
Browse files Browse the repository at this point in the history
When the `DynamicComponent` opcode was initial introduce, it did not
properly replicate the `SetComponentAttrs` toggling system that
`Ops.Component` has. This has the unfortunate result of making dynamic
component invocations not follow the established rules for
splattributes (invocation "wins" for all attributes other than `class`
and `class` is merged).

In this context `DynamicComponent` is specifically referring to angle
bracket invocation with either a named argument or path.
  • Loading branch information
rwjblue committed Sep 5, 2018
1 parent 25f84bd commit f370f2d
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 8 deletions.
19 changes: 11 additions & 8 deletions packages/@glimmer/opcode-compiler/lib/syntax.ts
Expand Up @@ -106,15 +106,18 @@ export function statementCompiler(): Compilers<WireFormat.Statement> {

STATEMENTS.add(Ops.DynamicComponent, (sexp: S.DynamicComponent, builder) => {
let [, definition, attrs, args, template] = sexp;

let block = builder.template(template);
let attrsBlock =
attrs.length > 0
? builder.inlineBlock({
statements: attrs,
parameters: EMPTY_ARRAY,
})
: null;

let attrsBlock = null;
if (attrs.length > 0) {
let wrappedAttrs: WireFormat.Statement[] = [
[Ops.ClientSideStatement, ClientSide.Ops.SetComponentAttrs, true],
...attrs,
[Ops.ClientSideStatement, ClientSide.Ops.SetComponentAttrs, false],
];

attrsBlock = builder.inlineBlock({ statements: wrappedAttrs, parameters: EMPTY_ARRAY });
}

builder.dynamicComponent(definition, attrsBlock, null, args, false, block, null);
});
Expand Down
54 changes: 54 additions & 0 deletions packages/@glimmer/test-helpers/lib/suites/components.ts
Expand Up @@ -198,6 +198,60 @@ export class BasicComponents extends RenderTest {
this.assertStableRerender();
}

@test({
kind: 'glimmer',
})
'invoking curried component with attributes via angle brackets (invocation attributes clobber)'() {
this.registerHelper('hash', (_positional, named) => named);
this.registerComponent('Glimmer', 'Foo', '<p data-foo="default"...attributes>hello world!</p>');
this.render({
layout: '<@stuff.Foo data-foo="invocation" />',
args: {
stuff: 'hash Foo=(component "Foo")',
},
});

this.assertHTML(`<div><p data-foo="invocation">hello world!</p></div>`);
this.assertStableRerender();
}

@test({
kind: 'glimmer',
})
'invoking curried component with attributes via angle brackets (invocation classes merge)'() {
this.registerHelper('hash', (_positional, named) => named);
this.registerComponent('Glimmer', 'Foo', '<p class="default" ...attributes>hello world!</p>');
this.render({
layout: '<@stuff.Foo class="invocation" />',
args: {
stuff: 'hash Foo=(component "Foo")',
},
});

this.assertHTML(`<div><p class="default invocation">hello world!</p></div>`);
this.assertStableRerender();
}

@test({
kind: 'glimmer',
})
'invoking dynamic component (named arg) via angle brackets supports attributes (invocation attributes clobber)'() {
this.registerComponent(
'Glimmer',
'Foo',
'<div data-test="default" ...attributes>hello world!</div>'
);
this.render({
layout: '<@foo data-test="foo"/>',
args: {
foo: 'component "Foo"',
},
});

this.assertHTML(`<div><div data-test="foo">hello world!</div></div>`);
this.assertStableRerender();
}

@test({
kind: 'glimmer',
})
Expand Down

0 comments on commit f370f2d

Please sign in to comment.