diff --git a/packages/@ember/-internals/glimmer/index.ts b/packages/@ember/-internals/glimmer/index.ts
index e936f345548..18fc753c6ea 100644
--- a/packages/@ember/-internals/glimmer/index.ts
+++ b/packages/@ember/-internals/glimmer/index.ts
@@ -265,8 +265,8 @@
export { default as RootTemplate } from './lib/templates/root';
export { default as template } from './lib/template';
export { default as Checkbox } from './lib/components/checkbox';
-export { default as TextField } from './lib/components/text_field';
-export { default as TextArea } from './lib/components/text_area';
+export { default as TextField } from './lib/components/text-field';
+export { default as TextArea } from './lib/components/textarea';
export { default as LinkComponent } from './lib/components/link-to';
export { default as Component, ROOT_REF } from './lib/component';
export { default as Helper, helper } from './lib/helper';
diff --git a/packages/@ember/-internals/glimmer/lib/components/input.ts b/packages/@ember/-internals/glimmer/lib/components/input.ts
new file mode 100644
index 00000000000..f47ee593196
--- /dev/null
+++ b/packages/@ember/-internals/glimmer/lib/components/input.ts
@@ -0,0 +1,177 @@
+/**
+@module @ember/component
+*/
+import { computed } from '@ember/-internals/metal';
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
+import { assert } from '@ember/debug';
+import { DEBUG } from '@glimmer/env';
+import Component from '../component';
+
+let Input;
+
+if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ /**
+ The `{{input}}` helper lets you create an HTML `` component.
+ It causes a `TextField` component to be rendered. For more info,
+ see the [TextField](/api/ember/release/classes/TextField) docs and
+ the [templates guide](https://guides.emberjs.com/release/templates/input-helpers/).
+
+ ```handlebars
+ {{input value="987"}}
+ ```
+
+ renders as:
+
+ ```HTML
+
+ ```
+
+ ### Text field
+
+ If no `type` option is specified, a default of type 'text' is used.
+ Many of the standard HTML attributes may be passed to this helper.
+
+
`readonly`
`required`
`autofocus`
+
`value`
`placeholder`
`disabled`
+
`size`
`tabindex`
`maxlength`
+
`name`
`min`
`max`
+
`pattern`
`accept`
`autocomplete`
+
`autosave`
`formaction`
`formenctype`
+
`formmethod`
`formnovalidate`
`formtarget`
+
`height`
`inputmode`
`multiple`
+
`step`
`width`
`form`
+
`selectionDirection`
`spellcheck`
+
+ When set to a quoted string, these values will be directly applied to the HTML
+ element. When left unquoted, these values will be bound to a property on the
+ template's current rendering context (most typically a controller instance).
+ A very common use of this helper is to bind the `value` of an input to an Object's attribute:
+
+ ```handlebars
+ Search:
+ {{input value=searchWord}}
+ ```
+
+ In this example, the initial value in the `` will be set to the value of `searchWord`.
+ If the user changes the text, the value of `searchWord` will also be updated.
+
+ ### Actions
+
+ The helper can send multiple actions based on user events.
+ The action property defines the action which is sent when
+ the user presses the return key.
+
+ ```handlebars
+ {{input action="submit"}}
+ ```
+
+ The helper allows some user events to send actions.
+
+ * `enter`
+ * `insert-newline`
+ * `escape-press`
+ * `focus-in`
+ * `focus-out`
+ * `key-press`
+ * `key-up`
+
+ For example, if you desire an action to be sent when the input is blurred,
+ you only need to setup the action name to the event name property.
+
+ ```handlebars
+ {{input focus-out="alertMessage"}}
+ ```
+ See more about [Text Support Actions](/api/ember/release/classes/TextField)
+
+ ### Extending `TextField`
+
+ Internally, `{{input type="text"}}` creates an instance of `TextField`, passing
+ arguments from the helper to `TextField`'s `create` method. You can extend the
+ capabilities of text inputs in your applications by reopening this class. For example,
+ if you are building a Bootstrap project where `data-*` attributes are used, you
+ can add one to the `TextField`'s `attributeBindings` property:
+
+ ```javascript
+ import TextField from '@ember/component/text-field';
+ TextField.reopen({
+ attributeBindings: ['data-error']
+ });
+ ```
+
+ Keep in mind when writing `TextField` subclasses that `TextField`
+ itself extends `Component`. Expect isolated component semantics, not
+ legacy 1.x view semantics (like `controller` being present).
+ See more about [Ember components](/api/ember/release/classes/Component)
+
+ ### Checkbox
+
+ Checkboxes are special forms of the `{{input}}` helper. To create a ``:
+
+ ```handlebars
+ Emberize Everything:
+ {{input type="checkbox" name="isEmberized" checked=isEmberized}}
+ ```
+
+ This will bind checked state of this checkbox to the value of `isEmberized` -- if either one changes,
+ it will be reflected in the other.
+
+ The following HTML attributes can be set via the helper:
+
+ * `checked`
+ * `disabled`
+ * `tabindex`
+ * `indeterminate`
+ * `name`
+ * `autofocus`
+ * `form`
+
+ ### Extending `Checkbox`
+
+ Internally, `{{input type="checkbox"}}` creates an instance of `Checkbox`, passing
+ arguments from the helper to `Checkbox`'s `create` method. You can extend the
+ capablilties of checkbox inputs in your applications by reopening this class. For example,
+ if you wanted to add a css class to all checkboxes in your application:
+
+ ```javascript
+ import Checkbox from '@ember/component/checkbox';
+
+ Checkbox.reopen({
+ classNames: ['my-app-checkbox']
+ });
+ ```
+
+ @method input
+ @for Ember.Templates.helpers
+ @param {Hash} options
+ @public
+ */
+ const Input = Component.extend({
+ tagName: '',
+
+ isCheckbox: computed('type', function(this: { type?: unknown }) {
+ return this.type === 'checkbox';
+ })
+ });
+
+ Input.toString = () => '@ember/component/input';
+
+ if (DEBUG) {
+ const UNSET = {};
+
+ Input.reopen({
+ value: UNSET,
+
+ didReceiveAttrs() {
+ this._super();
+
+ assert(
+ "`` is not supported; " +
+ "please use `` instead.",
+ !(this.type === 'checkbox' && this.value !== UNSET)
+ );
+ }
+ });
+ }
+}
+
+export default Input;
diff --git a/packages/@ember/-internals/glimmer/lib/components/text_field.ts b/packages/@ember/-internals/glimmer/lib/components/text-field.ts
similarity index 100%
rename from packages/@ember/-internals/glimmer/lib/components/text_field.ts
rename to packages/@ember/-internals/glimmer/lib/components/text-field.ts
diff --git a/packages/@ember/-internals/glimmer/lib/components/text_area.ts b/packages/@ember/-internals/glimmer/lib/components/textarea.ts
similarity index 100%
rename from packages/@ember/-internals/glimmer/lib/components/text_area.ts
rename to packages/@ember/-internals/glimmer/lib/components/textarea.ts
diff --git a/packages/@ember/-internals/glimmer/lib/resolver.ts b/packages/@ember/-internals/glimmer/lib/resolver.ts
index 00b91f9c100..225fb452ae5 100644
--- a/packages/@ember/-internals/glimmer/lib/resolver.ts
+++ b/packages/@ember/-internals/glimmer/lib/resolver.ts
@@ -319,7 +319,7 @@ export default class RuntimeResolver implements IRuntimeResolver
-) {
- let definition = builder.compiler['resolver'].lookupComponentDefinition(type, builder.referrer);
- builder.component.static(definition!, [params, hashToArgs(hash), null, null]);
- return true;
-}
-
-/**
- The `{{input}}` helper lets you create an HTML `` component.
- It causes a `TextField` component to be rendered. For more info,
- see the [TextField](/api/ember/release/classes/TextField) docs and
- the [templates guide](https://guides.emberjs.com/release/templates/input-helpers/).
-
- ```handlebars
- {{input value="987"}}
- ```
-
- renders as:
-
- ```HTML
-
- ```
-
- ### Text field
-
- If no `type` option is specified, a default of type 'text' is used.
- Many of the standard HTML attributes may be passed to this helper.
-
-
`readonly`
`required`
`autofocus`
-
`value`
`placeholder`
`disabled`
-
`size`
`tabindex`
`maxlength`
-
`name`
`min`
`max`
-
`pattern`
`accept`
`autocomplete`
-
`autosave`
`formaction`
`formenctype`
-
`formmethod`
`formnovalidate`
`formtarget`
-
`height`
`inputmode`
`multiple`
-
`step`
`width`
`form`
-
`selectionDirection`
`spellcheck`
-
- When set to a quoted string, these values will be directly applied to the HTML
- element. When left unquoted, these values will be bound to a property on the
- template's current rendering context (most typically a controller instance).
- A very common use of this helper is to bind the `value` of an input to an Object's attribute:
-
- ```handlebars
- Search:
- {{input value=searchWord}}
- ```
-
- In this example, the initial value in the `` will be set to the value of `searchWord`.
- If the user changes the text, the value of `searchWord` will also be updated.
-
- ### Actions
-
- The helper can send multiple actions based on user events.
- The action property defines the action which is sent when
- the user presses the return key.
-
- ```handlebars
- {{input action="submit"}}
- ```
-
- The helper allows some user events to send actions.
-
- * `enter`
- * `insert-newline`
- * `escape-press`
- * `focus-in`
- * `focus-out`
- * `key-press`
- * `key-up`
-
- For example, if you desire an action to be sent when the input is blurred,
- you only need to setup the action name to the event name property.
-
- ```handlebars
- {{input focus-out="alertMessage"}}
- ```
- See more about [Text Support Actions](/api/ember/release/classes/TextField)
-
- ### Extending `TextField`
-
- Internally, `{{input type="text"}}` creates an instance of `TextField`, passing
- arguments from the helper to `TextField`'s `create` method. You can extend the
- capabilities of text inputs in your applications by reopening this class. For example,
- if you are building a Bootstrap project where `data-*` attributes are used, you
- can add one to the `TextField`'s `attributeBindings` property:
-
- ```javascript
- import TextField from '@ember/component/text-field';
- TextField.reopen({
- attributeBindings: ['data-error']
- });
- ```
-
- Keep in mind when writing `TextField` subclasses that `TextField`
- itself extends `Component`. Expect isolated component semantics, not
- legacy 1.x view semantics (like `controller` being present).
- See more about [Ember components](/api/ember/release/classes/Component)
-
- ### Checkbox
-
- Checkboxes are special forms of the `{{input}}` helper. To create a ``:
-
- ```handlebars
- Emberize Everything:
- {{input type="checkbox" name="isEmberized" checked=isEmberized}}
- ```
-
- This will bind checked state of this checkbox to the value of `isEmberized` -- if either one changes,
- it will be reflected in the other.
-
- The following HTML attributes can be set via the helper:
-
- * `checked`
- * `disabled`
- * `tabindex`
- * `indeterminate`
- * `name`
- * `autofocus`
- * `form`
-
- ### Extending `Checkbox`
-
- Internally, `{{input type="checkbox"}}` creates an instance of `Checkbox`, passing
- arguments from the helper to `Checkbox`'s `create` method. You can extend the
- capablilties of checkbox inputs in your applications by reopening this class. For example,
- if you wanted to add a css class to all checkboxes in your application:
-
- ```javascript
- import Checkbox from '@ember/component/checkbox';
-
- Checkbox.reopen({
- classNames: ['my-app-checkbox']
- });
- ```
-
- @method input
- @for Ember.Templates.helpers
- @param {Hash} options
- @public
-*/
-
-export function inputMacro(
- _name: string,
+export let inputMacro: (
+ name: string,
params: Option,
hash: Option,
builder: OpcodeBuilder
-) {
- if (params === null) {
- params = [];
+) => boolean;
+
+if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ if (DEBUG) {
+ inputMacro = () => {
+ throw unreachable();
+ }
}
- if (hash !== null) {
- let keys = hash[0];
- let values = hash[1];
- let typeIndex = keys.indexOf('type');
-
- if (typeIndex > -1) {
- let typeArg = values[typeIndex];
- if (Array.isArray(typeArg)) {
- // there is an AST plugin that converts this to an expression
- // it really should just compile in the component call too.
- let inputTypeExpr = params[0] as WireFormat.Expression;
- builder.dynamicComponent(inputTypeExpr, null, params.slice(1), hash, true, null, null);
- return true;
- }
- if (typeArg === 'checkbox') {
- assert(
- "{{input type='checkbox'}} does not support setting `value=someBooleanValue`; " +
- 'you must use `checked=someBooleanValue` instead.',
- keys.indexOf('value') === -1
- );
- wrapComponentClassAttribute(hash);
- return buildSyntax('-checkbox', params, hash, builder);
+} else {
+ /**
+ The `{{input}}` helper lets you create an HTML `` component.
+ It causes a `TextField` component to be rendered. For more info,
+ see the [TextField](/api/ember/release/classes/TextField) docs and
+ the [templates guide](https://guides.emberjs.com/release/templates/input-helpers/).
+
+ ```handlebars
+ {{input value="987"}}
+ ```
+
+ renders as:
+
+ ```HTML
+
+ ```
+
+ ### Text field
+
+ If no `type` option is specified, a default of type 'text' is used.
+ Many of the standard HTML attributes may be passed to this helper.
+
+
`readonly`
`required`
`autofocus`
+
`value`
`placeholder`
`disabled`
+
`size`
`tabindex`
`maxlength`
+
`name`
`min`
`max`
+
`pattern`
`accept`
`autocomplete`
+
`autosave`
`formaction`
`formenctype`
+
`formmethod`
`formnovalidate`
`formtarget`
+
`height`
`inputmode`
`multiple`
+
`step`
`width`
`form`
+
`selectionDirection`
`spellcheck`
+
+ When set to a quoted string, these values will be directly applied to the HTML
+ element. When left unquoted, these values will be bound to a property on the
+ template's current rendering context (most typically a controller instance).
+ A very common use of this helper is to bind the `value` of an input to an Object's attribute:
+
+ ```handlebars
+ Search:
+ {{input value=searchWord}}
+ ```
+
+ In this example, the initial value in the `` will be set to the value of `searchWord`.
+ If the user changes the text, the value of `searchWord` will also be updated.
+
+ ### Actions
+
+ The helper can send multiple actions based on user events.
+ The action property defines the action which is sent when
+ the user presses the return key.
+
+ ```handlebars
+ {{input action="submit"}}
+ ```
+
+ The helper allows some user events to send actions.
+
+ * `enter`
+ * `insert-newline`
+ * `escape-press`
+ * `focus-in`
+ * `focus-out`
+ * `key-press`
+ * `key-up`
+
+ For example, if you desire an action to be sent when the input is blurred,
+ you only need to setup the action name to the event name property.
+
+ ```handlebars
+ {{input focus-out="alertMessage"}}
+ ```
+ See more about [Text Support Actions](/api/ember/release/classes/TextField)
+
+ ### Extending `TextField`
+
+ Internally, `{{input type="text"}}` creates an instance of `TextField`, passing
+ arguments from the helper to `TextField`'s `create` method. You can extend the
+ capabilities of text inputs in your applications by reopening this class. For example,
+ if you are building a Bootstrap project where `data-*` attributes are used, you
+ can add one to the `TextField`'s `attributeBindings` property:
+
+ ```javascript
+ import TextField from '@ember/component/text-field';
+ TextField.reopen({
+ attributeBindings: ['data-error']
+ });
+ ```
+
+ Keep in mind when writing `TextField` subclasses that `TextField`
+ itself extends `Component`. Expect isolated component semantics, not
+ legacy 1.x view semantics (like `controller` being present).
+ See more about [Ember components](/api/ember/release/classes/Component)
+
+ ### Checkbox
+
+ Checkboxes are special forms of the `{{input}}` helper. To create a ``:
+
+ ```handlebars
+ Emberize Everything:
+ {{input type="checkbox" name="isEmberized" checked=isEmberized}}
+ ```
+
+ This will bind checked state of this checkbox to the value of `isEmberized` -- if either one changes,
+ it will be reflected in the other.
+
+ The following HTML attributes can be set via the helper:
+
+ * `checked`
+ * `disabled`
+ * `tabindex`
+ * `indeterminate`
+ * `name`
+ * `autofocus`
+ * `form`
+
+ ### Extending `Checkbox`
+
+ Internally, `{{input type="checkbox"}}` creates an instance of `Checkbox`, passing
+ arguments from the helper to `Checkbox`'s `create` method. You can extend the
+ capablilties of checkbox inputs in your applications by reopening this class. For example,
+ if you wanted to add a css class to all checkboxes in your application:
+
+ ```javascript
+ import Checkbox from '@ember/component/checkbox';
+
+ Checkbox.reopen({
+ classNames: ['my-app-checkbox']
+ });
+ ```
+
+ @method input
+ @for Ember.Templates.helpers
+ @param {Hash} options
+ @public
+ */
+
+ function buildSyntax(
+ type: string,
+ params: any[],
+ hash: any,
+ builder: OpcodeBuilder
+ ) {
+ let definition = builder.compiler['resolver'].lookupComponentDefinition(type, builder.referrer);
+ builder.component.static(definition!, [params, hashToArgs(hash), null, null]);
+ return true;
+ }
+
+ inputMacro = function inputMacro(
+ _name: string,
+ params: Option,
+ hash: Option,
+ builder: OpcodeBuilder
+ ) {
+ if (params === null) {
+ params = [];
+ }
+ if (hash !== null) {
+ let keys = hash[0];
+ let values = hash[1];
+ let typeIndex = keys.indexOf('type');
+
+ if (typeIndex > -1) {
+ let typeArg = values[typeIndex];
+ if (Array.isArray(typeArg)) {
+ // there is an AST plugin that converts this to an expression
+ // it really should just compile in the component call too.
+ let inputTypeExpr = params[0] as WireFormat.Expression;
+ builder.dynamicComponent(inputTypeExpr, null, params.slice(1), hash, true, null, null);
+ return true;
+ }
+ if (typeArg === 'checkbox') {
+ assert(
+ "`{{input type='checkbox' value=...}}` is not supported; " +
+ "please use `{{input type='checkbox' checked=...}}` instead.",
+ keys.indexOf('value') === -1
+ );
+ wrapComponentClassAttribute(hash);
+ return buildSyntax('-checkbox', params, hash, builder);
+ }
}
}
+ return buildSyntax('-text-field', params, hash, builder);
}
- return buildSyntax('-text-field', params, hash, builder);
}
diff --git a/packages/@ember/-internals/glimmer/lib/templates/input.d.ts b/packages/@ember/-internals/glimmer/lib/templates/input.d.ts
new file mode 100644
index 00000000000..786bb5e91a8
--- /dev/null
+++ b/packages/@ember/-internals/glimmer/lib/templates/input.d.ts
@@ -0,0 +1,3 @@
+import { Factory } from '../template';
+declare const TEMPLATE: Factory;
+export default TEMPLATE;
diff --git a/packages/@ember/-internals/glimmer/lib/templates/input.hbs b/packages/@ember/-internals/glimmer/lib/templates/input.hbs
new file mode 100644
index 00000000000..3c66f23252f
--- /dev/null
+++ b/packages/@ember/-internals/glimmer/lib/templates/input.hbs
@@ -0,0 +1,70 @@
+{{~#if this.isCheckbox~}}
+
+{{~else~}}
+
+{{~/if~}}
+
diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/contextual-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/contextual-components-test.js
index 8dbbc40513d..fa1b777c336 100644
--- a/packages/@ember/-internals/glimmer/tests/integration/components/contextual-components-test.js
+++ b/packages/@ember/-internals/glimmer/tests/integration/components/contextual-components-test.js
@@ -1217,12 +1217,38 @@ moduleFor(
this.assertText('ab');
}
- ['@test GH#14632 give useful warning when calling contextual components with input as a name']() {
+ ['@feature(!ember-glimmer-angle-bracket-built-ins) GH#14632 give useful warning when calling contextual components with input as a name']() {
expectAssertion(() => {
this.render('{{component (component "input" type="text")}}');
}, 'Invoking `{{input}}` using angle bracket syntax or `component` helper is not yet supported.');
}
+ ['@feature(ember-glimmer-angle-bracket-built-ins) it can invoke input component']() {
+ this.render('{{component (component "input" type="text" value=value)}}', {
+ value: 'foo',
+ });
+
+ this.assertComponentElement(this.firstChild, {
+ tagName: 'input',
+ attrs: {
+ class: 'ember-text-field ember-view',
+ type: 'text',
+ },
+ });
+
+ this.assert.strictEqual('foo', this.firstChild.value);
+
+ this.assertStableRerender();
+
+ runTask(() => this.context.set('value', 'bar'));
+
+ this.assert.strictEqual('bar', this.firstChild.value);
+
+ runTask(() => this.context.set('value', 'foo'));
+
+ this.assert.strictEqual('foo', this.firstChild.value);
+ }
+
['@feature(!ember-glimmer-angle-bracket-built-ins) GH#14632 give useful warning when calling contextual components with textarea as a name']() {
expectAssertion(() => {
this.render('{{component (component "textarea")}}');
diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/input-angle-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/input-angle-test.js
new file mode 100644
index 00000000000..61aa01c5e9a
--- /dev/null
+++ b/packages/@ember/-internals/glimmer/tests/integration/components/input-angle-test.js
@@ -0,0 +1,1105 @@
+import { RenderingTestCase, moduleFor, runDestroy, runTask } from 'internal-test-helpers';
+
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
+import { assign } from '@ember/polyfills';
+import { set } from '@ember/-internals/metal';
+import { jQuery } from '@ember/-internals/views';
+
+import { Component } from '../../utils/helpers';
+
+if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ class InputRenderingTest extends RenderingTestCase {
+ $input() {
+ return this.$('input');
+ }
+
+ inputID() {
+ return this.$input().prop('id');
+ }
+
+ assertDisabled() {
+ this.assert.ok(this.$('input').prop('disabled'), 'The input is disabled');
+ }
+
+ assertNotDisabled() {
+ this.assert.ok(this.$('input').is(':not(:disabled)'), 'The input is not disabled');
+ }
+
+ assertInputId(expectedId) {
+ this.assert.equal(this.inputID(), expectedId, 'the input id should be `expectedId`');
+ }
+
+ assertSingleInput() {
+ this.assert.equal(this.$('input').length, 1, 'A single text field was inserted');
+ }
+
+ assertSingleCheckbox() {
+ this.assert.equal(this.$('input[type=checkbox]').length, 1, 'A single checkbox is added');
+ }
+
+ assertCheckboxIsChecked() {
+ this.assert.equal(this.$input().prop('checked'), true, 'the checkbox is checked');
+ }
+
+ assertCheckboxIsNotChecked() {
+ this.assert.equal(this.$input().prop('checked'), false, 'the checkbox is not checked');
+ }
+
+ assertValue(expected) {
+ this.assert.equal(this.$input().val(), expected, `the input value should be ${expected}`);
+ }
+
+ assertAttr(name, expected) {
+ this.assert.equal(
+ this.$input().attr(name),
+ expected,
+ `the input ${name} attribute has the value '${expected}'`
+ );
+ }
+
+ assertAllAttrs(names, expected) {
+ names.forEach(name => this.assertAttr(name, expected));
+ }
+
+ assertSelectionRange(start, end) {
+ let input = this.$input()[0];
+ this.assert.equal(
+ input.selectionStart,
+ start,
+ `the cursor start position should be ${start}`
+ );
+ this.assert.equal(input.selectionEnd, end, `the cursor end position should be ${end}`);
+ }
+
+ triggerEvent(type, options) {
+ let event = document.createEvent('Events');
+ event.initEvent(type, true, true);
+ assign(event, options);
+
+ let element = this.$input()[0];
+ runTask(() => {
+ element.dispatchEvent(event);
+ });
+ }
+ }
+
+ moduleFor(
+ 'Components test: ',
+ class extends InputRenderingTest {
+ ['@test a single text field is inserted into the DOM']() {
+ this.render(``, { value: 'hello' });
+
+ let id = this.inputID();
+
+ this.assertValue('hello');
+ this.assertSingleInput();
+
+ runTask(() => this.rerender());
+
+ this.assertValue('hello');
+ this.assertSingleInput();
+ this.assertInputId(id);
+
+ runTask(() => set(this.context, 'value', 'goodbye'));
+
+ this.assertValue('goodbye');
+ this.assertSingleInput();
+ this.assertInputId(id);
+
+ runTask(() => set(this.context, 'value', 'hello'));
+
+ this.assertValue('hello');
+ this.assertSingleInput();
+ this.assertInputId(id);
+ }
+
+ ['@test default type']() {
+ this.render(``);
+
+ this.assertAttr('type', 'text');
+
+ runTask(() => this.rerender());
+
+ this.assertAttr('type', 'text');
+ }
+
+ ['@test dynamic attributes (HTML attribute)']() {
+ this.render(
+ `
+ `,
+ {
+ value: 'Original value',
+ disabled: false,
+ placeholder: 'Original placeholder',
+ name: 'original-name',
+ maxlength: 10,
+ minlength: 5,
+ size: 20,
+ tabindex: 30,
+ }
+ );
+
+ this.assertNotDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => this.rerender());
+
+ this.assertNotDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => {
+ set(this.context, 'value', 'Updated value');
+ set(this.context, 'disabled', true);
+ set(this.context, 'placeholder', 'Updated placeholder');
+ set(this.context, 'name', 'updated-name');
+ set(this.context, 'maxlength', 11);
+ set(this.context, 'minlength', 6);
+ // set(this.context, 'size', 21); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // set(this.context, 'tabindex', 31); //NOTE: failing in IE (TEST_SUITE=sauce)
+ });
+
+ this.assertDisabled();
+ this.assertValue('Updated value');
+ this.assertAttr('placeholder', 'Updated placeholder');
+ this.assertAttr('name', 'updated-name');
+ this.assertAttr('maxlength', '11');
+ this.assertAttr('minlength', '6');
+ // this.assertAttr('size', '21'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '31'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => {
+ set(this.context, 'value', 'Original value');
+ set(this.context, 'disabled', false);
+ set(this.context, 'placeholder', 'Original placeholder');
+ set(this.context, 'name', 'original-name');
+ set(this.context, 'maxlength', 10);
+ set(this.context, 'minlength', 5);
+ // set(this.context, 'size', 20); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // set(this.context, 'tabindex', 30); //NOTE: failing in IE (TEST_SUITE=sauce)
+ });
+
+ this.assertNotDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ }
+
+ ['@test dynamic attributes (named argument)']() {
+ this.render(
+ `
+ `,
+ {
+ value: 'Original value',
+ disabled: false,
+ placeholder: 'Original placeholder',
+ name: 'original-name',
+ maxlength: 10,
+ minlength: 5,
+ size: 20,
+ tabindex: 30,
+ }
+ );
+
+ this.assertNotDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => this.rerender());
+
+ this.assertNotDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => {
+ set(this.context, 'value', 'Updated value');
+ set(this.context, 'disabled', true);
+ set(this.context, 'placeholder', 'Updated placeholder');
+ set(this.context, 'name', 'updated-name');
+ set(this.context, 'maxlength', 11);
+ set(this.context, 'minlength', 6);
+ // set(this.context, 'size', 21); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // set(this.context, 'tabindex', 31); //NOTE: failing in IE (TEST_SUITE=sauce)
+ });
+
+ this.assertDisabled();
+ this.assertValue('Updated value');
+ this.assertAttr('placeholder', 'Updated placeholder');
+ this.assertAttr('name', 'updated-name');
+ this.assertAttr('maxlength', '11');
+ this.assertAttr('minlength', '6');
+ // this.assertAttr('size', '21'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '31'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => {
+ set(this.context, 'value', 'Original value');
+ set(this.context, 'disabled', false);
+ set(this.context, 'placeholder', 'Original placeholder');
+ set(this.context, 'name', 'original-name');
+ set(this.context, 'maxlength', 10);
+ set(this.context, 'minlength', 5);
+ // set(this.context, 'size', 20); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // set(this.context, 'tabindex', 30); //NOTE: failing in IE (TEST_SUITE=sauce)
+ });
+
+ this.assertNotDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ }
+
+ ['@test static attributes (HTML attribute)']() {
+ this.render(`
+ `);
+
+ this.assertDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => this.rerender());
+
+ this.assertDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ }
+
+ ['@test static attributes (named argument)']() {
+ this.render(`
+ `);
+
+ this.assertDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+
+ runTask(() => this.rerender());
+
+ this.assertDisabled();
+ this.assertValue('Original value');
+ this.assertAttr('placeholder', 'Original placeholder');
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('maxlength', '10');
+ this.assertAttr('minlength', '5');
+ // this.assertAttr('size', '20'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ // this.assertAttr('tabindex', '30'); //NOTE: failing in IE (TEST_SUITE=sauce)
+ }
+
+ ['@test cursor selection range']() {
+ // Modifying input.selectionStart, which is utilized in the cursor tests,
+ // causes an event in Safari.
+ runDestroy(this.owner.lookup('event_dispatcher:main'));
+
+ this.render(``, { value: 'original' });
+
+ let input = this.$input()[0];
+
+ // See https://ember-twiddle.com/33e506329f8176ae874422644d4cc08c?openFiles=components.input-component.js%2Ctemplates.components.input-component.hbs
+ // this.assertSelectionRange(8, 8); //NOTE: this is (0, 0) on Firefox (TEST_SUITE=sauce)
+
+ runTask(() => this.rerender());
+
+ // this.assertSelectionRange(8, 8); //NOTE: this is (0, 0) on Firefox (TEST_SUITE=sauce)
+
+ runTask(() => {
+ input.selectionStart = 2;
+ input.selectionEnd = 4;
+ });
+
+ this.assertSelectionRange(2, 4);
+
+ runTask(() => this.rerender());
+
+ this.assertSelectionRange(2, 4);
+
+ // runTask(() => set(this.context, 'value', 'updated'));
+ //
+ // this.assertSelectionRange(7, 7); //NOTE: this fails in IE, the range is 0 -> 0 (TEST_SUITE=sauce)
+ //
+ // runTask(() => set(this.context, 'value', 'original'));
+ //
+ // this.assertSelectionRange(8, 8); //NOTE: this fails in IE, the range is 0 -> 0 (TEST_SUITE=sauce)
+ }
+
+ ['@test [DEPRECATED] sends an action with `` when is pressed'](
+ assert
+ ) {
+ assert.expect(4);
+
+ expectDeprecation(() => {
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed.');
+ },
+ },
+ });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``). (\'-top-level\' @ L1:C0) ');
+
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 13 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ }
+
+ ['@test sends an action with `` when is pressed'](
+ assert
+ ) {
+ assert.expect(2);
+
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+
+ this.triggerEvent('keyup', {
+ keyCode: 13,
+ });
+ }
+
+ ['@test [DEPRECATED] sends an action with `` is pressed'](assert) {
+ assert.expect(4);
+
+ expectDeprecation(() => {
+ this.render(``, {
+ value: 'initial',
+
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``). (\'-top-level\' @ L1:C0) ');
+
+ expectDeprecation(() => {
+ this.triggerEvent('keypress', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ }
+
+ ['@test sends an action with `` is pressed'](assert) {
+ assert.expect(2);
+
+ this.render(``, {
+ value: 'initial',
+
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+
+ this.triggerEvent('keypress', { keyCode: 65 });
+ }
+
+ ['@test sends an action to the parent level when `bubbles=true` is provided'](assert) {
+ assert.expect(1);
+
+ let ParentComponent = Component.extend({
+ change() {
+ assert.ok(true, 'bubbled upwards');
+ },
+ });
+
+ this.registerComponent('parent', {
+ ComponentClass: ParentComponent,
+ template: ``,
+ });
+ this.render(``);
+
+ this.triggerEvent('change');
+ }
+
+ ['@test triggers `focus-in` when focused'](assert) {
+ let wasFocused = false;
+
+ this.render(``, {
+ actions: {
+ foo() {
+ wasFocused = true;
+ },
+ },
+ });
+
+ runTask(() => {
+ this.$input().focus();
+ });
+
+ assert.ok(wasFocused, 'action was triggered');
+ }
+
+ ['@test sends `insert-newline` when is pressed'](assert) {
+ assert.expect(2);
+
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+
+ this.triggerEvent('keyup', {
+ keyCode: 13,
+ });
+ }
+
+ ['@test [DEPRECATED] sends an action with `` when is pressed'](
+ assert
+ ) {
+ assert.expect(4);
+
+ expectDeprecation(() => {
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``). (\'-top-level\' @ L1:C0) ');
+
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 27 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ }
+
+ ['@test sends an action with `` when is pressed'](
+ assert
+ ) {
+ assert.expect(2);
+
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+
+ this.triggerEvent('keyup', { keyCode: 27 });
+ }
+
+ ['@test [DEPRECATED] sends an action with `` when a key is pressed'](
+ assert
+ ) {
+ assert.expect(4);
+
+ expectDeprecation(() => {
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``). (\'-top-level\' @ L1:C0) ');
+
+ expectDeprecation(() => {
+ this.triggerEvent('keydown', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ }
+
+ ['@test sends an action with `` when a key is pressed'](
+ assert
+ ) {
+ assert.expect(2);
+
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+
+ this.triggerEvent('keydown', { keyCode: 65 });
+ }
+
+ ['@test [DEPRECATED] sends an action with `` when a key is pressed'](
+ assert
+ ) {
+ assert.expect(4);
+
+ expectDeprecation(() => {
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``). (\'-top-level\' @ L1:C0) ');
+
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ }
+
+ ['@test sends an action with `` when a key is pressed'](
+ assert
+ ) {
+ assert.expect(2);
+
+ this.render(``, {
+ actions: {
+ foo(value, event) {
+ assert.ok(true, 'action was triggered');
+ assert.ok(event instanceof jQuery.Event, 'jQuery event was passed');
+ },
+ },
+ });
+ this.triggerEvent('keyup', { keyCode: 65 });
+ }
+
+ ['@test GH#14727 can render a file input after having had render an input of other type']() {
+ this.render(``);
+
+ this.assert.equal(this.$input()[0].type, 'text');
+ this.assert.equal(this.$input()[1].type, 'file');
+ }
+ }
+ );
+
+ moduleFor(
+ 'Components test: with dynamic type',
+ class extends InputRenderingTest {
+ ['@test a bound property can be used to determine type']() {
+ this.render(``, { type: 'password' });
+
+ this.assertAttr('type', 'password');
+
+ runTask(() => this.rerender());
+
+ this.assertAttr('type', 'password');
+
+ runTask(() => set(this.context, 'type', 'text'));
+
+ this.assertAttr('type', 'text');
+
+ runTask(() => set(this.context, 'type', 'password'));
+
+ this.assertAttr('type', 'password');
+ }
+
+ ['@test a subexpression can be used to determine type']() {
+ this.render(``, {
+ isTruthy: true,
+ trueType: 'text',
+ falseType: 'password',
+ });
+
+ this.assertAttr('type', 'text');
+
+ runTask(() => this.rerender());
+
+ this.assertAttr('type', 'text');
+
+ runTask(() => set(this.context, 'isTruthy', false));
+
+ this.assertAttr('type', 'password');
+
+ runTask(() => set(this.context, 'isTruthy', true));
+
+ this.assertAttr('type', 'text');
+ }
+
+ ['@test GH16256 input macro does not modify params in place']() {
+ this.registerComponent('my-input', {
+ template: ``,
+ });
+
+ this.render(``, {
+ firstType: 'password',
+ secondType: 'email',
+ });
+
+ let inputs = this.element.querySelectorAll('input');
+ this.assert.equal(inputs.length, 2, 'there are two inputs');
+ this.assert.equal(inputs[0].getAttribute('type'), 'password');
+ this.assert.equal(inputs[1].getAttribute('type'), 'email');
+ }
+ }
+ );
+
+ moduleFor(
+ `Components test: `,
+ class extends InputRenderingTest {
+ ['@test dynamic attributes (HTML attribute)']() {
+ this.render(
+ ``,
+ {
+ disabled: false,
+ name: 'original-name',
+ checked: false,
+ tabindex: 10,
+ }
+ );
+
+ this.assertSingleCheckbox();
+ this.assertNotDisabled();
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('tabindex', '10');
+
+ runTask(() => this.rerender());
+
+ this.assertSingleCheckbox();
+ this.assertNotDisabled();
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('tabindex', '10');
+
+ runTask(() => {
+ set(this.context, 'disabled', true);
+ set(this.context, 'name', 'updated-name');
+ set(this.context, 'tabindex', 11);
+ });
+
+ this.assertSingleCheckbox();
+ this.assertDisabled();
+ this.assertAttr('name', 'updated-name');
+ this.assertAttr('tabindex', '11');
+
+ runTask(() => {
+ set(this.context, 'disabled', false);
+ set(this.context, 'name', 'original-name');
+ set(this.context, 'tabindex', 10);
+ });
+
+ this.assertSingleCheckbox();
+ this.assertNotDisabled();
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('tabindex', '10');
+ }
+
+ ['@test dynamic attributes (named argument)']() {
+ this.render(
+ ``,
+ {
+ disabled: false,
+ name: 'original-name',
+ checked: false,
+ tabindex: 10,
+ }
+ );
+
+ this.assertSingleCheckbox();
+ this.assertNotDisabled();
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('tabindex', '10');
+
+ runTask(() => this.rerender());
+
+ this.assertSingleCheckbox();
+ this.assertNotDisabled();
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('tabindex', '10');
+
+ runTask(() => {
+ set(this.context, 'disabled', true);
+ set(this.context, 'name', 'updated-name');
+ set(this.context, 'tabindex', 11);
+ });
+
+ this.assertSingleCheckbox();
+ this.assertDisabled();
+ this.assertAttr('name', 'updated-name');
+ this.assertAttr('tabindex', '11');
+
+ runTask(() => {
+ set(this.context, 'disabled', false);
+ set(this.context, 'name', 'original-name');
+ set(this.context, 'tabindex', 10);
+ });
+
+ this.assertSingleCheckbox();
+ this.assertNotDisabled();
+ this.assertAttr('name', 'original-name');
+ this.assertAttr('tabindex', '10');
+ }
+
+ ['@test `value` property assertion']() {
+ expectAssertion(() => {
+ this.render(``, {
+ value: 'value',
+ });
+ }, /checkbox.+@value.+not supported.+use.+@checked.+instead/);
+ }
+
+ ['@test with a bound type']() {
+ this.render(``, {
+ inputType: 'checkbox',
+ isChecked: true,
+ });
+
+ this.assertSingleCheckbox();
+ this.assertCheckboxIsChecked();
+
+ runTask(() => this.rerender());
+
+ this.assertCheckboxIsChecked();
+
+ runTask(() => set(this.context, 'isChecked', false));
+
+ this.assertCheckboxIsNotChecked();
+
+ runTask(() => set(this.context, 'isChecked', true));
+
+ this.assertCheckboxIsChecked();
+ }
+
+ ['@test native click changes check property']() {
+ this.render(``);
+
+ this.assertSingleCheckbox();
+ this.assertCheckboxIsNotChecked();
+ this.$input()[0].click();
+ this.assertCheckboxIsChecked();
+ this.$input()[0].click();
+ this.assertCheckboxIsNotChecked();
+ }
+
+ ['@test with static values (HTML attribute)']() {
+ this.render(
+ ``
+ );
+
+ this.assertSingleCheckbox();
+ this.assertCheckboxIsNotChecked();
+ this.assertNotDisabled();
+ this.assertAttr('tabindex', '10');
+ this.assertAttr('name', 'original-name');
+
+ runTask(() => this.rerender());
+
+ this.assertSingleCheckbox();
+ this.assertCheckboxIsNotChecked();
+ this.assertNotDisabled();
+ this.assertAttr('tabindex', '10');
+ this.assertAttr('name', 'original-name');
+ }
+
+ ['@test with static values (named argument)']() {
+ this.render(
+ ``
+ );
+
+ this.assertSingleCheckbox();
+ this.assertCheckboxIsNotChecked();
+ this.assertNotDisabled();
+ this.assertAttr('tabindex', '10');
+ this.assertAttr('name', 'original-name');
+
+ runTask(() => this.rerender());
+
+ this.assertSingleCheckbox();
+ this.assertCheckboxIsNotChecked();
+ this.assertNotDisabled();
+ this.assertAttr('tabindex', '10');
+ this.assertAttr('name', 'original-name');
+ }
+ }
+ );
+
+ moduleFor(
+ `Components test: `,
+ class extends InputRenderingTest {
+ ['@test null values (HTML attribute)']() {
+ let attributes = ['disabled', 'placeholder', 'name', 'maxlength', 'size', 'tabindex'];
+
+ this.render(
+ `
+ `,
+ {
+ value: null,
+ disabled: null,
+ placeholder: null,
+ name: null,
+ maxlength: null,
+ size: null,
+ tabindex: null,
+ }
+ );
+
+ this.assertValue('');
+ this.assertAllAttrs(attributes, undefined);
+
+ runTask(() => this.rerender());
+
+ this.assertValue('');
+ this.assertAllAttrs(attributes, undefined);
+
+ runTask(() => {
+ set(this.context, 'disabled', true);
+ set(this.context, 'value', 'Updated value');
+ set(this.context, 'placeholder', 'Updated placeholder');
+ set(this.context, 'name', 'updated-name');
+ set(this.context, 'maxlength', 11);
+ set(this.context, 'size', 21);
+ set(this.context, 'tabindex', 31);
+ });
+
+ this.assertDisabled();
+ this.assertValue('Updated value');
+ this.assertAttr('placeholder', 'Updated placeholder');
+ this.assertAttr('name', 'updated-name');
+ this.assertAttr('maxlength', '11');
+ this.assertAttr('size', '21');
+ this.assertAttr('tabindex', '31');
+
+ runTask(() => {
+ set(this.context, 'disabled', null);
+ set(this.context, 'value', null);
+ set(this.context, 'placeholder', null);
+ set(this.context, 'name', null);
+ set(this.context, 'maxlength', null);
+ // set(this.context, 'size', null); //NOTE: this fails with `Error: Failed to set the 'size' property on 'HTMLInputElement': The value provided is 0, which is an invalid size.` (TEST_SUITE=sauce)
+ set(this.context, 'tabindex', null);
+ });
+
+ this.assertAttr('disabled', undefined);
+ this.assertValue('');
+ // this.assertAttr('placeholder', undefined); //NOTE: this fails with a value of "null" (TEST_SUITE=sauce)
+ // this.assertAttr('name', undefined); //NOTE: this fails with a value of "null" (TEST_SUITE=sauce)
+ this.assertAttr('maxlength', undefined);
+ // this.assertAttr('size', undefined); //NOTE: re-enable once `size` bug above has been addressed
+ this.assertAttr('tabindex', undefined);
+ }
+
+ ['@test null values (named argument)']() {
+ let attributes = ['disabled', 'placeholder', 'name', 'maxlength', 'size', 'tabindex'];
+
+ this.render(
+ `
+ `,
+ {
+ value: null,
+ disabled: null,
+ placeholder: null,
+ name: null,
+ maxlength: null,
+ size: null,
+ tabindex: null,
+ }
+ );
+
+ this.assertValue('');
+ this.assertAllAttrs(attributes, undefined);
+
+ runTask(() => this.rerender());
+
+ this.assertValue('');
+ this.assertAllAttrs(attributes, undefined);
+
+ runTask(() => {
+ set(this.context, 'disabled', true);
+ set(this.context, 'value', 'Updated value');
+ set(this.context, 'placeholder', 'Updated placeholder');
+ set(this.context, 'name', 'updated-name');
+ set(this.context, 'maxlength', 11);
+ set(this.context, 'size', 21);
+ set(this.context, 'tabindex', 31);
+ });
+
+ this.assertDisabled();
+ this.assertValue('Updated value');
+ this.assertAttr('placeholder', 'Updated placeholder');
+ this.assertAttr('name', 'updated-name');
+ this.assertAttr('maxlength', '11');
+ this.assertAttr('size', '21');
+ this.assertAttr('tabindex', '31');
+
+ runTask(() => {
+ set(this.context, 'disabled', null);
+ set(this.context, 'value', null);
+ set(this.context, 'placeholder', null);
+ set(this.context, 'name', null);
+ set(this.context, 'maxlength', null);
+ // set(this.context, 'size', null); //NOTE: this fails with `Error: Failed to set the 'size' property on 'HTMLInputElement': The value provided is 0, which is an invalid size.` (TEST_SUITE=sauce)
+ set(this.context, 'tabindex', null);
+ });
+
+ this.assertAttr('disabled', undefined);
+ this.assertValue('');
+ // this.assertAttr('placeholder', undefined); //NOTE: this fails with a value of "null" (TEST_SUITE=sauce)
+ // this.assertAttr('name', undefined); //NOTE: this fails with a value of "null" (TEST_SUITE=sauce)
+ this.assertAttr('maxlength', undefined);
+ // this.assertAttr('size', undefined); //NOTE: re-enable once `size` bug above has been addressed
+ this.assertAttr('tabindex', undefined);
+ }
+ }
+ );
+
+ // These are the permutations of the set:
+ // ['type="range"', 'min="-5" max="50"', value="%x"']
+ [
+ // HTML attribute
+ '@type="range" min="-5" max="50" @value="%x"',
+ '@type="range" @value="%x" min="-5" max="50"',
+ 'min="-5" max="50" @type="range" @value="%x"',
+ 'min="-5" max="50" @value="%x" @type="range"',
+ '@value="%x" min="-5" max="50" @type="range"',
+ '@value="%x" @type="range" min="-5" max="50"',
+
+ // Named argument
+ '@type="range" @min="-5" @max="50" @value="%x"',
+ '@type="range" @value="%x" @min="-5" @max="50"',
+ '@min="-5" @max="50" @type="range" @value="%x"',
+ '@min="-5" @max="50" @value="%x" @type="range"',
+ '@value="%x" @min="-5" @max="50" @type="range"',
+ '@value="%x" @type="range" @min="-5" @max="50"',
+ ].forEach(attrs => {
+ moduleFor(
+ `[GH#15675] Components test: `,
+ class extends InputRenderingTest {
+ renderInput(value = 25) {
+ this.render(``);
+ }
+
+ ['@test value over default max but below set max is kept']() {
+ this.renderInput('25');
+ this.assertValue('25');
+ }
+
+ ['@test value below default min but above set min is kept']() {
+ this.renderInput('-2');
+ this.assertValue('-2');
+ }
+
+ ['@test in the valid default range is kept']() {
+ this.renderInput('5');
+ this.assertValue('5');
+ }
+
+ ['@test value above max is reset to max']() {
+ this.renderInput('55');
+ this.assertValue('50');
+ }
+
+ ['@test value below min is reset to min']() {
+ this.renderInput('-10');
+ this.assertValue('-5');
+ }
+ }
+ );
+ });
+} else {
+ moduleFor(
+ 'Components test: ',
+ class extends RenderingTestCase {
+ ['@test it is not allowed']() {
+ expectAssertion(() => {
+ this.render(``);
+ }, 'Invoking `{{input}}` using angle bracket syntax or `component` helper is not yet supported.');
+ }
+ }
+ );
+}
diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/input-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/input-curly-test.js
similarity index 83%
rename from packages/@ember/-internals/glimmer/tests/integration/helpers/input-test.js
rename to packages/@ember/-internals/glimmer/tests/integration/components/input-curly-test.js
index 4e799d1749c..e58971339bc 100644
--- a/packages/@ember/-internals/glimmer/tests/integration/helpers/input-test.js
+++ b/packages/@ember/-internals/glimmer/tests/integration/components/input-curly-test.js
@@ -1,5 +1,6 @@
import { RenderingTestCase, moduleFor, runDestroy, runTask } from 'internal-test-helpers';
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { assign } from '@ember/polyfills';
import { set } from '@ember/-internals/metal';
import { jQuery } from '@ember/-internals/views';
@@ -78,14 +79,8 @@ class InputRenderingTest extends RenderingTestCase {
}
moduleFor(
- 'Helpers test: {{input}}',
+ 'Components test: {{input}}',
class extends InputRenderingTest {
- ['@test should not allow angle bracket invocation']() {
- expectAssertion(() => {
- this.render('');
- }, 'Invoking `{{input}}` using angle bracket syntax or `component` helper is not yet supported.');
- }
-
['@test a single text field is inserted into the DOM']() {
this.render(`{{input type="text" value=value}}`, { value: 'hello' });
@@ -293,16 +288,24 @@ moduleFor(
},
},
});
- }, 'Please refactor `{{input enter="foo"}}` to `{{input enter=(action "foo")}}. (\'-top-level\' @ L1:C0) ');
- expectDeprecation(() => {
- this.triggerEvent('keyup', { keyCode: 13 });
- }, 'Passing actions to components as strings (like {{input enter="foo"}}) is deprecated. Please use closure actions instead ({{input enter=(action "foo")}})');
+ }, 'Passing actions to components as strings (like `{{input enter="foo"}}`) is deprecated. Please use closure actions instead (`{{input enter=(action "foo")}}`). (\'-top-level\' @ L1:C0) ');
+
+ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 13 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ } else {
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 13 });
+ }, 'Passing actions to components as strings (like `{{input enter="foo"}}`) is deprecated. Please use closure actions instead (`{{input enter=(action "foo")}}`).');
+ }
}
['@test sends an action with `{{input enter=(action "foo")}}` when is pressed'](
assert
) {
assert.expect(2);
+
this.render(`{{input enter=(action 'foo')}}`, {
actions: {
foo(value, event) {
@@ -331,11 +334,17 @@ moduleFor(
},
},
});
- }, 'Please refactor `{{input key-press="foo"}}` to `{{input key-press=(action "foo")}}. (\'-top-level\' @ L1:C0) ');
-
- expectDeprecation(() => {
- this.triggerEvent('keypress', { keyCode: 65 });
- }, 'Passing actions to components as strings (like {{input key-press="foo"}}) is deprecated. Please use closure actions instead ({{input key-press=(action "foo")}})');
+ }, 'Passing actions to components as strings (like `{{input key-press="foo"}}`) is deprecated. Please use closure actions instead (`{{input key-press=(action "foo")}}`). (\'-top-level\' @ L1:C0) ');
+
+ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ expectDeprecation(() => {
+ this.triggerEvent('keypress', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ } else {
+ expectDeprecation(() => {
+ this.triggerEvent('keypress', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like `{{input key-press="foo"}}`) is deprecated. Please use closure actions instead (`{{input key-press=(action "foo")}}`).');
+ }
}
['@test sends an action with `{{input key-press=(action "foo")}}` is pressed'](assert) {
@@ -422,11 +431,17 @@ moduleFor(
},
},
});
- }, 'Please refactor `{{input escape-press="foo"}}` to `{{input escape-press=(action "foo")}}. (\'-top-level\' @ L1:C0) ');
-
- expectDeprecation(() => {
- this.triggerEvent('keyup', { keyCode: 27 });
- }, 'Passing actions to components as strings (like {{input escape-press="foo"}}) is deprecated. Please use closure actions instead ({{input escape-press=(action "foo")}})');
+ }, 'Passing actions to components as strings (like `{{input escape-press="foo"}}`) is deprecated. Please use closure actions instead (`{{input escape-press=(action "foo")}}`). (\'-top-level\' @ L1:C0) ');
+
+ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 27 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ } else {
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 27 });
+ }, 'Passing actions to components as strings (like `{{input escape-press="foo"}}`) is deprecated. Please use closure actions instead (`{{input escape-press=(action "foo")}}`).');
+ }
}
['@test sends an action with `{{input escape-press=(action "foo")}}` when is pressed'](
@@ -460,11 +475,17 @@ moduleFor(
},
},
});
- }, 'Please refactor `{{input key-down="foo"}}` to `{{input key-down=(action "foo")}}. (\'-top-level\' @ L1:C0) ');
-
- expectDeprecation(() => {
- this.triggerEvent('keydown', { keyCode: 65 });
- }, 'Passing actions to components as strings (like {{input key-down="foo"}}) is deprecated. Please use closure actions instead ({{input key-down=(action "foo")}})');
+ }, 'Passing actions to components as strings (like `{{input key-down="foo"}}`) is deprecated. Please use closure actions instead (`{{input key-down=(action "foo")}}`). (\'-top-level\' @ L1:C0) ');
+
+ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ expectDeprecation(() => {
+ this.triggerEvent('keydown', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ } else {
+ expectDeprecation(() => {
+ this.triggerEvent('keydown', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like `{{input key-down="foo"}}`) is deprecated. Please use closure actions instead (`{{input key-down=(action "foo")}}`).');
+ }
}
['@test sends an action with `{{input key-down=(action "foo")}}` when a key is pressed'](
@@ -498,16 +519,20 @@ moduleFor(
},
},
});
- }, 'Please refactor `{{input key-up="foo"}}` to `{{input key-up=(action "foo")}}. (\'-top-level\' @ L1:C0) ');
-
- expectDeprecation(() => {
- this.triggerEvent('keyup', { keyCode: 65 });
- }, 'Passing actions to components as strings (like {{input key-up="foo"}}) is deprecated. Please use closure actions instead ({{input key-up=(action "foo")}})');
+ }, 'Passing actions to components as strings (like `{{input key-up="foo"}}`) is deprecated. Please use closure actions instead (`{{input key-up=(action "foo")}}`). (\'-top-level\' @ L1:C0) ');
+
+ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like ``) is deprecated. Please use closure actions instead (``).');
+ } else {
+ expectDeprecation(() => {
+ this.triggerEvent('keyup', { keyCode: 65 });
+ }, 'Passing actions to components as strings (like `{{input key-up="foo"}}`) is deprecated. Please use closure actions instead (`{{input key-up=(action "foo")}}`).');
+ }
}
- ['@test [DEPRECATED] sends an action with `{{input key-up=(action "foo")}}` when a key is pressed'](
- assert
- ) {
+ ['@test sends an action with `{{input key-up=(action "foo")}}` when a key is pressed'](assert) {
assert.expect(2);
this.render(`{{input key-up=(action 'foo')}}`, {
@@ -531,7 +556,7 @@ moduleFor(
);
moduleFor(
- 'Helpers test: {{input}} with dynamic type',
+ 'Components test: {{input}} with dynamic type',
class extends InputRenderingTest {
['@test a bound property can be used to determine type']() {
this.render(`{{input type=type}}`, { type: 'password' });
@@ -592,7 +617,7 @@ moduleFor(
);
moduleFor(
- `Helpers test: {{input type='checkbox'}}`,
+ `Components test: {{input type='checkbox'}}`,
class extends InputRenderingTest {
['@test dynamic attributes']() {
this.render(
@@ -651,7 +676,7 @@ moduleFor(
this.render(`{{input type="checkbox" value=value}}`, {
value: 'value',
});
- }, /you must use `checked=/);
+ }, /checkbox.+value.+not supported.+use.+checked.+instead/);
}
['@test with a bound type']() {
@@ -710,7 +735,7 @@ moduleFor(
);
moduleFor(
- `Helpers test: {{input type='text'}}`,
+ `Components test: {{input type='text'}}`,
class extends InputRenderingTest {
['@test null values']() {
let attributes = ['disabled', 'placeholder', 'name', 'maxlength', 'size', 'tabindex'];
@@ -795,7 +820,7 @@ moduleFor(
'value="%x" type="range" min="-5" max="50"',
].forEach(attrs => {
moduleFor(
- `[GH#15675] Helpers test: {{input ${attrs}}}`,
+ `[GH#15675] Components test: {{input ${attrs}}}`,
class extends InputRenderingTest {
renderInput(value = 25) {
this.render(`{{input ${attrs.replace('%x', value)}}}`);
diff --git a/packages/@ember/-internals/views/lib/mixins/text_support.js b/packages/@ember/-internals/views/lib/mixins/text_support.js
index 055908669b2..aa5173bd5c9 100644
--- a/packages/@ember/-internals/views/lib/mixins/text_support.js
+++ b/packages/@ember/-internals/views/lib/mixins/text_support.js
@@ -4,6 +4,7 @@
import { get, set, Mixin } from '@ember/-internals/metal';
import { TargetActionSupport } from '@ember/-internals/runtime';
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { deprecate } from '@ember/debug';
import { SEND_ACTION } from '@ember/deprecated-features';
@@ -310,15 +311,16 @@ function sendAction(eventName, view, event) {
let value = get(view, 'value');
if (SEND_ACTION && typeof actionName === 'string') {
- deprecate(
- `Passing actions to components as strings (like {{input ${eventName}="${actionName}"}}) is deprecated. Please use closure actions instead ({{input ${eventName}=(action "${actionName}")}})`,
- false,
- {
- id: 'ember-component.send-action',
- until: '4.0.0',
- url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action',
- }
- );
+ let message = EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS
+ ? `Passing actions to components as strings (like \`\`) is deprecated. Please use closure actions instead (\`\`).`
+ : `Passing actions to components as strings (like \`{{input ${eventName}="${actionName}"}}\`) is deprecated. Please use closure actions instead (\`{{input ${eventName}=(action "${actionName}")}}\`).`;
+
+ deprecate(message, false, {
+ id: 'ember-component.send-action',
+ until: '4.0.0',
+ url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action',
+ });
+
view.triggerAction({
action: actionName,
actionContext: [value, event],
diff --git a/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts b/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts
index e06bda6120a..7745d818ab4 100644
--- a/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts
+++ b/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts
@@ -1,3 +1,4 @@
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { deprecate } from '@ember/debug';
import { SEND_ACTION } from '@ember/deprecated-features';
import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax';
@@ -18,15 +19,48 @@ export default function deprecateSendAction(env: ASTPluginEnvironment): ASTPlugi
if (SEND_ACTION) {
let { moduleName } = env.meta;
- let deprecationMessage = (node: AST.MustacheStatement, evName: string, action: string) => {
+ let deprecationMessage = (node: AST.Node, eventName: string, actionName: string) => {
let sourceInformation = calculateLocationDisplay(moduleName, node.loc);
- return `Please refactor \`{{input ${evName}="${action}"}}\` to \`{{input ${evName}=(action "${action}")}}\. ${sourceInformation}`;
+
+ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS && node.type === 'ElementNode') {
+ return `Passing actions to components as strings (like \`\`) is deprecated. Please use closure actions instead (\`\`). ${sourceInformation}`;
+ } else {
+ return `Passing actions to components as strings (like \`{{input ${eventName}="${actionName}"}}\`) is deprecated. Please use closure actions instead (\`{{input ${eventName}=(action "${actionName}")}}\`). ${sourceInformation}`;
+ }
};
return {
name: 'deprecate-send-action',
visitor: {
+ ElementNode(node: AST.ElementNode) {
+ if (!EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS || node.tag !== 'Input') {
+ return;
+ }
+
+ node.attributes.forEach(({ name, value }) => {
+ if (name.charAt(0) === '@') {
+ let eventName = name.substring(1);
+
+ if (EVENTS.indexOf(eventName) > -1) {
+ if (value.type === 'TextNode') {
+ deprecate(deprecationMessage(node, eventName, value.chars), false, {
+ id: 'ember-component.send-action',
+ until: '4.0.0',
+ url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action',
+ });
+ } else if (value.type === 'MustacheStatement' && value.path.type === 'StringLiteral') {
+ deprecate(deprecationMessage(node, eventName, value.path.original), false, {
+ id: 'ember-component.send-action',
+ until: '4.0.0',
+ url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action',
+ });
+ }
+ }
+ }
+ });
+ },
+
MustacheStatement(node: AST.MustacheStatement) {
if (node.path.original !== 'input') {
return;
diff --git a/packages/ember-template-compiler/lib/plugins/index.ts b/packages/ember-template-compiler/lib/plugins/index.ts
index 2089090076f..3f407a518ac 100644
--- a/packages/ember-template-compiler/lib/plugins/index.ts
+++ b/packages/ember-template-compiler/lib/plugins/index.ts
@@ -17,6 +17,7 @@ import TransformOldClassBindingSyntax from './transform-old-class-binding-syntax
import TransformQuotedBindingsIntoJustBindings from './transform-quoted-bindings-into-just-bindings';
import TransformTopLevelComponents from './transform-top-level-components';
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { SEND_ACTION } from '@ember/deprecated-features';
import { ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax';
@@ -31,7 +32,6 @@ const transforms: Array = [
TransformQuotedBindingsIntoJustBindings,
AssertReservedNamedArguments,
TransformActionSyntax,
- TransformInputTypeSyntax,
TransformAttrsIntoArgs,
TransformEachInIntoEach,
TransformHasBlockSyntax,
@@ -42,6 +42,10 @@ const transforms: Array = [
AssertSplattributeExpressions,
];
+if (!EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ transforms.push(TransformInputTypeSyntax);
+}
+
if (SEND_ACTION) {
transforms.push(DeprecateSendAction);
}
diff --git a/packages/ember-template-compiler/lib/plugins/transform-input-type-syntax.ts b/packages/ember-template-compiler/lib/plugins/transform-input-type-syntax.ts
index 3f7b67d48ad..b6d879b83b9 100644
--- a/packages/ember-template-compiler/lib/plugins/transform-input-type-syntax.ts
+++ b/packages/ember-template-compiler/lib/plugins/transform-input-type-syntax.ts
@@ -1,4 +1,6 @@
+import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax';
+import { unreachable } from '@glimmer/util';
import { Builders } from '../types';
/**
@@ -26,36 +28,46 @@ import { Builders } from '../types';
@class TransformInputTypeSyntax
*/
-export default function transformInputTypeSyntax(env: ASTPluginEnvironment): ASTPlugin {
- let b = env.syntax.builders;
+let transformInputTypeSyntax: (env: ASTPluginEnvironment) => ASTPlugin;
- return {
- name: 'transform-input-type-syntax',
+if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
+ transformInputTypeSyntax = () => {
+ throw unreachable();
+ };
+} else {
+ transformInputTypeSyntax = function transformInputTypeSyntax(env: ASTPluginEnvironment): ASTPlugin {
+ let b = env.syntax.builders;
+
+ return {
+ name: 'transform-input-type-syntax',
- visitor: {
- MustacheStatement(node: AST.MustacheStatement) {
- if (isInput(node)) {
- insertTypeHelperParameter(node, b);
- }
+ visitor: {
+ MustacheStatement(node: AST.MustacheStatement) {
+ if (isInput(node)) {
+ insertTypeHelperParameter(node, b);
+ }
+ },
},
- },
- };
-}
+ };
+ }
-function isInput(node: AST.MustacheStatement) {
- return node.path.original === 'input';
-}
+ function isInput(node: AST.MustacheStatement) {
+ return node.path.original === 'input';
+ }
-function insertTypeHelperParameter(node: AST.MustacheStatement, builders: Builders) {
- let pairs = node.hash.pairs;
- let pair = null;
- for (let i = 0; i < pairs.length; i++) {
- if (pairs[i].key === 'type') {
- pair = pairs[i];
- break;
+ function insertTypeHelperParameter(node: AST.MustacheStatement, builders: Builders) {
+ let pairs = node.hash.pairs;
+ let pair = null;
+ for (let i = 0; i < pairs.length; i++) {
+ if (pairs[i].key === 'type') {
+ pair = pairs[i];
+ break;
+ }
+ }
+ if (pair && pair.value.type !== 'StringLiteral') {
+ node.params.unshift(builders.sexpr('-input-type', [pair.value], undefined, pair.loc));
}
- }
- if (pair && pair.value.type !== 'StringLiteral') {
- node.params.unshift(builders.sexpr('-input-type', [pair.value], undefined, pair.loc));
}
}
+
+export default transformInputTypeSyntax;
diff --git a/packages/ember-template-compiler/tests/plugins/deprecate-send-action-test.js b/packages/ember-template-compiler/tests/plugins/deprecate-send-action-test.js
index a5925fa41e0..9f7316f3b56 100644
--- a/packages/ember-template-compiler/tests/plugins/deprecate-send-action-test.js
+++ b/packages/ember-template-compiler/tests/plugins/deprecate-send-action-test.js
@@ -18,7 +18,7 @@ EVENTS.forEach(function(e) {
DeprecateSendActionTest.prototype[
`@test Using \`{{input ${e}="actionName"}}\` provides a deprecation`
] = function() {
- let expectedMessage = `Please refactor \`{{input ${e}="foo-bar"}}\` to \`{{input ${e}=(action "foo-bar")}}\. ('baz/foo-bar' @ L1:C0) `;
+ let expectedMessage = `Passing actions to components as strings (like \`{{input ${e}="foo-bar"}}\`) is deprecated. Please use closure actions instead (\`{{input ${e}=(action "foo-bar")}}\`)\. ('baz/foo-bar' @ L1:C0) `;
expectDeprecation(() => {
compile(`{{input ${e}="foo-bar"}}`, { moduleName: 'baz/foo-bar' });