Skip to content

Commit

Permalink
Introduce scoped target attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
sstephenson committed Jan 21, 2019
1 parent 2027303 commit 2235047
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 23 deletions.
50 changes: 38 additions & 12 deletions packages/@stimulus/core/src/target_set.ts
@@ -1,4 +1,3 @@
import { Schema } from "./schema"
import { Scope } from "./scope"
import { attributeValueContainsToken } from "./selectors"

Expand All @@ -9,37 +8,64 @@ export class TargetSet {
this.scope = scope
}

get element(): Element {
get element() {
return this.scope.element
}

get identifier(): string {
get identifier() {
return this.scope.identifier
}

get schema(): Schema {
get schema() {
return this.scope.schema
}

has(targetName: string): boolean {
has(targetName: string) {
return this.find(targetName) != null
}

find(...targetNames: string[]): Element | undefined {
const selector = this.getSelectorForTargetNames(targetNames)
find(...targetNames: string[]) {
return targetNames.reduce((target, targetName) =>
target
|| this.findTarget(targetName)
|| this.findLegacyTarget(targetName)
, undefined as Element | undefined)
}

findAll(...targetNames: string[]) {
return targetNames.reduce((targets, targetName) => [
...targets,
...this.findAllTargets(targetName),
...this.findAllLegacyTargets(targetName)
], [] as Element[])
}

private findTarget(targetName: string) {
const selector = this.getSelectorForTargetName(targetName)
return this.scope.findElement(selector)
}

findAll(...targetNames: string[]): Element[] {
const selector = this.getSelectorForTargetNames(targetNames)
private findAllTargets(targetName: string) {
const selector = this.getSelectorForTargetName(targetName)
return this.scope.findAllElements(selector)
}

private getSelectorForTargetNames(targetNames: string[]): string {
return targetNames.map(targetName => this.getSelectorForTargetName(targetName)).join(", ")
private getSelectorForTargetName(targetName: string) {
const attributeName = `data-${this.identifier}-target`
return attributeValueContainsToken(attributeName, targetName)
}

private findLegacyTarget(targetName: string) {
const selector = this.getLegacySelectorForTargetName(targetName)
return this.scope.findElement(selector)
}

private findAllLegacyTargets(targetName: string) {
const selector = this.getLegacySelectorForTargetName(targetName)
return this.scope.findAllElements(selector)
}

private getSelectorForTargetName(targetName: string): string {
private getLegacySelectorForTargetName(targetName: string) {
const targetDescriptor = `${this.identifier}.${targetName}`
return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor)
}
Expand Down
76 changes: 76 additions & 0 deletions packages/@stimulus/core/src/tests/modules/legacy_target_tests.ts
@@ -0,0 +1,76 @@
import { ControllerTestCase } from "../cases/controller_test_case"
import { TargetController } from "../controllers/target_controller"

export default class LegacyTargetTests extends ControllerTestCase(TargetController) {
fixtureHTML = `
<div data-controller="${this.identifier}">
<div data-target="${this.identifier}.alpha" id="alpha1"></div>
<div data-target="${this.identifier}.alpha" id="alpha2"></div>
<div data-target="${this.identifier}.beta" data-${this.identifier}-target="gamma" id="beta1">
<div data-target="${this.identifier}.gamma" id="gamma1"></div>
</div>
<div data-controller="${this.identifier}" id="child">
<div data-target="${this.identifier}.delta" id="delta1"></div>
</div>
<textarea data-target="${this.identifier}.input" id="input1"></textarea>
</div>
`

"test TargetSet#find"() {
this.assert.equal(this.controller.targets.find("alpha"), this.findElement("#alpha1"))
}

"test TargetSet#find prefers scoped target attributes"() {
this.assert.equal(this.controller.targets.find("gamma"), this.findElement("#beta1"))
}

"test TargetSet#findAll"() {
this.assert.deepEqual(
this.controller.targets.findAll("alpha"),
this.findElements("#alpha1", "#alpha2")
)
}

"test TargetSet#findAll prioritizes scoped target attributes"() {
this.assert.deepEqual(
this.controller.targets.findAll("gamma"),
this.findElements("#beta1", "#gamma1")
)
}

"test TargetSet#findAll with multiple arguments"() {
this.assert.deepEqual(
this.controller.targets.findAll("alpha", "beta"),
this.findElements("#alpha1", "#alpha2", "#beta1")
)
}

"test TargetSet#has"() {
this.assert.equal(this.controller.targets.has("gamma"), true)
this.assert.equal(this.controller.targets.has("delta"), false)
}

"test TargetSet#find ignores child controller targets"() {
this.assert.equal(this.controller.targets.find("delta"), null)
this.findElement("#child").removeAttribute("data-controller")
this.assert.equal(this.controller.targets.find("delta"), this.findElement("#delta1"))
}

"test linked target properties"() {
this.assert.equal(this.controller.betaTarget, this.findElement("#beta1"))
this.assert.deepEqual(this.controller.betaTargets, this.findElements("#beta1"))
this.assert.equal(this.controller.hasBetaTarget, true)
}

"test inherited linked target properties"() {
this.assert.equal(this.controller.alphaTarget, this.findElement("#alpha1"))
this.assert.deepEqual(this.controller.alphaTargets, this.findElements("#alpha1", "#alpha2"))
}

"test singular linked target property throws an error when no target is found"() {
this.findElement("#beta1").removeAttribute("data-target")
this.assert.equal(this.controller.hasBetaTarget, false)
this.assert.equal(this.controller.betaTargets.length, 0)
this.assert.throws(() => this.controller.betaTarget)
}
}
18 changes: 7 additions & 11 deletions packages/@stimulus/core/src/tests/modules/target_tests.ts
Expand Up @@ -4,15 +4,15 @@ import { TargetController } from "../controllers/target_controller"
export default class TargetTests extends ControllerTestCase(TargetController) {
fixtureHTML = `
<div data-controller="${this.identifier}">
<div data-target="${this.identifier}.alpha" id="alpha1"></div>
<div data-target="${this.identifier}.alpha" id="alpha2"></div>
<div data-target="${this.identifier}.beta" id="beta1">
<div data-target="${this.identifier}.gamma" id="gamma1"></div>
<div data-${this.identifier}-target="alpha" id="alpha1"></div>
<div data-${this.identifier}-target="alpha" id="alpha2"></div>
<div data-${this.identifier}-target="beta" id="beta1">
<div data-${this.identifier}-target="gamma" id="gamma1"></div>
</div>
<div data-controller="${this.identifier}" id="child">
<div data-target="${this.identifier}.delta" id="delta1"></div>
<div data-${this.identifier}-target="delta" id="delta1"></div>
</div>
<textarea data-target="${this.identifier}.input" id="input1"></textarea>
<textarea data-${this.identifier}-target="input" id="input1"></textarea>
</div>
`

Expand Down Expand Up @@ -57,13 +57,9 @@ export default class TargetTests extends ControllerTestCase(TargetController) {
}

"test singular linked target property throws an error when no target is found"() {
this.findElement("#beta1").removeAttribute("data-target")
this.findElement("#beta1").removeAttribute(`data-${this.identifier}-target`)
this.assert.equal(this.controller.hasBetaTarget, false)
this.assert.equal(this.controller.betaTargets.length, 0)
this.assert.throws(() => this.controller.betaTarget)
}

"test has*Target property names are not localized"() {
this.assert.equal(this.controller.hasInputTarget, true)
}
}

1 comment on commit 2235047

@jankeesvw
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case anyone needs it, I made this regular expression to migrate:

data-target="((.+?)\.(.+?))"
data-$2-target="$3"

Please sign in to comment.