Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix splattributes handling of type attribute. #1178

Closed
wants to merge 1 commit into from
Closed
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
9 changes: 8 additions & 1 deletion packages/@glimmer/compiler/lib/template-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,15 @@ export default class TemplateCompiler implements Processor<InputOps> {
// TODO: Assert no attributes
let typeAttr: Option<AST.AttrNode> = null;
let attrs = action.attributes;

for (let i = 0; i < attrs.length; i++) {
if (attrs[i].name === 'type') {
// Unlike most attributes, the `type` attribute can change how
// subsequent attributes are interpreted by the browser. To address
// this, in simple cases, we special case the `type` attribute to be set
// last. For elements with splattributes, where attribute order affects
// precedence, this re-ordering happens at runtime instead.
// See https://github.com/glimmerjs/glimmer-vm/pull/726
if (simple && attrs[i].name === 'type') {
typeAttr = attrs[i];
continue;
}
Expand Down
20 changes: 20 additions & 0 deletions packages/@glimmer/integration-tests/lib/suites/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,16 @@ export class BasicComponents extends RenderTest {
this.assertHTML('<div id="top"></div>');
}

@test({ kind: 'glimmer' })
'angle bracket invocation can allow invocation side to override the type attribute with ...attributes'() {
this.registerComponent('Glimmer', 'Qux', '<div type="qux" ...attributes />');
this.registerComponent('Glimmer', 'Bar', '<Qux type="bar" ...attributes />');
this.registerComponent('Glimmer', 'Foo', '<Bar type="foo" ...attributes />');

this.render('<Foo type="top" />');
this.assertHTML('<div type="top"></div>');
}

@test({ kind: 'glimmer' })
'angle bracket invocation can override invocation side attributes with ...attributes'() {
this.registerComponent('Glimmer', 'Qux', '<div ...attributes id="qux" />');
Expand All @@ -775,6 +785,16 @@ export class BasicComponents extends RenderTest {
this.assertHTML('<div id="qux"></div>');
}

@test({ kind: 'glimmer' })
'angle bracket invocation can override invocation side type attribute with ...attributes'() {
this.registerComponent('Glimmer', 'Qux', '<div ...attributes type="qux" />');
this.registerComponent('Glimmer', 'Bar', '<Qux ...attributes type="bar" />');
this.registerComponent('Glimmer', 'Foo', '<Bar ...attributes type="foo" />');

this.render('<Foo type="top" />');
this.assertHTML('<div type="qux"></div>');
}

@test({ kind: 'glimmer' })
'angle bracket invocation can forward classes before ...attributes to a nested component'() {
this.registerComponent('Glimmer', 'Qux', '<div class="qux" ...attributes />');
Expand Down
92 changes: 61 additions & 31 deletions packages/@glimmer/integration-tests/test/input-range-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,53 +76,83 @@ class EmberInputRangeComponent extends EmberishCurlyComponent {
type = 'range';
}

abstract class EmberComponentRangeTests extends RangeTests {
abstract component(): EmberishCurlyComponentFactory;

renderRange(value: number): void {
this.registerComponent('Curly', 'range-input', '', this.component());
this.render(`{{range-input max=max min=min value=value}}`, {
max: this.max,
min: this.min,
value,
});
}

assertRangeValue(value: number): void {
let attr = (this.element.firstChild as any)['value'];
this.assert.equal(attr, value.toString());
}
}

jitSuite(
class extends EmberComponentRangeTests {
class EmberComponentRangeTests extends RangeTests {
static suiteName = `Components - [emberjs/ember.js#15675] - type value min max`;

component(): EmberishCurlyComponentFactory {
return class extends EmberInputRangeComponent {
attributeBindings = ['type', 'value', 'min', 'max'];
} as any;
}

renderRange(value: number): void {
this.registerComponent('Curly', 'range-input', '', this.component());
this.render(`{{range-input max=max min=min value=value}}`, {
max: this.max,
min: this.min,
value,
});
}

assertRangeValue(value: number): void {
let attr = (this.element.firstChild as any)['value'];
this.assert.equal(attr, value.toString());
}
}
);

class BasicComponentImplicitAttributesRangeTest extends RangeTests {
attrs!: string;
jitSuite(
class BasicComponentImplicitAttributesRangeTest extends RangeTests {
static suiteName = `integration - GlimmerComponent - [emberjs/ember.js#15675] ...attributes <input type="range" value="%x" min="-5" max="50" />`;
attrs = 'type="range" value="%x" min="-5" max="50"';

renderRange(value: number): void {
this.registerComponent('Glimmer', 'RangeInput', '<input ...attributes/>');
this.render(`<RangeInput ${this.attrs.replace('%x', value.toString())} />`);
}
renderRange(value: number): void {
this.registerComponent('Glimmer', 'RangeInput', '<input ...attributes/>');
this.render(`<RangeInput ${this.attrs.replace('%x', value.toString())} />`);
}

assertRangeValue(value: number): void {
let attr = this.readDOMAttr('value');
this.assert.equal(attr, value.toString());
assertRangeValue(value: number): void {
let attr = this.readDOMAttr('value');
this.assert.equal(attr, value.toString());
}
}
}
);

jitSuite(
class extends BasicComponentImplicitAttributesRangeTest {
static suiteName = `integration - GlimmerComponent - [emberjs/ember.js#15675] ...attributes <input type="range" value="%x" min="-5" max="50" />`;
class BasicComponentSplattributesLastRangeTest extends RangeTests {
static suiteName = `integration - GlimmerComponent - [emberjs/ember.js#15675] ...attributes last <input type="range" value="%x" min="-5" max="50" />`;
attrs = 'type="range" value="%x" min="-5" max="50"';

renderRange(value: number): void {
this.registerComponent('Glimmer', 'RangeInput', '<input type="text" ...attributes/>');
this.render(`<RangeInput ${this.attrs.replace('%x', value.toString())} />`);
}

assertRangeValue(value: number): void {
let attr = this.readDOMAttr('value');
this.assert.equal(attr, value.toString());
}
}
);

jitSuite(
class BasicComponentSplattributesFirstRangeTest extends RangeTests {
static suiteName = `integration - GlimmerComponent - [emberjs/ember.js#15675] ...attributes first <input type="range" value="%x" min="-5" max="50" />`;
attrs = 'type="text" min="-5" max="50"';

renderRange(value: number): void {
this.registerComponent(
'Glimmer',
'RangeInput',
`<input ...attributes type="range" value="${value}" />`
);
this.render(`<RangeInput ${this.attrs.replace('%x', value.toString())} />`);
}

assertRangeValue(value: number): void {
let attr = this.readDOMAttr('value');
this.assert.equal(attr, value.toString());
}
}
);