feat : ajout des tests et retour sur le composant
This commit is contained in:
@@ -32,6 +32,10 @@
|
|||||||
disabled
|
disabled
|
||||||
label="Champ désactivé"
|
label="Champ désactivé"
|
||||||
/>
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
disabled
|
||||||
|
label="Champ désactivé"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-lg border p-4">
|
<div class="rounded-lg border p-4">
|
||||||
|
|||||||
@@ -1,23 +1,319 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import {describe, expect, it} from 'vitest'
|
||||||
import { mount } from '@vue/test-utils'
|
import {config, mount} from '@vue/test-utils'
|
||||||
import Input from './Input.vue'
|
import type {DefineComponent} from 'vue'
|
||||||
|
import Input from './InputText.vue'
|
||||||
|
|
||||||
|
|
||||||
|
type InputProps = {
|
||||||
|
id?: string
|
||||||
|
label?: string
|
||||||
|
name?: string
|
||||||
|
autocomplete?: string
|
||||||
|
modelValue?: string | null
|
||||||
|
textSize?: string
|
||||||
|
labelClass?: string
|
||||||
|
required?: boolean
|
||||||
|
maxLength?: number | string
|
||||||
|
minLength?: number | string
|
||||||
|
disabled?: boolean
|
||||||
|
readonly?: boolean
|
||||||
|
hint?: string
|
||||||
|
error?: string
|
||||||
|
success?: string
|
||||||
|
iconName?: string
|
||||||
|
iconSize?: string | number
|
||||||
|
iconColor?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputForTest = Input as DefineComponent<InputProps>
|
||||||
|
const iconStub = {
|
||||||
|
template: '<span data-test="icon" v-bind="$attrs" />',
|
||||||
|
}
|
||||||
|
config.global.stubs = {
|
||||||
|
...(config.global.stubs ?? {}),
|
||||||
|
Icon: iconStub,
|
||||||
|
}
|
||||||
|
|
||||||
describe('MalioInput', () => {
|
describe('MalioInput', () => {
|
||||||
it('affiche la valeur initiale', () => {
|
// Props de base: valeur, label, name, id, autocomplete
|
||||||
const wrapper = mount(Input, {
|
it('renders the initial input value', () => {
|
||||||
props: { modelValue: 'hello' },
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {modelValue: 'initialValueTest'},
|
||||||
})
|
})
|
||||||
|
expect(wrapper.get('input').element.value).toBe('initialValueTest')
|
||||||
expect(wrapper.get('input').element.value).toBe('hello')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emet update:modelValue au changement', async () => {
|
it('renders the label text', () => {
|
||||||
const wrapper = mount(Input, {
|
const wrapper = mount(InputForTest, {
|
||||||
props: { modelValue: '' },
|
props: {label: 'labelTest'},
|
||||||
})
|
})
|
||||||
|
expect(wrapper.get('label').text()).toBe('labelTest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies the name attribute', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {name: 'nameTest'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('name')).toBe('nameTest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses provided id on input and label', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {id: 'custom-id', label: 'Label'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('id')).toBe('custom-id')
|
||||||
|
expect(wrapper.get('label').attributes('for')).toBe('custom-id')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies a different size of rounded', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {rounded: 'rounded-md'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').classes()).toContain('rounded-md')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('generates an id when missing and reuses it on label', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {label: 'Label'},
|
||||||
|
})
|
||||||
|
const inputId = wrapper.get('input').attributes('id')
|
||||||
|
expect(inputId).toBeDefined()
|
||||||
|
expect(inputId?.startsWith('malio-input-text-')).toBe(true)
|
||||||
|
expect(wrapper.get('label').attributes('for')).toBe(inputId)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies the autocomplete attribute', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {autocomplete: 'autocompleteTest'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('autocomplete')).toBe('autocompleteTest')
|
||||||
|
})
|
||||||
|
|
||||||
|
// États HTML: required, readonly, disabled
|
||||||
|
it('does not set required when false', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {required: false},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('required')).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets required when true', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {required: true},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('required')).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not set readonly when false', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {readonly: false},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('readonly')).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets readonly when true', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {readonly: true},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('readonly')).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not set disabled and keeps text cursor when false', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {disabled: false},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('disabled')).toBeUndefined()
|
||||||
|
expect(wrapper.get('input').classes()).toContain('cursor-text')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets disabled styles when true', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {disabled: true},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('disabled')).toBeDefined()
|
||||||
|
expect(wrapper.get('input').classes()).toContain('cursor-not-allowed')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('text-black/60')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Émission d'événements
|
||||||
|
it('emits update:modelValue on input change', async () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {modelValue: ''},
|
||||||
|
})
|
||||||
await wrapper.get('input').setValue('new value')
|
await wrapper.get('input').setValue('new value')
|
||||||
|
|
||||||
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['new value'])
|
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['new value'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Contraintes et classes de texte
|
||||||
|
it('applies maxLength to input', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {maxLength: 25},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('maxlength')).toBe('25')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies minLength to input', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {minLength: 25},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('input').attributes('minlength')).toBe('25')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies textSize class on label', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {label: 'Label', textLabel: 'text-sm'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-sm')
|
||||||
|
})
|
||||||
|
|
||||||
|
// États visuels: erreur et succès
|
||||||
|
it('shows error message without label and icon', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {error: 'Error message test'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-error').text()).toBe('Error message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-error')
|
||||||
|
expect(wrapper.get('p').classes()).toContain('text-m-error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows error message with label and without icon', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {error: 'Error message test', label: 'Error message'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-error').text()).toBe('Error message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-error')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('p').classes()).toContain('text-m-error')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows error message with label and icon', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {error: 'Error message test', label: 'Error message', iconName: 'mdi:key-outline'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-error').text()).toBe('Error message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-error')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-error')
|
||||||
|
expect(wrapper.get('p').classes()).toContain('text-m-error')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows error message with icon and without label', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {error: 'Error message test', iconName: 'mdi:key-outline'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-error').text()).toBe('Error message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-error')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows success message without label and icon', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {success: 'Success message test'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-success').text()).toBe('Success message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows success message with label and without icon', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {success: 'Success message test', label: 'Success message'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-success').text()).toBe('Success message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-success')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-success')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-success')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows success message with label and icon', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {success: 'Success message test', label: 'Success message', iconName: 'mdi:key-outline'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-success').text()).toBe('Success message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-success')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-success')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-success')
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-m-success')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows success message with icon and without label', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {success: 'Success message test', iconName: 'mdi:key-outline'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-success').text()).toBe('Success message test')
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-success')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('prioritizes error over success when both are provided', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {
|
||||||
|
error: 'Error message test',
|
||||||
|
success: 'Success message test',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(wrapper.find('p.text-m-error').exists()).toBe(true)
|
||||||
|
expect(wrapper.get('p.text-m-error').text()).toBe('Error message test')
|
||||||
|
expect(wrapper.find('p.text-m-success').exists()).toBe(false)
|
||||||
|
expect(wrapper.get('input').classes()).toContain('border-m-error')
|
||||||
|
expect(wrapper.get('input').classes()).not.toContain('border-m-success')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Aide et classes de label
|
||||||
|
it('shows hint message', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {hint: 'Hint message test'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('p.text-m-muted').text()).toBe('Hint message test')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies labelClass on label', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {label: 'Label', labelClass: 'text-red-500'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('label').classes()).toContain('text-red-500')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not render label when label prop is missing', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {labelClass: 'text-red-500'},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('label').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Icône : rendu et options
|
||||||
|
it('renders icon with default positioning and muted color', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {iconName: 'mdi:key-outline'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-muted')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('pointer-events-none')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('absolute')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('right-2')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('top-1/2')
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('-translate-y-1/2')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes icon size prop to icon component', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {iconName: 'mdi:key-outline', iconSize: 'text-2xl'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('[data-test="icon"]').attributes('size')).toBe('text-2xl')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('applies icon color class', () => {
|
||||||
|
const wrapper = mount(InputForTest, {
|
||||||
|
props: {iconName: 'mdi:key-outline', iconColor: 'text-m-primary'},
|
||||||
|
})
|
||||||
|
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-primary')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="relative mt-4 w-full"
|
class="relative mt-4 flex h-12 w-full items-center"
|
||||||
:class="[minWidth, maxWidth]"
|
:class="[minWidth, maxWidth]"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -8,19 +8,19 @@
|
|||||||
v-maska="mask"
|
v-maska="mask"
|
||||||
:name="name"
|
:name="name"
|
||||||
:autocomplete="autocomplete"
|
:autocomplete="autocomplete"
|
||||||
class="floating-input grow-height peer min-h-[40px] w-full border bg-white px-3 py-1 outline-none focus:border-2"
|
class="floating-input grow-height peer min-h-[40px] w-full border bg-white pl-3 pr-3 py-1 outline-none border-m-muted focus:border-2"
|
||||||
:class="[
|
:class="[
|
||||||
disabled ? 'cursor-not-allowed bg-m-primary ' : 'cursor-text',
|
disabled ? 'cursor-not-allowed text-black/60 [&:not(:placeholder-shown)]:border-m-muted border-m-muted' : 'cursor-text',
|
||||||
hasError
|
hasError
|
||||||
? 'border-m-error focus:border-m-error [&:not(:placeholder-shown)]:border-m-error'
|
? 'border-m-error focus:border-m-error focus:pl-[11px] [&:not(:placeholder-shown)]:border-m-error'
|
||||||
: hasSuccess
|
: hasSuccess
|
||||||
? 'border-m-success focus:border-m-success [&:not(:placeholder-shown)]:border-m-success'
|
? 'border-m-success focus:border-m-success focus:pl-[11px] [&:not(:placeholder-shown)]:border-m-success'
|
||||||
: 'border-m-border focus:border-m-primary [&:not(:placeholder-shown)]:border-m-primary',
|
: 'border-m-border focus:border-m-primary focus:pl-[11px]',
|
||||||
text,
|
textInput,
|
||||||
iconInputPaddingClass,
|
iconInputPaddingClass,
|
||||||
inputClass,
|
inputClass,
|
||||||
rounded,
|
rounded,
|
||||||
]"
|
]"
|
||||||
:required="required"
|
:required="required"
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
:minlength="minLength"
|
:minlength="minLength"
|
||||||
@@ -38,18 +38,19 @@
|
|||||||
<label
|
<label
|
||||||
v-if="label"
|
v-if="label"
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="floating-label absolute left-3 top-2 origin-left transition-transform duration-150 peer-focus:translate-y-[-1.15rem] peer-focus:scale-90"
|
class="floating-label absolute left-3 top-2 mt-1 origin-left transition-transform duration-150 font-medium"
|
||||||
:class="[
|
:class="[
|
||||||
hasError
|
disabled ? 'peer-[&:not(:placeholder-shown):not(:focus)]:text-black/60' : '',
|
||||||
? 'text-m-error peer-valid:text-m-error peer-focus:text-m-error'
|
hasError
|
||||||
: hasSuccess
|
? 'text-m-error'
|
||||||
? 'text-m-success peer-valid:text-m-success peer-focus:text-m-success'
|
: hasSuccess
|
||||||
: 'text-m-muted peer-valid:text-m-primary peer-focus:text-m-primary',
|
? 'text-m-success'
|
||||||
labelClass,
|
: 'peer-placeholder-shown:text-m-muted peer-[&:not(:placeholder-shown):not(:focus)]:text-black peer-focus:text-m-primary',
|
||||||
textSize,
|
labelClass,
|
||||||
|
textLabel,
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
@@ -69,27 +70,18 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
v-if="hint && !hasError && !hasSuccess"
|
v-if="hint || hasError || hasSuccess"
|
||||||
:id="`${inputId}-hint`"
|
:id="`${inputId}-describedby`"
|
||||||
class="mt-1 text-xs text-m-muted"
|
:class="[
|
||||||
|
hasError
|
||||||
|
? 'text-m-error'
|
||||||
|
: hasSuccess
|
||||||
|
? 'text-m-success'
|
||||||
|
: 'text-m-muted',
|
||||||
|
'mt-1 text-xs ml-[2px] ',
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
{{ hint }}
|
{{ hint || error || successMessage }}
|
||||||
</p>
|
|
||||||
|
|
||||||
<p
|
|
||||||
v-if="hasError"
|
|
||||||
:id="`${inputId}-error`"
|
|
||||||
class="mt-1 text-xs text-m-error"
|
|
||||||
>
|
|
||||||
{{ error }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p
|
|
||||||
v-if="hasSuccess && !hasError"
|
|
||||||
:id="`${inputId}-success`"
|
|
||||||
class="mt-1 text-xs text-m-success"
|
|
||||||
>
|
|
||||||
{{ successMessage }}
|
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -109,8 +101,8 @@ const props = withDefaults(
|
|||||||
modelValue?: string | null | undefined
|
modelValue?: string | null | undefined
|
||||||
minWidth?: string
|
minWidth?: string
|
||||||
maxWidth?: string
|
maxWidth?: string
|
||||||
text?: string
|
textInput?: string
|
||||||
textSize?: string
|
textLabel?: string
|
||||||
inputClass?: string
|
inputClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
@@ -139,12 +131,12 @@ const props = withDefaults(
|
|||||||
maxWidth: '',
|
maxWidth: '',
|
||||||
inputClass: '',
|
inputClass: '',
|
||||||
labelClass: '',
|
labelClass: '',
|
||||||
text: 'text-lg',
|
textInput: 'text-lg',
|
||||||
required: false,
|
required: false,
|
||||||
maxLength: undefined,
|
maxLength: undefined,
|
||||||
minLength: undefined,
|
minLength: undefined,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
textSize: 'text-sm',
|
textLabel: 'text-sml',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rounded: 'rounded-md',
|
rounded: 'rounded-md',
|
||||||
hint: '',
|
hint: '',
|
||||||
@@ -199,17 +191,14 @@ const iconInputPaddingClass = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grow-height {
|
.grow-height {
|
||||||
transition: transform 160ms ease, background 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
|
transition: border-color 160ms ease, box-shadow 160ms ease, padding-top 160ms ease, padding-bottom 160ms ease;
|
||||||
transform-origin: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grow-height:focus {
|
.grow-height:focus {
|
||||||
transform: scaleY(1.2);
|
padding-top: 0.625rem;
|
||||||
|
padding-bottom: 0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.grow-height {
|
.grow-height { transition: none; }
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
26
pre-commit
26
pre-commit
@@ -3,23 +3,14 @@ set -e
|
|||||||
|
|
||||||
echo "######### Pre-commit hook start #############"
|
echo "######### Pre-commit hook start #############"
|
||||||
|
|
||||||
if ! command -v npm >/dev/null 2>&1; then
|
# Prefer the exact Node version from .nvmrc for hooks (IDE + CLI consistency).
|
||||||
if [ -f ".nvmrc" ]; then
|
if [ -f ".nvmrc" ]; then
|
||||||
NVM_VERSION="$(tr -d '\r\n' < .nvmrc)"
|
NVM_VERSION="$(tr -d '\r\n' < .nvmrc)"
|
||||||
NVM_VERSION="${NVM_VERSION#v}"
|
NVM_VERSION="${NVM_VERSION#v}"
|
||||||
NPM_BIN="$HOME/.nvm/versions/node/v$NVM_VERSION/bin"
|
NVM_BIN="$HOME/.nvm/versions/node/v$NVM_VERSION/bin"
|
||||||
if [ -x "$NPM_BIN/npm" ]; then
|
if [ -x "$NVM_BIN/node" ] && [ -x "$NVM_BIN/npm" ]; then
|
||||||
PATH="$NPM_BIN:$PATH"
|
PATH="$NVM_BIN:$PATH"
|
||||||
export PATH
|
export PATH
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v npm >/dev/null 2>&1; then
|
|
||||||
if [ -s "$HOME/.nvm/nvm.sh" ]; then
|
|
||||||
# shellcheck disable=SC1090
|
|
||||||
. "$HOME/.nvm/nvm.sh"
|
|
||||||
nvm use >/dev/null 2>&1 || true
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -28,6 +19,7 @@ if ! command -v npm >/dev/null 2>&1; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Node $(node -v) / npm $(npm -v)"
|
||||||
echo "--- make pre-commit start ---"
|
echo "--- make pre-commit start ---"
|
||||||
make pre-commit
|
make pre-commit
|
||||||
echo "--- make pre-commit finished ---"
|
echo "--- make pre-commit finished ---"
|
||||||
|
|||||||
Reference in New Issue
Block a user