feat: add icon support to button, alert, badge, and form input

Button:
- :icon-left, :icon-right props for buttons with leading/trailing icons
- :icon prop for icon-only buttons (square padding via .btn-icon class)
- Icon size scales with button size (sm→sm, md/lg→sm/md)

Alert:
- Auto-assigns variant-specific icons (circle-check, alert-triangle,
  alert-circle, info) per variant
- :icon-name prop to override default, false to suppress
- Layout restructured with .alert-icon + .alert-content wrapper

Badge:
- :icon-name prop adds a leading icon before text
- .badge-icon CSS scales icon to match badge font size

Form input:
- :icon-left and :icon-right props on form-input
- Wraps input in .form-input-wrap with absolutely-positioned icon spans
- Padding adjusts automatically via .form-input--icon-left/right

All three dev targets (hiccup, replicant, squint) updated with demos.
This commit is contained in:
Florian Schroedl
2026-03-05 19:33:55 +01:00
parent e3132a3cb4
commit 93edebf144
15 changed files with 438 additions and 122 deletions

View File

@@ -1,6 +1,7 @@
(ns ui.form-test
(:require [clojure.test :refer [deftest is testing]]
[ui.form :as form]))
[ui.form :as form]
[ui.icon :as icon]))
;; ── form-field ──────────────────────────────────────────────────────
@@ -102,6 +103,41 @@
(let [result (form/form-input {:error true})]
(is (= "form-input form-input--error" (get-in result [1 :class]))))))
(deftest form-input-icon-test
(testing "input with icon-left wraps in form-input-wrap"
(let [result (form/form-input {:icon-left :search :placeholder "Search..."})]
(is (= :div (first result)))
(is (= "form-input-wrap" (get-in result [1 :class])))
;; first child: left icon span
(let [icon-span (nth result 2)]
(is (= :span (first icon-span)))
(is (= "form-input-icon form-input-icon--left" (get-in icon-span [1 :class]))))
;; second child: the actual input
(let [input (nth result 3)]
(is (= :input (first input)))
(is (clojure.string/includes? (get-in input [1 :class]) "form-input--icon-left")))))
(testing "input with icon-right wraps in form-input-wrap"
(let [result (form/form-input {:icon-right :check})]
(is (= :div (first result)))
;; second child: the input
(let [input (nth result 2)]
(is (= :input (first input)))
(is (clojure.string/includes? (get-in input [1 :class]) "form-input--icon-right")))
;; third child: right icon span
(let [icon-span (nth result 3)]
(is (= :span (first icon-span)))
(is (= "form-input-icon form-input-icon--right" (get-in icon-span [1 :class]))))))
(testing "input with both icons"
(let [result (form/form-input {:icon-left :search :icon-right :x})]
(is (= :div (first result)))
(is (= 5 (count result))))) ;; div + attrs + left-icon + input + right-icon
(testing "input without icons returns bare input"
(let [result (form/form-input {:placeholder "Name"})]
(is (= :input (first result))))))
;; ── form-textarea ───────────────────────────────────────────────────
(deftest form-textarea-class-list-test