From 9d1fd16f0facfce3f6133d15f2d4b835229b1c95 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 18 May 2026 10:34:21 +0200 Subject: [PATCH] feat(gui-v2): StatusTag (PrimeVue Tag + statusSeverity map) + story Co-Authored-By: Claude Sonnet 4.6 --- .../components-v2/shared/StatusTag.stories.ts | 24 ++++++++++ .../src/components-v2/shared/StatusTag.vue | 44 +++++++++++++++++++ .../shared/__tests__/StatusTag.spec.ts | 35 +++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 apps/app/src/components-v2/shared/StatusTag.stories.ts create mode 100644 apps/app/src/components-v2/shared/StatusTag.vue create mode 100644 apps/app/src/components-v2/shared/__tests__/StatusTag.spec.ts diff --git a/apps/app/src/components-v2/shared/StatusTag.stories.ts b/apps/app/src/components-v2/shared/StatusTag.stories.ts new file mode 100644 index 00000000..c2f6cf8b --- /dev/null +++ b/apps/app/src/components-v2/shared/StatusTag.stories.ts @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' +import StatusTag from '@/components-v2/shared/StatusTag.vue' + +const meta: Meta = { + title: 'Shared/StatusTag', + component: StatusTag, + tags: ['autodocs'], + argTypes: { + status: { control: 'text' }, + label: { control: 'text' }, + dot: { control: 'boolean' }, + }, +} + +export default meta +type Story = StoryObj + +export const Success: Story = { args: { status: 'approved' } } +export const Warn: Story = { args: { status: 'pending_approval' } } +export const Info: Story = { args: { status: 'invited' } } +export const Secondary: Story = { args: { status: 'draft' } } +export const Danger: Story = { args: { status: 'no_show' } } +export const WithDot: Story = { args: { status: 'confirmed', dot: true } } +export const CustomLabel: Story = { args: { status: 'rejected', label: 'Afgewezen' } } diff --git a/apps/app/src/components-v2/shared/StatusTag.vue b/apps/app/src/components-v2/shared/StatusTag.vue new file mode 100644 index 00000000..d2a63aaf --- /dev/null +++ b/apps/app/src/components-v2/shared/StatusTag.vue @@ -0,0 +1,44 @@ + + + diff --git a/apps/app/src/components-v2/shared/__tests__/StatusTag.spec.ts b/apps/app/src/components-v2/shared/__tests__/StatusTag.spec.ts new file mode 100644 index 00000000..f06b5cd8 --- /dev/null +++ b/apps/app/src/components-v2/shared/__tests__/StatusTag.spec.ts @@ -0,0 +1,35 @@ +// apps/app/src/components-v2/shared/__tests__/StatusTag.spec.ts +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import { defineComponent } from 'vue' +import StatusTag from '@/components-v2/shared/StatusTag.vue' + +const TagStub = defineComponent({ + name: 'TagStub', + props: { value: { type: String, default: '' }, severity: { type: String, default: '' }, pt: { type: Object, default: () => ({}) } }, + template: '{{ value }}', +}) + +const mountTag = (props: { status: string; label?: string; dot?: boolean }) => + mount(StatusTag, { props, global: { stubs: { Tag: TagStub } } }) + +describe('StatusTag', () => { + it('resolves severity from statusSeverity for the status prop', () => { + expect(mountTag({ status: 'approved' }).get('.tag-stub').attributes('data-severity')).toBe('success') + expect(mountTag({ status: 'no_show' }).get('.tag-stub').attributes('data-severity')).toBe('danger') + expect(mountTag({ status: 'deposit_paid' }).get('.tag-stub').attributes('data-severity')).toBe('info') + }) + + it('renders the label prop, defaulting to the humanised status', () => { + expect(mountTag({ status: 'pending_approval' }).text()).toContain('pending approval') + expect(mountTag({ status: 'approved', label: 'Goedgekeurd' }).text()).toContain('Goedgekeurd') + }) + + it('adds the dot passthrough only when dot=true', () => { + expect(mountTag({ status: 'draft' }).get('.tag-stub').attributes('data-severity')).toBe('secondary') + + const pt = mountTag({ status: 'draft', dot: true }).getComponent(TagStub).props('pt') as Record + + expect(pt).toHaveProperty('root') + }) +})