-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add polyfill for angle brackets <LinkTo>
Partially implements ember-polyfills#66
- Loading branch information
1 parent
01654c8
commit 074a8ab
Showing
5 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { helper } from '@ember/component/helper'; | ||
|
||
function linkToParams(_params, { route, model, models, query }) { | ||
let params = []; | ||
|
||
if (route) { | ||
params.push(route); | ||
} | ||
|
||
if (model) { | ||
params.push(model); | ||
} | ||
|
||
if (models) { | ||
params.push(...models); | ||
} | ||
|
||
if (query) { | ||
params.push({ | ||
isQueryParams: true, | ||
values: query, | ||
}); | ||
} | ||
|
||
return params; | ||
} | ||
|
||
export default helper(linkToParams); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from 'ember-angle-bracket-invocation-polyfill/helpers/-link-to-params'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
'use strict'; | ||
|
||
const reservedProps = ['@route', '@model', '@models', '@query']; | ||
const supportedHTMLAttributes = ['id', 'class', 'rel', 'tabindex', 'title', 'target', 'role']; | ||
const attributeToPropertyMap = { | ||
role: 'ariaRole', | ||
}; | ||
|
||
class AngleBracketLinkToPolyfill { | ||
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) { | ||
if (node.tag.toLowerCase() === 'linkto') { | ||
let { children, blockParams, attributes } = node; | ||
let params = []; | ||
let helperParams = []; | ||
|
||
let route = attributes.find(({ name }) => name === '@route'); | ||
let model = attributes.find(({ name }) => name === '@model'); | ||
let models = attributes.find(({ name }) => name === '@models'); | ||
let query = attributes.find(({ name }) => name === '@query'); | ||
|
||
let needsParamsHelper = | ||
(models && models.value.path.original !== 'array') || | ||
(query && query.value.path.original !== 'hash'); | ||
|
||
if (route) { | ||
if (needsParamsHelper) { | ||
helperParams.push(b.pair('route', transformAttributeValue(route.value))); | ||
} else { | ||
params.push(transformAttributeValue(route.value)); | ||
} | ||
} | ||
|
||
if (model) { | ||
if (needsParamsHelper) { | ||
helperParams.push(b.pair('model', transformAttributeValue(model.value))); | ||
} else { | ||
params.push(transformAttributeValue(model.value)); | ||
} | ||
} | ||
|
||
if (models) { | ||
if (models.value.path.original === 'array') { | ||
params.push(...models.value.params); | ||
} else { | ||
helperParams.push(b.pair('models', transformAttributeValue(models.value))); | ||
} | ||
} | ||
|
||
if (query) { | ||
if (query.value.path.original === 'hash') { | ||
params.push(b.sexpr('query-params', null, query.value.hash, query.loc)); | ||
} else { | ||
helperParams.push(b.pair('query', transformAttributeValue(query.value))); | ||
} | ||
} | ||
|
||
let props = attributes | ||
.filter(({ name }) => name.charAt(0) === '@' && !reservedProps.includes(name)) | ||
.map(attribute => Object.assign({}, attribute, { name: attribute.name.slice(1) })); | ||
let attrs = attributes | ||
.filter(({ name }) => supportedHTMLAttributes.includes(name)) | ||
.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) | ||
) | ||
); | ||
|
||
if (needsParamsHelper) { | ||
hash.pairs.push( | ||
b.pair('params', b.sexpr('-link-to-params', null, b.hash(helperParams))) | ||
); | ||
|
||
return b.block( | ||
b.path('link-to'), | ||
null, | ||
hash, | ||
b.program(children, blockParams), | ||
null, | ||
node.loc | ||
); | ||
} else { | ||
return b.block( | ||
b.path('link-to'), | ||
params, | ||
hash, | ||
b.program(children, blockParams), | ||
null, | ||
node.loc | ||
); | ||
} | ||
} | ||
}, | ||
}; | ||
// **** copy to here **** | ||
|
||
this.syntax.traverse(ast, visitor); | ||
|
||
return ast; | ||
} | ||
} | ||
|
||
module.exports = AngleBracketLinkToPolyfill; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { module, test } from 'qunit'; | ||
import { setupRenderingTest } from 'ember-qunit'; | ||
import { render } from '@ember/test-helpers'; | ||
import hbs from 'htmlbars-inline-precompile'; | ||
import EmberRouter from '@ember/routing/router'; | ||
import EmberObject from '@ember/object'; | ||
|
||
module('Integration | Component | link-to', function(hooks) { | ||
const Router = EmberRouter.extend(); | ||
Router.map(function() { | ||
this.route('foo'); | ||
this.route('bar', { path: '/bar/:bar_id' }, function() { | ||
this.route('sub', { path: '/sub/:sub_id' }); | ||
}); | ||
}); | ||
|
||
const modelA = EmberObject.create({ | ||
id: '1', | ||
}); | ||
const modelB = EmberObject.create({ | ||
id: '2', | ||
}); | ||
|
||
setupRenderingTest(hooks); | ||
hooks.beforeEach(function() { | ||
this.owner.register('router:main', Router); | ||
this.owner.setupRouter(); | ||
this.setProperties({ | ||
modelA, | ||
modelB, | ||
}); | ||
}); | ||
|
||
test('it supports static route', async function(assert) { | ||
await render(hbs`<LinkTo @route="foo" class="main">Link</LinkTo>`); | ||
|
||
assert.dom('a').hasAttribute('href', '#/foo'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
|
||
test('it supports dynamic route', async function(assert) { | ||
await render(hbs`<LinkTo @route="bar" @model={{this.modelA}} class="main">Link</LinkTo>`); | ||
|
||
assert.dom('a').hasAttribute('href', '#/bar/1'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
|
||
test('it supports dynamic route w/ multiple models', async function(assert) { | ||
this.set('models', [this.modelA, this.modelB]); | ||
await render(hbs`<LinkTo @route="bar.sub" @models={{this.models}} class="main">Link</LinkTo>`); | ||
|
||
assert.dom('a').hasAttribute('href', '#/bar/1/sub/2'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
|
||
test('it supports dynamic route w/ multiple models and array helper', async function(assert) { | ||
this.set('models', [this.modelA, this.modelB]); | ||
await render( | ||
hbs`<LinkTo @route="bar.sub" @models={{array this.modelA this.modelB}} class="main">Link</LinkTo>` | ||
); | ||
|
||
assert.dom('a').hasAttribute('href', '#/bar/1/sub/2'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
|
||
test('it supports query params', async function(assert) { | ||
this.set('query', { | ||
q1: 1, | ||
q2: 'some', | ||
q3: 'value', | ||
}); | ||
await render(hbs`<LinkTo @route="foo" @query={{this.query}} class="main">Link</LinkTo>`); | ||
|
||
assert.dom('a').hasAttribute('href', '#/foo?q1=1&q2=some&q3=value'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
|
||
test('it supports query params w/ hash helper', async function(assert) { | ||
this.set('q3', 'value'); | ||
await render( | ||
hbs`<LinkTo @route="foo" @query={{hash q1=1 q2="some" q3=this.q3}} class="main">Link</LinkTo>` | ||
); | ||
|
||
assert.dom('a').hasAttribute('href', '#/foo?q1=1&q2=some&q3=value'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
|
||
test('it passes supported properties and attributes', async function(assert) { | ||
this.owner.startRouting(); | ||
|
||
await render( | ||
hbs`<LinkTo @route="index" @activeClass="act" class="main" id="test" role="nav" rel="noopener" tabindex="1" title="something" target="_blank">Link</LinkTo>` | ||
); | ||
assert.dom('a').hasAttribute('href', '#/'); | ||
assert.dom('a').hasClass('act'); | ||
assert.dom('a').hasClass('main'); | ||
assert.dom('a').hasAttribute('id', 'test'); | ||
assert.dom('a').hasAttribute('role', 'nav'); | ||
assert.dom('a').hasAttribute('rel', 'noopener'); | ||
assert.dom('a').hasAttribute('tabindex', '1'); | ||
assert.dom('a').hasAttribute('title', 'something'); | ||
assert.dom('a').hasAttribute('target', '_blank'); | ||
assert.dom('a').hasText('Link'); | ||
}); | ||
}); |