Skip to content

Commit

Permalink
fixes zio#2963: PartialBooleanAttribute (support no value) (zio#2973)
Browse files Browse the repository at this point in the history
  • Loading branch information
jgoday authored Aug 2, 2024
1 parent 66d3eea commit b9a6443
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 3 deletions.
16 changes: 16 additions & 0 deletions zio-http/jvm/src/test/scala/zio/http/template/DomSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ object DomSpec extends ZIOHttpSpec {
assertTrue(dom.encode == """<a href="https://www.zio-http.com" title="click me!"></a>""")
},
),
test("element with non value required attribute") {
val dom = Dom.element(
"input",
Dom.booleanAttr("required"),
)

assertTrue(dom.encode == """<input required/>""")
},
test("element with value required attribute") {
val dom = Dom.element(
"input",
Dom.booleanAttr("required", Some(true)),
)

assertTrue(dom.encode == """<input required="true"/>""")
},
test("element with attribute & children") {
val dom = Dom.element(
"a",
Expand Down
10 changes: 10 additions & 0 deletions zio-http/jvm/src/test/scala/zio/http/template/HtmlSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ case object HtmlSpec extends ZIOHttpSpec {
val expected = """<div class="container">Hello!</div>"""
assert(view.encode)(equalTo(expected.stripMargin))
},
test("tags with default boolean attributes") {
val view = input(typeAttr := "text", requiredAttr)
val expected = """<input type="text" required/>"""
assert(view.encode)(equalTo(expected.stripMargin))
},
test("tags with boolean attributes") {
val view = input(typeAttr := "text", requiredAttr := false)
val expected = """<input type="text" required="false"/>"""
assert(view.encode)(equalTo(expected.stripMargin))
},
suite("implicit conversions")(
test("from unit") {
val view: Html = {}
Expand Down
13 changes: 11 additions & 2 deletions zio-http/shared/src/main/scala/zio/http/template/Attributes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package zio.http.template

import zio.http.template.Attributes.PartialAttribute
import scala.language.implicitConversions

import zio.http.template.Attributes.{PartialAttribute, PartialBooleanAttribute}

trait Attributes {
final def acceptAttr: PartialAttribute[String] = PartialAttribute("accept")
Expand Down Expand Up @@ -301,7 +303,7 @@ trait Attributes {

final def relAttr: PartialAttribute[String] = PartialAttribute("rel")

final def requiredAttr: PartialAttribute[String] = PartialAttribute("required")
final def requiredAttr: PartialBooleanAttribute = PartialBooleanAttribute("required")

final def reversedAttr: PartialAttribute[String] = PartialAttribute("reversed")

Expand Down Expand Up @@ -365,9 +367,16 @@ trait Attributes {

final def cellspacingAttr: PartialAttribute[String] = PartialAttribute("cellspacing")

implicit def partialBooleanToHtml(attr: PartialBooleanAttribute): Html = attr.apply()
}

object Attributes {
case class PartialBooleanAttribute(name: String) {
def :=(value: Boolean): Html = Dom.booleanAttr(name, Some(value))
def apply(value: Boolean): Html = Dom.booleanAttr(name, Some(value))
def apply(): Html = Dom.booleanAttr(name, None)
}

case class PartialAttribute[A](name: String) {
def :=(value: A)(implicit ev: IsAttributeValue[A]): Html = Dom.attr(name, ev(value))
def apply(value: A)(implicit ev: IsAttributeValue[A]): Html = Dom.attr(name, ev(value))
Expand Down
12 changes: 11 additions & 1 deletion zio-http/shared/src/main/scala/zio/http/template/Dom.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ sealed trait Dom { self =>
private[template] def encode(state: EncodingState, encodeHtml: Boolean = true): CharSequence = self match {
case Dom.Element(name, children) =>
val encode = if (name == "script" || name == "style") false else encodeHtml
val attributes = children.collect { case self: Dom.Attribute => self.encode }
val attributes = children.collect {
case self: Dom.Attribute => self.encode
case self: Dom.BooleanAttribute => self.encode
}

val innerState = state.inner
val elements = children.collect {
Expand Down Expand Up @@ -76,6 +79,9 @@ sealed trait Dom { self =>
case Dom.Attribute(name, value) => s"""$name="${OutputEncoder.encodeHtml(value.toString)}""""
case Dom.Empty => ""
case Dom.Raw(raw) => raw

case Dom.BooleanAttribute(name, None) => s"$name"
case Dom.BooleanAttribute(name, Some(value)) => s"""$name="${value}""""
}
}

Expand All @@ -94,6 +100,8 @@ object Dom {

def attr(name: CharSequence, value: CharSequence): Dom = Dom.Attribute(name, value)

def booleanAttr(name: CharSequence, value: Option[Boolean] = None): Dom = Dom.BooleanAttribute(name, value)

def element(name: CharSequence, children: Dom*): Dom = Dom.Element(name, children)

def empty: Dom = Empty
Expand All @@ -110,5 +118,7 @@ object Dom {

private[zio] final case class Attribute(name: CharSequence, value: CharSequence) extends Dom

private[zio] final case class BooleanAttribute(name: CharSequence, value: Option[Boolean] = None) extends Dom

private[zio] object Empty extends Dom
}

0 comments on commit b9a6443

Please sign in to comment.