quasar 2.4.12 → 2.5.2

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 (211) hide show
  1. package/dist/api/BottomSheet.json +4 -0
  2. package/dist/api/Dialog.json +5 -1
  3. package/dist/api/Notify.json +20 -10
  4. package/dist/api/QBtnDropdown.json +4 -2
  5. package/dist/api/QCheckbox.json +27 -0
  6. package/dist/api/QEditor.json +4 -2
  7. package/dist/api/QExpansionItem.json +6 -3
  8. package/dist/api/QFab.json +4 -2
  9. package/dist/api/QFabAction.json +4 -2
  10. package/dist/api/QFile.json +4 -2
  11. package/dist/api/QImg.json +1 -2
  12. package/dist/api/QInnerLoading.json +2 -1
  13. package/dist/api/QInput.json +4 -2
  14. package/dist/api/QPagination.json +4 -2
  15. package/dist/api/QRadio.json +18 -0
  16. package/dist/api/QRange.json +9 -0
  17. package/dist/api/QScrollArea.json +13 -5
  18. package/dist/api/QSelect.json +6 -3
  19. package/dist/api/QSlider.json +9 -0
  20. package/dist/api/QSplitter.json +8 -4
  21. package/dist/api/QTable.json +9 -0
  22. package/dist/api/QTime.json +8 -2
  23. package/dist/api/QToggle.json +24 -24
  24. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  25. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  26. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  27. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  28. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  29. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  30. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  31. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  32. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  33. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  34. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  35. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  36. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  37. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  38. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  39. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  40. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  41. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  42. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  43. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  44. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  45. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
  46. package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
  47. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
  48. package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
  49. package/dist/icon-set/svg-mdi-v4.umd.prod.js +1 -1
  50. package/dist/icon-set/svg-mdi-v5.umd.prod.js +1 -1
  51. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  52. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  53. package/dist/icon-set/themify.umd.prod.js +1 -1
  54. package/dist/lang/ar.umd.prod.js +1 -1
  55. package/dist/lang/az-Latn.umd.prod.js +1 -1
  56. package/dist/lang/bg.umd.prod.js +1 -1
  57. package/dist/lang/bn.umd.prod.js +1 -1
  58. package/dist/lang/ca.umd.prod.js +1 -1
  59. package/dist/lang/cs.umd.prod.js +1 -1
  60. package/dist/lang/da.umd.prod.js +1 -1
  61. package/dist/lang/de.umd.prod.js +1 -1
  62. package/dist/lang/el.umd.prod.js +1 -1
  63. package/dist/lang/en-GB.umd.prod.js +1 -1
  64. package/dist/lang/en-US.umd.prod.js +1 -1
  65. package/dist/lang/eo.umd.prod.js +1 -1
  66. package/dist/lang/es.umd.prod.js +1 -1
  67. package/dist/lang/et.umd.prod.js +1 -1
  68. package/dist/lang/fa-IR.umd.prod.js +1 -1
  69. package/dist/lang/fa.umd.prod.js +1 -1
  70. package/dist/lang/fi.umd.prod.js +1 -1
  71. package/dist/lang/fr.umd.prod.js +1 -1
  72. package/dist/lang/gn.umd.prod.js +1 -1
  73. package/dist/lang/he.umd.prod.js +1 -1
  74. package/dist/lang/hr.umd.prod.js +1 -1
  75. package/dist/lang/hu.umd.prod.js +1 -1
  76. package/dist/lang/id.umd.prod.js +1 -1
  77. package/dist/lang/is.umd.prod.js +1 -1
  78. package/dist/lang/it.umd.prod.js +1 -1
  79. package/dist/lang/ja.umd.prod.js +1 -1
  80. package/dist/lang/km.umd.prod.js +1 -1
  81. package/dist/lang/ko-KR.umd.prod.js +1 -1
  82. package/dist/lang/kur-CKB.umd.prod.js +1 -1
  83. package/dist/lang/lt.umd.prod.js +1 -1
  84. package/dist/lang/lu.umd.prod.js +1 -1
  85. package/dist/lang/lv.umd.prod.js +1 -1
  86. package/dist/lang/ml.umd.prod.js +1 -1
  87. package/dist/lang/ms.umd.prod.js +1 -1
  88. package/dist/lang/my.umd.prod.js +1 -1
  89. package/dist/lang/nb-NO.umd.prod.js +1 -1
  90. package/dist/lang/nl.umd.prod.js +1 -1
  91. package/dist/lang/pl.umd.prod.js +1 -1
  92. package/dist/lang/pt-BR.umd.prod.js +1 -1
  93. package/dist/lang/pt.umd.prod.js +1 -1
  94. package/dist/lang/ro.umd.prod.js +1 -1
  95. package/dist/lang/ru.umd.prod.js +1 -1
  96. package/dist/lang/sk.umd.prod.js +1 -1
  97. package/dist/lang/sl.umd.prod.js +1 -1
  98. package/dist/lang/sr-CYR.umd.prod.js +1 -1
  99. package/dist/lang/sr.umd.prod.js +1 -1
  100. package/dist/lang/sv.umd.prod.js +1 -1
  101. package/dist/lang/ta.umd.prod.js +1 -1
  102. package/dist/lang/th.umd.prod.js +1 -1
  103. package/dist/lang/tr.umd.prod.js +1 -1
  104. package/dist/lang/ug.umd.prod.js +1 -1
  105. package/dist/lang/uk.umd.prod.js +1 -1
  106. package/dist/lang/vi.umd.prod.js +1 -1
  107. package/dist/lang/zh-CN.umd.prod.js +1 -1
  108. package/dist/lang/zh-TW.umd.prod.js +1 -1
  109. package/dist/quasar.cjs.prod.js +2 -2
  110. package/dist/quasar.css +38 -10
  111. package/dist/quasar.esm.prod.js +2 -2
  112. package/dist/quasar.prod.css +1 -1
  113. package/dist/quasar.rtl.css +55 -23
  114. package/dist/quasar.rtl.prod.css +1 -1
  115. package/dist/quasar.sass +31 -11
  116. package/dist/quasar.umd.js +313 -163
  117. package/dist/quasar.umd.prod.js +2 -2
  118. package/dist/ssr-directives/Morph.js +1 -1
  119. package/dist/transforms/auto-import.json +39 -3
  120. package/dist/transforms/import-map.json +18 -0
  121. package/dist/types/api/slider.d.ts +6 -7
  122. package/dist/types/api/vue-prop-types.ts +15 -0
  123. package/dist/types/api.d.ts +1 -0
  124. package/dist/types/index.d.ts +110 -184
  125. package/dist/types/utils/date.d.ts +25 -13
  126. package/dist/types/utils/dom.d.ts +4 -2
  127. package/dist/types/utils.d.ts +3 -1
  128. package/dist/vetur/quasar-attributes.json +62 -42
  129. package/dist/vetur/quasar-tags.json +8 -3
  130. package/dist/web-types/web-types.json +103 -58
  131. package/package.json +13 -3
  132. package/src/components/avatar/__tests__/QAvatar.spec.js +121 -0
  133. package/src/components/badge/__tests__/QBadge.spec.js +74 -0
  134. package/src/components/breadcrumbs/QBreadcrumbs.js +1 -1
  135. package/src/components/btn/__tests__/QBtn.spec.js +55 -0
  136. package/src/components/btn/__tests__/use-btn.spec.js +189 -0
  137. package/src/components/btn-dropdown/QBtnDropdown.json +4 -2
  138. package/src/components/checkbox/QCheckbox.js +34 -3
  139. package/src/components/checkbox/QCheckbox.json +17 -0
  140. package/src/components/checkbox/QCheckbox.sass +17 -5
  141. package/src/components/checkbox/use-checkbox.js +4 -0
  142. package/src/components/checkbox/use-checkbox.json +18 -0
  143. package/src/components/chip/__tests__/QChip.spec.js +155 -0
  144. package/src/components/date/__tests__/QDate.spec.js +189 -0
  145. package/src/components/date/__tests__/use-datetime.spec.js +83 -0
  146. package/src/components/dialog/__tests__/QDialog.spec.js +129 -0
  147. package/src/components/editor/QEditor.json +3 -1
  148. package/src/components/editor/__tests__/QEditor.spec.js +195 -0
  149. package/src/components/expansion-item/QExpansionItem.json +6 -3
  150. package/src/components/fab/use-fab.json +4 -2
  151. package/src/components/field/QField.sass +2 -0
  152. package/src/components/file/QFile.json +4 -2
  153. package/src/components/form/QForm.js +26 -24
  154. package/src/components/img/QImg.json +2 -3
  155. package/src/components/inner-loading/QInnerLoading.json +2 -1
  156. package/src/components/input/QInput.js +9 -0
  157. package/src/components/input/QInput.json +4 -2
  158. package/src/components/input/__tests__/QInput.spec.js +105 -0
  159. package/src/components/input/__tests__/use-mask.spec.js +29 -0
  160. package/src/components/menu/__tests__/QMenu.spec.js +610 -0
  161. package/src/components/menu/__tests__/WrapperOne.vue +51 -0
  162. package/src/components/menu/__tests__/WrapperTwo.vue +38 -0
  163. package/src/components/pagination/QPagination.json +4 -2
  164. package/src/components/radio/QRadio.js +26 -1
  165. package/src/components/radio/QRadio.json +16 -0
  166. package/src/components/radio/QRadio.sass +17 -6
  167. package/src/components/scroll-area/QScrollArea.json +13 -5
  168. package/src/components/select/QSelect.js +1 -1
  169. package/src/components/select/QSelect.json +6 -3
  170. package/src/components/select/__tests__/QSelect.spec.js +2003 -0
  171. package/src/components/select/__tests__/WrapperOne.vue +28 -0
  172. package/src/components/slider/use-slider.json +9 -0
  173. package/src/components/splitter/QSplitter.json +8 -4
  174. package/src/components/stepper/QStep.js +1 -1
  175. package/src/components/table/QTable.json +9 -0
  176. package/src/components/table/__tests__/QTable.spec.js +635 -0
  177. package/src/components/table/__tests__/QTd.spec.js +35 -0
  178. package/src/components/table/__tests__/QTh.spec.js +27 -0
  179. package/src/components/table/__tests__/QTr.spec.js +27 -0
  180. package/src/components/tabs/__tests__/QRouteTab.spec.js +9 -0
  181. package/src/components/tabs/__tests__/QTab.spec.js +79 -0
  182. package/src/components/tabs/__tests__/QTabs.spec.js +147 -0
  183. package/src/components/time/QTime.json +2 -2
  184. package/src/components/toggle/QToggle.js +5 -13
  185. package/src/components/toggle/QToggle.json +3 -12
  186. package/src/components/tree/QTree.js +39 -41
  187. package/src/components/uploader/__tests__/QUploader.spec.js +161 -0
  188. package/src/components/virtual-scroll/use-virtual-scroll.js +6 -4
  189. package/src/composables/private/__tests__/FieldWrapper.vue +39 -0
  190. package/src/composables/private/__tests__/use-anchor.spec.js +99 -0
  191. package/src/composables/private/__tests__/use-field.spec.js +351 -0
  192. package/src/composables/private/__tests__/use-file.spec.js +69 -0
  193. package/src/composables/private/__tests__/use-form.spec.js +11 -0
  194. package/src/composables/private/__tests__/use-fullscreen.spec.js +37 -0
  195. package/src/composables/private/__tests__/use-model-toggle.spec.js +306 -0
  196. package/src/composables/private/__tests__/use-portal.spec.js +4 -0
  197. package/src/composables/private/__tests__/use-router-link.spec.js +55 -0
  198. package/src/composables/private/__tests__/use-size.spec.js +37 -0
  199. package/src/composables/private/__tests__/use-transition.spec.js +108 -0
  200. package/src/composables/private/__tests__/use-validate.spec.js +111 -0
  201. package/src/composables/private/use-fullscreen.js +1 -1
  202. package/src/composables/private/use-model-toggle.js +1 -1
  203. package/src/composables/private/use-validate.js +22 -22
  204. package/src/plugins/BottomSheet.json +1 -0
  205. package/src/plugins/Dialog.json +1 -0
  206. package/src/plugins/Meta.js +1 -1
  207. package/src/plugins/Notify.json +20 -10
  208. package/src/plugins/Screen.js +15 -9
  209. package/src/utils/date.js +111 -32
  210. package/src/utils/private/global-dialog.json +3 -0
  211. package/src/utils/private/vm.js +14 -8
@@ -0,0 +1,610 @@
1
+ /* eslint-disable no-unused-expressions */
2
+ import { mount } from '@cypress/vue'
3
+ import WrapperOne from './WrapperOne.vue'
4
+ import WrapperTwo from './WrapperTwo.vue'
5
+
6
+ describe('Menu API', () => {
7
+ describe('Props', () => {
8
+ describe('Category: behavior', () => {
9
+ describe('(prop): scroll-target', () => {
10
+ it.skip(' ', () => {
11
+ // Todo: Check if test for this needs to be added.
12
+ })
13
+ })
14
+
15
+ describe('(prop): touch-position', () => {
16
+ it('should show menu at the position of the click', () => {
17
+ mount(WrapperOne, {
18
+ attrs: {
19
+ 'touch-position': true
20
+ }
21
+ })
22
+
23
+ const mouseX = 75
24
+ const mouseY = 25
25
+ let elementX = 0
26
+ let elementY = 0
27
+
28
+ cy.dataCy('wrapper')
29
+ .then(($el) => {
30
+ const rect = $el[ 0 ].getBoundingClientRect()
31
+ elementX = rect.left
32
+ elementY = rect.top
33
+ })
34
+ .click(mouseX, mouseY)
35
+ cy.dataCy('menu')
36
+ .should('exist')
37
+ .then(($el) => {
38
+ expect($el[ 0 ].offsetLeft).to.equal(mouseX + elementX)
39
+ // TODO: check if the Y position being off by 1 is intentional
40
+ expect($el[ 0 ].offsetTop).to.equal(mouseY + 1 + elementY)
41
+ })
42
+ })
43
+ })
44
+
45
+ describe('(prop): persistent', () => {
46
+ it('should close the menu when clicking outside the menu', () => {
47
+ mount(WrapperOne)
48
+
49
+ cy.dataCy('wrapper')
50
+ .click()
51
+ cy.dataCy('menu')
52
+ .should('exist')
53
+ cy.get('body')
54
+ .click(499, 0)
55
+ .dataCy('menu')
56
+ .should('not.exist')
57
+ })
58
+
59
+ it('should close the menu when hitting the escape key', () => {
60
+ mount(WrapperOne)
61
+
62
+ cy.dataCy('wrapper')
63
+ .click()
64
+ cy.dataCy('menu')
65
+ .should('exist')
66
+ cy.get('body')
67
+ .type('{esc}')
68
+ .dataCy('menu')
69
+ .should('not.exist')
70
+ })
71
+
72
+ it('should not close the menu when clicking outside the menu when persistent', () => {
73
+ mount(WrapperOne, {
74
+ attrs: {
75
+ persistent: true
76
+ }
77
+ })
78
+
79
+ cy.dataCy('wrapper')
80
+ .click()
81
+ cy.dataCy('menu')
82
+ .should('exist')
83
+ cy.get('body')
84
+ .click(499, 0)
85
+ .wait(300) // Await menu animation otherwise it always passes
86
+ .dataCy('menu')
87
+ .should('exist')
88
+ })
89
+
90
+ it('should not close the menu when hitting the escape key when persistent', () => {
91
+ mount(WrapperOne, {
92
+ attrs: {
93
+ persistent: true
94
+ }
95
+ })
96
+
97
+ cy.dataCy('wrapper')
98
+ .click()
99
+ cy.dataCy('menu')
100
+ .should('exist')
101
+ cy.get('body')
102
+ .type('{esc}')
103
+ .wait(300) // Await menu animation otherwise it always passes
104
+ .dataCy('menu')
105
+ .should('exist')
106
+ })
107
+ })
108
+
109
+ describe('(prop): no-route-dismiss', () => {
110
+ it.skip(' ', () => {
111
+ // This needs a vue-router, not possible with a unit test
112
+ })
113
+ })
114
+
115
+ describe('(prop): auto-close', () => {
116
+ it('should not close the menu when clicking a menu child without v-close-popup', () => {
117
+ mount(WrapperOne)
118
+
119
+ cy.dataCy('wrapper')
120
+ .click()
121
+ cy.dataCy('menu')
122
+ .should('exist')
123
+ .dataCy('keep-open')
124
+ .click()
125
+ .wait(300) // Await menu animation otherwise it always passes
126
+ cy.dataCy('menu')
127
+ .should('exist')
128
+ })
129
+
130
+ it('should close the menu when clicking a menu child without v-close-popup when auto-close is true', () => {
131
+ mount(WrapperOne, {
132
+ attrs: {
133
+ 'auto-close': true
134
+ }
135
+ })
136
+
137
+ cy.dataCy('wrapper')
138
+ .click()
139
+ cy.dataCy('menu')
140
+ .should('exist')
141
+ .dataCy('keep-open')
142
+ .click()
143
+ .wait(300) // Await menu animation otherwise it always passes
144
+ cy.dataCy('menu')
145
+ .should('not.exist')
146
+ })
147
+ })
148
+
149
+ describe('(prop): separate-close-popup', () => {
150
+ it.skip(' ', () => {
151
+ // Todo: Check if this needs to be tested
152
+ })
153
+ })
154
+
155
+ describe('(prop): no-refocus', () => {
156
+ // TODO: it is not clear from the docs that refocussing does not happen when clicking outside or closing by escape/programatticaly. Should this be added?
157
+ it('should switch focus back to parent element when closing', () => {
158
+ mount(WrapperOne)
159
+
160
+ cy.dataCy('wrapper')
161
+ .focus()
162
+ .should('have.focus')
163
+ .click()
164
+ cy.dataCy('menu')
165
+ .should('exist')
166
+ .should('have.focus')
167
+ .dataCy('close-popup')
168
+ .click()
169
+ .wait(300) // Wait for menu transition
170
+ cy.dataCy('wrapper')
171
+ .get('.q-focus-helper')
172
+ .should('have.focus')
173
+ })
174
+
175
+ it('should not switch focus back to parent element when closing if no-refocus is true', () => {
176
+ mount(WrapperOne, {
177
+ attrs: {
178
+ 'no-refocus': true
179
+ }
180
+ })
181
+
182
+ cy.dataCy('wrapper')
183
+ .focus()
184
+ .should('have.focus')
185
+ .click()
186
+ cy.dataCy('menu')
187
+ .should('exist')
188
+ .should('have.focus')
189
+ .dataCy('close-popup')
190
+ .click()
191
+ .wait(300) // Wait for menu transition
192
+ cy.dataCy('wrapper')
193
+ .get('.q-focus-helper')
194
+ .should('not.have.focus')
195
+ })
196
+ })
197
+
198
+ describe('(prop): no-focus', () => {
199
+ it('should switch focus to the menu when opening', () => {
200
+ mount(WrapperOne)
201
+
202
+ cy.dataCy('wrapper')
203
+ .click()
204
+ cy.dataCy('menu')
205
+ .should('exist')
206
+ .should('have.focus')
207
+ })
208
+
209
+ it('should no switch focus to the menu when opening with no-focus is true', () => {
210
+ mount(WrapperOne, {
211
+ attrs: {
212
+ 'no-focus': true
213
+ }
214
+ })
215
+
216
+ cy.dataCy('wrapper')
217
+ .click()
218
+ cy.dataCy('menu')
219
+ .should('exist')
220
+ .should('not.have.focus')
221
+ })
222
+ })
223
+ })
224
+
225
+ describe('Category: position', () => {
226
+ describe('(prop): fit', () => {
227
+ it('should show a menu that matches the full with of the target when fit is supplied', () => {
228
+ mount(WrapperOne, {
229
+ attrs: {
230
+ target: '.other-target',
231
+ fit: true
232
+ }
233
+ })
234
+ let targetWidth = 0
235
+ cy.dataCy('other-target')
236
+ .then(($el) => {
237
+ targetWidth = $el[ 0 ].clientWidth
238
+ })
239
+ .click()
240
+ cy.dataCy('menu')
241
+ .then(($el) => {
242
+ expect($el[ 0 ].clientWidth).to.equal(targetWidth)
243
+ })
244
+ })
245
+
246
+ it('should show a menu that not matches the full with of the target when fit is false', () => {
247
+ mount(WrapperOne, {
248
+ attrs: {
249
+ target: '.other-target',
250
+ fit: false
251
+ }
252
+ })
253
+ let targetWidth = 0
254
+ cy.dataCy('other-target')
255
+ .then(($el) => {
256
+ targetWidth = $el[ 0 ].clientWidth
257
+ })
258
+ .click()
259
+ cy.dataCy('menu')
260
+ .then(($el) => {
261
+ expect($el[ 0 ].clientWidth).not.to.equal(targetWidth)
262
+ })
263
+ })
264
+ })
265
+
266
+ describe('(prop): cover', () => {
267
+ it('should show a menu that overlays the target when using cover', () => {
268
+ mount(WrapperOne, {
269
+ attrs: {
270
+ cover: true
271
+ }
272
+ })
273
+ cy.dataCy('wrapper')
274
+ .click()
275
+ cy.dataCy('menu')
276
+ .checkVerticalPosition('wrapper', 'center', 'center')
277
+ .checkHorizontalPosition('wrapper', 'middle', 'middle')
278
+ })
279
+
280
+ it('should show a menu that overlays the target when using cover', () => {
281
+ mount(WrapperOne, {
282
+ attrs: {
283
+ cover: true,
284
+ target: '.other-target'
285
+ }
286
+ })
287
+ cy.dataCy('other-target')
288
+ .click()
289
+ cy.dataCy('menu')
290
+ .checkVerticalPosition('other-target', 'center', 'center')
291
+ .checkHorizontalPosition('other-target', 'middle', 'middle')
292
+ })
293
+
294
+ it('should ignore self property when using cover', () => {
295
+ mount(WrapperOne, {
296
+ attrs: {
297
+ cover: true,
298
+ self: 'center right',
299
+ target: '.other-target'
300
+ }
301
+ })
302
+ cy.dataCy('other-target')
303
+ .click()
304
+ cy.dataCy('menu')
305
+ .checkVerticalPosition('other-target', 'center', 'center')
306
+ .checkHorizontalPosition('other-target', 'middle', 'middle')
307
+ })
308
+ })
309
+
310
+ describe('(prop): anchor & self', () => {
311
+ it('should show a menu at anchor: bottom left and self: top left by default', () => {
312
+ mount(WrapperOne)
313
+
314
+ cy.dataCy('wrapper')
315
+ .click()
316
+ cy.dataCy('menu')
317
+ .checkVerticalPosition('wrapper', 'bottom', 'top')
318
+ .checkHorizontalPosition('wrapper', 'left', 'left')
319
+ })
320
+
321
+ const verticalAnchor = [ 'top', 'center', 'bottom' ]
322
+ const horizontalAnchor = [ 'left', 'middle', 'right' ]
323
+ const verticalSelf = [ 'top', 'center', 'bottom' ]
324
+ const horizontalSelf = [ 'left', 'middle', 'right' ]
325
+ verticalAnchor.forEach((vA) => {
326
+ horizontalAnchor.forEach((hA) => {
327
+ verticalSelf.forEach((vS) => {
328
+ horizontalSelf.forEach((hS) => {
329
+ it(`should position Anchor(${ vA } ${ hA }) & Self(${ vS } ${ hS }) correctly`, () => {
330
+ mount(WrapperOne, {
331
+ attrs: {
332
+ anchor: `${ vA } ${ hA }`,
333
+ self: `${ vS } ${ hS }`
334
+ }
335
+ })
336
+
337
+ cy.dataCy('wrapper')
338
+ .click()
339
+ cy.dataCy('menu')
340
+ .checkVerticalPosition('wrapper', vA, vS)
341
+ .checkHorizontalPosition('wrapper', hA, hS)
342
+ })
343
+ })
344
+ })
345
+ })
346
+ })
347
+ })
348
+
349
+ describe.skip('(prop): self', () => {
350
+ // This property is tested together with anchor above
351
+ })
352
+
353
+ describe('(prop): offset', () => {
354
+ const verticalAnchor = [ 'top', 'center', 'bottom' ]
355
+ const verticalSelf = [ 'top', 'center', 'bottom' ]
356
+ verticalAnchor.forEach((vA) => {
357
+ verticalSelf.forEach((vS) => {
358
+ it(`should offset vertical position Anchor(${ vA } left) & Self(${ vS } left) correctly`, () => {
359
+ mount(WrapperOne, {
360
+ attrs: {
361
+ anchor: `${ vA } left`,
362
+ self: `${ vS } left`,
363
+ offset: [ 0, 20 ]
364
+ }
365
+ })
366
+
367
+ cy.dataCy('wrapper')
368
+ .click()
369
+ cy.dataCy('menu')
370
+ .checkVerticalPosition('wrapper', vA, vS, 20)
371
+ })
372
+ })
373
+ })
374
+
375
+ const horizontalAnchor = [ 'left', 'middle', 'right' ]
376
+ const horizontalSelf = [ 'left', 'middle', 'right' ]
377
+ horizontalAnchor.forEach((hA) => {
378
+ horizontalSelf.forEach((hS) => {
379
+ it(`should offset horizontal position Anchor(top ${ hA }) & Self(top ${ hS }) correctly`, () => {
380
+ mount(WrapperOne, {
381
+ attrs: {
382
+ anchor: `top ${ hA }`,
383
+ self: `top ${ hS }`,
384
+ offset: [ 20, 0 ]
385
+ }
386
+ })
387
+
388
+ cy.dataCy('wrapper')
389
+ .click()
390
+ cy.dataCy('menu')
391
+ .checkHorizontalPosition('wrapper', hA, hS, 20)
392
+ })
393
+ })
394
+ })
395
+ })
396
+ })
397
+
398
+ describe('Category: style', () => {
399
+ describe('(prop): dark', () => {
400
+ it('should set the --q-dark color as background and white text color', () => {
401
+ mount(WrapperOne, {
402
+ attrs: {
403
+ dark: true
404
+ }
405
+ })
406
+ cy.dataCy('wrapper')
407
+ .click()
408
+ cy.dataCy('menu')
409
+ .should('have.color', 'white')
410
+ .should('have.backgroundColor', 'var(--q-dark)')
411
+ })
412
+ })
413
+
414
+ describe('(prop): square', () => {
415
+ it('should not have border-radius when using this prop', () => {
416
+ mount(WrapperOne, {
417
+ attrs: {
418
+ square: true
419
+ }
420
+ })
421
+ cy.dataCy('wrapper')
422
+ .click()
423
+ cy.dataCy('menu')
424
+ .should('have.css', 'border-radius', '0px')
425
+ })
426
+ })
427
+
428
+ describe('(prop): max-height', () => {
429
+ it('should specify a max-height when setting this prop', () => {
430
+ const maxHeight = '30px'
431
+ mount(WrapperOne, {
432
+ attrs: {
433
+ maxHeight
434
+ }
435
+ })
436
+ cy.dataCy('wrapper')
437
+ .click()
438
+ cy.dataCy('menu')
439
+ .should('have.css', 'max-height', maxHeight)
440
+ })
441
+ })
442
+
443
+ describe('(prop): max-width', () => {
444
+ it('should specify a max-width when setting this prop', () => {
445
+ const maxWidth = '30px'
446
+ mount(WrapperOne, {
447
+ attrs: {
448
+ maxWidth
449
+ }
450
+ })
451
+ cy.dataCy('wrapper')
452
+ .click()
453
+ cy.dataCy('menu')
454
+ .should('have.css', 'max-width', maxWidth)
455
+ })
456
+ })
457
+ })
458
+ })
459
+
460
+ describe('Slots', () => {
461
+ describe('(slot): default', () => {
462
+ it.skip(' ', () => {
463
+ //
464
+ })
465
+ })
466
+ })
467
+
468
+ describe('Events', () => {
469
+ describe('(event): escape-key', () => {
470
+ it('should emit @escape-key event when escape key is pressed', () => {
471
+ const fn = cy.stub()
472
+ mount(WrapperOne, {
473
+ attrs: {
474
+ onEscapeKey: fn
475
+ }
476
+ })
477
+
478
+ expect(fn).not.to.be.called
479
+ cy.dataCy('wrapper')
480
+ .click()
481
+ cy.dataCy('menu')
482
+ .should('exist')
483
+ .then(() => {
484
+ expect(fn).not.to.be.called
485
+ })
486
+ cy.get('body')
487
+ .type('{esc}')
488
+ .then(() => {
489
+ expect(fn).to.be.called
490
+ })
491
+ })
492
+
493
+ it('should not emit @escape-key event when menu is persistent', () => {
494
+ const fn = cy.stub()
495
+ mount(WrapperOne, {
496
+ attrs: {
497
+ onEscapeKey: fn,
498
+ persistent: true
499
+ }
500
+ })
501
+
502
+ expect(fn).not.to.be.called
503
+ cy.dataCy('wrapper')
504
+ .click()
505
+ cy.dataCy('menu')
506
+ .should('exist')
507
+ .then(() => {
508
+ expect(fn).not.to.be.called
509
+ })
510
+ cy.get('body')
511
+ .type('{esc}')
512
+ .then(() => {
513
+ expect(fn).not.to.be.called
514
+ })
515
+ })
516
+ })
517
+ })
518
+
519
+ describe('Methods', () => {
520
+ describe('(method): updatePosition', () => {
521
+ it('should reposition the menu when it is no longer in correct position', () => {
522
+ mount(WrapperTwo, {
523
+ attrs: {
524
+ anchor: 'bottom left',
525
+ self: 'bottom left'
526
+ }
527
+ })
528
+
529
+ let bottom = null
530
+ let left = null
531
+
532
+ cy.dataCy('wrapper')
533
+ .click()
534
+ .wait(300)
535
+ cy.dataCy('menu')
536
+ .should('exist')
537
+ .checkVerticalPosition('wrapper', 'bottom', 'bottom')
538
+ .checkHorizontalPosition('wrapper', 'left', 'left')
539
+ .then(($el) => {
540
+ const rect = $el[ 0 ].getBoundingClientRect()
541
+ bottom = rect.bottom
542
+ left = rect.left
543
+ })
544
+ cy.dataCy('div')
545
+ .then(($el) => {
546
+ $el[ 0 ].style.height = '100px'
547
+ cy.dataCy('menu')
548
+ .then(($el) => {
549
+ const rect = $el[ 0 ].getBoundingClientRect()
550
+ expect(rect.bottom).to.equal(bottom - 100)
551
+ expect(rect.left).to.equal(left)
552
+ })
553
+ cy.dataCy('wrapper')
554
+ .then(() => {
555
+ Cypress.vueWrapper.vm.updatePosition()
556
+ })
557
+ cy.dataCy('menu')
558
+ .checkVerticalPosition('wrapper', 'bottom', 'bottom')
559
+ .checkHorizontalPosition('wrapper', 'left', 'left')
560
+ })
561
+ })
562
+ })
563
+
564
+ describe('(method): focus', () => {
565
+ it('should focus the menu', () => {
566
+ mount(WrapperOne, {
567
+ attrs: {
568
+ 'no-focus': true
569
+ }
570
+ })
571
+
572
+ cy.dataCy('wrapper')
573
+ .click()
574
+ cy.dataCy('menu')
575
+ .should('exist')
576
+ .should('not.have.focus')
577
+ cy.dataCy('wrapper')
578
+ .then(() => {
579
+ // Need to call method from wrapper
580
+ // Click a button closes the menu
581
+ Cypress.vueWrapper.vm.focusMethod()
582
+ })
583
+ cy.dataCy('menu')
584
+ .should('have.focus')
585
+ })
586
+
587
+ it('should focus the autofocus element inside the menu', () => {
588
+ mount(WrapperTwo, {
589
+ attrs: {
590
+ 'no-focus': true
591
+ }
592
+ })
593
+
594
+ cy.dataCy('wrapper')
595
+ .click()
596
+ cy.dataCy('menu')
597
+ .should('exist')
598
+ .should('not.have.focus')
599
+ cy.dataCy('wrapper')
600
+ .then(() => {
601
+ // Need to call method from wrapper
602
+ // Click a button closes the menu
603
+ Cypress.vueWrapper.vm.focusMethod()
604
+ })
605
+ cy.dataCy('input-2')
606
+ .should('have.focus')
607
+ })
608
+ })
609
+ })
610
+ })
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <div style="margin: 150px auto" class="row justify-center">
3
+ <q-btn color="primary" data-cy="wrapper" label="Wrapper" style="width: 100px;">
4
+ <q-menu v-bind="$attrs" data-cy="menu" ref="menuRef">
5
+ <q-list style="min-width: 120px">
6
+ <q-item clickable v-close-popup data-cy="close-popup">
7
+ <q-item-section>VClosePopup</q-item-section>
8
+ </q-item>
9
+ <q-item clickable data-cy="keep-open">
10
+ <q-item-section>KeepOpen</q-item-section>
11
+ </q-item>
12
+ </q-list>
13
+ </q-menu>
14
+ </q-btn>
15
+ </div>
16
+ <div data-cy="other-target" class="col-12 other-target bg-red q-pa-lg text-white">Other target</div>
17
+ <q-btn class="hidden" data-cy="method-show" @click="show"/>
18
+ <q-btn class="hidden" data-cy="method-hide" @click="hide"/>
19
+ <q-btn class="hidden" data-cy="method-toggle" @click="toggle"/>
20
+ <q-btn class="hidden" data-cy="method-focus" @click="focusMethod"/>
21
+ </template>
22
+
23
+ <script>
24
+ import { defineComponent, ref } from 'vue'
25
+
26
+ export default defineComponent({
27
+ inheritAttrs: false,
28
+ setup () {
29
+ const menuRef = ref(null)
30
+
31
+ function show () {
32
+ menuRef.value.show()
33
+ }
34
+
35
+ function hide () {
36
+ menuRef.value.hide()
37
+ }
38
+
39
+ function toggle () {
40
+ menuRef.value.toggle()
41
+ }
42
+
43
+ function focusMethod () {
44
+ menuRef.value.focus()
45
+ }
46
+
47
+ return { menuRef, show, hide, toggle, focusMethod }
48
+ }
49
+ })
50
+
51
+ </script>
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <div style="margin: 200px auto" class="row justify-center">
3
+ <q-btn color="primary" data-cy="wrapper" label="Wrapper" style="width: 100px;">
4
+ <q-menu v-bind="$attrs" data-cy="menu" ref="menuRef">
5
+ <q-form data-cy="div" style="height: 200px">
6
+ <q-input label="input" data-cy="input"></q-input>
7
+ <q-input label="input" data-cy="input-2" autofocus></q-input>
8
+ </q-form>
9
+ </q-menu>
10
+ </q-btn>
11
+ </div>
12
+ </template>
13
+
14
+ <script>
15
+ import { defineComponent, ref } from 'vue'
16
+
17
+ export default defineComponent({
18
+ inheritAttrs: false,
19
+ setup () {
20
+ const menuRef = ref(null)
21
+
22
+ function toggle () {
23
+ menuRef.value.toggle()
24
+ }
25
+
26
+ function focusMethod () {
27
+ menuRef.value.focus()
28
+ }
29
+
30
+ function updatePosition () {
31
+ menuRef.value.updatePosition()
32
+ }
33
+
34
+ return { menuRef, toggle, focusMethod, updatePosition }
35
+ }
36
+ })
37
+
38
+ </script>