diff --git a/cl-sbt.asd b/cl-sbt.asd index 1edd24a..234f289 100644 --- a/cl-sbt.asd +++ b/cl-sbt.asd @@ -37,6 +37,7 @@ "rove") :components ((:module "tests" :components - ((:file "main")))) + ((:file "main") + (:file "grid")))) :description "Test system for cl-sbt" :perform (test-op (op c) (symbol-call :rove :run c))) diff --git a/src/layout/grid.lisp b/src/layout/grid.lisp index 29f2156..3633007 100644 --- a/src/layout/grid.lisp +++ b/src/layout/grid.lisp @@ -49,20 +49,18 @@ (defun string-clean (str) (string-trim " " (string-downcase str))) -(defun make-con-class (name value default-class) +(defun make-con-class (name value) "Generates a Bootstrap container class string for a particular breakpoint. NAME is the name of the breakpoint (e.g., 'xs', 'sm', 'md', etc.). -VALUE is non-nil when the container's width is set for the breakpoint. - -DEFAULT-CLASS is a string that represents the default container class. - The function generates a ' container-NAME' class string if VALUE is non-nil. Example: - (make-container-class \"md\" t \"container\") ; => \" container-md\"" - (if value (format nil "~a-~a " default-class name) "")) + (make-container-class \"md\" t) ; => \" container-md\"" + (if value + (format nil "container-~a " name) + "")) (defun make-row-class (name value) "Generates a Bootstrap row class string for a particular breakpoint or a general column setting. @@ -98,13 +96,19 @@ the size or offset is nil, it omits the corresponding part. Examples: (make-col-class \"md\" '(3 1)) ; => \" col-md-3 offset-md-1\" (make-col-class \"lg\" '(4 nil)) ; => \" col-lg-4\"" - (if size-offset-pair - (let ((size (first size-offset-pair)) - (offset (second size-offset-pair))) - (concatenate 'string - (if size (format nil "col-~a-~d" name size) "") - (if offset (format nil " offset-~a-~d" name offset) "") " ")) - "")) + (let* ((size (first size-offset-pair)) + (offset (second size-offset-pair)) + (size-string (if size + (if (string= name "") + (format nil "col-~d " size) + (format nil "col-~a-~d " name size)) + "")) + (offset-string (if offset + (if (string= name "") + (format nil "offset-~d " offset) + (format nil "offset-~a-~d " name offset)) + ""))) + (concatenate 'string size-string offset-string))) (defun breakpoint-class (&key (kind :con) (xs nil) (sm nil) (md nil) (lg nil) (xl nil) (xxl nil)) "Generates Bootstrap class string for different kinds of elements across breakpoints. @@ -137,15 +141,16 @@ Examples: (breakpoint-class :kind :con :xs t :sm t :md t :lg t :xl t :xxl t) ; will generate a string for a fluid container that spans all breakpoints." (let ((breakpoint-values (list xs sm md lg xl xxl))) - (apply #'concatenate 'string - (loop for i from 0 below (length *breakpoints*) - for breakpoint-name = (nth i *breakpoints*) - for breakpoint-value = (nth i breakpoint-values) - collect - (cond - ((eq kind :con) (make-con-class breakpoint-name breakpoint-value "container")) - ((eq kind :row) (make-row-class breakpoint-name breakpoint-value)) - ((eq kind :col) (make-col-class breakpoint-name breakpoint-value))))))) + (string-clean + (apply #'concatenate 'string + (loop for i from 0 below (length *breakpoints*) + for breakpoint-name = (nth i *breakpoints*) + for breakpoint-value = (nth i breakpoint-values) + collect + (cond + ((eq kind :con) (make-con-class breakpoint-name breakpoint-value)) + ((eq kind :row) (make-row-class breakpoint-name breakpoint-value)) + ((eq kind :col) (make-col-class breakpoint-name breakpoint-value)))))))) (defmacro con ((&key (fluid nil) (breakpoint nil) (text nil) (spacing nil)) &body body) "Generates a Bootstrap container. @@ -175,22 +180,22 @@ Examples: ,(string-clean (concatenate 'string (if (null fluid) "container " "container-fluid ") (if (null breakpoint) "" - (apply #'breakpoint-class breakpoint)) + (format nil "~a " (apply #'breakpoint-class breakpoint))) (if (null text) "" - (apply #'cl-sbt/utility:text text)) + (format nil "~a " (apply #'cl-sbt/utility:text text))) (if (null spacing) "" (apply #'cl-sbt/utility:spacing spacing)))) ,@body))) -(defmacro row ((&key (cols nil) (breakpoint nil) (align-items nil) (justify-content nil) (spacing nil)) &body body) +(defmacro row ((&key (cols nil) (breakpoint nil) (alignitems nil) (justifycontent nil) (spacing nil)) &body body) "Generates a Bootstrap row. -BREAKPOINT: Specifies the number of equal-width columns at various -breakpoints. It should be :xs, :sm, :md, :lg, :xl, or :xxl. - COLS: Specifies the number of columns irrespective of the viewport or breakpoint size. +BREAKPOINT: Specifies the number of equal-width columns at various +breakpoints. It should be :xs, :sm, :md, :lg, :xl, or :xxl. + ALIGN-ITEMS: Specifies the vertical alignment of columns. It can be :start, :center, :end, :stretch, or :baseline. @@ -198,11 +203,11 @@ JUSTIFY-CONTENT: Specifies the horizontal alignment of columns. It can be :start, :center, :end, :around, or :between. Examples: - (row (:breakpoint (:xs 2)) \"Hello, world!\") + (row (:breakpoint (:kind :row :xs 2)) \"Hello, world!\") ; Creates a row with two equal-width columns for extra small devices, ; containing the text 'Hello, world!' - (row (:breakpoint (:sm 4 :md 3 :lg 2)) \"Hello, world!\") + (row (:breakpoint (:kind :row :sm 4 :md 3 :lg 2)) \"Hello, world!\") ; Creates a row with four equal-width columns for small devices, three for ; medium devices, and two for large devices, containing the text 'Hello, world!' @@ -210,7 +215,7 @@ Examples: ; Creates a row with three equal-width columns irrespective of the viewport ; or breakpoint size, containing the text 'Hello, world!' - (row (:align-items :center :justify-content :between) \"Hello, world!\") + (row (:alignitems :center :justifycontent :between) \"Hello, world!\") ; Creates a row with centered items that are evenly distributed in the ; horizontal direction, containing the text 'Hello, world!' @@ -221,17 +226,16 @@ arguments, containing the specified body content." ,(string-clean (concatenate 'string "row " - (if (null cols) "" (format nil "cols-~d " cols)) + (if (null cols) "" (make-row-class "cols" cols)) (if (null breakpoint) "" (apply #'breakpoint-class breakpoint)) - ;(make-row-class "cols" cols) - (if (null align-items) "" (format nil "align-items-~a " align-items)) - (if (null justify-content) "" (format nil "justify-content-~a " justify-content)) + (if (null alignitems) "" (format nil "align-items-~a " alignitems)) + (if (null justifycontent) "" (format nil "justify-content-~a " justifycontent)) (if (null spacing) "" (apply #'cl-sbt/utility:spacing spacing)))) ,@body))) -(defmacro col ((&key (col nil) (breakpoint nil) (align-self nil) (spacing nil)) &body body) +(defmacro col ((&key (default nil) (breakpoint nil) (alignself nil) (spacing nil)) &body body) "Generates a Bootstrap column. COL: Specifies the number of columns the element spans. @@ -254,7 +258,7 @@ Examples: ; This will generate a column that spans 8 medium-sized columns with an ; offset of 2 medium-sized columns, containing the text 'Hello, world!'. - (col (:align-self :center) \"Hello, world!\") + (col (:alignself :center) \"Hello, world!\") ; This will generate a column that aligns its content in the center, ; containing the text 'Hello, world!'. @@ -265,10 +269,10 @@ Examples: (:div :class ,(string-clean (concatenate 'string - (if (null col) "col " (format nil "col-~d " col)) + (if (null default) "col " (format nil "col-~d " default)) (if (null breakpoint) "" (apply #'breakpoint-class breakpoint)) - (if (null align-self) "" (format nil "align-self-~a " align-self)) + (if (null alignself) "" (format nil "align-self-~a " alignself)) (if (null spacing) "" (apply #'cl-sbt/utility:spacing spacing)))) ,@body))) diff --git a/tests/grid.lisp b/tests/grid.lisp new file mode 100644 index 0000000..e9238a1 --- /dev/null +++ b/tests/grid.lisp @@ -0,0 +1,181 @@ +(defpackage cl-sbt/tests/grid + (:use + :cl + :cl-sbt + :rove) + (:import-from + :cl-sbt/grid + :make-con-class + :make-row-class + :make-col-class + :breakpoint-class + :con + :row + :col)) + +(in-package :cl-sbt/tests/grid) + +(deftest test-make-con-class-no-value + (testing "Generates an empty string when value is NIL" + (ok (string= (make-con-class "xs" nil) "")))) + +(deftest test-make-con-class-xs + (testing "Generates correct class for 'xs' breakpoint" + (ok (string= (make-con-class "xs" t) "container-xs ")))) + +(deftest test-make-con-class-sm + (testing "Generates correct class for 'sm' breakpoint" + (ok (string= (make-con-class "sm" t) "container-sm ")))) + +(deftest test-make-con-class-md + (testing "Generates correct class for 'md' breakpoint" + (ok (string= (make-con-class "md" t) "container-md ")))) + +(deftest test-make-con-class-lg + (testing "Generates correct class for 'lg' breakpoint" + (ok (string= (make-con-class "lg" t) "container-lg ")))) + +(deftest test-make-row-class-no-value + (testing "Generates an empty string when value is NIL" + (ok (string= (make-row-class "xs" nil) "")))) + +(deftest test-make-row-class-general-cols + (testing "Generates correct class for 'cols' with 2 columns" + (ok (string= (make-row-class "cols" 2) "row-cols-2 ")))) + +(deftest test-make-row-class-xs + (testing "Generates correct class for 'xs' breakpoint with 2 columns" + (ok (string= (make-row-class "xs" 2) "row-cols-xs-2 ")))) + +(deftest test-make-row-class-md + (testing "Generates correct class for 'md' breakpoint with 3 columns" + (ok (string= (make-row-class "md" 3) "row-cols-md-3 ")))) + +(deftest test-make-row-class-lg + (testing "Generates correct class for 'lg' breakpoint with 4 columns" + (ok (string= (make-row-class "lg" 4) "row-cols-lg-4 ")))) + +(deftest test-make-col-class-no-size-no-offset + (testing "Generates an empty string when both size and offset are NIL" + (ok (string= (make-col-class "xs" '(nil nil)) "")))) + +(deftest test-make-col-class-size-only + (testing "Generates correct class for 'md' breakpoint with size 3 and no offset" + (ok (string= (make-col-class "md" '(3 nil)) "col-md-3 ")))) + +(deftest test-make-col-class-offset-only + (testing "Generates correct class for 'md' breakpoint with no size and offset 2" + (ok (string= (make-col-class "md" '(nil 2)) "offset-md-2 ")))) + +(deftest test-make-col-class-size-and-offset + (testing "Generates correct class for 'lg' breakpoint with size 4 and offset 1" + (ok (string= (make-col-class "lg" '(4 1)) "col-lg-4 offset-lg-1 ")))) + +(deftest test-make-col-class-no-name + (testing "Generates correct class when no name is provided, with size 2 and offset 1" + (ok (string= (make-col-class "" '(2 1)) "col-2 offset-1 ")))) + +(deftest test-breakpoint-class-con + (testing "Generates correct class for container" + (ok (string= (breakpoint-class :kind :con :md t) "container-md")))) + +(deftest test-breakpoint-class-row + (testing "Generates correct class for row" + (ok (string= (breakpoint-class :kind :row :sm 2 :md 3) "row-cols-sm-2 row-cols-md-3")))) + +(deftest test-breakpoint-class-col + (testing "Generates correct class for column" + (ok (string= (breakpoint-class :kind :col :lg '(4 1)) "col-lg-4 offset-lg-1")))) + +(deftest test-breakpoint-class-con-fluid + (testing "Generates correct class for fluid container" + (ok (string= (breakpoint-class :kind :con :xs t :sm t :md t :lg t :xl t :xxl t) "container-xs container-sm container-md container-lg container-xl container-xxl")))) + +(deftest test-breakpoint-class-col-no-offset + (testing "Generates correct class for column with no offset" + (ok (string= (breakpoint-class :kind :col :md '(3)) "col-md-3")))) + +(deftest test-con-fluid + (let ((result (spinneret:with-html-string (con (:fluid t))))) + (testing "Generates correct HTML for fluid container" + (ok (string= result "
"))))) + +(deftest test-con-breakpoint + (let ((result (spinneret:with-html-string (con (:breakpoint (:kind :con :md t)))))) + (testing "Generates correct HTML for container with breakpoint" + (ok (string= result ""))))) + +(deftest test-con-text + (let ((result (spinneret:with-html-string (con (:text (:alignment "center")))))) + (testing "Generates correct HTML for container with text utilities" + (ok (string= result ""))))) + +(deftest test-con-fluid-breakpoint-text + (let ((result (spinneret:with-html-string (con (:fluid t :breakpoint (:kind :con :sm t) :text (:weight "bold")))))) + (testing "Generates correct HTML for fluid container with breakpoint and text utilities" + (ok (string= result ""))))) + +(deftest test-con-no-arguments + (let ((result (spinneret:with-html-string (con ())))) + (testing "Generates correct HTML for container with no arguments" + (ok (string= result ""))))) + +(deftest test-row-cols + (let ((result (spinneret:with-html-string (cl-sbt/grid:row (:cols 2))))) + (testing "Generates correct HTML when cols is provided" + (ok (string= result ""))))) + +(deftest test-row-breakpoint + (let ((result (spinneret:with-html-string (cl-sbt/grid:row (:breakpoint (:kind :row :sm 2)))))) + (testing "Generates correct HTML when breakpoint is provided" + (ok (string= result ""))))) + +(deftest test-row-align-items + (let ((result (spinneret:with-html-string (cl-sbt/grid:row (:alignitems "center"))))) + (testing "Generates correct HTML when align-items is provided" + (ok (string= result ""))))) + +(deftest test-row-justify-content + (let ((result (spinneret:with-html-string (cl-sbt/grid:row (:justifycontent "between"))))) + (testing "Generates correct HTML when justify-content is provided" + (ok (string= result ""))))) + +(deftest test-row-spacing + (let ((result (spinneret:with-html-string (cl-sbt/grid:row (:spacing (:property :m :size 2)))))) + (testing "Generates correct HTML when spacing is provided" + (ok (string= result ""))))) + +(deftest test-row-no-args + (let ((result (spinneret:with-html-string (cl-sbt/grid:row ())))) + (testing "Generates correct HTML when no arguments are provided" + (ok (string= result ""))))) + +(deftest test-row-null-cols + (let ((result (spinneret:with-html-string (cl-sbt/grid:row (:cols nil))))) + (testing "Generates correct HTML when cols is null" + (ok (string= result ""))))) + +(deftest test-col-breakpoint + (let ((result (spinneret:with-html-string (col (:breakpoint (:kind :col :md (8 2))))))) + (testing "Generates correct HTML for column with breakpoint" + (ok (string= result ""))))) + +(deftest test-col-align-self + (let ((result (spinneret:with-html-string (col (:alignself "center"))))) + (testing "Generates correct HTML for column with align-self center" + (ok (string= result ""))))) + +(deftest test-col-spacing + (let ((result (spinneret:with-html-string (col (:spacing (:property :p :size 2)))))) + (testing "Generates correct HTML for column with padding on all sides" + (ok (string= result ""))))) + +(deftest test-col-no-arguments + (let ((result (spinneret:with-html-string (col ())))) + (testing "Generates correct HTML for column with no arguments" + (ok (string= result ""))))) + +(deftest test-col-default + (let ((result (spinneret:with-html-string (col (:default 3))))) + (testing "Generates correct HTML for column with no arguments" + (ok (string= result "")))))