quasar 2.12.7 → 2.13.1

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 (210) hide show
  1. package/dist/api/BottomSheet.json +7 -2
  2. package/dist/api/Dialog.json +7 -2
  3. package/dist/api/QBanner.json +7 -2
  4. package/dist/api/QBar.json +7 -2
  5. package/dist/api/QBtn.json +7 -2
  6. package/dist/api/QBtnDropdown.json +7 -2
  7. package/dist/api/QCard.json +7 -2
  8. package/dist/api/QCarousel.json +7 -2
  9. package/dist/api/QCheckbox.json +8 -3
  10. package/dist/api/QChip.json +13 -3
  11. package/dist/api/QColor.json +7 -2
  12. package/dist/api/QDate.json +7 -2
  13. package/dist/api/QDrawer.json +7 -2
  14. package/dist/api/QEditor.json +7 -2
  15. package/dist/api/QExpansionItem.json +7 -2
  16. package/dist/api/QFab.json +7 -2
  17. package/dist/api/QFabAction.json +7 -2
  18. package/dist/api/QField.json +28 -7
  19. package/dist/api/QFile.json +14 -4
  20. package/dist/api/QImg.json +1 -1
  21. package/dist/api/QInnerLoading.json +7 -2
  22. package/dist/api/QInput.json +16 -5
  23. package/dist/api/QIntersection.json +7 -2
  24. package/dist/api/QItem.json +12 -4
  25. package/dist/api/QLinearProgress.json +7 -2
  26. package/dist/api/QList.json +7 -2
  27. package/dist/api/QMarkupTable.json +7 -2
  28. package/dist/api/QMenu.json +21 -6
  29. package/dist/api/QOptionGroup.json +7 -2
  30. package/dist/api/QPagination.json +37 -11
  31. package/dist/api/QRadio.json +7 -2
  32. package/dist/api/QRange.json +7 -2
  33. package/dist/api/QScrollArea.json +14 -4
  34. package/dist/api/QSelect.json +24 -8
  35. package/dist/api/QSeparator.json +7 -2
  36. package/dist/api/QSkeleton.json +7 -2
  37. package/dist/api/QSlideItem.json +7 -2
  38. package/dist/api/QSlider.json +7 -2
  39. package/dist/api/QStepper.json +7 -2
  40. package/dist/api/QTabPanel.json +7 -2
  41. package/dist/api/QTable.json +124 -21
  42. package/dist/api/QTime.json +17 -6
  43. package/dist/api/QTimeline.json +7 -2
  44. package/dist/api/QToggle.json +8 -3
  45. package/dist/api/QTooltip.json +14 -4
  46. package/dist/api/QTree.json +7 -2
  47. package/dist/api/QUploader.json +7 -2
  48. package/dist/api/QVirtualScroll.json +2 -1
  49. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  50. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  51. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  52. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  53. package/dist/icon-set/fontawesome-v6-pro.umd.prod.js +1 -1
  54. package/dist/icon-set/fontawesome-v6.umd.prod.js +1 -1
  55. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  56. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  57. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  58. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  59. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  60. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  61. package/dist/icon-set/material-symbols-outlined.umd.prod.js +1 -1
  62. package/dist/icon-set/material-symbols-rounded.umd.prod.js +1 -1
  63. package/dist/icon-set/material-symbols-sharp.umd.prod.js +1 -1
  64. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  65. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  66. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  67. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  68. package/dist/icon-set/mdi-v7.umd.prod.js +1 -1
  69. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  70. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  71. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  72. package/dist/icon-set/svg-fontawesome-v6.umd.prod.js +1 -1
  73. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  74. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  75. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  76. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  77. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +2 -2
  78. package/dist/icon-set/svg-material-icons-round.umd.prod.js +2 -2
  79. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +2 -2
  80. package/dist/icon-set/svg-material-icons.umd.prod.js +2 -2
  81. package/dist/icon-set/svg-material-symbols-outlined.umd.prod.js +2 -2
  82. package/dist/icon-set/svg-material-symbols-rounded.umd.prod.js +2 -2
  83. package/dist/icon-set/svg-material-symbols-sharp.umd.prod.js +2 -2
  84. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  85. package/dist/icon-set/svg-mdi-v7.umd.prod.js +1 -1
  86. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  87. package/dist/icon-set/themify.umd.prod.js +1 -1
  88. package/dist/lang/ar-TN.umd.prod.js +1 -1
  89. package/dist/lang/ar.umd.prod.js +1 -1
  90. package/dist/lang/az-Latn.umd.prod.js +1 -1
  91. package/dist/lang/bg.umd.prod.js +1 -1
  92. package/dist/lang/bn.umd.prod.js +1 -1
  93. package/dist/lang/ca.umd.prod.js +1 -1
  94. package/dist/lang/cs.umd.prod.js +1 -1
  95. package/dist/lang/da.umd.prod.js +1 -1
  96. package/dist/lang/de-CH.umd.prod.js +6 -0
  97. package/dist/lang/de-DE.umd.prod.js +6 -0
  98. package/dist/lang/de.umd.prod.js +1 -1
  99. package/dist/lang/el.umd.prod.js +1 -1
  100. package/dist/lang/en-GB.umd.prod.js +1 -1
  101. package/dist/lang/en-US.umd.prod.js +1 -1
  102. package/dist/lang/eo.umd.prod.js +1 -1
  103. package/dist/lang/es.umd.prod.js +1 -1
  104. package/dist/lang/et.umd.prod.js +1 -1
  105. package/dist/lang/eu.umd.prod.js +1 -1
  106. package/dist/lang/fa-IR.umd.prod.js +1 -1
  107. package/dist/lang/fa.umd.prod.js +1 -1
  108. package/dist/lang/fi.umd.prod.js +1 -1
  109. package/dist/lang/fr.umd.prod.js +1 -1
  110. package/dist/lang/gn.umd.prod.js +1 -1
  111. package/dist/lang/he.umd.prod.js +1 -1
  112. package/dist/lang/hi.umd.prod.js +1 -1
  113. package/dist/lang/hr.umd.prod.js +1 -1
  114. package/dist/lang/hu.umd.prod.js +1 -1
  115. package/dist/lang/id.umd.prod.js +1 -1
  116. package/dist/lang/is.umd.prod.js +1 -1
  117. package/dist/lang/it.umd.prod.js +1 -1
  118. package/dist/lang/ja.umd.prod.js +1 -1
  119. package/dist/lang/kk.umd.prod.js +1 -1
  120. package/dist/lang/km.umd.prod.js +1 -1
  121. package/dist/lang/ko-KR.umd.prod.js +1 -1
  122. package/dist/lang/kur-CKB.umd.prod.js +1 -1
  123. package/dist/lang/lt.umd.prod.js +1 -1
  124. package/dist/lang/lu.umd.prod.js +1 -1
  125. package/dist/lang/lv.umd.prod.js +1 -1
  126. package/dist/lang/mk.umd.prod.js +2 -2
  127. package/dist/lang/ml.umd.prod.js +1 -1
  128. package/dist/lang/mm.umd.prod.js +1 -1
  129. package/dist/lang/ms.umd.prod.js +1 -1
  130. package/dist/lang/my.umd.prod.js +1 -1
  131. package/dist/lang/nb-NO.umd.prod.js +1 -1
  132. package/dist/lang/nl.umd.prod.js +1 -1
  133. package/dist/lang/pl.umd.prod.js +1 -1
  134. package/dist/lang/pt-BR.umd.prod.js +1 -1
  135. package/dist/lang/pt.umd.prod.js +1 -1
  136. package/dist/lang/ro.umd.prod.js +1 -1
  137. package/dist/lang/ru.umd.prod.js +1 -1
  138. package/dist/lang/sk.umd.prod.js +1 -1
  139. package/dist/lang/sl.umd.prod.js +1 -1
  140. package/dist/lang/sm.umd.prod.js +1 -1
  141. package/dist/lang/sr-CYR.umd.prod.js +1 -1
  142. package/dist/lang/sr.umd.prod.js +1 -1
  143. package/dist/lang/sv.umd.prod.js +1 -1
  144. package/dist/lang/ta.umd.prod.js +1 -1
  145. package/dist/lang/th.umd.prod.js +1 -1
  146. package/dist/lang/tr.umd.prod.js +1 -1
  147. package/dist/lang/ug.umd.prod.js +1 -1
  148. package/dist/lang/uk.umd.prod.js +1 -1
  149. package/dist/lang/uz-Cyrl.umd.prod.js +1 -1
  150. package/dist/lang/uz-Latn.umd.prod.js +1 -1
  151. package/dist/lang/vi.umd.prod.js +1 -1
  152. package/dist/lang/zh-CN.umd.prod.js +1 -1
  153. package/dist/lang/zh-TW.umd.prod.js +1 -1
  154. package/dist/quasar.addon.prod.css +1 -1
  155. package/dist/quasar.addon.rtl.prod.css +1 -1
  156. package/dist/quasar.cjs.prod.js +2 -2
  157. package/dist/quasar.esm.js +136 -43
  158. package/dist/quasar.esm.prod.js +2 -2
  159. package/dist/quasar.prod.css +1 -1
  160. package/dist/quasar.rtl.prod.css +1 -1
  161. package/dist/quasar.sass +1 -1
  162. package/dist/quasar.umd.js +136 -43
  163. package/dist/quasar.umd.prod.js +2 -2
  164. package/dist/types/api/qtree.d.ts +115 -21
  165. package/dist/types/index.d.ts +98 -83
  166. package/dist/vetur/quasar-attributes.json +70 -66
  167. package/dist/vetur/quasar-tags.json +2 -1
  168. package/dist/web-types/web-types.json +134 -184
  169. package/lang/de-CH.js +98 -0
  170. package/lang/de-CH.mjs +93 -0
  171. package/lang/de-DE.js +98 -0
  172. package/lang/de-DE.mjs +93 -0
  173. package/lang/index.json +8 -0
  174. package/lang/mk.js +4 -4
  175. package/lang/mk.mjs +4 -4
  176. package/package.json +4 -3
  177. package/src/api.extends.json +2 -1
  178. package/src/components/btn/use-btn.json +2 -1
  179. package/src/components/checkbox/use-checkbox.json +1 -1
  180. package/src/components/chip/QChip.json +2 -1
  181. package/src/components/date/QDate.js +4 -4
  182. package/src/components/fab/use-fab.json +2 -1
  183. package/src/components/field/QField.js +14 -2
  184. package/src/components/field/QField.json +7 -0
  185. package/src/components/field/__tests__/QField.cy.js +156 -0
  186. package/src/components/img/QImg.json +1 -1
  187. package/src/components/input/QInput.json +2 -1
  188. package/src/components/input/use-mask.js +6 -1
  189. package/src/components/intersection/QIntersection.json +2 -1
  190. package/src/components/item/QItem.js +1 -1
  191. package/src/components/item/QItem.json +3 -3
  192. package/src/components/menu/QMenu.json +4 -0
  193. package/src/components/pagination/QPagination.json +9 -5
  194. package/src/components/scroll-area/QScrollArea.js +16 -1
  195. package/src/components/scroll-area/QScrollArea.json +2 -1
  196. package/src/components/select/QSelect.js +42 -17
  197. package/src/components/select/QSelect.json +3 -2
  198. package/src/components/select/__tests__/QSelect.cy.js +2 -1
  199. package/src/components/table/QTable.json +38 -1
  200. package/src/components/table/table-sort.js +5 -0
  201. package/src/components/time/QTime.js +16 -3
  202. package/src/components/time/QTime.json +4 -3
  203. package/src/components/tooltip/QTooltip.json +4 -0
  204. package/src/components/tree/QTree.js +8 -3
  205. package/src/components/virtual-scroll/use-virtual-scroll.json +1 -1
  206. package/src/composables/private/__tests__/FieldWrapper.vue +17 -2
  207. package/src/composables/private/__tests__/use-field.cy.js +334 -137
  208. package/src/composables/private/__tests__/use-validate.cy.js +162 -15
  209. package/src/composables/private/use-field.js +21 -8
  210. package/src/composables/private/use-validate.json +2 -1
@@ -1,32 +1,118 @@
1
1
  import FieldWrapper from './FieldWrapper.vue'
2
2
  import Icons from '../../../../icon-set/material-icons.mjs'
3
+ import { ref } from 'vue'
4
+ import { vModelAdapter } from '@quasar/quasar-app-extension-testing-e2e-cypress'
3
5
 
4
- // const snapshotOptions = { customSnapshotsDir: '../__tests__' }
6
+ function mountQFieldWrapper (options) {
7
+ return cy.mount(FieldWrapper, options)
8
+ }
9
+
10
+ function getHostElement () {
11
+ return cy.get('.select-root')
12
+ }
5
13
 
6
14
  describe('use-validate API', () => {
7
15
  describe('Props', () => {
8
16
  describe('Category: behavior', () => {
9
17
  describe('(prop): error', () => {
10
- it.skip(' ', () => {
11
- //
18
+ it('should mark the field as having an error', () => {
19
+ mountQFieldWrapper()
20
+ getHostElement().should('not.have.class', 'q-field--error')
21
+
22
+ getHostElement().then(() => {
23
+ Cypress.vueWrapper.setProps({ error: true })
24
+ })
25
+
26
+ getHostElement().should('have.class', 'q-field--error')
12
27
  })
13
28
  })
14
29
 
15
30
  describe('(prop): rules', () => {
16
- it.skip(' ', () => {
17
- //
31
+ it('should validate value using custom validation logic', () => {
32
+ const errorMessage = 'Selected value should be greater than 10 characters'
33
+ const model = ref('Option 1')
34
+ const options = [ 'Option 1', 'Option 2' ]
35
+
36
+ mountQFieldWrapper({
37
+ props: {
38
+ ...vModelAdapter(model),
39
+ options,
40
+ rules: [ val => val.length > 10 || errorMessage ]
41
+ }
42
+ })
43
+
44
+ getHostElement().then(() => {
45
+ model.value = 'Option 2'
46
+ })
47
+ getHostElement().get('.q-field__messages').should('contain.text', errorMessage)
48
+ })
49
+
50
+ it('should validate email using inbuilt validation logic', () => {
51
+ const errorMessage = 'Enter a valid email address'
52
+ const model = ref('Option 1')
53
+ const options = [ 'Option 1', 'Option 2' ]
54
+ mountQFieldWrapper({
55
+ props: {
56
+ ...vModelAdapter(model),
57
+ options,
58
+ rules: [ (val, rules) => rules.email(val) || errorMessage ]
59
+ }
60
+ })
61
+ getHostElement().then(() => {
62
+ model.value = 'Option 2'
63
+ })
64
+ getHostElement().get('.q-field__messages').should('contain.text', errorMessage)
18
65
  })
19
66
  })
20
67
 
21
68
  describe('(prop): reactive-rules', () => {
22
- it.skip(' ', () => {
23
- //
69
+ it('should trigger validation when there\'s a change of rules', () => {
70
+ const errorMessage = 'Error message'
71
+ const model = ref('Option 1')
72
+ const options = [ 'Option 1', 'Option 2' ]
73
+ mountQFieldWrapper({
74
+ props: {
75
+ ...vModelAdapter(model),
76
+ options
77
+ }
78
+ })
79
+
80
+ getHostElement().then(() => {
81
+ Cypress.vueWrapper.setProps({ rules: [ (value) => value.length < 3 || errorMessage ] })
82
+
83
+ Cypress.vueWrapper.vm.focusMethod()
84
+ Cypress.vueWrapper.vm.blur()
85
+ })
86
+ getHostElement().get('.q-field__messages').should('not.exist')
87
+
88
+ getHostElement().then(() => {
89
+ Cypress.vueWrapper.setProps({ reactiveRules: true, rules: [ (value, rules) => rules.email(value) || errorMessage ] })
90
+ })
91
+ getHostElement().get('.q-field__messages').should('contain.text', errorMessage)
24
92
  })
25
93
  })
26
94
 
27
95
  describe('(prop): lazy-rules', () => {
28
- it.skip(' ', () => {
29
- //
96
+ it('should validate the input only when component loses focus', () => {
97
+ const errorMessage = 'Use a max 3 of characters'
98
+ const model = ref('Option 1')
99
+ const options = [ 'Option 1', 'Option 2' ]
100
+ mountQFieldWrapper({
101
+ props: {
102
+ ...vModelAdapter(model),
103
+ options,
104
+ lazyRules: true,
105
+ rules: [ () => errorMessage ]
106
+ }
107
+ })
108
+
109
+ getHostElement().select('Option 2')
110
+ getHostElement().get('.q-field__messages').should('not.contain', errorMessage)
111
+
112
+ getHostElement().then(() => {
113
+ Cypress.vueWrapper.vm.blur()
114
+ getHostElement().get('.q-field__messages').should('contain.text', errorMessage)
115
+ })
30
116
  })
31
117
  })
32
118
  })
@@ -87,8 +173,22 @@ describe('use-validate API', () => {
87
173
 
88
174
  describe('Category: model', () => {
89
175
  describe('(prop): model-value', () => {
90
- it.skip(' ', () => {
91
- //
176
+ it('should correctly update the model value', () => {
177
+ const model = ref()
178
+ const options = [ 'Option 1', 'Option 2' ]
179
+ mountQFieldWrapper({
180
+ props: {
181
+ ...vModelAdapter(model),
182
+ options
183
+ }
184
+ })
185
+ getHostElement().get('input').should('not.have.value', options[ 0 ])
186
+
187
+ getHostElement().then(() => {
188
+ model.value = options[ 0 ]
189
+ })
190
+
191
+ getHostElement().get('input').should('have.value', options[ 0 ])
92
192
  })
93
193
  })
94
194
  })
@@ -96,14 +196,61 @@ describe('use-validate API', () => {
96
196
 
97
197
  describe('Methods', () => {
98
198
  describe('(method): resetValidation', () => {
99
- it.skip(' ', () => {
100
- //
199
+ it('should reset validation', () => {
200
+ const errorMessage = 'Use a max 3 of characters'
201
+ const model = ref('Option 1')
202
+ const options = [ 'Option 1', 'Option 2' ]
203
+ mountQFieldWrapper({
204
+ props: {
205
+ ...vModelAdapter(model),
206
+ options,
207
+ rules: [ val => val.length <= 3 || errorMessage ],
208
+ lazyRules: 'ondemand'
209
+ }
210
+ })
211
+
212
+ getHostElement().then(() => {
213
+ model.value = 'Option 2'
214
+ })
215
+ getHostElement().get('.q-field__messages').should('not.contain', errorMessage)
216
+
217
+ getHostElement().then(() => {
218
+ Cypress.vueWrapper.vm.validate()
219
+ getHostElement().get('.q-field__messages').should('contain', errorMessage)
220
+ })
221
+
222
+ getHostElement().then(() => {
223
+ Cypress.vueWrapper.vm.resetValidation()
224
+ getHostElement().get('.q-field__messages').should('not.contain', errorMessage)
225
+ })
101
226
  })
102
227
  })
103
228
 
104
229
  describe('(method): validate', () => {
105
- it.skip(' ', () => {
106
- //
230
+ it('should validate the input only when component\'s validate() method is called', () => {
231
+ const errorMessage = 'Use a max 3 of characters'
232
+ const model = ref('Option 1')
233
+ const options = [ 'Option 1', 'Option 2' ]
234
+ mountQFieldWrapper({
235
+ props: {
236
+ ...vModelAdapter(model),
237
+ options,
238
+ rules: [ val => val.length <= 3 || errorMessage ],
239
+ lazyRules: 'ondemand'
240
+ }
241
+ })
242
+
243
+ getHostElement().then(() => {
244
+ model.value = 'Option 2'
245
+ })
246
+ getHostElement().get('.q-field__messages').should('not.contain', errorMessage)
247
+
248
+ getHostElement().get('input').then(() => {
249
+ Cypress.vueWrapper.vm.blur()
250
+ getHostElement().get('.q-field__messages').should('not.contain', errorMessage)
251
+ Cypress.vueWrapper.vm.validate()
252
+ getHostElement().get('.q-field__messages').should('contain', errorMessage)
253
+ })
107
254
  })
108
255
  })
109
256
  })
@@ -14,8 +14,10 @@ import uid from '../../utils/uid.js'
14
14
  import { prevent, stopAndPrevent } from '../../utils/event.js'
15
15
  import { addFocusFn, removeFocusFn } from '../../utils/private/focus-manager.js'
16
16
 
17
- function getTargetUid (val) {
18
- return val === void 0 ? `f_${ uid() }` : val
17
+ function getTargetUid (val, requiredForAttr) {
18
+ return val === void 0
19
+ ? (requiredForAttr === true ? `f_${ uid() }` : void 0)
20
+ : val
19
21
  }
20
22
 
21
23
  export function fieldValueIsFilled (val) {
@@ -74,12 +76,17 @@ export const useFieldProps = {
74
76
 
75
77
  export const useFieldEmits = [ 'update:modelValue', 'clear', 'focus', 'blur', 'popupShow', 'popupHide' ]
76
78
 
77
- export function useFieldState () {
79
+ export function useFieldState ({ requiredForAttr = true, tagProp } = {}) {
78
80
  const { props, attrs, proxy, vnode } = getCurrentInstance()
79
81
 
80
82
  const isDark = useDark(props, proxy.$q)
81
83
 
82
84
  return {
85
+ requiredForAttr,
86
+ tag: tagProp === true
87
+ ? computed(() => props.tag)
88
+ : { value: 'label' },
89
+
83
90
  isDark,
84
91
 
85
92
  editable: computed(() =>
@@ -91,7 +98,9 @@ export function useFieldState () {
91
98
  hasPopupOpen: false,
92
99
 
93
100
  splitAttrs: useSplitAttrs(attrs, vnode),
94
- targetUid: ref(getTargetUid(props.for)),
101
+ targetUid: ref(
102
+ getTargetUid(props.for, requiredForAttr)
103
+ ),
95
104
 
96
105
  rootRef: ref(null),
97
106
  targetRef: ref(null),
@@ -260,7 +269,7 @@ export default function (state) {
260
269
  watch(() => props.for, val => {
261
270
  // don't transform targetUid into a computed
262
271
  // prop as it will break SSR
263
- state.targetUid.value = getTargetUid(val)
272
+ state.targetUid.value = getTargetUid(val, state.requiredForAttr)
264
273
  })
265
274
 
266
275
  function focusHandler () {
@@ -545,8 +554,12 @@ export default function (state) {
545
554
  })
546
555
 
547
556
  onMounted(() => {
548
- if (isRuntimeSsrPreHydration.value === true && props.for === void 0) {
549
- state.targetUid.value = getTargetUid()
557
+ if (
558
+ isRuntimeSsrPreHydration.value === true
559
+ && state.requiredForAttr === true
560
+ && props.for === void 0
561
+ ) {
562
+ state.targetUid.value = `f_${ uid() }` // getTargetUid(void 0, true)
550
563
  }
551
564
 
552
565
  props.autofocus === true && proxy.focus()
@@ -568,7 +581,7 @@ export default function (state) {
568
581
  }
569
582
  : attributes.value
570
583
 
571
- return h('label', {
584
+ return h(state.tag.value, {
572
585
  ref: state.rootRef,
573
586
  class: [
574
587
  classes.value,
@@ -7,7 +7,8 @@
7
7
  },
8
8
 
9
9
  "error": {
10
- "type": "Boolean",
10
+ "type": [ "Boolean", "null" ],
11
+ "default": null,
11
12
  "desc": "Does field have validation errors?",
12
13
  "category": "behavior"
13
14
  },