Merge pull request #4930 from penpot/ladybenko-8413-asset-ids

Assert icon and svg IDs in DS components
This commit is contained in:
Andrey Antukh 2024-07-29 08:45:53 +02:00 committed by GitHub
commit 40910703ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 30 additions and 51 deletions

View File

@ -26,8 +26,8 @@
:RawSvg raw-svg* :RawSvg raw-svg*
:Text text* :Text text*
;; meta / misc ;; meta / misc
:meta #js {:icons icon-list :meta #js {:icons (clj->js (sort icon-list))
:svgs raw-svg-list :svgs (clj->js (sort raw-svg-list))
:typography (clj->js typography-list)} :typography (clj->js typography-list)}
:storybook #js {:StoryGrid sb/story-grid* :storybook #js {:StoryGrid sb/story-grid*
:StoryGridCell sb/story-grid-cell* :StoryGridCell sb/story-grid-cell*

View File

@ -9,7 +9,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.style :as stl]) [app.main.style :as stl])
(:require (:require
[app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def button-variants (set '("primary" "secondary" "ghost" "destructive"))) (def button-variants (set '("primary" "secondary" "ghost" "destructive")))
@ -18,6 +18,7 @@
{::mf/props :obj} {::mf/props :obj}
[{:keys [variant icon children class] :rest props}] [{:keys [variant icon children class] :rest props}]
(assert (or (nil? variant) (contains? button-variants variant) "expected valid variant")) (assert (or (nil? variant) (contains? button-variants variant) "expected valid variant"))
(assert (or (nil? icon) (contains? icon-list icon) "expected valid icon id"))
(let [variant (or variant "primary") (let [variant (or variant "primary")
class (dm/str class " " (stl/css-case :button true class (dm/str class " " (stl/css-case :button true
:button-primary (= variant "primary") :button-primary (= variant "primary")

View File

@ -10,18 +10,12 @@ import Components from "@target/components";
const { Button } = Components; const { Button } = Components;
const { icons } = Components.meta; const { icons } = Components.meta;
const iconList = [
...Object.entries(icons)
.map(([_, value]) => value)
.sort(),
];
export default { export default {
title: "Buttons/Button", title: "Buttons/Button",
component: Components.Button, component: Components.Button,
argTypes: { argTypes: {
icon: { icon: {
options: iconList, options: icons,
control: { type: "select" }, control: { type: "select" },
}, },
disabled: { control: "boolean" }, disabled: { control: "boolean" },

View File

@ -9,7 +9,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.style :as stl]) [app.main.style :as stl])
(:require (:require
[app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def button-variants (set '("primary" "secondary" "ghost" "destructive"))) (def button-variants (set '("primary" "secondary" "ghost" "destructive")))
@ -17,9 +17,9 @@
(mf/defc icon-button* (mf/defc icon-button*
{::mf/props :obj} {::mf/props :obj}
[{:keys [class icon variant aria-label] :rest props}] [{:keys [class icon variant aria-label] :rest props}]
(assert (or (not variant) (contains? button-variants variant)) "invalid variant") (assert (contains? icon-list icon) "expected valid icon id")
(assert (or (not variant) (contains? button-variants variant)) "expected valid variant")
(assert (some? aria-label) "aria-label must be provided") (assert (some? aria-label) "aria-label must be provided")
(assert (some? icon) "an icon id must be provided")
(let [variant (or variant "primary") (let [variant (or variant "primary")
class (dm/str class " " (stl/css-case :icon-button true class (dm/str class " " (stl/css-case :icon-button true
:icon-button-primary (= variant "primary") :icon-button-primary (= variant "primary")

View File

@ -7,15 +7,11 @@
(ns app.main.ui.ds.foundations.assets.icon (ns app.main.ui.ds.foundations.assets.icon
(:require (:require
[clojure.core :as c] [clojure.core :as c]
[cuerdas.core :as str]
[rumext.v2])) [rumext.v2]))
(defmacro collect-icons (defmacro collect-icons []
[]
(let [ns-info (:ns &env)] (let [ns-info (:ns &env)]
`(cljs.core/js-obj `(set '~(->> (:defs ns-info)
~@(->> (:defs ns-info) (map val)
(map val) (filter (fn [entry] (-> entry :meta :icon-id)))
(filter (fn [entry] (-> entry :meta :icon-id))) (map (fn [{:keys [name]}] (c/name name)))))))
(mapcat (fn [{:keys [name]}]
[(-> name c/name str/camel str/capital) name]))))))

View File

@ -5,7 +5,7 @@
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.main.ui.ds.foundations.assets.icon (ns app.main.ui.ds.foundations.assets.icon
(:refer-clojure :exclude [mask]) (:refer-clojure :exclude [mask drop filter remove])
(:require-macros (:require-macros
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.style :as stl] [app.main.style :as stl]
@ -116,7 +116,7 @@
(def ^:icon-id distribute-vertical-spacing "distribute-vertical-spacing") (def ^:icon-id distribute-vertical-spacing "distribute-vertical-spacing")
(def ^:icon-id document "document") (def ^:icon-id document "document")
(def ^:icon-id download "download") (def ^:icon-id download "download")
(def ^:icon-id drop-icon "drop") (def ^:icon-id drop "drop")
(def ^:icon-id easing-ease-in-out "easing-ease-in-out") (def ^:icon-id easing-ease-in-out "easing-ease-in-out")
(def ^:icon-id easing-ease-in "easing-ease-in") (def ^:icon-id easing-ease-in "easing-ease-in")
(def ^:icon-id easing-ease-out "easing-ease-out") (def ^:icon-id easing-ease-out "easing-ease-out")
@ -128,7 +128,7 @@
(def ^:icon-id expand "expand") (def ^:icon-id expand "expand")
(def ^:icon-id feedback "feedback") (def ^:icon-id feedback "feedback")
(def ^:icon-id fill-content "fill-content") (def ^:icon-id fill-content "fill-content")
(def ^:icon-id filter-icon "filter") (def ^:icon-id filter "filter")
(def ^:icon-id fixed-width "fixed-width") (def ^:icon-id fixed-width "fixed-width")
(def ^:icon-id flex-grid "flex-grid") (def ^:icon-id flex-grid "flex-grid")
(def ^:icon-id flex-horizontal "flex-horizontal") (def ^:icon-id flex-horizontal "flex-horizontal")
@ -209,7 +209,7 @@
(def ^:icon-id puzzle "puzzle") (def ^:icon-id puzzle "puzzle")
(def ^:icon-id rectangle "rectangle") (def ^:icon-id rectangle "rectangle")
(def ^:icon-id reload "reload") (def ^:icon-id reload "reload")
(def ^:icon-id remove-icon "remove") (def ^:icon-id remove "remove")
(def ^:icon-id rgba "rgba") (def ^:icon-id rgba "rgba")
(def ^:icon-id rgba-complementary "rgba-complementary") (def ^:icon-id rgba-complementary "rgba-complementary")
(def ^:icon-id rotation "rotation") (def ^:icon-id rotation "rotation")
@ -280,6 +280,7 @@
(mf/defc icon* (mf/defc icon*
{::mf/props :obj} {::mf/props :obj}
[{:keys [id size class] :rest props}] [{:keys [id size class] :rest props}]
(assert (contains? icon-list id) "invalid icon id")
(let [class (dm/str (or class "") " " (stl/css :icon)) (let [class (dm/str (or class "") " " (stl/css :icon))
props (mf/spread-props props {:class class :width icon-size-m :height icon-size-m}) props (mf/spread-props props {:class class :width icon-size-m :height icon-size-m})
size-px (cond (= size "s") icon-size-s :else icon-size-m) size-px (cond (= size "s") icon-size-s :else icon-size-m)

View File

@ -5,16 +5,12 @@ const { Icon } = Components;
const { StoryGrid, StoryGridCell, StoryHeader } = Components.storybook; const { StoryGrid, StoryGridCell, StoryHeader } = Components.storybook;
const { icons } = Components.meta; const { icons } = Components.meta;
const iconList = Object.entries(icons)
.map(([_, value]) => value)
.sort();
export default { export default {
title: "Foundations/Assets/Icon", title: "Foundations/Assets/Icon",
component: Components.Icon, component: Components.Icon,
argTypes: { argTypes: {
id: { id: {
options: iconList, options: icons,
control: { type: "select" }, control: { type: "select" },
}, },
size: { size: {
@ -33,7 +29,7 @@ export const All = {
<p>Hover on an icon to see its ID.</p> <p>Hover on an icon to see its ID.</p>
</StoryHeader> </StoryHeader>
<StoryGrid> <StoryGrid>
{iconList.map((iconId) => ( {icons.map((iconId) => (
<StoryGridCell <StoryGridCell
title={iconId} title={iconId}
key={iconId} key={iconId}

View File

@ -7,15 +7,11 @@
(ns app.main.ui.ds.foundations.assets.raw-svg (ns app.main.ui.ds.foundations.assets.raw-svg
(:require (:require
[clojure.core :as c] [clojure.core :as c]
[cuerdas.core :as str]
[rumext.v2])) [rumext.v2]))
(defmacro collect-raw-svgs (defmacro collect-raw-svgs []
[]
(let [ns-info (:ns &env)] (let [ns-info (:ns &env)]
`(cljs.core/js-obj `(set '~(->> (:defs ns-info)
~@(->> (:defs ns-info) (map val)
(map val) (filter (fn [entry] (-> entry :meta :svg-id)))
(filter (fn [entry] (-> entry :meta :svg-id))) (map (fn [{:keys [name]}] (c/name name)))))))
(mapcat (fn [{:keys [name]}]
[(-> name c/name str/camel str/capital) name]))))))

View File

@ -16,22 +16,21 @@
(def ^:svg-id brand-github "brand-github") (def ^:svg-id brand-github "brand-github")
(def ^:svg-id brand-gitlab "brand-gitlab") (def ^:svg-id brand-gitlab "brand-gitlab")
(def ^:svg-id brand-google "brand-google") (def ^:svg-id brand-google "brand-google")
(def ^:svg-id loader "loader") (def ^:svg-id loader "loader")
(def ^:svg-id logo "penpot-logo")
(def ^:svg-id logo-icon "penpot-logo-icon")
(def ^:svg-id logo-error-screen "logo-error-screen") (def ^:svg-id logo-error-screen "logo-error-screen")
(def ^:svg-id login-illustration "login-illustration") (def ^:svg-id login-illustration "login-illustration")
(def ^:svg-id marketing-arrows "marketing-arrows") (def ^:svg-id marketing-arrows "marketing-arrows")
(def ^:svg-id marketing-exchange "marketing-exchange") (def ^:svg-id marketing-exchange "marketing-exchange")
(def ^:svg-id marketing-file "marketing-file") (def ^:svg-id marketing-file "marketing-file")
(def ^:svg-id marketing-layers "marketing-layers") (def ^:svg-id marketing-layers "marketing-layers")
(def ^:svg-id penpot-logo "penpot-logo")
(def ^:svg-id penpot-logo-icon "penpot-logo-icon")
(def raw-svg-list "A collection of all raw SVG assets" (collect-raw-svgs)) (def raw-svg-list "A collection of all raw SVG assets" (collect-raw-svgs))
(mf/defc raw-svg* (mf/defc raw-svg*
{::mf/props :obj} {::mf/props :obj}
[{:keys [id] :rest props}] [{:keys [id] :rest props}]
(assert (contains? raw-svg-list id) "invalid raw svg id")
[:> "svg" props [:> "svg" props
[:use {:href (dm/str "#asset-" id)}]]) [:use {:href (dm/str "#asset-" id)}]])

View File

@ -5,16 +5,12 @@ const { RawSvg } = Components;
const { StoryGrid, StoryGridCell, StoryHeader } = Components.storybook; const { StoryGrid, StoryGridCell, StoryHeader } = Components.storybook;
const { svgs } = Components.meta; const { svgs } = Components.meta;
const assetList = Object.entries(svgs)
.map(([_, value]) => value)
.sort();
export default { export default {
title: "Foundations/Assets/RawSvg", title: "Foundations/Assets/RawSvg",
component: Components.RawSvg, component: Components.RawSvg,
argTypes: { argTypes: {
id: { id: {
options: assetList, options: svgs,
control: { type: "select" }, control: { type: "select" },
}, },
}, },
@ -30,7 +26,7 @@ export const All = {
</StoryHeader> </StoryHeader>
<StoryGrid size="200"> <StoryGrid size="200">
{assetList.map((x) => ( {svgs.map((x) => (
<StoryGridCell key={x} title={x}> <StoryGridCell key={x} title={x}>
<RawSvg id={x} style={{ maxWidth: "100%" }} /> <RawSvg id={x} style={{ maxWidth: "100%" }} />
</StoryGridCell> </StoryGridCell>