Skip to content
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Unreleased changes are available via `io.github.borkdude/html {:git/sha "..."}`
## Unreleased

- Fix shorthand classes being dropped when the attribute map has no `:class`: `[:div.card {:id "a"}]` rendered `<div id="a">`. The same path interpolated a symbol's _name_ when `:class` was bound to an expression, so `(let [x "big"] [:div.card {:class x}])` rendered `class="card x"`.
- Omit attributes whose value is `nil`, or `false` in html mode: `[:input {:disabled false}]` rendered `disabled="false"`, which disables the input. This also applies to non-boolean attributes: `[:option {:value false}]` now renders `<option></option>`. Matches hiccup ([#19](https://github.com/borkdude/html/pull/19), [@telekid](https://github.com/telekid))
- Render `true` and `false` as `"true"` and `"false"` for `aria-*`, `data-*`, `contenteditable`, `draggable`, `focusable` and `spellcheck`. Names match in any case, so `:spellCheck` works as well
- Fix `[:div {}]` rendering `<div >`

## 0.2.6

Expand Down
33 changes: 20 additions & 13 deletions src/borkdude/html.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,30 @@
(str (name k) ": " (name v) ";"))
m)))

(defn- string-boolean-attr? [k]
(let [k (str/lower-case k)]
(or (str/starts-with? k "aria-")
(str/starts-with? k "data-")
(contains? #{"contenteditable" "draggable" "focusable" "spellcheck"} k))))

(defn ->attrs
"Implementation, do not use"
([opts m]
(let [xml? (= :xml (:mode opts))]
(str/join " "
(map (fn [[k v]]
(if (and (true? v) (not xml?))
(name k)
(str (name k)
(apply str
(keep (fn [[k v]]
(let [k (name k)]
(cond
(nil? v) nil
(and (boolean? v) (not xml?) (not (string-boolean-attr? k)))
(when v (str " " k))
:else
(str " " k
"=" (cond (string? v) (pr-str (escape-html v))
(keyword? v) (pr-str (name v))
(and (map? v) (= "style" (name k))) (pr-str (escape-html (->css v)))
:else (pr-str (escape-html (str v)))))))
m))))
(and (map? v) (= "style" k)) (pr-str (escape-html (->css v)))
:else (pr-str (escape-html (str v))))))))
m))))
([opts m base-map]
(let [m (merge base-map m)]
(->attrs opts m))))
Expand Down Expand Up @@ -126,18 +136,15 @@
omit-tag? (= "<>" tag)
unsafe? (= "$" tag)
attrs (if ?attrs
(let [a (compile-attrs opts ?attrs)]
(if (string? a)
(str " " a)
a))
(compile-attrs opts ?attrs)
"")]
(if unsafe?
`(->Html (str ~(first children)))
`(->Html (str ~@(if omit-tag?
nil
(if (string? attrs)
[(str "<" tag attrs ">")]
["<" tag " " attrs ">"]))
["<" tag attrs ">"]))
~@(map #(list (if xml?
`xml
`html) %) children)
Expand Down
55 changes: 55 additions & 0 deletions test/borkdude/html_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,61 @@
"<input checked=\"true\"></input>"
(xml [:input {:checked true}])

"<input>"
(html [:input {:disabled false}])

"<input>"
(html [:input {:disabled nil}])

"<input type=\"text\">"
(let [d false]
(html [:input {:type "text" :disabled d}]))

"<input disabled=\"false\"></input>"
(xml [:input {:disabled false}])

"<input></input>"
(xml [:input {:disabled nil}])

"<button aria-expanded=\"true\"></button>"
(html [:button {:aria-expanded true}])

"<button aria-expanded=\"false\"></button>"
(let [b false]
(html [:button {:aria-expanded b}]))

"<span></span>"
(html [:span {:aria-label nil}])

"<div spellcheck=\"false\"></div>"
(html [:div {:spellcheck false}])

"<div draggable=\"true\"></div>"
(html [:div {:draggable true}])

"<div spellCheck=\"false\"></div>"
(html [:div {:spellCheck false}])

"<svg focusable=\"false\"></svg>"
(html [:svg {:focusable false}])

"<div data-active=\"false\"></div>"
(html [:div {:data-active false}])

"<div data-active=\"true\"></div>"
(let [a true]
(html [:div {:data-active a}]))

"<option></option>"
(html [:option {:value false}])

"<div></div>"
(html [:div {}])

"<div></div>"
(let [m {:disabled false}]
(html [:div {:& m}]))

"<a href=\"http://dude\"></a>"
(html [:a {:href (str "http://" "dude")}])

Expand Down
Loading