Skip to content

Commit

Permalink
Fix combined dot-notation and non-literal classes
Browse files Browse the repository at this point in the history
Generate the correct class attribute of an element when the class names
are both present as:

- part of the element keyword (using the dot notation)
- a non-literal value of the :class key in the attribute map
  (specifically when using a symbol as the value).

Fixes issue weavejester#143
  • Loading branch information
Pieter van Prooijen committed Apr 23, 2018
1 parent 846d7ef commit 6a72187
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
38 changes: 31 additions & 7 deletions src/hiccup/compiler.clj
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,53 @@
(or content
(and (html-mode?) (not (void-tags tag)))))

(defn- merge-classes [class classes]
(defn merge-classes [class classes]
(cond
(nil? class) classes
(string? class) (str classes " " class)
:else (str classes " " (str/join " " class))))

(declare literal?)

(defn- merge-classes-form [class-form classes]
(if (literal? class-form)
(merge-classes class-form classes)
`(merge-classes ~class-form ~classes)))

(defn- merge-attributes [map-attrs id classes]
(-> map-attrs
(cond-> id (assoc :id (or (:id map-attrs) id)))
(cond-> classes (assoc :class (merge-classes (:class map-attrs) classes)))))

(defn normalize-element
"Ensure an element vector is of the form [tag-name attrs content]."
[[tag & content]]
(defn- merge-attributes-form [map-attrs id classes]
(-> map-attrs
(cond-> id (assoc :id (or (:id map-attrs) id)))
(cond-> classes (assoc :class (merge-classes-form (:class map-attrs) classes)))))

(defn- normalize-element*
"Ensure an element vector is of the form [tag-name attrs content. Merge the attributes
using a compile / render mode dependent function."
[[tag & content] merge-attributes-fn]
(when (not (or (keyword? tag) (symbol? tag) (string? tag)))
(throw (IllegalArgumentException. (str tag " is not a valid element name."))))
(let [[_ tag id class] (re-matches re-tag (util/as-str tag))
classes (if class (str/replace class "." " "))
map-attrs (first content)]
(if (map? map-attrs)
[tag (merge-attributes map-attrs id classes) (next content)]
[tag (merge-attributes-fn map-attrs id classes) (next content)]
[tag {:id id, :class classes} content])))

(defn normalize-element
"Ensure an element vector is of the form [tag-name attrs content]."
[[tag & content :as tag-content]]
(normalize-element* tag-content merge-attributes))

(defn- normalize-element-form
"Ensure an element vector is of the form [tag-name attrs content]."
[[tag & content :as tag-content]]
(normalize-element* tag-content merge-attributes-form))


(defprotocol HtmlRenderer
(render-html [this]
"Turn a Clojure data type into a string of HTML."))
Expand Down Expand Up @@ -225,7 +249,7 @@

(defmethod compile-element ::literal-tag-and-attributes
[[tag attrs & content]]
(let [[tag attrs _] (normalize-element [tag attrs])]
(let [[tag attrs _] (normalize-element-form [tag attrs])]
(if (container-tag? tag content)
`(str ~(str "<" tag) ~(compile-attr-map attrs) ">"
~@(compile-seq content)
Expand All @@ -238,7 +262,7 @@

(defmethod compile-element ::literal-tag
[[tag attrs & content]]
(let [[tag tag-attrs _] (normalize-element [tag])
(let [[tag tag-attrs _] (normalize-element-form [tag])
attrs-sym (gensym "attrs")]
`(let [~attrs-sym ~attrs]
(if (map? ~attrs-sym)
Expand Down
6 changes: 5 additions & 1 deletion test/hiccup/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@
(let [times-called (atom 0)
foo #(swap! times-called inc)]
(html [:div (foo)])
(is (= @times-called 1)))))
(is (= @times-called 1))))
(testing "defer evaluation of non-literal class names when combined with tag classes"
(let [x "attr-class"]
(is (= (html [:div.tag-class {:class x}])
"<div class=\"tag-class attr-class\"></div>")))))

(deftest render-modes
(testing "closed tag"
Expand Down

0 comments on commit 6a72187

Please sign in to comment.