Skip to content
Open
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
26 changes: 26 additions & 0 deletions src/core/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@ export class Controller<ElementType extends Element = Element> {
return this.scope.data
}

findElement(selector: string): Element | undefined {
const elementWithId = document.getElementById(selector)
if (elementWithId && this.scope.containsElement(elementWithId)) {
return elementWithId
}
const newSelector = this.classifySelector(selector)
return this.scope.findElement(newSelector)
}

findAllElements(selector: string): Element[] {
const newSelector = this.classifySelector(selector)
return this.scope.findAllElements(newSelector)
}

initialize() {
// Override in your subclass to set up initial controller state
}
Expand Down Expand Up @@ -100,4 +114,16 @@ export class Controller<ElementType extends Element = Element> {
target.dispatchEvent(event)
return event
}

private classifySelector(selector: string | string[]): string {
const tokens = Array.isArray(selector) ? selector : [selector]

const definedClasses: string[] = (this.constructor as any).classes.flatMap((key: string) => {
const value = (this as any)[`${key}Classes`] as string[] | undefined
return value ?? []
})

const allTokensDefined = tokens.every((token) => definedClasses.includes(token))
return "." + tokens.join(allTokensDefined ? "." : " ")
}
}
37 changes: 37 additions & 0 deletions src/tests/modules/core/find_element_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ClassController } from "../../controllers/class_controller"
import { ControllerTestCase } from "../../cases/controller_test_case"

export default class FindElementTests extends ControllerTestCase(ClassController) {
fixtureHTML = `
<div data-controller="${this.identifier}"
data-${this.identifier}-active-class="active"
data-${this.identifier}-loading-class="busy"
>
<div id="inside" class="busy"></div>
</div>
<div id="outside" class="busy"></div>

"test findElement finds element by id inside scope"() {
const result = this.controller.findElement("inside")
this.assert.equal(result?.id, "inside")
}

"test findElement does not find element by id outside scope"() {
const result = this.controller.findElement("outside")
this.assert.equal(result, undefined)
}

"test findElement finds element by defined class"() {
const result = this.controller.findElement(this.controller.loadingClass)
this.assert.ok(result instanceof Element)
this.assert.ok(result?.classList.contains("busy"))
}

"test findAllElements returns multiple matches for defined class"() {
const results = this.controller.findAllElements(this.controller.loadingClass)
this.assert.ok(Array.isArray(results))
this.assert.equal(results.length, 1)
this.assert.ok(results[0].classList.contains("busy"))
}
`
}