From cd6cae20403080adda49d4775566e0733aa66fbf Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Tue, 3 Mar 2026 16:04:31 -0800 Subject: [PATCH] =?UTF-8?q?feat(avocet):=20LabelBucketGrid=20=E2=80=94=20n?= =?UTF-8?q?umpad=20layout,=20bucket-mode=20expansion,=20drag=20drop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/LabelBucketGrid.test.ts | 34 ++++++ web/src/components/LabelBucketGrid.vue | 118 +++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 web/src/components/LabelBucketGrid.test.ts create mode 100644 web/src/components/LabelBucketGrid.vue diff --git a/web/src/components/LabelBucketGrid.test.ts b/web/src/components/LabelBucketGrid.test.ts new file mode 100644 index 0000000..d1d6e27 --- /dev/null +++ b/web/src/components/LabelBucketGrid.test.ts @@ -0,0 +1,34 @@ +import { mount } from '@vue/test-utils' +import LabelBucketGrid from './LabelBucketGrid.vue' +import { describe, it, expect } from 'vitest' + +const labels = [ + { name: 'interview_scheduled', emoji: '🗓️', color: '#4CAF50', key: '1' }, + { name: 'offer_received', emoji: '🎉', color: '#2196F3', key: '2' }, + { name: 'rejected', emoji: '❌', color: '#F44336', key: '3' }, +] + +describe('LabelBucketGrid', () => { + it('renders all labels', () => { + const w = mount(LabelBucketGrid, { props: { labels, isBucketMode: false } }) + expect(w.findAll('[data-testid="label-btn"]')).toHaveLength(3) + }) + + it('emits label event on click', async () => { + const w = mount(LabelBucketGrid, { props: { labels, isBucketMode: false } }) + await w.find('[data-testid="label-btn"]').trigger('click') + expect(w.emitted('label')?.[0]).toEqual(['interview_scheduled']) + }) + + it('applies bucket-mode class when isBucketMode is true', () => { + const w = mount(LabelBucketGrid, { props: { labels, isBucketMode: true } }) + expect(w.find('.label-grid').classes()).toContain('bucket-mode') + }) + + it('shows key hint and emoji', () => { + const w = mount(LabelBucketGrid, { props: { labels, isBucketMode: false } }) + const btn = w.find('[data-testid="label-btn"]') + expect(btn.text()).toContain('1') + expect(btn.text()).toContain('🗓️') + }) +}) diff --git a/web/src/components/LabelBucketGrid.vue b/web/src/components/LabelBucketGrid.vue new file mode 100644 index 0000000..9e6bfa1 --- /dev/null +++ b/web/src/components/LabelBucketGrid.vue @@ -0,0 +1,118 @@ + + + + +