diff --git a/packages/ui/src/components/va-counter/VaCounter.demo.vue b/packages/ui/src/components/va-counter/VaCounter.demo.vue deleted file mode 100644 index c86e65a2ce..0000000000 --- a/packages/ui/src/components/va-counter/VaCounter.demo.vue +++ /dev/null @@ -1,393 +0,0 @@ - - - - - diff --git a/packages/ui/src/components/va-counter/VaCounter.stories.ts b/packages/ui/src/components/va-counter/VaCounter.stories.ts index 8e8d26049b..ea27f5fd48 100644 --- a/packages/ui/src/components/va-counter/VaCounter.stories.ts +++ b/packages/ui/src/components/va-counter/VaCounter.stories.ts @@ -1,23 +1,375 @@ -import { defineComponent } from 'vue' -import VaCounterDemo from './VaCounter.demo.vue' -import VaCounter from './VaCounter.vue' +import { addText } from '../../../.storybook/interaction-utils/addText' +import { userEvent } from '../../../.storybook/interaction-utils/userEvent' +import { expect } from '@storybook/jest' +import { VaCounter } from './' export default { title: 'VaCounter', component: VaCounter, + tags: ['autodocs'], } -export const OldDemos = () => ({ - components: { VaCounterDemo }, - template: '', +export const Default = () => ({ + components: { VaCounter }, + data: () => ({ value: 0 }), + template: ` + + `, }) -export const Default = () => ({ +Default.play = async ({ step }) => { + const increaseButton = document.querySelector('[aria-label="increase counter"]') as HTMLElement + const decreaseButton = document.querySelector('[aria-label="decrease counter"]') as HTMLElement + const input = document.querySelector('[aria-label="counter value"]') as HTMLInputElement + + await step('Increase on click', async () => { + await userEvent.click(increaseButton) + expect(input.value).toEqual('1') + }) + + await step('Decrease on click', async () => { + await userEvent.click(decreaseButton) + expect(input.value).toEqual('0') + }) +} + +export const LongPressDelay = () => ({ + components: { VaCounter }, + template: ` +

[500]

+ + +

[1500]

+ + `, +}) + +export const Stateful = () => ({ + components: { VaCounter }, + template: ` +

[true]

+ +

[false]

+ + `, +}) + +Stateful.play = async ({ step }) => { + const increaseButtons = document.querySelectorAll('[aria-label="increase counter"]') + const inputs = document.querySelectorAll('[aria-label="counter value"]') + + await step('Increase on click', async () => { + await userEvent.click(increaseButtons[0]) + expect((inputs[0] as HTMLInputElement).value).toEqual('1') + }) + + await step('No effect on click', async () => { + await userEvent.click(increaseButtons[1]) + expect((inputs[1] as HTMLInputElement).value).toEqual('0') + }) +} + +export const Min = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +Min.play = async ({ step }) => { + const decreaseButton = document.querySelector('[aria-label="decrease counter"]') as HTMLButtonElement + + await step('Decrease button must be disabled', async () => { + expect(decreaseButton.getAttribute('aria-disabled')).toEqual('true') + }) +} + +export const Max = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +Max.play = async ({ step }) => { + const increaseButton = document.querySelector('[aria-label="increase counter"]') as HTMLElement + + await step('Increase button must be disabled', async () => { + expect(increaseButton.getAttribute('aria-disabled')).toEqual('true') + }) +} + +export const Step = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +Step.play = async ({ step }) => { + const increaseButton = document.querySelector('[aria-label="increase counter"]') as HTMLElement + const decreaseButton = document.querySelector('[aria-label="decrease counter"]') as HTMLElement + const input = document.querySelector('[aria-label="counter value"]') as HTMLInputElement + + await step('Increase with step on click', async () => { + await userEvent.click(increaseButton) + expect(input.value).toEqual('2') + }) + + await step('Decrease with step on click', async () => { + await userEvent.click(decreaseButton) + expect(input.value).toEqual('0') + }) +} + +export const ManualInput = () => ({ + components: { VaCounter }, + template: ` +

[true]

+ +

[false]

+ + `, +}) + +export const Color = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +export const Background = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +export const IncreaseIcon = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +export const DecreaseIcon = () => ({ components: { VaCounter }, - template: '', + template: ` + + `, +}) + +export const Disabled = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +export const Readonly = () => ({ + components: { VaCounter }, + template: ` + + `, }) export const Loading = () => ({ components: { VaCounter }, - template: '', + template: ` + + `, +}) + +export const Preset = () => ({ + components: { VaCounter }, + template: ` +

['']

+ +

[solid]

+ +

[bordered]

+ + `, +}) + +export const Buttons = () => ({ + components: { VaCounter }, + template: ` +

[true]

+ +

[false]

+ + `, +}) + +export const Rounded = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +export const Margins = () => ({ + components: { VaCounter }, + template: ` +

[4px]

+ +

[8px]

+ +

[12x]

+ + `, +}) + +export const Flat = () => ({ + components: { VaCounter }, + template: ` +

[true]

+ +

[false]

+ + `, +}) + +export const Error = () => ({ + components: { VaCounter }, + data: () => ({ value: -1 }), + template: ` + + `, +}) + +addText(Error, "There's a problem with gap while an icon is rendered", 'stale') + +export const ErrorMessages = () => ({ + components: { VaCounter }, + template: ` + + `, +}) +addText( + ErrorMessages, + "Icon doesn't belong here", + 'broken', +) + +export const ErrorCount = () => ({ + components: { VaCounter }, + template: ` + + `, +}) + +export const Rules = () => ({ + components: { VaCounter }, + data: () => ({ value: 0 }), + template: ` + + `, +}) + +export const ImmediateValidation = () => ({ + components: { VaCounter }, + data: () => ({ value: 0 }), + template: ` + + `, +}) +addText( + ImmediateValidation, + "Icon doesn't belong here", + 'broken', +) + +export const Success = () => ({ + components: { VaCounter }, + template: ` + + `, +}) +addText( + Success, + "Icon doesn't belong here", + 'broken', +) + +export const ContentSlot = () => ({ + components: { VaCounter }, + template: ` + + + + `, +}) + +export const DecreaseActionSlot = () => ({ + components: { VaCounter }, + template: ` + + + + `, +}) +addText( + DecreaseActionSlot, + "Click doesn't affect value", + 'broken', +) + +export const IncreaseActionSlot = () => ({ + components: { VaCounter }, + template: ` + + + + `, }) +addText( + IncreaseActionSlot, + "Click doesn't affect value", + 'broken', +) diff --git a/packages/ui/src/components/va-counter/VaCounter.vue b/packages/ui/src/components/va-counter/VaCounter.vue index c70cc1303b..15c195fabd 100644 --- a/packages/ui/src/components/va-counter/VaCounter.vue +++ b/packages/ui/src/components/va-counter/VaCounter.vue @@ -5,6 +5,8 @@ :class="classComputed" :style="styleComputed" :focused="isFocused" + :error="computedError" + :error-messages="computedErrorMessages" @keydown.up.prevent="increaseCount" @keydown.right.prevent="increaseCount" @keydown.down.prevent="decreaseCount" @@ -101,7 +103,6 @@ import { shallowRef, defineComponent, InputHTMLAttributes, - PropType, ComputedRef, toRef, } from 'vue' @@ -119,6 +120,9 @@ import { useTranslation, useLongPress, useTemplateRef, + useValidation, + useClearableProps, + useValidationEmits, } from '../../composables' import useCounterPropsValidation from './hooks/useCounterPropsValidation' @@ -148,6 +152,7 @@ export default defineComponent({ ...useFormFieldProps, ...useStatefulProps, ...useComponentPresetProp, + ...useClearableProps, ...VaInputWrapperProps, // input modelValue: { type: [String, Number], default: 0 }, @@ -172,6 +177,7 @@ export default defineComponent({ emits: [ 'update:modelValue', + ...useValidationEmits, ...createInputEmits(), ...createFieldEmits(), ...useFocusEmits, @@ -191,6 +197,19 @@ export default defineComponent({ const { valueComputed } = useStateful(props, emit) + const reset = () => withoutValidation(() => { + emit('update:modelValue', props.clearValue) + emit('clear') + resetValidation() + }) + + const { + computedError, + computedErrorMessages, + withoutValidation, + resetValidation, + } = useValidation(props, emit, { reset, focus, value: valueComputed }) + const setCountInput = ({ target }: Event) => { valueComputed.value = Number((target as HTMLInputElement | null)?.value) } @@ -230,7 +249,7 @@ export default defineComponent({ }) const isMaxReached = computed(() => { - if (!max.value) { return false } + if (typeof max.value === 'undefined') { return false } return step.value ? Number(valueComputed.value) > (max.value - step.value) @@ -357,6 +376,9 @@ export default defineComponent({ valueComputed, isFocused, + computedError, + computedErrorMessages, + fieldListeners: createFieldListeners(emit), inputListeners: createInputListeners(emit), inputWrapperPropsComputed: filterComponentProps(VaInputWrapperProps), @@ -486,4 +508,4 @@ export default defineComponent({ margin: 0 0.5rem; } } - + \ No newline at end of file diff --git a/packages/ui/src/components/va-input-wrapper/VaInputWrapper.vue b/packages/ui/src/components/va-input-wrapper/VaInputWrapper.vue index 6ddf0239fc..eccec7b36d 100644 --- a/packages/ui/src/components/va-input-wrapper/VaInputWrapper.vue +++ b/packages/ui/src/components/va-input-wrapper/VaInputWrapper.vue @@ -146,7 +146,6 @@ export default defineComponent({ placeholder: { type: String, default: '' }, color: { type: String, default: 'primary' }, background: { type: String }, - error: { type: Boolean, default: false }, success: { type: Boolean, default: false }, loading: { type: Boolean, default: false }, requiredMark: { type: Boolean, default: false }, @@ -173,6 +172,7 @@ export default defineComponent({ props.counter && typeof vModel.value === 'string' ? vModel.value.length : undefined, ) + //@ts-ignore const wrapperClass = useBem('va-input-wrapper', () => ({ ...pick(props, ['success', 'error', 'disabled', 'readonly']), focused: Boolean(isFocused.value),