diff --git a/packages/library/src/components/index.ts b/packages/library/src/components/index.ts index b4fe48d..d75a3c1 100644 --- a/packages/library/src/components/index.ts +++ b/packages/library/src/components/index.ts @@ -8,3 +8,4 @@ export { Switch } from './ui/switch' export { Badge, badgeVariants } from './ui/badge' export { Checkbox } from './ui/checkbox' export { CheckboxGroup } from './ui/checkbox-group' +export { RadioGroup, RadioGroupItem } from './ui/radio-group' diff --git a/packages/library/src/components/ui/radio-group/RadioGroup.vue b/packages/library/src/components/ui/radio-group/RadioGroup.vue new file mode 100644 index 0000000..7f35f19 --- /dev/null +++ b/packages/library/src/components/ui/radio-group/RadioGroup.vue @@ -0,0 +1,25 @@ + + + diff --git a/packages/library/src/components/ui/radio-group/RadioGroupItem.vue b/packages/library/src/components/ui/radio-group/RadioGroupItem.vue new file mode 100644 index 0000000..18960ab --- /dev/null +++ b/packages/library/src/components/ui/radio-group/RadioGroupItem.vue @@ -0,0 +1,41 @@ + + + diff --git a/packages/library/src/components/ui/radio-group/index.ts b/packages/library/src/components/ui/radio-group/index.ts new file mode 100644 index 0000000..7cb0e3b --- /dev/null +++ b/packages/library/src/components/ui/radio-group/index.ts @@ -0,0 +1,2 @@ +export { default as RadioGroup } from "./RadioGroup.vue" +export { default as RadioGroupItem } from "./RadioGroupItem.vue" diff --git a/packages/library/src/index.ts b/packages/library/src/index.ts index 1f27b8f..0a51792 100644 --- a/packages/library/src/index.ts +++ b/packages/library/src/index.ts @@ -14,8 +14,8 @@ export { Textarea, Checkbox, CheckboxGroup, - RadioButton, - RadioButtonGroup, + RadioGroup, + RadioGroupItem, Switch, Select, Card, diff --git a/packages/library/src/lib/index.ts b/packages/library/src/lib/index.ts index 23498af..4efbb97 100644 --- a/packages/library/src/lib/index.ts +++ b/packages/library/src/lib/index.ts @@ -1,7 +1,5 @@ import type { App } from 'vue' import { - RadioButton as PrimeRadioButton, - RadioButtonGroup as PrimeRadioButtonGroup, Select as PrimeSelect, Card as PrimeCard, Dialog as PrimeDialog, @@ -19,9 +17,23 @@ import { badgeVariants, Checkbox, CheckboxGroup, + RadioGroup, + RadioGroupItem, } from '../components' -export { Button, buttonVariants, Input, Textarea, Switch, Badge, badgeVariants, Checkbox, CheckboxGroup } +export { + Button, + buttonVariants, + Input, + Textarea, + Switch, + Badge, + badgeVariants, + Checkbox, + CheckboxGroup, + RadioGroup, + RadioGroupItem, +} /** * Build a Themeable that themes a PrimeVue component through its passthrough (`pt`) API. @@ -65,8 +77,6 @@ const loadingIconThemeable: Themeable = { const nychthemeron: ThemeLibrary = { components: { LoadingIcon: loadingIconThemeable, - RadioButton: ptThemeable(PrimeRadioButton, 'radio', ['box', 'input', 'icon']), - RadioButtonGroup: ptThemeable(PrimeRadioButtonGroup, 'radio-group'), Select: ptThemeable(PrimeSelect, 'select', [ 'label', 'dropdown', @@ -110,10 +120,6 @@ const nychthemeron: ThemeLibrary = { const _engine = new ThemeEngine(nychthemeron) export const LoadingIcon = _engine.getComponent('LoadingIcon') -export const RadioButton: typeof PrimeRadioButton = - _engine.getComponent('RadioButton') -export const RadioButtonGroup: typeof PrimeRadioButtonGroup = - _engine.getComponent('RadioButtonGroup') export const Select: typeof PrimeSelect = _engine.getComponent('Select') export const Card: typeof PrimeCard = _engine.getComponent('Card') export const Dialog: typeof PrimeDialog = _engine.getComponent('Dialog') @@ -126,8 +132,8 @@ export const createNychthemeron = (): { Textarea: typeof Textarea Checkbox: typeof Checkbox CheckboxGroup: typeof CheckboxGroup - RadioButton: typeof PrimeRadioButton - RadioButtonGroup: typeof PrimeRadioButtonGroup + RadioGroup: typeof RadioGroup + RadioGroupItem: typeof RadioGroupItem Switch: typeof Switch Select: typeof PrimeSelect Card: typeof PrimeCard @@ -142,8 +148,8 @@ export const createNychthemeron = (): { Textarea, Checkbox, CheckboxGroup, - RadioButton, - RadioButtonGroup, + RadioGroup, + RadioGroupItem, Switch, Select, Card, @@ -157,8 +163,8 @@ export const createNychthemeron = (): { app.component('NychTextarea', Textarea) app.component('NychCheckbox', Checkbox) app.component('NychCheckboxGroup', CheckboxGroup) - app.component('NychRadioButton', RadioButton) - app.component('NychRadioButtonGroup', RadioButtonGroup) + app.component('NychRadioGroup', RadioGroup) + app.component('NychRadioGroupItem', RadioGroupItem) app.component('NychSwitch', Switch) app.component('NychSelect', Select) app.component('NychCard', Card) diff --git a/packages/library/tests/RadioGroup.spec.ts b/packages/library/tests/RadioGroup.spec.ts new file mode 100644 index 0000000..7a06724 --- /dev/null +++ b/packages/library/tests/RadioGroup.spec.ts @@ -0,0 +1,32 @@ +import { describe, it, expect } from 'vitest' +import { h } from 'vue' +import { mount } from '@vue/test-utils' +import { RadioGroup, RadioGroupItem } from '../src/lib' + +describe('RadioGroup', () => { + it('marks the item matching modelValue as checked', () => { + const wrapper = mount(RadioGroup, { + props: { modelValue: 'hades' }, + slots: { + default: () => [ + h(RadioGroupItem, { value: 'apollo' }), + h(RadioGroupItem, { value: 'hades' }), + ], + }, + }) + const items = wrapper.findAllComponents(RadioGroupItem) + expect(items[0]?.get('button').attributes('data-state')).toBe('unchecked') + expect(items[1]?.get('button').attributes('data-state')).toBe('checked') + }) + + it('emits update:modelValue when an item is selected', async () => { + const wrapper = mount(RadioGroup, { + props: { modelValue: 'apollo' }, + slots: { + default: () => [h(RadioGroupItem, { value: 'hades' })], + }, + }) + await wrapper.findComponent(RadioGroupItem).get('button').trigger('click') + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['hades']) + }) +}) diff --git a/packages/library/tests/lib.spec.ts b/packages/library/tests/lib.spec.ts index 62e3929..4a523c5 100644 --- a/packages/library/tests/lib.spec.ts +++ b/packages/library/tests/lib.spec.ts @@ -3,8 +3,6 @@ import type { App } from 'vue' import { createNychthemeron, LoadingIcon, - RadioButton, - RadioButtonGroup, Select, Card, Dialog, @@ -65,8 +63,6 @@ describe('LoadingIcon', () => { describe('themed pt components', () => { const cases = [ - { name: 'RadioButton', component: RadioButton, base: 'nych-radio' }, - { name: 'RadioButtonGroup', component: RadioButtonGroup, base: 'nych-radio-group' }, { name: 'Select', component: Select, base: 'nych-select' }, { name: 'Card', component: Card, base: 'nych-card' }, { name: 'Dialog', component: Dialog, base: 'nych-dialog' }, diff --git a/packages/playground/stories/RadioButton.stories.ts b/packages/playground/stories/RadioButton.stories.ts deleted file mode 100644 index 3e73ad4..0000000 --- a/packages/playground/stories/RadioButton.stories.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { ref } from 'vue' - -import { - RadioButton as NychRadioButton, - RadioButtonGroup as NychRadioButtonGroup, -} from '@nychthemeron/library' - -const meta = { - title: 'Components/RadioButton', - component: NychRadioButton, - parameters: { - layout: 'centered', - }, - tags: ['autodocs'], - argTypes: { - disabled: { - control: { type: 'boolean' }, - description: 'Disable the radio button', - }, - }, -} satisfies Meta - -export default meta -type Story = StoryObj - -export const Default: Story = { - render: () => ({ - components: { NychRadioButton }, - setup() { - const realm = ref('underworld') - return { realm } - }, - template: ` -
- - -
- `, - }), -} - -export const Disabled: Story = { - render: () => ({ - components: { NychRadioButton }, - setup() { - const realm = ref('sky') - return { realm } - }, - template: ` -
- - -
- `, - }), -} - -export const WithLabels: Story = { - render: () => ({ - components: { NychRadioButton }, - setup() { - const god = ref('apollo') - return { god } - }, - template: ` -
- - - -

Chosen: {{ god }}

-
- `, - }), -} - -// RadioButtonGroup binds the v-model once on the group and shares a name across -// children — the children only carry a `value`. -export const Group: Story = { - render: () => ({ - components: { NychRadioButton, NychRadioButtonGroup }, - setup() { - const god = ref('apollo') - return { god } - }, - template: ` -
- - - - - -

Chosen: {{ god }}

-
- `, - }), -} diff --git a/packages/playground/stories/RadioGroup.stories.ts b/packages/playground/stories/RadioGroup.stories.ts new file mode 100644 index 0000000..c425d82 --- /dev/null +++ b/packages/playground/stories/RadioGroup.stories.ts @@ -0,0 +1,70 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' +import { ref } from 'vue' + +import { RadioGroup as NychRadioGroup, RadioGroupItem as NychRadioGroupItem } from '@nychthemeron/library' + +const meta = { + title: 'Components/RadioGroup', + component: NychRadioGroup, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + render: () => ({ + components: { NychRadioGroup, NychRadioGroupItem }, + setup() { + const realm = ref('underworld') + return { realm } + }, + template: ` + + + + + `, + }), +} + +export const Disabled: Story = { + render: () => ({ + components: { NychRadioGroup, NychRadioGroupItem }, + template: ` + + + + + `, + }), +} + +export const WithLabels: Story = { + render: () => ({ + components: { NychRadioGroup, NychRadioGroupItem }, + setup() { + const god = ref('apollo') + return { god } + }, + template: ` +
+ + + + + +

Chosen: {{ god }}

+
+ `, + }), +}