vueless 1.2.15-beta.2 → 1.2.15-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/bin/commands/copy.js +1 -1
  2. package/composables/tests/useUI.test.ts +1 -1
  3. package/composables/useBreakpoint.ts +1 -1
  4. package/composables/useUI.ts +1 -5
  5. package/constants.d.ts +1 -1
  6. package/constants.js +1 -1
  7. package/index.d.ts +3 -3
  8. package/index.ts +3 -3
  9. package/locales/en.json +1 -1
  10. package/package.json +1 -1
  11. package/types.ts +2 -2
  12. package/ui.boilerplate/UBoilerplate.vue +1 -1
  13. package/ui.button/UButton.vue +1 -1
  14. package/ui.button-link/ULink.vue +1 -1
  15. package/ui.button-toggle/UToggle.vue +1 -1
  16. package/ui.container-accordion/UAccordion.vue +1 -1
  17. package/ui.container-accordion-item/UAccordionItem.vue +2 -6
  18. package/ui.container-accordion-item/tests/UAccordionItem.test.ts +5 -5
  19. package/ui.container-card/UCard.vue +1 -1
  20. package/ui.container-col/UCol.vue +1 -1
  21. package/ui.container-divider/UDivider.vue +1 -1
  22. package/ui.container-drawer/UDrawer.vue +1 -1
  23. package/ui.container-group/UGroup.vue +1 -1
  24. package/ui.container-groups/UGroups.vue +1 -1
  25. package/ui.container-modal/UModal.vue +1 -1
  26. package/ui.container-modal-confirm/UModalConfirm.vue +1 -1
  27. package/ui.container-page/UPage.vue +1 -1
  28. package/ui.container-page/config.ts +3 -3
  29. package/ui.container-row/URow.vue +1 -1
  30. package/ui.data-list/UDataList.vue +1 -1
  31. package/ui.data-list/config.ts +1 -1
  32. package/ui.data-table/UTable.vue +1 -1
  33. package/ui.data-table/UTableRow.vue +1 -1
  34. package/ui.data-table/config.ts +2 -0
  35. package/ui.data-table/storybook/stories.ts +3 -1
  36. package/ui.dropdown-badge/UDropdownBadge.vue +1 -1
  37. package/ui.dropdown-button/UDropdownButton.vue +1 -1
  38. package/ui.dropdown-link/UDropdownLink.vue +1 -1
  39. package/ui.form-calendar/UCalendar.vue +5 -1
  40. package/ui.form-calendar/UCalendarDayView.vue +1 -1
  41. package/ui.form-calendar/UCalendarMonthView.vue +1 -1
  42. package/ui.form-calendar/UCalendarYearView.vue +1 -1
  43. package/ui.form-calendar/config.ts +5 -0
  44. package/ui.form-checkbox/UCheckbox.vue +24 -5
  45. package/ui.form-checkbox/config.ts +4 -0
  46. package/ui.form-checkbox/tests/UCheckbox.test.ts +1 -1
  47. package/ui.form-checkbox-group/UCheckboxGroup.vue +1 -1
  48. package/ui.form-checkbox-multi-state/UCheckboxMultiState.vue +1 -1
  49. package/{ui.form-color-picker/UColorPicker.vue → ui.form-color-toggle/UColorToggle.vue} +1 -1
  50. package/{ui.form-color-picker → ui.form-color-toggle}/constants.ts +1 -1
  51. package/{ui.form-color-picker → ui.form-color-toggle}/storybook/stories.ts +14 -14
  52. package/{ui.form-color-picker/tests/UColorPicker.test.ts → ui.form-color-toggle/tests/UColorToggle.test.ts} +8 -8
  53. package/ui.form-date-picker/UDatePicker.vue +1 -1
  54. package/ui.form-date-picker/config.ts +5 -0
  55. package/ui.form-date-picker-range/UDatePickerRange.vue +1 -1
  56. package/ui.form-input/UInput.vue +10 -4
  57. package/ui.form-input-counter/UInputCounter.vue +10 -1
  58. package/ui.form-input-counter/config.ts +5 -0
  59. package/ui.form-input-file/UInputFile.vue +1 -1
  60. package/ui.form-input-number/UInputNumber.vue +1 -1
  61. package/ui.form-input-password/UInputPassword.vue +12 -13
  62. package/ui.form-input-password/tests/UInputPassword.test.ts +2 -2
  63. package/ui.form-input-rating/UInputRating.vue +1 -1
  64. package/ui.form-input-search/UInputSearch.vue +1 -1
  65. package/ui.form-label/ULabel.vue +6 -2
  66. package/ui.form-label/types.ts +5 -0
  67. package/ui.form-listbox/UListbox.vue +38 -12
  68. package/ui.form-radio/URadio.vue +1 -1
  69. package/ui.form-radio-group/URadioGroup.vue +1 -1
  70. package/ui.form-select/USelect.vue +38 -3
  71. package/ui.form-select/tests/USelect.test.ts +3 -1
  72. package/ui.form-switch/USwitch.vue +21 -8
  73. package/ui.form-switch/config.ts +2 -0
  74. package/ui.form-switch/tests/USwitch.test.ts +6 -4
  75. package/ui.form-textarea/UTextarea.vue +16 -6
  76. package/ui.form-textarea/tests/UTextarea.test.ts +1 -1
  77. package/ui.image-avatar/UAvatar.vue +1 -1
  78. package/ui.image-icon/UIcon.vue +1 -1
  79. package/ui.loader/ULoader.vue +1 -1
  80. package/ui.loader-overlay/ULoaderOverlay.vue +1 -1
  81. package/ui.loader-progress/ULoaderProgress.vue +1 -1
  82. package/ui.navigation-breadcrumbs/UBreadcrumbs.vue +1 -1
  83. package/ui.navigation-pagination/UPagination.vue +15 -1
  84. package/ui.navigation-pagination/config.ts +9 -0
  85. package/ui.navigation-progress/UProgress.vue +1 -1
  86. package/ui.navigation-progress/UStepperProgress.vue +1 -1
  87. package/ui.navigation-tab/UTab.vue +1 -1
  88. package/ui.navigation-tabs/UTabs.vue +1 -1
  89. package/ui.other-chip/UChip.vue +1 -1
  90. package/ui.other-dot/UDot.vue +1 -1
  91. package/ui.other-theme-color-toggle/UThemeColorToggle.vue +4 -4
  92. package/ui.other-theme-color-toggle/config.ts +2 -2
  93. package/ui.other-theme-color-toggle/tests/UThemeColorToggle.test.ts +10 -10
  94. package/ui.skeleton/USkeleton.vue +1 -1
  95. package/ui.skeleton-choice/USkeletonChoice.vue +1 -1
  96. package/ui.skeleton-input/USkeletonInput.vue +1 -1
  97. package/ui.skeleton-text/USkeletonText.vue +1 -1
  98. package/ui.text-alert/UAlert.vue +9 -1
  99. package/ui.text-alert/config.ts +4 -0
  100. package/ui.text-badge/UBadge.vue +1 -1
  101. package/ui.text-block/UText.vue +1 -1
  102. package/ui.text-block/config.ts +5 -4
  103. package/ui.text-empty/UEmpty.vue +1 -1
  104. package/ui.text-file/UFile.vue +1 -1
  105. package/ui.text-files/UFiles.vue +1 -1
  106. package/ui.text-header/UHeader.vue +1 -1
  107. package/ui.text-notify/UNotify.vue +1 -1
  108. package/ui.text-number/UNumber.vue +1 -1
  109. /package/{ui.form-color-picker → ui.form-color-toggle}/config.ts +0 -0
  110. /package/{ui.form-color-picker → ui.form-color-toggle}/storybook/docs.mdx +0 -0
  111. /package/{ui.form-color-picker → ui.form-color-toggle}/types.ts +0 -0
@@ -1,9 +1,10 @@
1
1
  <script setup lang="ts">
2
- import { inject, ref, onMounted, computed, watchEffect, toValue, useId } from "vue";
2
+ import { inject, ref, onMounted, computed, watchEffect, toValue, useId, useSlots } from "vue";
3
3
  import { isEqual } from "lodash-es";
4
4
 
5
- import useUI from "../composables/useUI";
5
+ import { useUI } from "../composables/useUI";
6
6
  import { getDefaults } from "../utils/ui";
7
+ import { useComponentLocaleMessages } from "../composables/useComponentLocaleMassages";
7
8
 
8
9
  import UIcon from "../ui.image-icon/UIcon.vue";
9
10
  import ULabel from "../ui.form-label/ULabel.vue";
@@ -48,12 +49,24 @@ const emit = defineEmits([
48
49
  "input",
49
50
  ]);
50
51
 
52
+ const { localeMessages } = useComponentLocaleMessages<typeof defaultConfig.i18n>(
53
+ COMPONENT_NAME,
54
+ defaultConfig.i18n,
55
+ props?.config?.i18n,
56
+ );
57
+
58
+ const slots = useSlots();
59
+
51
60
  const checkboxName = ref("");
52
61
  const checkboxSize = ref(props.size);
53
62
  const checkboxColor = ref(props.color);
54
63
 
55
64
  const elementId = props.id || useId();
56
65
 
66
+ const hasLabel = computed(() => Boolean(props.label || slots.label));
67
+
68
+ const inputAriaLabelledBy = computed(() => (hasLabel.value ? elementId : undefined));
69
+
57
70
  const isBinary = computed(() => !Array.isArray(props.modelValue));
58
71
  const isCheckboxInGroup = computed(() => Boolean(toValue(getCheckboxGroupName)));
59
72
 
@@ -108,6 +121,10 @@ function onChange() {
108
121
  emit("input", newModelValue);
109
122
  }
110
123
 
124
+ function onIconClick() {
125
+ document.getElementById(elementId)?.click();
126
+ }
127
+
111
128
  /**
112
129
  * Get element / nested component attributes for each config token ✨
113
130
  * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
@@ -161,15 +178,17 @@ const {
161
178
  :name="checkboxName"
162
179
  :checked="isChecked"
163
180
  :disabled="disabled"
181
+ :aria-labelledby="inputAriaLabelledBy"
182
+ :aria-label="!hasLabel ? localeMessages.checkbox : undefined"
164
183
  v-bind="checkboxAttrs"
165
184
  :data-test="getDataTest()"
166
185
  @change="onChange"
167
186
  />
168
187
 
169
- <label
188
+ <div
170
189
  v-if="isChecked"
171
190
  v-bind="partial ? partiallyCheckedAttrs : checkedAttrs"
172
- :for="elementId"
191
+ @click="onIconClick"
173
192
  >
174
193
  <UIcon
175
194
  v-if="partial"
@@ -179,7 +198,7 @@ const {
179
198
  />
180
199
 
181
200
  <UIcon v-else :name="config.defaults.checkedIcon" color="inherit" v-bind="checkedIconAttrs" />
182
- </label>
201
+ </div>
183
202
 
184
203
  <template #bottom>
185
204
  <!-- @slot Use it to add something below the checkbox. -->
@@ -51,6 +51,10 @@ export default /*tw*/ {
51
51
  },
52
52
  },
53
53
  partiallyCheckedIcon: "{>checkedIcon}",
54
+ /* These are used for a11y. */
55
+ i18n: {
56
+ checkbox: "Checkbox",
57
+ },
54
58
  defaults: {
55
59
  color: "primary",
56
60
  size: "md",
@@ -198,7 +198,7 @@ describe("UCheckbox.vue", () => {
198
198
  },
199
199
  });
200
200
 
201
- expect(component.find("label").attributes("class")).toContain(color);
201
+ expect(component.get("[vl-key='checked']").attributes("class")).toContain(`bg-${color}`);
202
202
  });
203
203
  });
204
204
 
@@ -2,7 +2,7 @@
2
2
  import { provide, ref, watch, useTemplateRef } from "vue";
3
3
  import { isEqual } from "lodash-es";
4
4
 
5
- import useUI from "../composables/useUI";
5
+ import { useUI } from "../composables/useUI";
6
6
  import { getDefaults } from "../utils/ui";
7
7
 
8
8
  import ULabel from "../ui.form-label/ULabel.vue";
@@ -2,7 +2,7 @@
2
2
  import { computed, ref, watchEffect } from "vue";
3
3
  import { cloneDeep } from "../utils/helper";
4
4
 
5
- import useUI from "../composables/useUI";
5
+ import { useUI } from "../composables/useUI";
6
6
  import { getDefaults } from "../utils/ui";
7
7
 
8
8
  import UCheckbox from "../ui.form-checkbox/UCheckbox.vue";
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed, useId, useTemplateRef } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
6
 
7
7
  import vTooltip from "../v.tooltip/vTooltip";
@@ -2,4 +2,4 @@
2
2
  This const is needed to prevent the issue in script setup:
3
3
  `defineProps` is referencing locally declared variables. (vue/valid-define-props)
4
4
  */
5
- export const COMPONENT_NAME = "UColorPicker";
5
+ export const COMPONENT_NAME = "UColorToggle";
@@ -6,22 +6,22 @@ import {
6
6
  getDocsDescription,
7
7
  } from "../../utils/storybook";
8
8
 
9
- import UColorPicker from "../UColorPicker.vue";
9
+ import UColorToggle from "../UColorToggle.vue";
10
10
  import UCol from "../../ui.container-col/UCol.vue";
11
11
  import UButton from "../../ui.button/UButton.vue";
12
12
 
13
13
  import type { Meta, StoryFn } from "@storybook/vue3-vite";
14
14
  import type { Props } from "../types";
15
15
 
16
- interface UColorPickerArgs extends Props {
16
+ interface UColorToggleArgs extends Props {
17
17
  slotTemplate?: string;
18
18
  enum: "size";
19
19
  }
20
20
 
21
21
  export default {
22
22
  id: "3190",
23
- title: "Form Inputs & Controls / Color Picker",
24
- component: UColorPicker,
23
+ title: "Form Inputs & Controls / Color Toggle",
24
+ component: UColorToggle,
25
25
  args: {
26
26
  modelValue: "",
27
27
  colors: /*tw*/ {
@@ -47,31 +47,31 @@ export default {
47
47
  },
48
48
  },
49
49
  argTypes: {
50
- ...getArgTypes(UColorPicker.__name),
50
+ ...getArgTypes(UColorToggle.__name),
51
51
  },
52
52
  parameters: {
53
53
  docs: {
54
- ...getDocsDescription(UColorPicker.__name),
54
+ ...getDocsDescription(UColorToggle.__name),
55
55
  },
56
56
  },
57
57
  } as Meta;
58
58
 
59
- const DefaultTemplate: StoryFn<UColorPickerArgs> = (args: UColorPickerArgs) => ({
60
- components: { UColorPicker, UButton, UCol },
61
- setup: () => ({ args, slots: getSlotNames(UColorPicker.__name) }),
59
+ const DefaultTemplate: StoryFn<UColorToggleArgs> = (args: UColorToggleArgs) => ({
60
+ components: { UColorToggle, UButton, UCol },
61
+ setup: () => ({ args, slots: getSlotNames(UColorToggle.__name) }),
62
62
  template: `
63
- <UColorPicker v-bind="args" v-model="args.modelValue">
63
+ <UColorToggle v-bind="args" v-model="args.modelValue">
64
64
  ${args.slotTemplate || getSlotsFragment("")}
65
- </UColorPicker>
65
+ </UColorToggle>
66
66
  `,
67
67
  });
68
68
 
69
- const EnumTemplate: StoryFn<UColorPickerArgs> = (args: UColorPickerArgs, { argTypes }) => ({
70
- components: { UCol, UColorPicker },
69
+ const EnumTemplate: StoryFn<UColorToggleArgs> = (args: UColorToggleArgs, { argTypes }) => ({
70
+ components: { UCol, UColorToggle },
71
71
  setup: () => ({ args, argTypes, getArgs }),
72
72
  template: `
73
73
  <UCol>
74
- <UColorPicker
74
+ <UColorToggle
75
75
  v-for="option in argTypes?.[args.enum]?.options"
76
76
  v-bind="getArgs(args, option)"
77
77
  :key="option"
@@ -1,12 +1,12 @@
1
1
  import { mount } from "@vue/test-utils";
2
2
  import { describe, it, expect } from "vitest";
3
3
 
4
- import UColorPicker from "../UColorPicker.vue";
4
+ import UColorToggle from "../UColorToggle.vue";
5
5
  import UButton from "../../ui.button/UButton.vue";
6
6
 
7
7
  import type { Props } from "../types";
8
8
 
9
- describe("UColorPicker.vue", () => {
9
+ describe("UColorToggle.vue", () => {
10
10
  describe("Props", () => {
11
11
  it("Model Value – selects the correct color based on modelValue", async () => {
12
12
  const modelValue = "primary";
@@ -18,7 +18,7 @@ describe("UColorPicker.vue", () => {
18
18
  secondary: "bg-secondary-500",
19
19
  };
20
20
 
21
- const component = mount(UColorPicker, {
21
+ const component = mount(UColorToggle, {
22
22
  props: {
23
23
  modelValue,
24
24
  "onUpdate:modelValue": (value) => component.setProps({ modelValue: value }),
@@ -47,7 +47,7 @@ describe("UColorPicker.vue", () => {
47
47
  };
48
48
 
49
49
  Object.entries(sizes).forEach(([size, classes]) => {
50
- const component = mount(UColorPicker, {
50
+ const component = mount(UColorToggle, {
51
51
  props: {
52
52
  modelValue: "",
53
53
  size: size as Props["size"],
@@ -70,7 +70,7 @@ describe("UColorPicker.vue", () => {
70
70
  error: "bg-error-500",
71
71
  };
72
72
 
73
- const component = mount(UColorPicker, {
73
+ const component = mount(UColorToggle, {
74
74
  props: {
75
75
  modelValue: "",
76
76
  colors,
@@ -89,7 +89,7 @@ describe("UColorPicker.vue", () => {
89
89
  it("Id – applies the correct id attribute", () => {
90
90
  const id = "test-color-picker-id";
91
91
 
92
- const component = mount(UColorPicker, {
92
+ const component = mount(UColorToggle, {
93
93
  props: {
94
94
  modelValue: "",
95
95
  id,
@@ -107,7 +107,7 @@ describe("UColorPicker.vue", () => {
107
107
  secondary: "bg-secondary-500",
108
108
  };
109
109
 
110
- const component = mount(UColorPicker, {
110
+ const component = mount(UColorToggle, {
111
111
  props: {
112
112
  modelValue: "",
113
113
  dataTest,
@@ -127,7 +127,7 @@ describe("UColorPicker.vue", () => {
127
127
 
128
128
  describe("Exposed properties", () => {
129
129
  it("ListRef – exposes listRef", () => {
130
- const component = mount(UColorPicker, {
130
+ const component = mount(UColorToggle, {
131
131
  props: {
132
132
  modelValue: "",
133
133
  colors: {
@@ -2,7 +2,7 @@
2
2
  import { computed, nextTick, ref, useId, useTemplateRef, watchEffect } from "vue";
3
3
  import { merge } from "lodash-es";
4
4
 
5
- import useUI from "../composables/useUI";
5
+ import { useUI } from "../composables/useUI";
6
6
 
7
7
  import UIcon from "../ui.image-icon/UIcon.vue";
8
8
  import UInput from "../ui.form-input/UInput.vue";
@@ -125,6 +125,11 @@ export default /*tw*/ {
125
125
  },
126
126
  timeLabel: "Time",
127
127
  okLabel: "Ok",
128
+ /* These are used for a11y. */
129
+ previousYear: "Previous Year",
130
+ nextYear: "Next Year",
131
+ previousMonth: "Previous Month",
132
+ nextMonth: "Next Month",
128
133
  },
129
134
  defaults: {
130
135
  size: "md",
@@ -3,7 +3,7 @@ import { computed, watch, ref, nextTick, provide, useId, useTemplateRef, watchEf
3
3
 
4
4
  import { merge } from "lodash-es";
5
5
 
6
- import useUI from "../composables/useUI";
6
+ import { useUI } from "../composables/useUI";
7
7
  import { getDefaults } from "../utils/ui";
8
8
 
9
9
  import UIcon from "../ui.image-icon/UIcon.vue";
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, onMounted, useSlots, useId, useTemplateRef, watch } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
6
  import { hasSlotContent } from "../utils/helper";
7
7
  import { useMutationObserver } from "../composables/useMutationObserver";
@@ -91,7 +91,7 @@ const VALIDATION_RULES_REG_EX = {
91
91
 
92
92
  const slots = useSlots();
93
93
 
94
- const wrapperRef = useTemplateRef<HTMLLabelElement>("wrapper");
94
+ const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
95
95
  const inputRef = useTemplateRef<HTMLInputElement>("input");
96
96
  const leftSlotWrapperRef = useTemplateRef<HTMLSpanElement>("leftSlotWrapper");
97
97
  const labelComponentRef = useTemplateRef<InstanceType<typeof ULabel>>("labelComponent");
@@ -176,6 +176,10 @@ function onKeydown(event: KeyboardEvent) {
176
176
  emit("keydown", event);
177
177
  }
178
178
 
179
+ function onSlotClick() {
180
+ inputRef.value?.focus();
181
+ }
182
+
179
183
  /**
180
184
  * This trick prevents default browser autocomplete behavior.
181
185
  * @param toggleState { boolean }
@@ -286,11 +290,12 @@ const {
286
290
  <slot name="label" :label="label" />
287
291
  </template>
288
292
 
289
- <label ref="wrapper" :for="elementId" v-bind="wrapperAttrs">
293
+ <div ref="wrapper" v-bind="wrapperAttrs">
290
294
  <span
291
295
  v-if="hasSlotContent($slots['left'], { iconName: leftIcon }) || leftIcon"
292
296
  v-bind="leftSlotAttrs"
293
297
  ref="leftSlotWrapper"
298
+ @click="onSlotClick"
294
299
  >
295
300
  <!--
296
301
  @slot Use it to add something before the text.
@@ -328,6 +333,7 @@ const {
328
333
  <span
329
334
  v-if="hasSlotContent($slots['right'], { iconName: rightIcon }) || rightIcon"
330
335
  v-bind="rightSlotAttrs"
336
+ @click="onSlotClick"
331
337
  >
332
338
  <!--
333
339
  @slot Use it to add something after the text.
@@ -337,6 +343,6 @@ const {
337
343
  <UIcon v-if="rightIcon" color="neutral" :name="rightIcon" v-bind="rightIconAttrs" />
338
344
  </slot>
339
345
  </span>
340
- </label>
346
+ </div>
341
347
  </ULabel>
342
348
  </template>
@@ -1,8 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed, useTemplateRef, watch } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
+ import { useComponentLocaleMessages } from "../composables/useComponentLocaleMassages";
6
7
 
7
8
  import UIcon from "../ui.image-icon/UIcon.vue";
8
9
  import UButton from "../ui.button/UButton.vue";
@@ -41,6 +42,12 @@ const emit = defineEmits([
41
42
  "blur",
42
43
  ]);
43
44
 
45
+ const { localeMessages } = useComponentLocaleMessages<typeof defaultConfig.i18n>(
46
+ COMPONENT_NAME,
47
+ defaultConfig.i18n,
48
+ props?.config?.i18n,
49
+ );
50
+
44
51
  const inputComponentRef = useTemplateRef<InstanceType<typeof UInput>>("inputComponent");
45
52
 
46
53
  const addIntervalId = ref<number | null>(null);
@@ -181,6 +188,7 @@ const {
181
188
  :size="size"
182
189
  variant="outlined"
183
190
  :disabled="isSubtractButtonDisabled || disabled"
191
+ :aria-label="localeMessages.subtract"
184
192
  v-bind="subtractButtonAttrs"
185
193
  :data-test="getDataTest('subtract')"
186
194
  @click.prevent
@@ -221,6 +229,7 @@ const {
221
229
  square
222
230
  variant="outlined"
223
231
  :disabled="isAddButtonDisabled || disabled"
232
+ :aria-label="localeMessages.add"
224
233
  v-bind="addButtonAttrs"
225
234
  :data-test="getDataTest('add')"
226
235
  @click.prevent
@@ -31,6 +31,11 @@ export default /*tw*/ {
31
31
  subtractButton: "{>actionButton}",
32
32
  addIcon: "{UIcon}",
33
33
  subtractIcon: "{UIcon}",
34
+ /* These are used for a11y. */
35
+ i18n: {
36
+ add: "Add",
37
+ subtract: "Subtract",
38
+ },
34
39
  defaults: {
35
40
  size: "md",
36
41
  decimalSeparator: ",",
@@ -10,7 +10,7 @@ import {
10
10
  useTemplateRef,
11
11
  } from "vue";
12
12
 
13
- import useUI from "../composables/useUI";
13
+ import { useUI } from "../composables/useUI";
14
14
  import { getDefaults } from "../utils/ui";
15
15
  import { hasSlotContent } from "../utils/helper";
16
16
  import { getFileMbSize } from "./utilFileForm";
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, watch, onMounted, useId, useTemplateRef } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
6
 
7
7
  import UInput from "../ui.form-input/UInput.vue";
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { ref, computed, useId } from "vue";
2
+ import { ref, computed, useId, useTemplateRef, nextTick } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
6
 
7
7
  import defaultConfig from "./config";
@@ -42,6 +42,7 @@ const emit = defineEmits([
42
42
 
43
43
  const elementId = props.id || useId();
44
44
 
45
+ const inputRef = useTemplateRef<InstanceType<typeof UInput>>("input");
45
46
  const isShownPassword = ref(false);
46
47
 
47
48
  const localValue = computed({
@@ -59,8 +60,12 @@ const passwordIcon = computed(() => {
59
60
  : config.value.defaults.passwordHiddenIcon || "";
60
61
  });
61
62
 
62
- function onClickShowPassword() {
63
+ async function onClickShowPassword() {
63
64
  isShownPassword.value = !isShownPassword.value;
65
+
66
+ await nextTick();
67
+
68
+ inputRef.value?.inputRef?.focus();
64
69
  }
65
70
 
66
71
  function onFocus(event: FocusEvent) {
@@ -86,6 +91,7 @@ const { getDataTest, config, passwordInputAttrs, passwordIconAttrs, passwordIcon
86
91
  <template>
87
92
  <UInput
88
93
  :id="elementId"
94
+ ref="input"
89
95
  v-model="localValue"
90
96
  :type="inputType"
91
97
  :label="label"
@@ -120,29 +126,22 @@ const { getDataTest, config, passwordInputAttrs, passwordIconAttrs, passwordIcon
120
126
  </template>
121
127
 
122
128
  <template #right>
123
- <label v-bind="passwordIconWrapperAttrs" :for="elementId">
129
+ <div v-bind="passwordIconWrapperAttrs" @click="onClickShowPassword">
124
130
  <!--
125
131
  @slot Use it to add something instead of the password icon.
126
132
  @binding {string} icon-name
127
133
  @binding {boolean} visible
128
- @binding {function} toggle
129
134
  -->
130
- <slot
131
- name="right"
132
- :icon-name="passwordIcon"
133
- :visible="isShownPassword"
134
- :toggle="onClickShowPassword"
135
- >
135
+ <slot name="right" :icon-name="passwordIcon" :visible="isShownPassword">
136
136
  <UIcon
137
137
  :name="passwordIcon"
138
138
  color="neutral"
139
139
  interactive
140
140
  v-bind="passwordIconAttrs"
141
141
  :data-test="getDataTest('password-icon')"
142
- @click="onClickShowPassword"
143
142
  />
144
143
  </slot>
145
- </label>
144
+ </div>
146
145
  </template>
147
146
  </UInput>
148
147
  </template>
@@ -190,14 +190,14 @@ describe("UInputPassword.vue", () => {
190
190
  expect(input.attributes("type")).toBe("password");
191
191
  expect(passwordIcon.props("name")).toBe("visibility_off-fill");
192
192
 
193
- await flushPromises();
194
193
  await passwordIcon.trigger("click");
194
+ await flushPromises();
195
195
 
196
196
  expect(input.attributes("type")).toBe("text");
197
197
  expect(passwordIcon.props("name")).toBe("visibility-fill");
198
198
 
199
- await flushPromises();
200
199
  await passwordIcon.trigger("click");
200
+ await flushPromises();
201
201
 
202
202
  expect(input.attributes("type")).toBe("password");
203
203
  expect(passwordIcon.props("name")).toBe("visibility_off-fill");
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, useTemplateRef } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { hasSlotContent } from "../utils/helper";
6
6
  import { getDefaults } from "../utils/ui";
7
7
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { useId, ref, computed, watchEffect, useTemplateRef } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
6
  import { createDebounce } from "../utils/helper";
7
7
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { computed, useTemplateRef, useSlots } from "vue";
2
+ import { computed, useTemplateRef, useSlots, useId } from "vue";
3
3
 
4
- import useUI from "../composables/useUI";
4
+ import { useUI } from "../composables/useUI";
5
5
  import { getDefaults } from "../utils/ui";
6
6
  import { hasSlotContent } from "../utils/helper";
7
7
 
@@ -26,6 +26,8 @@ const emit = defineEmits([
26
26
 
27
27
  const slots = useSlots();
28
28
 
29
+ const elementId = props.id || useId();
30
+
29
31
  const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
30
32
  const labelRef = useTemplateRef<HTMLLabelElement>("label");
31
33
 
@@ -102,6 +104,7 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
102
104
  <component
103
105
  :is="tag"
104
106
  v-if="label || hasSlotContent(slots['label'], { label })"
107
+ :id="elementId"
105
108
  ref="label"
106
109
  :for="props.for"
107
110
  v-bind="labelAttrs"
@@ -140,6 +143,7 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
140
143
  <component
141
144
  :is="tag"
142
145
  v-if="label || hasSlotContent(slots['label'], { label })"
146
+ :id="elementId"
143
147
  v-bind="labelAttrs"
144
148
  ref="label"
145
149
  :for="props.for"
@@ -15,6 +15,11 @@ export interface Props {
15
15
  */
16
16
  for?: string;
17
17
 
18
+ /**
19
+ * Component id (used for ARIA attributes).
20
+ */
21
+ id?: string;
22
+
18
23
  /**
19
24
  * Label description.
20
25
  */