Skip to content

Commit

Permalink
Add polyfill for <Input> and <Textarea>
Browse files Browse the repository at this point in the history
  • Loading branch information
simonihmig committed Jul 17, 2019
1 parent 2d43783 commit be98d31
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 3 deletions.
24 changes: 21 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,21 @@ module.exports = {
}

if (this.shouldPolyfillBuiltinComponents) {
let pluginObj = this._buildLinkToPlugin();
pluginObj.parallelBabel = {
let linktoPluginObj = this._buildLinkToPlugin();
linktoPluginObj.parallelBabel = {
requireFile: __filename,
buildUsing: '_buildLinkToPlugin',
params: {},
};
registry.add('htmlbars-ast-plugin', pluginObj);
registry.add('htmlbars-ast-plugin', linktoPluginObj);

let inputPluginObj = this._buildInputPlugin();
inputPluginObj.parallelBabel = {
requireFile: __filename,
buildUsing: '_buildInputPlugin',
params: {},
};
registry.add('htmlbars-ast-plugin', inputPluginObj);
}
},

Expand Down Expand Up @@ -86,6 +94,16 @@ module.exports = {
};
},

_buildInputPlugin() {
return {
name: 'input-component-invocation-support',
plugin: require('./lib/ast-input-transform'),
baseDir() {
return __dirname;
},
};
},

included() {
this._super.included.apply(this, arguments);

Expand Down
56 changes: 56 additions & 0 deletions lib/ast-input-transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

const attributeToPropertyMap = {
role: 'ariaRole',
};

class AngleBracketInputPolyfill {
transform(ast) {
let b = this.syntax.builders;

// in order to debug in https://https://astexplorer.net/#/gist/0590eb883edfcd163b183514df4cc717
// **** copy from here ****
function transformAttributeValue(attributeValue) {
switch (attributeValue.type) {
case 'TextNode':
return b.string(attributeValue.chars);
case 'MustacheStatement':
return b.path(attributeValue.path);
}
}

let visitor = {
ElementNode(node) {
let tag = node.tag.toLowerCase();

if (tag === 'input' || tag === 'textarea') {
let { attributes } = node;

let props = attributes
.filter(({ name }) => name.charAt(0) === '@')
.map(attribute => Object.assign({}, attribute, { name: attribute.name.slice(1) }));
let attrs = attributes.map(attribute =>
attributeToPropertyMap[attribute.name]
? Object.assign({}, attribute, { name: attributeToPropertyMap[attribute.name] })
: attribute
);

let hash = b.hash(
[...props, ...attrs].map(({ name, value, loc }) =>
b.pair(name, transformAttributeValue(value), loc)
)
);

return b.mustache(b.path(tag), null, hash, false, node.loc);
}
},
};
// **** copy to here ****

this.syntax.traverse(ast, visitor);

return ast;
}
}

module.exports = AngleBracketLinkToPolyfill;
62 changes: 62 additions & 0 deletions tests/integration/components/input-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | input', function(hooks) {
setupRenderingTest(hooks);

test('it supports text field', async function(assert) {
this.set('value', 'foo');
await render(hbs`<Input @type="text" @value={{this.value}} />`);

assert.dom('input').hasAttribute('type', 'text');
assert.dom('input').hasValue('foo');
});

test('it supports different input types', async function(assert) {
this.set('value', '[email protected]');
await render(hbs`<Input @type="email" @value={{this.value}} />`);

assert.dom('input').hasAttribute('type', 'email');
assert.dom('input').hasValue('[email protected]');
});

test('it supports checkbox', async function(assert) {
this.set('value', true);
await render(hbs`<Input @type="checkbox" @checked={{this.value}} />`);

assert.dom('input').hasAttribute('type', 'checkbox');
assert.dom('input').isChecked();
});

test('it supports textarea', async function(assert) {
this.set('value', 'foo bar');
await render(hbs`<Textarea @value={{this.value}} />`);

assert.dom('textarea').exists();
assert.dom('textarea').hasValue('foo bar');
});

test('it passes supported properties and attributes', async function(assert) {
await render(
hbs`<Input
@value="foo"
@size="20"
name="username"
placeholder="Enter username"
class="form-input"
role="searchbox"
/>`
);

// does not test each and every supported property / HTML attribute, as the list is rather long, and the transform
// will just pass anything through, so either all work or no one does.
assert.dom('input').hasAttribute('type', 'text');
assert.dom('input').hasValue('foo');
assert.dom('input').hasAttribute('name', 'username');
assert.dom('input').hasAttribute('placeholder', 'Enter username');
assert.dom('input').hasClass('form-input');
assert.dom('input').hasAttribute('role', 'searchbox');
});
});

0 comments on commit be98d31

Please sign in to comment.