From 60cba6c9f3fc5316900b6877ccf07d0cbb59f6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 23 Jul 2024 15:42:02 +0200 Subject: [PATCH 1/2] :sparkles: Implement button* and icon-button* for the design system --- frontend/src/app/main/ui/ds.cljs | 6 +- frontend/src/app/main/ui/ds/_borders.scss | 10 ++ frontend/src/app/main/ui/ds/_sizes.scss | 10 ++ .../src/app/main/ui/ds/buttons/_buttons.scss | 132 ++++++++++++++++++ .../src/app/main/ui/ds/buttons/button.cljs | 30 ++++ .../src/app/main/ui/ds/buttons/button.scss | 35 +++++ .../app/main/ui/ds/buttons/button.stories.jsx | 74 ++++++++++ .../app/main/ui/ds/buttons/icon_button.cljs | 30 ++++ .../app/main/ui/ds/buttons/icon_button.scss | 33 +++++ .../ui/ds/buttons/icon_button.stories.jsx | 66 +++++++++ 10 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 frontend/src/app/main/ui/ds/_borders.scss create mode 100644 frontend/src/app/main/ui/ds/_sizes.scss create mode 100644 frontend/src/app/main/ui/ds/buttons/_buttons.scss create mode 100644 frontend/src/app/main/ui/ds/buttons/button.cljs create mode 100644 frontend/src/app/main/ui/ds/buttons/button.scss create mode 100644 frontend/src/app/main/ui/ds/buttons/button.stories.jsx create mode 100644 frontend/src/app/main/ui/ds/buttons/icon_button.cljs create mode 100644 frontend/src/app/main/ui/ds/buttons/icon_button.scss create mode 100644 frontend/src/app/main/ui/ds/buttons/icon_button.stories.jsx diff --git a/frontend/src/app/main/ui/ds.cljs b/frontend/src/app/main/ui/ds.cljs index b15d52724..59895744a 100644 --- a/frontend/src/app/main/ui/ds.cljs +++ b/frontend/src/app/main/ui/ds.cljs @@ -6,6 +6,8 @@ (ns app.main.ui.ds (:require + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]] [app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg* raw-svg-list]] [app.main.ui.ds.foundations.typography :refer [typography-list]] @@ -16,8 +18,10 @@ (def default "A export used for storybook" - #js {:Heading heading* + #js {:Button button* + :Heading heading* :Icon icon* + :IconButton icon-button* :Loader loader* :RawSvg raw-svg* :Text text* diff --git a/frontend/src/app/main/ui/ds/_borders.scss b/frontend/src/app/main/ui/ds/_borders.scss new file mode 100644 index 000000000..165ade57d --- /dev/null +++ b/frontend/src/app/main/ui/ds/_borders.scss @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@use "./utils.scss" as *; + +// TODO: create actual tokens once we have them from design +$br-8: px2rem(8); diff --git a/frontend/src/app/main/ui/ds/_sizes.scss b/frontend/src/app/main/ui/ds/_sizes.scss new file mode 100644 index 000000000..f27838b6a --- /dev/null +++ b/frontend/src/app/main/ui/ds/_sizes.scss @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@use "./utils.scss" as *; + +// TODO: create actual tokens once we have them from design +$sz-32: px2rem(32); diff --git a/frontend/src/app/main/ui/ds/buttons/_buttons.scss b/frontend/src/app/main/ui/ds/buttons/_buttons.scss new file mode 100644 index 000000000..7d8c896ac --- /dev/null +++ b/frontend/src/app/main/ui/ds/buttons/_buttons.scss @@ -0,0 +1,132 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@use "../_borders.scss" as *; +@use "../_sizes.scss" as *; +@use "../utils.scss" as *; + +%base-button { + --button-bg-color: initial; + --button-fg-color: initial; + --button-hover-bg-color: initial; + --button-hover-fg-color: initial; + --button-active-bg-color: initial; + --button-disabled-bg-color: initial; + --button-disabled-fg-color: initial; + --button-border-color: var(--button-bg-color); + --button-focus-inner-ring-color: initial; + --button-focus-outer-ring-color: initial; + + appearance: none; + height: $sz-32; + border: none; + border-radius: $br-8; + + background: var(--button-bg-color); + color: var(--button-fg-color); + border: 1px solid var(--button-border-color); + + &:hover { + --button-bg-color: var(--button-hover-bg-color); + --button-fg-color: var(--button-hover-fg-color); + } + + &:active { + --button-bg-color: var(--button-active-bg-color); + } + + &:focus-visible { + outline: var(--button-focus-inner-ring-color) solid #{px2rem(2)}; + outline-offset: -#{px2rem(3)}; + --button-border-color: var(--button-focus-outer-ring-color); + --button-fg-color: var(--button-focus-fg-color); + } + + &:disabled { + --button-bg-color: var(--button-disabled-bg-color); + --button-fg-color: var(--button-disabled-fg-color); + } +} + +%base-button-primary { + --button-bg-color: var(--color-accent-primary); + --button-fg-color: var(--color-background-secondary); + + --button-hover-bg-color: var(--color-accent-tertiary); + --button-hover-fg-color: var(--color-background-secondary); + + --button-active-bg-color: var(--color-accent-tertiary); + + --button-disabled-bg-color: var(--color-accent-primary-muted); + --button-disabled-fg-color: var(--color-background-secondary); + + --button-focus-bg-color: var(--color-accent-primary); + --button-focus-fg-color: var(--color-background-secondary); + --button-focus-inner-ring-color: var(--color-background-secondary); + --button-focus-outer-ring-color: var(--color-accent-primary); + + &:active { + box-shadow: inset 0 0 #{px2rem(10)} #{px2rem(2)} rgba(0, 0, 0, 0.2); + } +} + +%base-button-secondary { + --button-bg-color: var(--color-background-tertiary); + --button-fg-color: var(--color-foreground-secondary); + + --button-hover-bg-color: var(--color-background-tertiary); + --button-hover-fg-color: var(--color-accent-primary); + + --button-active-bg-color: var(--color-background-quaternary); + + --button-disabled-bg-color: transparent; + --button-disabled-fg-color: var(--color-foreground-secondary); + + --button-focus-bg-color: var(--color-background-tertiary); + --button-focus-fg-color: var(--color-foreground-primary); + --button-focus-inner-ring-color: var(--color-background-secondary); + --button-focus-outer-ring-color: var(--color-accent-primary); +} + +%base-button-ghost { + --button-bg-color: transparent; + --button-fg-color: var(--color-foreground-secondary); + + --button-hover-bg-color: var(--color-background-tertiary); + --button-hover-fg-color: var(--color-accent-primary); + + --button-active-bg-color: var(--color-background-quaternary); + + --button-disabled-bg-color: transparent; + --button-disabled-fg-color: var(--color-accent-primary-muted); + + --button-focus-bg-color: transparent; + --button-focus-fg-color: var(--color-foreground-secondary); + --button-focus-inner-ring-color: transparent; + --button-focus-outer-ring-color: var(--color-accent-primary); +} + +%base-button-destructive { + --button-bg-color: var(--color-accent-error); + --button-fg-color: var(--color-foreground-primary); + + --button-hover-bg-color: var(--color-background-error); + --button-hover-fg-color: var(--color-foreground-primary); + + --button-active-bg-color: var(--color-accent-error); + + --button-disabled-bg-color: var(--color-background-error); + --button-disabled-fg-color: var(--color-accent-error); + + --button-focus-bg-color: var(--color-accent-error); + --button-focus-fg-color: var(--color-foreground-primary); + --button-focus-inner-ring-color: var(--color-background-primary); + --button-focus-outer-ring-color: var(--color-accent-primary); + + &:active { + box-shadow: inset 0 0 #{px2rem(10)} #{px2rem(2)} rgba(0, 0, 0, 0.2); + } +} diff --git a/frontend/src/app/main/ui/ds/buttons/button.cljs b/frontend/src/app/main/ui/ds/buttons/button.cljs new file mode 100644 index 000000000..8086758bb --- /dev/null +++ b/frontend/src/app/main/ui/ds/buttons/button.cljs @@ -0,0 +1,30 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.main.ui.ds.buttons.button + (:require-macros + [app.common.data.macros :as dm] + [app.main.style :as stl]) + (:require + [app.main.ui.ds.foundations.assets.icon :refer [icon*]] + [rumext.v2 :as mf])) + +(def button-variants (set '("primary" "secondary" "ghost" "destructive"))) + +(mf/defc button* + {::mf/props :obj} + [{:keys [variant icon children class] :rest props}] + (assert (or (nil? variant) (contains? button-variants variant) "expected valid variant")) + (let [variant (or variant "primary") + class (dm/str class " " (stl/css-case :button true + :button-primary (= variant "primary") + :button-secondary (= variant "secondary") + :button-ghost (= variant "ghost") + :button-destructive (= variant "destructive"))) + props (mf/spread-props props {:class class})] + [:> "button" props + (when icon [:> icon* {:id icon :size "m"}]) + [:span {:class (stl/css :label-wrapper)} children]])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/ds/buttons/button.scss b/frontend/src/app/main/ui/ds/buttons/button.scss new file mode 100644 index 000000000..5e7b2cfe6 --- /dev/null +++ b/frontend/src/app/main/ui/ds/buttons/button.scss @@ -0,0 +1,35 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@use "../typography.scss" as *; +@use "./buttons" as *; + +.button { + @extend %base-button; + + @include use-typography("headline-small"); + padding: 0 var(--sp-m); + + display: inline-flex; + align-items: center; + column-gap: var(--sp-xs); +} + +.button-primary { + @extend %base-button-primary; +} + +.button-secondary { + @extend %base-button-secondary; +} + +.button-ghost { + @extend %base-button-ghost; +} + +.button-destructive { + @extend %base-button-destructive; +} diff --git a/frontend/src/app/main/ui/ds/buttons/button.stories.jsx b/frontend/src/app/main/ui/ds/buttons/button.stories.jsx new file mode 100644 index 000000000..8a2c78a15 --- /dev/null +++ b/frontend/src/app/main/ui/ds/buttons/button.stories.jsx @@ -0,0 +1,74 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +import * as React from "react"; +import Components from "@target/components"; + +const { Button } = Components; +const { icons } = Components.meta; + +const iconList = [ + ...Object.entries(icons) + .map(([_, value]) => value) + .sort(), +]; + +export default { + title: "Buttons/Button", + component: Components.Button, + argTypes: { + icon: { + options: iconList, + control: { type: "select" }, + }, + disabled: { control: "boolean" }, + variant: { + options: ["primary", "secondary", "ghost", "destructive"], + control: { type: "select" }, + }, + }, + args: { + children: "Lorem ipsum", + disabled: false, + variant: undefined, + }, + parameters: { + controls: { exclude: ["children"] }, + }, + render: ({ ...args }) =>