From 5238d61f24684ce02f008760ababd0e9cd645ba9 Mon Sep 17 00:00:00 2001 From: Matthew L McPeak Date: Sat, 11 Jul 2026 14:32:18 -0400 Subject: [PATCH] feat: migrate Tag to shadcn-vue Badge Also fixes Card.stories.ts, which imported the now-removed Tag export. Co-Authored-By: Claude Sonnet 5 --- packages/library/src/components/index.ts | 1 + .../library/src/components/ui/badge/Badge.vue | 19 +++++++ .../library/src/components/ui/badge/index.ts | 24 ++++++++ packages/library/src/index.ts | 2 +- packages/library/src/lib/index.ts | 22 +++++--- packages/library/tests/Badge.spec.ts | 19 +++++++ packages/library/tests/lib.spec.ts | 2 - packages/playground/stories/Badge.stories.ts | 46 ++++++++++++++++ packages/playground/stories/Card.stories.ts | 6 +- packages/playground/stories/Tag.stories.ts | 55 ------------------- 10 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 packages/library/src/components/ui/badge/Badge.vue create mode 100644 packages/library/src/components/ui/badge/index.ts create mode 100644 packages/library/tests/Badge.spec.ts create mode 100644 packages/playground/stories/Badge.stories.ts delete mode 100644 packages/playground/stories/Tag.stories.ts diff --git a/packages/library/src/components/index.ts b/packages/library/src/components/index.ts index 8f3f37d..a759617 100644 --- a/packages/library/src/components/index.ts +++ b/packages/library/src/components/index.ts @@ -5,3 +5,4 @@ export type { ButtonVariants } from './ui/button' export { Input } from './ui/input' export { Textarea } from './ui/textarea' export { Switch } from './ui/switch' +export { Badge, badgeVariants } from './ui/badge' diff --git a/packages/library/src/components/ui/badge/Badge.vue b/packages/library/src/components/ui/badge/Badge.vue new file mode 100644 index 0000000..369c21f --- /dev/null +++ b/packages/library/src/components/ui/badge/Badge.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/library/src/components/ui/badge/index.ts b/packages/library/src/components/ui/badge/index.ts new file mode 100644 index 0000000..87e70aa --- /dev/null +++ b/packages/library/src/components/ui/badge/index.ts @@ -0,0 +1,24 @@ +import type { VariantProps } from 'class-variance-authority' +import { cva } from 'class-variance-authority' + +export { default as Badge } from './Badge.vue' + +export const badgeVariants = cva( + 'inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-full border border-transparent px-2 py-0.5 text-xs font-semibold [&>svg]:size-3', + { + variants: { + variant: { + primary: 'bg-primary text-primary-foreground', + secondary: 'bg-secondary text-secondary-foreground', + info: 'bg-info text-info-foreground', + success: 'bg-success text-success-foreground', + warning: 'bg-warning text-warning-foreground', + danger: 'bg-destructive text-destructive-foreground', + }, + }, + defaultVariants: { + variant: 'primary', + }, + }, +) +export type BadgeVariants = VariantProps diff --git a/packages/library/src/index.ts b/packages/library/src/index.ts index 57fc4a8..1f27b8f 100644 --- a/packages/library/src/index.ts +++ b/packages/library/src/index.ts @@ -21,5 +21,5 @@ export { Card, Dialog, Message, - Tag, + Badge, } from './lib' diff --git a/packages/library/src/lib/index.ts b/packages/library/src/lib/index.ts index 83479a6..7245831 100644 --- a/packages/library/src/lib/index.ts +++ b/packages/library/src/lib/index.ts @@ -8,12 +8,20 @@ import { Card as PrimeCard, Dialog as PrimeDialog, Message as PrimeMessage, - Tag as PrimeTag, } from 'primevue' import { ThemeLibrary, type Themeable, ThemeEngine, type LibComponent, type Key } from '../engine' -import { NychLoadingIcon, Button, buttonVariants, Input, Textarea, Switch } from '../components' +import { + NychLoadingIcon, + Button, + buttonVariants, + Input, + Textarea, + Switch, + Badge, + badgeVariants, +} from '../components' -export { Button, buttonVariants, Input, Textarea, Switch } +export { Button, buttonVariants, Input, Textarea, Switch, Badge, badgeVariants } /** * Build a Themeable that themes a PrimeVue component through its passthrough (`pt`) API. @@ -98,7 +106,6 @@ const nychthemeron: ThemeLibrary = { 'closeButton', 'closeIcon', ]), - Tag: ptThemeable(PrimeTag, 'tag', ['label', 'icon']), }, } @@ -116,7 +123,6 @@ export const Select: typeof PrimeSelect = _engine.getComponent('Card') export const Dialog: typeof PrimeDialog = _engine.getComponent('Dialog') export const Message: typeof PrimeMessage = _engine.getComponent('Message') -export const Tag: typeof PrimeTag = _engine.getComponent('Tag') export const createNychthemeron = (): { Button: typeof Button @@ -132,7 +138,7 @@ export const createNychthemeron = (): { Card: typeof PrimeCard Dialog: typeof PrimeDialog Message: typeof PrimeMessage - Tag: typeof PrimeTag + Badge: typeof Badge install: (app: App) => void } => ({ Button, @@ -148,7 +154,7 @@ export const createNychthemeron = (): { Card, Dialog, Message, - Tag, + Badge, install(app: App) { app.component('NychButton', Button) app.component('NychLoadingIcon', LoadingIcon) @@ -163,6 +169,6 @@ export const createNychthemeron = (): { app.component('NychCard', Card) app.component('NychDialog', Dialog) app.component('NychMessage', Message) - app.component('NychTag', Tag) + app.component('NychBadge', Badge) }, }) diff --git a/packages/library/tests/Badge.spec.ts b/packages/library/tests/Badge.spec.ts new file mode 100644 index 0000000..9bda700 --- /dev/null +++ b/packages/library/tests/Badge.spec.ts @@ -0,0 +1,19 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import { Badge } from '../src/lib' + +describe('Badge', () => { + it('defaults to the primary variant', () => { + const wrapper = mount(Badge, { slots: { default: 'Sacred' } }) + expect(wrapper.classes().join(' ')).toContain('bg-primary') + }) + + it.each(['secondary', 'info', 'success', 'warning', 'danger'] as const)( + 'applies the %s variant class', + (variant) => { + const wrapper = mount(Badge, { props: { variant } }) + const expectedClass = variant === 'danger' ? 'bg-destructive' : `bg-${variant}` + expect(wrapper.classes().join(' ')).toContain(expectedClass) + }, + ) +}) diff --git a/packages/library/tests/lib.spec.ts b/packages/library/tests/lib.spec.ts index c2ae6b2..0f62279 100644 --- a/packages/library/tests/lib.spec.ts +++ b/packages/library/tests/lib.spec.ts @@ -11,7 +11,6 @@ import { Card, Dialog, Message, - Tag, } from '../src/index' //eslint-disable-next-line @@ -76,7 +75,6 @@ describe('themed pt components', () => { { name: 'Card', component: Card, base: 'nych-card' }, { name: 'Dialog', component: Dialog, base: 'nych-dialog' }, { name: 'Message', component: Message, base: 'nych-message' }, - { name: 'Tag', component: Tag, base: 'nych-tag' }, ] it.each(cases)('$name is a valid Vue component', ({ component }) => { diff --git a/packages/playground/stories/Badge.stories.ts b/packages/playground/stories/Badge.stories.ts new file mode 100644 index 0000000..dd34175 --- /dev/null +++ b/packages/playground/stories/Badge.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' + +import { Badge as NychBadge } from '@nychthemeron/library' + +const meta = { + title: 'Components/Badge', + component: NychBadge, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + variant: { + control: { type: 'select' }, + options: ['primary', 'secondary', 'info', 'success', 'warning', 'danger'], + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { variant: 'primary' }, + render: (args) => ({ + components: { NychBadge }, + setup: () => ({ args }), + template: `Mortal`, + }), +} + +export const AllVariants: Story = { + render: () => ({ + components: { NychBadge }, + template: ` +
+ Primary + Secondary + Info + Success + Warning + Danger +
+ `, + }), +} diff --git a/packages/playground/stories/Card.stories.ts b/packages/playground/stories/Card.stories.ts index f066d34..e005029 100644 --- a/packages/playground/stories/Card.stories.ts +++ b/packages/playground/stories/Card.stories.ts @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { Card as NychCard, Button as NychButton, Tag as NychTag } from '@nychthemeron/library' +import { Card as NychCard, Button as NychButton, Badge as NychBadge } from '@nychthemeron/library' const meta = { title: 'Components/Card', @@ -52,7 +52,7 @@ export const WithFooter: Story = { export const WithHeaderAndTag: Story = { render: () => ({ - components: { NychCard, NychTag }, + components: { NychCard, NychBadge }, template: `