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 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Focus (blur after 2 sec)
-
-
-
- {{ value }} times
-
-
- Focus (blur after 2 sec)
-
-
-
-
-
-
-
-
-
-
-
- {{ value }} times
-
-
-
-
- {{ value }} times
-
-
-
-
-
-
-
- decrease
-
-
- increase
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -{{ step }}
-
-
-
-
-
-
- +{{ step }}
-
-
-
-
-
-
-
-
-
-
-
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: `
+
+
+ [{{v.value}}]
+
+
+ `,
+})
+
+export const DecreaseActionSlot = () => ({
+ components: { VaCounter },
+ template: `
+
+
+ [dA]
+
+
+ `,
+})
+addText(
+ DecreaseActionSlot,
+ "Click doesn't affect value",
+ 'broken',
+)
+
+export const IncreaseActionSlot = () => ({
+ components: { VaCounter },
+ template: `
+
+
+ [iA]
+
+
+ `,
})
+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),