From ae9011329ba9803c8333227ee6c23aec30543289 Mon Sep 17 00:00:00 2001 From: Gerard Date: Wed, 17 Jan 2024 11:44:31 +0100 Subject: [PATCH 1/3] Add BccReact component --- .../src/components/BccReact/BccReact.css | 5 + .../src/components/BccReact/BccReact.spec.ts | 31 ++++ .../components/BccReact/BccReact.stories.ts | 164 ++++++++++++++++++ .../src/components/BccReact/BccReact.vue | 82 +++++++++ .../src/components/BccReact/BccReactEmoji.vue | 35 ++++ .../__snapshots__/BccReact.spec.ts.snap | 18 ++ design-library/src/css/helpers.css | 31 +++- design-library/src/css/index.css | 1 + design-library/src/index.ts | 1 + .../src/tokens/tailwind/bccForbundetTheme.ts | 3 + .../src/tokens/tailwind/transitions.ts | 10 ++ 11 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 design-library/src/components/BccReact/BccReact.css create mode 100644 design-library/src/components/BccReact/BccReact.spec.ts create mode 100644 design-library/src/components/BccReact/BccReact.stories.ts create mode 100644 design-library/src/components/BccReact/BccReact.vue create mode 100644 design-library/src/components/BccReact/BccReactEmoji.vue create mode 100644 design-library/src/components/BccReact/__snapshots__/BccReact.spec.ts.snap create mode 100644 design-library/src/tokens/tailwind/transitions.ts diff --git a/design-library/src/components/BccReact/BccReact.css b/design-library/src/components/BccReact/BccReact.css new file mode 100644 index 00000000..7285480a --- /dev/null +++ b/design-library/src/components/BccReact/BccReact.css @@ -0,0 +1,5 @@ +@layer components { + .bcc-react { + + } +} diff --git a/design-library/src/components/BccReact/BccReact.spec.ts b/design-library/src/components/BccReact/BccReact.spec.ts new file mode 100644 index 00000000..8af7d79e --- /dev/null +++ b/design-library/src/components/BccReact/BccReact.spec.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from "vitest"; + +import { mount } from "@vue/test-utils"; +import BccReact from "./BccReact.vue"; + +describe("BccReact", () => { + it("renders reactions", () => { + expect(BccReact).toBeTruthy(); + + const wrapper = mount(BccReact, { + props: { + emojis: [ + { + id: "thumbsup", + emoji: "👍", + count: 1, + }, + { + id: "happy", + emoji: "😃", + count: 4, + }, + ], + }, + }); + + expect(wrapper.text()).toContain("4"); + expect(wrapper.text()).not.toContain("1"); + expect(wrapper.html()).toMatchSnapshot(); + }); +}); diff --git a/design-library/src/components/BccReact/BccReact.stories.ts b/design-library/src/components/BccReact/BccReact.stories.ts new file mode 100644 index 00000000..bd91670e --- /dev/null +++ b/design-library/src/components/BccReact/BccReact.stories.ts @@ -0,0 +1,164 @@ +import BccReact from "./BccReact.vue"; + +import type { Meta, StoryFn } from "@storybook/vue3"; + +export default { + title: "Components/BccReact", + component: BccReact, + argTypes: {}, +} as Meta; + +const Template: StoryFn = (args) => ({ + components: { BccReact }, + setup() { + return { args }; + }, + methods: { + onToggle(emojiId: string) { + const emoji = args.emojis.find((e) => e.id === emojiId); + if (!emoji) return; + if (emoji.count === undefined) emoji.count = 0; + emoji.count += emoji.selected ? -1 : 1; + emoji.selected = !emoji.selected; + + // Enable for only allowing single reaction per user + /* + if (args.singleReaction && emoji.selected) { + args.emojis.forEach((e) => { + if (e.id !== emojiId && e.selected) { + e.selected = false; + e.count = (e.count || 1) - 1; + } + }); + } + */ + }, + }, + template: ` +
+ +
+ `, +}); + +export const Example = Template.bind({}); +Example.args = { + top: false, + emojis: [ + { + id: "thumbsup", + emoji: "👍", + count: 0, + }, + { + id: "happy", + emoji: "😃", + count: 2, + selected: true, + }, + { + id: "smile", + emoji: "😊", + count: 0, + }, + { + id: "glasses", + emoji: "😎", + count: 0, + }, + { + id: "love", + emoji: "😍", + count: 0, + }, + { + id: "stars", + emoji: "🤩", + count: 0, + }, + { + id: "rocket", + emoji: "🚀", + count: 93, + }, + ], +}; + +Example.parameters = { + docs: { + source: { + language: "html", + code: ` +<--script--> + onToggle(emojiId: string) { + const emoji = args.emojis.find((e) => e.id === emojiId); + if (!emoji) return; + if (emoji.count === undefined) emoji.count = 0; + emoji.count += emoji.selected ? -1 : 1; + emoji.selected = !emoji.selected; + + // Enable for only allowing single reaction per user + /* + if (args.singleReaction && emoji.selected) { + args.emojis.forEach((e) => { + if (e.id !== emojiId && e.selected) { + e.selected = false; + e.count = (e.count || 1) - 1; + } + }); + } + */ + } + + + + `, + }, + }, +}; + +export const EmptyEmojis = Template.bind({}); +EmptyEmojis.args = { + top: true, + emojis: [ + { + id: "thumbsup", + emoji: "👍", + count: 0, + }, + { + id: "happy", + emoji: "😃", + count: 0, + }, + { + id: "smile", + emoji: "😊", + count: 0, + }, + { + id: "glasses", + emoji: "😎", + count: 0, + }, + { + id: "love", + emoji: "😍", + count: 0, + }, + { + id: "stars", + emoji: "🤩", + count: 0, + }, + { + id: "rocket", + emoji: "🚀", + count: 0, + }, + ], +}; diff --git a/design-library/src/components/BccReact/BccReact.vue b/design-library/src/components/BccReact/BccReact.vue new file mode 100644 index 00000000..32e2a964 --- /dev/null +++ b/design-library/src/components/BccReact/BccReact.vue @@ -0,0 +1,82 @@ + + + diff --git a/design-library/src/components/BccReact/BccReactEmoji.vue b/design-library/src/components/BccReact/BccReactEmoji.vue new file mode 100644 index 00000000..85741bbd --- /dev/null +++ b/design-library/src/components/BccReact/BccReactEmoji.vue @@ -0,0 +1,35 @@ + + + diff --git a/design-library/src/components/BccReact/__snapshots__/BccReact.spec.ts.snap b/design-library/src/components/BccReact/__snapshots__/BccReact.spec.ts.snap new file mode 100644 index 00000000..24bae30b --- /dev/null +++ b/design-library/src/components/BccReact/__snapshots__/BccReact.spec.ts.snap @@ -0,0 +1,18 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`BccReact > renders reactions 1`] = ` +"
+ +
+ +
+
+ + + +
" +`; diff --git a/design-library/src/css/helpers.css b/design-library/src/css/helpers.css index 7eebc8d5..c02e7fd0 100644 --- a/design-library/src/css/helpers.css +++ b/design-library/src/css/helpers.css @@ -35,4 +35,33 @@ opacity: 0; box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); transform: scaleY(0.4); -} \ No newline at end of file +} + +/* Scale */ +.bcc-scale-enter-active, +.bcc-scale-leave-active { + transition: transform .6s ease, opacity .4s ease; +} + +.bcc-scale-fast-enter-active, +.bcc-scale-fast-leave-active { + transition: transform .3s ease, opacity .3s ease; +} + +.bcc-scale-enter-from, +.bcc-scale-leave-to, +.bcc-scale-fast-enter-from, +.bcc-scale-fast-leave-to { + @apply opacity-0 scale-0 shadow-lg; +} + +/* Explode */ +.bcc-explode-enter-active, +.bcc-explode-leave-active { + transition: transform .3s ease, opacity .2s ease; +} + +.bcc-explode-enter-from, +.bcc-explode-leave-to { + @apply scale-[200%] opacity-0; +} diff --git a/design-library/src/css/index.css b/design-library/src/css/index.css index 0828feb6..214e70bc 100644 --- a/design-library/src/css/index.css +++ b/design-library/src/css/index.css @@ -24,3 +24,4 @@ @import "../components/BccTabs/BccTabs.css"; @import "../components/BccBanner/BccBanner.css"; @import "../components/BccSpinner/BccSpinner.css"; +@import "../components/BccReact/BccReact.css"; diff --git a/design-library/src/index.ts b/design-library/src/index.ts index 80d8ee7a..601a2335 100644 --- a/design-library/src/index.ts +++ b/design-library/src/index.ts @@ -31,3 +31,4 @@ export { default as BccToggle } from "./components/BccToggle/BccToggle.vue"; export { default as BccBanner } from "./components/BccBanner/BccBanner.vue"; export { default as BccSpinner } from "./components/BccSpinner/BccSpinner.vue"; export { default as BccProgress } from "./components/BccProgress/BccProgress.vue"; +export { default as BccReact } from "./components/BccReact/BccReact.vue"; diff --git a/design-library/src/tokens/tailwind/bccForbundetTheme.ts b/design-library/src/tokens/tailwind/bccForbundetTheme.ts index 7180f3c6..db77a860 100644 --- a/design-library/src/tokens/tailwind/bccForbundetTheme.ts +++ b/design-library/src/tokens/tailwind/bccForbundetTheme.ts @@ -8,6 +8,7 @@ import { backgroundColor } from "./backgroundColor"; import { outlineColor } from "./outlineColor"; import { ringColor } from "./ringColor"; import type { Config } from "tailwindcss"; +import { animation, keyframes } from "./transitions"; const bccForbundetTheme: Partial = { theme: { @@ -21,6 +22,8 @@ const bccForbundetTheme: Partial = { backgroundColor, outlineColor, ringColor, + keyframes, + animation }, }, }; diff --git a/design-library/src/tokens/tailwind/transitions.ts b/design-library/src/tokens/tailwind/transitions.ts new file mode 100644 index 00000000..ebd6e3a7 --- /dev/null +++ b/design-library/src/tokens/tailwind/transitions.ts @@ -0,0 +1,10 @@ +export const keyframes = { + wiggle: { + '0%, 100%': { transform: 'rotate(-3deg)' }, + '50%': { transform: 'rotate(3deg)' }, + } +} + +export const animation = { + wiggle: 'wiggle 0.3s ease-in-out infinite', +} \ No newline at end of file From 5ed1087f08bf0b0f4aea0a82ec7603ea8180715a Mon Sep 17 00:00:00 2001 From: Gerard Date: Wed, 17 Jan 2024 12:43:35 +0100 Subject: [PATCH 2/3] Moved styles to classes --- .../src/components/BccReact/BccReact.css | 32 +++++++++++++++++++ .../components/BccReact/BccReact.stories.ts | 1 + .../src/components/BccReact/BccReact.vue | 30 ++++++++++------- .../src/components/BccReact/BccReactEmoji.vue | 10 +++--- .../__snapshots__/BccReact.spec.ts.snap | 8 ++--- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/design-library/src/components/BccReact/BccReact.css b/design-library/src/components/BccReact/BccReact.css index 7285480a..a8360df5 100644 --- a/design-library/src/components/BccReact/BccReact.css +++ b/design-library/src/components/BccReact/BccReact.css @@ -1,5 +1,37 @@ @layer components { .bcc-react { + @apply relative flex w-full items-center overflow-visible; + } + .bcc-react-toggle { + @apply mr-1 flex shrink-0 cursor-pointer items-center justify-center rounded-full p-1 leading-tight transition; + } + .bcc-react-list { + @apply hide-scrollbar flex flex-1 items-center gap-1 overflow-x-auto overflow-y-hidden rounded-full p-1; + } + .bcc-react-empty { + @apply text-label flex items-center; + } + + .bcc-react-selector { + @apply absolute -left-1 z-50 flex items-center gap-1 rounded-full bg-neutral-100 px-1 dark:bg-neutral-600; + /* Default --bottom */ + @apply top-11 origin-top-left shadow-lg; + } + + .bcc-react-selector--top { + @apply -top-10 origin-bottom-left shadow; + } + + .bcc-react-selector-item { + @apply p-2 text-xl leading-none; + } + .bcc-react-emoji-list-item { + @apply flex cursor-pointer items-center justify-center rounded-full p-1 text-2xl leading-none shadow transition-all hover:scale-105; + /* Default --not-selected */ + @apply text-black bg-neutral-100; + } + .bcc-react-emoji-list-item--selected { + @apply bg-neutral-800 text-white; } } diff --git a/design-library/src/components/BccReact/BccReact.stories.ts b/design-library/src/components/BccReact/BccReact.stories.ts index bd91670e..1e07c29e 100644 --- a/design-library/src/components/BccReact/BccReact.stories.ts +++ b/design-library/src/components/BccReact/BccReact.stories.ts @@ -124,6 +124,7 @@ Example.parameters = { export const EmptyEmojis = Template.bind({}); EmptyEmojis.args = { top: true, + placeholder: "No reactions yet", emojis: [ { id: "thumbsup", diff --git a/design-library/src/components/BccReact/BccReact.vue b/design-library/src/components/BccReact/BccReact.vue index 32e2a964..b295da3f 100644 --- a/design-library/src/components/BccReact/BccReact.vue +++ b/design-library/src/components/BccReact/BccReact.vue @@ -7,9 +7,13 @@ import type { EmojiStat } from "./BccReactEmoji.vue"; type Props = { emojis: EmojiStat[]; top?: boolean; + placeholder?: string; }; -const props = defineProps(); +const props = withDefaults(defineProps(), { + top: false, + placeholder: "Be the first to react 😉", +}); const emit = defineEmits<{ (e: "toggle", id: string): void; }>(); @@ -31,22 +35,20 @@ function selectEmoji(emoji: EmojiStat) { -

+

{{ placeholder }}