So, the components themselves will look something like this:
fun HtmlBlockTag.radioButtonWithLabel(
groupName: String,
id: String,
hidden: Boolean = false,
radioButtonFunc: (INPUT.() -> Unit)? = null,
func: LABEL.() -> Unit
) {
radioInput(name=groupName) {
this.id = id
this.hidden = hidden
radioButtonFunc?.invoke(this)
}
label { this.htmlFor = id; func() }
}
And then use of them will be like this: call.respondHtml {
body {
div(CSS_CLASS_NAME) {
radioButtonWIthLabel(MORE_CSS_CLASS_NAME, "group", "id") {
+"Text for the label"
}
}
}
}
More complicated examples just extend that quite a lot.I've also got whole files dedicated to single extension functions that end up being a whole section that I can place anywhere.
---
And then to test those single-function components, I'll do something like this:
class SingleSectionTest {
private suspend fun ApplicationTestBuilder.buildApplicationAndCall(
data: DataUsedForSection
): Document {
application {
routing {
get("test") {
call.respondHtml {
body {
renderSingleSection(data)
}
}
}
}
}
val response = client.get("test")
val body = Jsoup.parse(response.bodyAsText())
return body;
}
@Test
fun `simple test case`() = testApplication {
val data = DataUsedForSection("a", "b", "c")
val body = buildApplicationAndCall(data)
// all the asserts
}
}
And so on. Is this what you were wondering? Or would you like a different sort of example?I've gotten more used to them and I get why they can be so great now; but there's still some real annoyances with them I just can't shake (like the import problem and the related "where is this code?!" problem)
---
Purely importing a component that's just a simple class, like you can do in Java/Typescript with their jsx and tsx files would be pretty cool, yeah. You could fake it by making a data class and adding a
fun renderInto(tag: HtmlBlockTag) { ... }
type method, but with how Ktor's Dsl is implemented, you're still going to need to connect it into the giant StringBuilder (or whatever) that the dsl is building to.To help me get something close to that idea, I tend to dedicate whole files to bigger components and even name the file the same as the root HtmlBlockTag Extension method (like RadioButtonWithLabel.kt if I did it for the earlier one). Those files are great because you can put a bunch of helper methods in the same file and keep it all contained, a lot like a class.