feat: migrate Textarea to shadcn-vue
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
f3002c7eb1
commit
382d76c2d7
7 changed files with 51 additions and 37 deletions
|
|
@ -3,3 +3,4 @@ export { default as NychLoadingIcon } from './NychLoadingIcon.vue'
|
|||
export { Button, buttonVariants } from './ui/button'
|
||||
export type { ButtonVariants } from './ui/button'
|
||||
export { Input } from './ui/input'
|
||||
export { Textarea } from './ui/textarea'
|
||||
|
|
|
|||
28
packages/library/src/components/ui/textarea/Textarea.vue
Normal file
28
packages/library/src/components/ui/textarea/Textarea.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
defaultValue?: string | number
|
||||
modelValue?: string | number
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: "update:modelValue", payload: string | number): void
|
||||
}>()
|
||||
|
||||
const modelValue = useVModel(props, "modelValue", emits, {
|
||||
passive: true,
|
||||
defaultValue: props.defaultValue,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<textarea
|
||||
v-model="modelValue"
|
||||
data-slot="textarea"
|
||||
:class="cn('border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors focus-visible:ring-3 aria-invalid:ring-3 md:text-sm flex field-sizing-content min-h-16 w-full outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)"
|
||||
/>
|
||||
</template>
|
||||
1
packages/library/src/components/ui/textarea/index.ts
Normal file
1
packages/library/src/components/ui/textarea/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as Textarea } from "./Textarea.vue"
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import type { App } from 'vue'
|
||||
import {
|
||||
Textarea as PrimeTextarea,
|
||||
Checkbox as PrimeCheckbox,
|
||||
CheckboxGroup as PrimeCheckboxGroup,
|
||||
RadioButton as PrimeRadioButton,
|
||||
|
|
@ -13,9 +12,9 @@ import {
|
|||
Tag as PrimeTag,
|
||||
} from 'primevue'
|
||||
import { ThemeLibrary, type Themeable, ThemeEngine, type LibComponent, type Key } from '../engine'
|
||||
import { NychLoadingIcon, Button, buttonVariants, Input } from '../components'
|
||||
import { NychLoadingIcon, Button, buttonVariants, Input, Textarea } from '../components'
|
||||
|
||||
export { Button, buttonVariants, Input }
|
||||
export { Button, buttonVariants, Input, Textarea }
|
||||
|
||||
/**
|
||||
* Build a Themeable that themes a PrimeVue component through its passthrough (`pt`) API.
|
||||
|
|
@ -59,7 +58,6 @@ const loadingIconThemeable: Themeable<typeof NychLoadingIcon> = {
|
|||
const nychthemeron: ThemeLibrary = {
|
||||
components: {
|
||||
LoadingIcon: loadingIconThemeable,
|
||||
Textarea: ptThemeable(PrimeTextarea, 'textarea'),
|
||||
Checkbox: ptThemeable(PrimeCheckbox, 'checkbox', ['box', 'input', 'icon']),
|
||||
CheckboxGroup: ptThemeable(PrimeCheckboxGroup, 'checkbox-group'),
|
||||
RadioButton: ptThemeable(PrimeRadioButton, 'radio', ['box', 'input', 'icon']),
|
||||
|
|
@ -109,7 +107,6 @@ const nychthemeron: ThemeLibrary = {
|
|||
const _engine = new ThemeEngine(nychthemeron)
|
||||
|
||||
export const LoadingIcon = _engine.getComponent<typeof NychLoadingIcon>('LoadingIcon')
|
||||
export const Textarea: typeof PrimeTextarea = _engine.getComponent<typeof PrimeTextarea>('Textarea')
|
||||
export const Checkbox: typeof PrimeCheckbox = _engine.getComponent<typeof PrimeCheckbox>('Checkbox')
|
||||
export const CheckboxGroup: typeof PrimeCheckboxGroup =
|
||||
_engine.getComponent<typeof PrimeCheckboxGroup>('CheckboxGroup')
|
||||
|
|
@ -129,7 +126,7 @@ export const createNychthemeron = (): {
|
|||
Button: typeof Button
|
||||
LoadingIcon: typeof NychLoadingIcon
|
||||
Input: typeof Input
|
||||
Textarea: typeof PrimeTextarea
|
||||
Textarea: typeof Textarea
|
||||
Checkbox: typeof PrimeCheckbox
|
||||
CheckboxGroup: typeof PrimeCheckboxGroup
|
||||
RadioButton: typeof PrimeRadioButton
|
||||
|
|
|
|||
18
packages/library/tests/Textarea.spec.ts
Normal file
18
packages/library/tests/Textarea.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Textarea } from '../src/lib'
|
||||
|
||||
describe('Textarea', () => {
|
||||
it('renders a native textarea element', () => {
|
||||
const wrapper = mount(Textarea)
|
||||
expect(wrapper.element.tagName).toBe('TEXTAREA')
|
||||
})
|
||||
|
||||
it('supports v-model', async () => {
|
||||
const wrapper = mount(Textarea, { props: { modelValue: 'An omen' } })
|
||||
expect((wrapper.element as HTMLTextAreaElement).value).toBe('An omen')
|
||||
|
||||
await wrapper.setValue('A crow')
|
||||
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['A crow'])
|
||||
})
|
||||
})
|
||||
|
|
@ -3,7 +3,6 @@ import type { App } from 'vue'
|
|||
import {
|
||||
createNychthemeron,
|
||||
LoadingIcon,
|
||||
Textarea,
|
||||
Checkbox,
|
||||
RadioButton,
|
||||
CheckboxGroup,
|
||||
|
|
@ -70,7 +69,6 @@ describe('LoadingIcon', () => {
|
|||
|
||||
describe('themed pt components', () => {
|
||||
const cases = [
|
||||
{ name: 'Textarea', component: Textarea, base: 'nych-textarea' },
|
||||
{ name: 'Checkbox', component: Checkbox, base: 'nych-checkbox' },
|
||||
{ name: 'CheckboxGroup', component: CheckboxGroup, base: 'nych-checkbox-group' },
|
||||
{ name: 'RadioButton', component: RadioButton, base: 'nych-radio' },
|
||||
|
|
|
|||
|
|
@ -10,24 +10,6 @@ const meta = {
|
|||
layout: 'centered',
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
placeholder: {
|
||||
control: { type: 'text' },
|
||||
description: 'Placeholder text',
|
||||
},
|
||||
rows: {
|
||||
control: { type: 'number' },
|
||||
description: 'Visible rows',
|
||||
},
|
||||
disabled: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Disable the textarea',
|
||||
},
|
||||
invalid: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Mark the textarea as invalid',
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof NychTextarea>
|
||||
|
||||
export default meta
|
||||
|
|
@ -57,17 +39,6 @@ export const Filled: Story = {
|
|||
}),
|
||||
}
|
||||
|
||||
export const Invalid: Story = {
|
||||
render: () => ({
|
||||
components: { NychTextarea },
|
||||
setup() {
|
||||
const value = ref('')
|
||||
return { value }
|
||||
},
|
||||
template: `<NychTextarea v-model="value" rows="4" invalid placeholder="Required" style="width: 22rem;" />`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Disabled: Story = {
|
||||
render: () => ({
|
||||
components: { NychTextarea },
|
||||
|
|
|
|||
Loading…
Reference in a new issue