@watermarkinsights/ripple 4.0.0-9 → 4.0.0

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 (217) hide show
  1. package/dist/cjs/{chartFunctions-33cb3097.js → chartFunctions-f5ded027.js} +1 -1
  2. package/dist/cjs/{functions-833ccc83.js → functions-e7db4a26.js} +67 -824
  3. package/dist/cjs/{global-d57c118b.js → global-b33cf49b.js} +1 -1
  4. package/dist/cjs/{intl-ab07bd0b.js → intl-9ef93563.js} +3 -3
  5. package/dist/cjs/loader.cjs.js +3 -3
  6. package/dist/cjs/priv-chart-popover.cjs.entry.js +1 -1
  7. package/dist/cjs/priv-datepicker.cjs.entry.js +1 -1
  8. package/dist/cjs/ripple.cjs.js +3 -3
  9. package/dist/cjs/wm-action-menu_2.cjs.entry.js +2 -2
  10. package/dist/cjs/wm-button.cjs.entry.js +1 -1
  11. package/dist/cjs/wm-chart.cjs.entry.js +4 -4
  12. package/dist/cjs/wm-datepicker.cjs.entry.js +1 -1
  13. package/dist/cjs/wm-file.cjs.entry.js +7 -4
  14. package/dist/cjs/wm-input.cjs.entry.js +4 -9
  15. package/dist/cjs/wm-line-chart.cjs.entry.js +3 -3
  16. package/dist/cjs/wm-modal-header.cjs.entry.js +2 -2
  17. package/dist/cjs/wm-modal.cjs.entry.js +1 -1
  18. package/dist/cjs/wm-navigation_3.cjs.entry.js +2 -2
  19. package/dist/cjs/wm-navigator.cjs.entry.js +1 -1
  20. package/dist/cjs/wm-option_2.cjs.entry.js +68 -119
  21. package/dist/cjs/wm-pagination.cjs.entry.js +1 -1
  22. package/dist/cjs/wm-progress-indicator_3.cjs.entry.js +4 -4
  23. package/dist/cjs/wm-search.cjs.entry.js +2 -2
  24. package/dist/cjs/wm-snackbar.cjs.entry.js +2 -2
  25. package/dist/cjs/wm-tab-item_3.cjs.entry.js +2 -2
  26. package/dist/cjs/wm-tag-input.cjs.entry.js +521 -617
  27. package/dist/cjs/wm-tag-option.cjs.entry.js +43 -0
  28. package/dist/cjs/wm-timepicker.cjs.entry.js +1 -1
  29. package/dist/cjs/wm-toggletip.cjs.entry.js +1 -1
  30. package/dist/cjs/wm-uploader.cjs.entry.js +2 -2
  31. package/dist/collection/collection-manifest.json +3 -3
  32. package/dist/collection/components/charts/wm-chart/wm-chart.css +0 -1
  33. package/dist/collection/components/charts/wm-progress-monitor/wm-progress-indicator.css +0 -1
  34. package/dist/collection/components/charts/wm-progress-monitor/wm-progress-monitor.css +9 -1
  35. package/dist/collection/components/wm-file/wm-file.js +23 -3
  36. package/dist/collection/components/wm-file/wm-file.spec.js +83 -34
  37. package/dist/collection/components/wm-file-list/wm-file-list.spec.js +32 -59
  38. package/dist/collection/components/wm-input/wm-input.js +1 -8
  39. package/dist/collection/components/wm-menuitem/wm-menuitem.js +1 -1
  40. package/dist/collection/components/wm-option/wm-option.css +6 -6
  41. package/dist/collection/components/wm-option/wm-option.js +47 -3
  42. package/dist/collection/components/wm-select/wm-select.e2e.js +60 -114
  43. package/dist/collection/components/wm-select/wm-select.js +80 -141
  44. package/dist/collection/components/wm-select/wm-select.spec.js +9 -11
  45. package/dist/collection/components/wm-tabs/wm-tab-list/wm-tab-list.css +0 -1
  46. package/dist/collection/components/wm-tag-input/wm-tag-input.css +19 -22
  47. package/dist/collection/components/wm-tag-input/wm-tag-input.e2e.js +3 -3
  48. package/dist/collection/components/wm-tag-input/wm-tag-input.js +597 -726
  49. package/dist/collection/components/wm-tag-input/wm-tag-option/wm-tag-option.js +241 -0
  50. package/dist/collection/components/wm-uploader/wm-uploader.e2e.js +26 -47
  51. package/dist/collection/components/wm-uploader/wm-uploader.js +3 -3
  52. package/dist/collection/components/wm-uploader/wm-uploader.spec.js +67 -140
  53. package/dist/collection/global/functions.js +22 -10
  54. package/dist/collection/global/intl.js +2 -2
  55. package/dist/collection/lang/lang.spec.js +2 -2
  56. package/dist/esm/{chartFunctions-36eb2d1a.js → chartFunctions-2a4603c6.js} +1 -1
  57. package/dist/esm/{functions-061ab506.js → functions-358a1a02.js} +66 -824
  58. package/dist/esm/{global-509460f7.js → global-ba03a879.js} +1 -1
  59. package/dist/esm/{intl-d698d52f.js → intl-48057c4d.js} +3 -3
  60. package/dist/esm/loader.js +3 -3
  61. package/dist/esm/polyfills/core-js.js +0 -0
  62. package/dist/esm/polyfills/dom.js +0 -0
  63. package/dist/esm/polyfills/es5-html-element.js +0 -0
  64. package/dist/esm/polyfills/index.js +0 -0
  65. package/dist/esm/polyfills/system.js +0 -0
  66. package/dist/esm/priv-chart-popover.entry.js +1 -1
  67. package/dist/esm/priv-datepicker.entry.js +1 -1
  68. package/dist/esm/ripple.js +3 -3
  69. package/dist/esm/wm-action-menu_2.entry.js +2 -2
  70. package/dist/esm/wm-button.entry.js +1 -1
  71. package/dist/esm/wm-chart.entry.js +4 -4
  72. package/dist/esm/wm-datepicker.entry.js +1 -1
  73. package/dist/esm/wm-file.entry.js +7 -4
  74. package/dist/esm/wm-input.entry.js +4 -9
  75. package/dist/esm/wm-line-chart.entry.js +3 -3
  76. package/dist/esm/wm-modal-header.entry.js +2 -2
  77. package/dist/esm/wm-modal.entry.js +1 -1
  78. package/dist/esm/wm-navigation_3.entry.js +2 -2
  79. package/dist/esm/wm-navigator.entry.js +1 -1
  80. package/dist/esm/wm-option_2.entry.js +68 -119
  81. package/dist/esm/wm-pagination.entry.js +1 -1
  82. package/dist/esm/wm-progress-indicator_3.entry.js +4 -4
  83. package/dist/esm/wm-search.entry.js +2 -2
  84. package/dist/esm/wm-snackbar.entry.js +2 -2
  85. package/dist/esm/wm-tab-item_3.entry.js +2 -2
  86. package/dist/esm/wm-tag-input.entry.js +522 -618
  87. package/dist/esm/wm-tag-option.entry.js +39 -0
  88. package/dist/esm/wm-timepicker.entry.js +1 -1
  89. package/dist/esm/wm-toggletip.entry.js +1 -1
  90. package/dist/esm/wm-uploader.entry.js +2 -2
  91. package/dist/esm-es5/{chartFunctions-36eb2d1a.js → chartFunctions-2a4603c6.js} +1 -1
  92. package/dist/esm-es5/functions-358a1a02.js +1 -0
  93. package/dist/esm-es5/global-ba03a879.js +1 -0
  94. package/dist/esm-es5/{intl-d698d52f.js → intl-48057c4d.js} +1 -1
  95. package/dist/esm-es5/loader.js +1 -1
  96. package/dist/esm-es5/priv-chart-popover.entry.js +1 -1
  97. package/dist/esm-es5/priv-datepicker.entry.js +1 -1
  98. package/dist/esm-es5/ripple.js +1 -1
  99. package/dist/esm-es5/wm-action-menu_2.entry.js +1 -1
  100. package/dist/esm-es5/wm-button.entry.js +1 -1
  101. package/dist/esm-es5/wm-chart.entry.js +1 -1
  102. package/dist/esm-es5/wm-datepicker.entry.js +1 -1
  103. package/dist/esm-es5/wm-file.entry.js +1 -1
  104. package/dist/esm-es5/wm-input.entry.js +1 -1
  105. package/dist/esm-es5/wm-line-chart.entry.js +1 -1
  106. package/dist/esm-es5/wm-modal-header.entry.js +1 -1
  107. package/dist/esm-es5/wm-modal.entry.js +1 -1
  108. package/dist/esm-es5/wm-navigation_3.entry.js +1 -1
  109. package/dist/esm-es5/wm-navigator.entry.js +1 -1
  110. package/dist/esm-es5/wm-option_2.entry.js +1 -1
  111. package/dist/esm-es5/wm-pagination.entry.js +1 -1
  112. package/dist/esm-es5/wm-progress-indicator_3.entry.js +1 -1
  113. package/dist/esm-es5/wm-search.entry.js +1 -1
  114. package/dist/esm-es5/wm-snackbar.entry.js +1 -1
  115. package/dist/esm-es5/wm-tab-item_3.entry.js +1 -1
  116. package/dist/esm-es5/wm-tag-input.entry.js +1 -1
  117. package/dist/esm-es5/wm-tag-option.entry.js +1 -0
  118. package/dist/esm-es5/wm-timepicker.entry.js +1 -1
  119. package/dist/esm-es5/wm-toggletip.entry.js +1 -1
  120. package/dist/esm-es5/wm-uploader.entry.js +1 -1
  121. package/dist/ripple/{p-3f159fa3.entry.js → p-05ef4092.entry.js} +1 -1
  122. package/dist/ripple/p-11d629cb.system.entry.js +1 -0
  123. package/dist/ripple/p-126fbcdb.entry.js +1 -0
  124. package/dist/ripple/p-12a140e0.system.entry.js +1 -0
  125. package/dist/ripple/{p-e61e2d7f.entry.js → p-191fafc6.entry.js} +1 -1
  126. package/dist/ripple/p-1ab62a21.system.entry.js +1 -0
  127. package/dist/ripple/{p-d8287161.entry.js → p-1e7e2ca4.entry.js} +1 -1
  128. package/dist/ripple/{p-fef28649.system.entry.js → p-1ee49e28.system.entry.js} +1 -1
  129. package/dist/ripple/{p-e82eae12.entry.js → p-299bf10c.entry.js} +1 -1
  130. package/dist/ripple/{p-c20c248a.entry.js → p-2d6bb6d7.entry.js} +1 -1
  131. package/dist/ripple/{p-a31e736a.entry.js → p-366a9608.entry.js} +1 -1
  132. package/dist/ripple/p-3a20b1ed.system.entry.js +1 -0
  133. package/dist/ripple/p-3bb79457.entry.js +1 -0
  134. package/dist/ripple/{p-1f7a67cc.system.js → p-426fa249.system.js} +1 -1
  135. package/dist/ripple/p-44d4705c.system.js +1 -0
  136. package/dist/ripple/p-492dd748.system.entry.js +1 -0
  137. package/dist/ripple/p-4aa8e2cf.entry.js +1 -0
  138. package/dist/ripple/{p-ff891d67.js → p-52f5ec85.js} +1 -1
  139. package/dist/ripple/{p-484d57e1.entry.js → p-546d5c1d.entry.js} +1 -1
  140. package/dist/ripple/{p-d231aed1.system.entry.js → p-585732f7.system.entry.js} +1 -1
  141. package/dist/ripple/p-6767b009.system.js +1 -0
  142. package/dist/ripple/{p-c6ba5d3d.system.entry.js → p-681c9539.system.entry.js} +1 -1
  143. package/dist/ripple/{p-d2c9264d.entry.js → p-68cade03.entry.js} +1 -1
  144. package/dist/ripple/{p-260fd686.system.entry.js → p-6c27afee.system.entry.js} +1 -1
  145. package/dist/ripple/{p-d108107c.entry.js → p-7740db9a.entry.js} +1 -1
  146. package/dist/ripple/{p-055d1c23.system.entry.js → p-7d005413.system.entry.js} +1 -1
  147. package/dist/ripple/{p-c9830db6.system.entry.js → p-7e2c2c46.system.entry.js} +1 -1
  148. package/dist/ripple/p-7fa84884.system.entry.js +1 -0
  149. package/dist/ripple/{p-9a3d8f0b.system.entry.js → p-8b143e9d.system.entry.js} +1 -1
  150. package/dist/ripple/{p-0790bfed.entry.js → p-8ea235b6.entry.js} +1 -1
  151. package/dist/ripple/{p-4eae76a6.entry.js → p-8fadf5dd.entry.js} +1 -1
  152. package/dist/ripple/{p-8df34bf3.system.entry.js → p-94c65a69.system.entry.js} +1 -1
  153. package/dist/ripple/{p-3bd6839a.entry.js → p-9690de6c.entry.js} +1 -1
  154. package/dist/ripple/p-acb0156f.system.entry.js +1 -0
  155. package/dist/ripple/p-ae7290c2.entry.js +1 -0
  156. package/dist/ripple/{p-030b527a.js → p-aea9a33a.js} +1 -1
  157. package/dist/ripple/p-b6e5408c.js +1 -0
  158. package/dist/ripple/p-b75c0973.system.js +1 -0
  159. package/dist/ripple/{p-21f73fee.system.entry.js → p-b858d526.system.entry.js} +1 -1
  160. package/dist/ripple/{p-40b5b7d1.system.entry.js → p-b92c2e16.system.entry.js} +1 -1
  161. package/dist/ripple/p-be79e95d.entry.js +1 -0
  162. package/dist/ripple/{p-b623fdc8.entry.js → p-bfff12b4.entry.js} +1 -1
  163. package/dist/ripple/{p-68d7cf2b.entry.js → p-c028f29c.entry.js} +1 -1
  164. package/dist/ripple/p-c19ed569.entry.js +1 -0
  165. package/dist/ripple/{p-f42031f5.system.js → p-c3da681d.system.js} +1 -1
  166. package/dist/ripple/{p-9b94467e.entry.js → p-c5105455.entry.js} +1 -1
  167. package/dist/ripple/{p-15457a4b.system.entry.js → p-c86a7f4d.system.entry.js} +1 -1
  168. package/dist/ripple/{p-b9283910.entry.js → p-db58d96b.entry.js} +1 -1
  169. package/dist/ripple/p-dd92850a.js +1 -0
  170. package/dist/ripple/p-e39e6c2b.entry.js +1 -0
  171. package/dist/ripple/{p-a8ea87d1.system.entry.js → p-ec831e59.system.entry.js} +1 -1
  172. package/dist/ripple/{p-f1029090.system.entry.js → p-ee51efe0.system.entry.js} +1 -1
  173. package/dist/ripple/{p-777ced5b.entry.js → p-eec01bbe.entry.js} +1 -1
  174. package/dist/ripple/{p-5ed1b0a2.system.entry.js → p-f339d590.system.entry.js} +1 -1
  175. package/dist/ripple/{p-5b593411.system.entry.js → p-f3407959.system.entry.js} +1 -1
  176. package/dist/ripple/{p-da727af8.system.entry.js → p-f3a374ff.system.entry.js} +1 -1
  177. package/dist/ripple/{p-867b20a9.system.entry.js → p-f43fda55.system.entry.js} +1 -1
  178. package/dist/ripple/ripple.esm.js +1 -1
  179. package/dist/ripple/ripple.js +1 -1
  180. package/dist/types/components/wm-file/wm-file.d.ts +1 -1
  181. package/dist/types/components/wm-input/wm-input.d.ts +0 -1
  182. package/dist/types/components/wm-option/wm-option.d.ts +2 -0
  183. package/dist/types/components/wm-select/wm-select.d.ts +7 -7
  184. package/dist/types/components/wm-tag-input/wm-tag-input.d.ts +65 -85
  185. package/dist/types/components/wm-tag-input/wm-tag-option/wm-tag-option.d.ts +18 -0
  186. package/dist/types/components/wm-uploader/wm-uploader.d.ts +1 -1
  187. package/dist/types/components.d.ts +30 -27
  188. package/dist/types/global/functions.d.ts +2 -1
  189. package/dist/types/global/intl.d.ts +2 -2
  190. package/package.json +1 -1
  191. package/dist/cjs/wm-tag-input-row.cjs.entry.js +0 -23
  192. package/dist/collection/components/wm-tag-input/wm-tag-input-row/wm-tag-input-row.js +0 -122
  193. package/dist/collection/components/wm-tag-input/wm-tag-input.spec.js +0 -1039
  194. package/dist/esm/wm-tag-input-row.entry.js +0 -19
  195. package/dist/esm-es5/functions-061ab506.js +0 -1
  196. package/dist/esm-es5/global-509460f7.js +0 -1
  197. package/dist/esm-es5/wm-tag-input-row.entry.js +0 -1
  198. package/dist/ripple/p-1c3ba701.system.entry.js +0 -1
  199. package/dist/ripple/p-4a8c95b9.system.entry.js +0 -1
  200. package/dist/ripple/p-5f2c09f6.entry.js +0 -1
  201. package/dist/ripple/p-647a4a4a.system.entry.js +0 -1
  202. package/dist/ripple/p-7011accc.entry.js +0 -1
  203. package/dist/ripple/p-707383d5.system.js +0 -1
  204. package/dist/ripple/p-7c2e47bc.system.entry.js +0 -1
  205. package/dist/ripple/p-839d7e0f.system.js +0 -1
  206. package/dist/ripple/p-928cc755.system.entry.js +0 -1
  207. package/dist/ripple/p-9888c825.js +0 -1
  208. package/dist/ripple/p-a5308115.js +0 -1
  209. package/dist/ripple/p-b45a2fc3.entry.js +0 -1
  210. package/dist/ripple/p-b4b57baf.system.entry.js +0 -1
  211. package/dist/ripple/p-c15f29e5.system.js +0 -1
  212. package/dist/ripple/p-d38882eb.entry.js +0 -1
  213. package/dist/ripple/p-d601c5a1.entry.js +0 -1
  214. package/dist/ripple/p-d68678d2.entry.js +0 -1
  215. package/dist/ripple/p-e703d9cd.entry.js +0 -1
  216. package/dist/ripple/p-eb0d569a.system.entry.js +0 -1
  217. package/dist/types/components/wm-tag-input/wm-tag-input-row/wm-tag-input-row.d.ts +0 -11
@@ -1,1039 +0,0 @@
1
- import { newSpecPage } from "@stencil/core/testing";
2
- import { TagInput } from "./wm-tag-input";
3
- import * as globalFunctions from "../../global/functions";
4
- // mock ResizeObserver
5
- global.ResizeObserver = jest.fn().mockImplementation(() => ({
6
- observe: jest.fn(),
7
- unobserve: jest.fn(),
8
- disconnect: jest.fn(),
9
- }));
10
- // mockComputedStyle, component measures the placeholder on render to properly position the input
11
- global.getComputedStyle = jest
12
- .fn()
13
- .mockImplementation(() => ({ paddingLeft: "0px", paddingRight: "0px", marginLeft: "0px", marginRight: "0px" }));
14
- describe("taginput", () => {
15
- let page;
16
- beforeEach(async () => {
17
- page = await newSpecPage({
18
- components: [TagInput],
19
- html: `<wm-tag-input id="input" label="label" options="one,two,three,four"></wm-tag-input>`,
20
- });
21
- });
22
- it("builds", async () => {
23
- expect(page.root).toMatchSnapshot();
24
- });
25
- it("has right aria roles", async () => {
26
- page = await newSpecPage({
27
- components: [TagInput],
28
- html: `<wm-tag-input label="label" options="one,two,three,four" selected-tags="one,three" info="info text"></wm-tag-input>`,
29
- });
30
- const label = await page.root.shadowRoot.querySelector("label");
31
- expect(label.textContent).toBe("label");
32
- const input = await page.root.shadowRoot.querySelector("input");
33
- expect(input).toEqualAttribute("role", "combobox");
34
- expect(input).toEqualAttribute("aria-label", "label 50 characters allowed.");
35
- const helpText = await page.root.shadowRoot.querySelector(".help-text");
36
- expect(input).toEqualAttribute("aria-describedby", helpText.id);
37
- const dropdown = await page.root.shadowRoot.querySelector(".dropdown");
38
- expect(dropdown).toEqualAttribute("role", "listbox");
39
- expect(dropdown).toHaveAttribute("aria-multiselectable");
40
- const options = await page.root.shadowRoot.querySelectorAll(".option");
41
- options.forEach((option) => {
42
- expect(option).toEqualAttribute("role", "option");
43
- });
44
- const tagArea = await page.root.shadowRoot.querySelector(".tag-area");
45
- expect(tagArea).toEqualAttribute("role", "listbox");
46
- expect(tagArea).toEqualAttribute("aria-label", "label tag selection. Press Backspace or Delete to remove a tag.");
47
- expect(tagArea).toEqualAttribute("aria-describedby", "info max-tags");
48
- expect(tagArea).toEqualAttribute("aria-orientation", "horizontal");
49
- const tags = await page.root.shadowRoot.querySelectorAll(".tag");
50
- tags.forEach((tag) => {
51
- expect(tag).toEqualAttribute("role", "option");
52
- });
53
- });
54
- it("changes help text when options are not provided", async () => {
55
- // not editable with options
56
- page = await newSpecPage({
57
- components: [TagInput],
58
- html: `<wm-tag-input label="label" add-new="false" options="one,two,three"></wm-tag-input>`,
59
- });
60
- let helpText = await page.root.shadowRoot.querySelector(".help-text");
61
- expect(helpText.textContent).toBe("Search and select a tag.");
62
- // editable with options
63
- page = await newSpecPage({
64
- components: [TagInput],
65
- html: `<wm-tag-input label="label" options="one,two,three"></wm-tag-input>`,
66
- });
67
- helpText = await page.root.shadowRoot.querySelector(".help-text");
68
- expect(helpText.textContent).toBe("Search and select a tag. Press the Enter or Comma key to add a new tag.");
69
- // editable without options
70
- page = await newSpecPage({
71
- components: [TagInput],
72
- html: `<wm-tag-input label="label"></wm-tag-input>`,
73
- });
74
- helpText = await page.root.shadowRoot.querySelector(".help-text");
75
- expect(helpText.textContent).toBe(" Press the Enter or Comma key to add a new tag.");
76
- });
77
- it("shows the appropriate default placeholder text", async () => {
78
- // editable with options
79
- await page.setContent(`<wm-tag-input label="label" options="one,two"></wm-tag-input>`);
80
- let input = await page.root.shadowRoot.querySelector("input");
81
- expect(input).toEqualAttribute("placeholder", "Add or search for a tag");
82
- // editable without options
83
- page = await newSpecPage({
84
- components: [TagInput],
85
- html: `<wm-tag-input label="label"></wm-tag-input>`,
86
- });
87
- input = await page.root.shadowRoot.querySelector("input");
88
- expect(input).toEqualAttribute("placeholder", "Add a new tag");
89
- // not editable
90
- page = await newSpecPage({
91
- components: [TagInput],
92
- html: `<wm-tag-input label="label" add-new="false" options="one,two,three"></wm-tag-input>`,
93
- });
94
- input = await page.root.shadowRoot.querySelector("input");
95
- expect(input).toEqualAttribute("placeholder", "Search and select a tag");
96
- });
97
- it("shows options in original case", async () => {
98
- await page.setContent('<wm-tag-input label="label" options="one two, Three four, five Six, sEVen, eight nine ten" selected-tags="one two, Three four, five Six, sEVen,eight nine ten"></wm-tag-input>');
99
- const tags = page.root.shadowRoot.querySelectorAll(".tag");
100
- expect(tags[0].dataset.tag).toBe("one two");
101
- expect(tags[1].dataset.tag).toBe("Three four");
102
- expect(tags[2].dataset.tag).toBe("five Six");
103
- expect(tags[3].dataset.tag).toBe("sEVen");
104
- expect(tags[4].dataset.tag).toBe("eight nine ten");
105
- });
106
- it("alphabetizes list items", async () => {
107
- await page.setContent('<wm-tag-input label="label" options="C, B, E, D, F, A"></wm-tag-input>');
108
- // List items appear in alphabetical orderconst listItems = page.root!.shadowRoot!.querySelectorAll(".option");
109
- const listItems = page.root.shadowRoot.querySelectorAll(".option");
110
- expect(listItems[0].textContent).toBe("A");
111
- expect(listItems[1].textContent).toBe("B");
112
- expect(listItems[2].textContent).toBe("C");
113
- expect(listItems[3].textContent).toBe("D");
114
- expect(listItems[4].textContent).toBe("E");
115
- expect(listItems[5].textContent).toBe("F");
116
- });
117
- it("handles different casing of identical options", async () => {
118
- await page.setContent(`<wm-tag-input label="label" options="one,two,three" selected-tags="oNE,tWO,thREe"></wm-tag-input>`);
119
- const listItems = page.root.shadowRoot.querySelectorAll(".option");
120
- const tags = page.root.shadowRoot.querySelectorAll(".tag");
121
- expect(listItems.length).toBe(3);
122
- expect(tags.length).toBe(3);
123
- });
124
- it("consolidates pre-selected tags into the options", async () => {
125
- await page.setContent(`<wm-tag-input label="label" selected-tags="a,b,c"></wm-tag-input>`);
126
- const listItems = page.root.shadowRoot.querySelectorAll(".option");
127
- expect(listItems.length).toBe(3);
128
- expect(page.root.options).toBe("a,b,c");
129
- expect(listItems[0].textContent).toBe("a");
130
- expect(listItems[1].textContent).toBe("b");
131
- expect(listItems[2].textContent).toBe("c");
132
- });
133
- it("reflects selected tags", async () => {
134
- const component = new TagInput();
135
- const mockListToCSV = (component.listToCSV = jest.fn());
136
- const mockWmTagInputChanged = (component.wmTagInputChanged.emit = jest.fn());
137
- const tagsList = ["one", "two", "three"];
138
- component.tagsList = tagsList;
139
- component.reflectSelectedTags(tagsList, ["one", "two"]);
140
- expect(mockListToCSV).toHaveBeenCalledTimes(1);
141
- expect(mockListToCSV).toHaveBeenLastCalledWith(tagsList);
142
- expect(mockWmTagInputChanged).toHaveBeenCalledTimes(1);
143
- expect(mockWmTagInputChanged).toHaveBeenLastCalledWith({
144
- value: tagsList,
145
- tagChanged: "three",
146
- });
147
- jest.restoreAllMocks();
148
- });
149
- it("checks case insensitive list inclusion", async () => {
150
- // includesCaseInsensitive
151
- const component = new TagInput();
152
- const testArray = ["One", "TWO", "three", "fOUr and FIve", "s I x"];
153
- expect(component.includesCaseInsensitive(testArray, "one")).toBe(true);
154
- expect(component.includesCaseInsensitive(testArray, "oNe")).toBe(true);
155
- expect(component.includesCaseInsensitive(testArray, "seven")).toBe(false);
156
- });
157
- it("filters case insensitive strings from arrays", async () => {
158
- // filterCaseInsensitive
159
- const component = new TagInput();
160
- const testArray = ["One", "TWO", "three", "fOUr and FIve", "s I x"];
161
- const answers = [
162
- ["TWO", "three", "fOUr and FIve", "s I x"],
163
- ["One", "three", "fOUr and FIve", "s I x"],
164
- ["One", "TWO", "fOUr and FIve", "s I x"],
165
- ["One", "TWO", "three", "s I x"],
166
- ["One", "TWO", "three", "fOUr and FIve"],
167
- ];
168
- testArray.forEach((str, idx) => {
169
- const lowercaseWord = str.toLowerCase();
170
- expect(component.filterCaseInsensitive(testArray, lowercaseWord)).toStrictEqual(answers[idx]);
171
- });
172
- });
173
- it("throws error if no label prop is passed", async () => {
174
- const mockConsoleError = (console.error = jest.fn());
175
- await page.setContent(`<wm-tag-input></wm-tag-input>`);
176
- expect(mockConsoleError).toHaveBeenCalledWith("wm-tag-input must have a label property");
177
- jest.restoreAllMocks();
178
- });
179
- describe("handles user keys", () => {
180
- describe("handles input keys", () => {
181
- afterEach(() => {
182
- jest.restoreAllMocks();
183
- });
184
- it("handles input key down", async () => {
185
- // handleInputKeyDown
186
- const component = new TagInput();
187
- const mockPreventDefault = jest.fn();
188
- const mockStopPropagation = jest.fn();
189
- const event = {
190
- key: "",
191
- preventDefault: mockPreventDefault,
192
- stopPropagation: mockStopPropagation,
193
- };
194
- //@ts-ignore
195
- component.inputEl = { value: "new tag" };
196
- // Enter
197
- const mockHandleInputSubmit = (component.submitInput = jest.fn());
198
- event.key = "Enter";
199
- component.handleInputKeyDown(event);
200
- expect(mockStopPropagation).toHaveBeenCalledTimes(1);
201
- expect(mockHandleInputSubmit).toHaveBeenCalledTimes(1);
202
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
203
- jest.clearAllMocks();
204
- // mock listItemEls getter
205
- jest.spyOn(component, "listItemEls", "get").mockReturnValue([{}]);
206
- // ArrowDown
207
- const mockHandleInputArrowDown = (component.handleInputArrowDown = jest.fn());
208
- event.key = "ArrowDown";
209
- component.handleInputKeyDown(event);
210
- expect(mockHandleInputArrowDown).toHaveBeenCalledTimes(1);
211
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
212
- jest.clearAllMocks();
213
- // ArrowUp
214
- const mockHandleInputArrowUp = (component.handleInputArrowUp = jest.fn());
215
- event.key = "ArrowUp";
216
- component.handleInputKeyDown(event);
217
- expect(mockHandleInputArrowUp).toHaveBeenCalledTimes(1);
218
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
219
- jest.clearAllMocks();
220
- // Escape
221
- const spyCloseDropdown = jest.spyOn(component, "closeDropdown");
222
- jest.spyOn(component, "clearListItemFocus").mockImplementation(() => { });
223
- event.key = "Escape";
224
- component.isExpanded = true;
225
- component.handleInputKeyDown(event);
226
- expect(spyCloseDropdown).toHaveBeenCalledTimes(1);
227
- jest.clearAllMocks();
228
- // Backspace
229
- const mockHandleInputBackspace = (component.handleInputBackspace = jest.fn());
230
- event.key = "Backspace";
231
- component.handleInputKeyDown(event);
232
- expect(mockHandleInputBackspace).toHaveBeenCalledTimes(1);
233
- jest.restoreAllMocks();
234
- });
235
- it("handles input change", async () => {
236
- // handleInputChanged
237
- const component = new TagInput();
238
- const mockClearListItemFocus = (component.clearListItemFocus = jest.fn());
239
- const mockGenerateCharacterLimitWarning = (component.generateCharacterLimitWarning = jest.fn());
240
- component.announce = jest.fn();
241
- const mockAnnounceExistingOptions = (component.announceExistingOptions = jest.fn());
242
- const mockOpenDropdown = (component.openDropdown = jest.fn());
243
- component.handleInputChanged("word");
244
- expect(component.charCount).toBe("word".length);
245
- expect(mockClearListItemFocus).toHaveBeenCalledTimes(1);
246
- expect(mockOpenDropdown).toHaveBeenCalledTimes(1);
247
- expect(mockAnnounceExistingOptions).toHaveBeenCalledTimes(1);
248
- expect(mockGenerateCharacterLimitWarning).toHaveBeenCalledTimes(0);
249
- jest.clearAllMocks();
250
- component.characterLimit = 3;
251
- component.isExpanded = true;
252
- component.handleInputChanged("more than 3 characters");
253
- expect(mockClearListItemFocus).toHaveBeenCalledTimes(1);
254
- expect(mockOpenDropdown).toHaveBeenCalledTimes(0);
255
- expect(mockAnnounceExistingOptions).toHaveBeenCalledTimes(1);
256
- expect(mockGenerateCharacterLimitWarning).toHaveBeenCalledTimes(1);
257
- jest.restoreAllMocks();
258
- });
259
- it("handles input arrow down", async () => {
260
- // handleInputArrowDown
261
- const component = new TagInput();
262
- const mockOpenDropdown = (component.openDropdown = jest.fn());
263
- const mockMoveDownListItem = (component.moveDownListItem = jest.fn());
264
- component.isExpanded = false;
265
- component.handleInputArrowDown();
266
- expect(mockOpenDropdown).toHaveBeenCalledTimes(1);
267
- expect(mockMoveDownListItem).toHaveBeenCalledTimes(0);
268
- jest.clearAllMocks();
269
- component.isExpanded = true;
270
- component.handleInputArrowDown();
271
- expect(mockOpenDropdown).toHaveBeenCalledTimes(0);
272
- expect(mockMoveDownListItem).toHaveBeenCalledTimes(1);
273
- jest.restoreAllMocks();
274
- });
275
- it("handles input arrow up", async () => {
276
- // handleInputArrowUp
277
- const component = new TagInput();
278
- const mockOpenDropdown = (component.openDropdown = jest.fn());
279
- const mockMoveUpListItem = (component.moveUpListItem = jest.fn());
280
- component.isExpanded = false;
281
- component.handleInputArrowUp();
282
- expect(mockOpenDropdown).toHaveBeenCalledTimes(1);
283
- expect(mockMoveUpListItem).toHaveBeenCalledTimes(0);
284
- jest.clearAllMocks();
285
- component.isExpanded = true;
286
- component.handleInputArrowUp();
287
- expect(mockOpenDropdown).toHaveBeenCalledTimes(0);
288
- expect(mockMoveUpListItem).toHaveBeenCalledTimes(1);
289
- jest.restoreAllMocks();
290
- });
291
- it("handles input submit", async () => {
292
- // submitInput
293
- const component = new TagInput();
294
- const mockAddTag = (component.addTag = jest.fn(() => ["first", "second"]));
295
- const mockAddOption = (component.addOption = jest.fn());
296
- const mockResetInput = (component.resetInput = jest.fn());
297
- let canAddNew = true;
298
- component.submitInput(canAddNew, "new tag");
299
- expect(mockAddTag).toHaveBeenCalledTimes(1);
300
- expect(mockAddOption).toHaveBeenCalledTimes(1);
301
- expect(mockResetInput).toHaveBeenCalledTimes(1);
302
- expect((component.charCount = 0));
303
- jest.clearAllMocks();
304
- component.submitInput(canAddNew, "");
305
- expect(mockAddTag).toHaveBeenCalledTimes(0);
306
- expect(mockAddOption).toHaveBeenCalledTimes(0);
307
- expect(mockResetInput).toHaveBeenCalledTimes(0);
308
- jest.clearAllMocks();
309
- component.maxTags = 3;
310
- component.submitInput(canAddNew, "new tag");
311
- expect(mockAddTag).toHaveBeenCalledTimes(1);
312
- expect(mockAddOption).toHaveBeenCalledTimes(1);
313
- expect(mockResetInput).toHaveBeenCalledTimes(1);
314
- jest.clearAllMocks();
315
- component.maxTags = 2;
316
- component.submitInput(canAddNew, "new tag");
317
- expect(mockAddTag).toHaveBeenCalledTimes(1);
318
- expect(mockAddOption).toHaveBeenCalledTimes(1);
319
- expect(mockResetInput).toHaveBeenCalledTimes(1);
320
- jest.clearAllMocks();
321
- canAddNew = false;
322
- component.submitInput(canAddNew, "new tag");
323
- expect(mockAddTag).toHaveBeenCalledTimes(0);
324
- expect(mockAddOption).toHaveBeenCalledTimes(0);
325
- expect(mockResetInput).toHaveBeenCalledTimes(0);
326
- jest.restoreAllMocks();
327
- });
328
- it("handles input backspace", async () => {
329
- // handleInputBackspace
330
- const component = new TagInput();
331
- const mockFocus = jest.fn();
332
- const tagAreaEl = { focus: mockFocus };
333
- jest.spyOn(component, "focusTag").mockImplementation(() => { });
334
- const mockRequestAnimationFrame = jest
335
- .spyOn(window, "requestAnimationFrame")
336
- //@ts-ignore
337
- .mockImplementation((cb) => cb());
338
- jest.spyOn(component, "tagEls", "get").mockReturnValue([{}]);
339
- //@ts-ignore
340
- component.tagAreaEl = tagAreaEl;
341
- //@ts-ignore
342
- component.inputEl = { value: "new tag" };
343
- component.tagsList = ["one", "two"];
344
- component.handleInputBackspace();
345
- expect(mockRequestAnimationFrame).toHaveBeenCalledTimes(0);
346
- expect(mockFocus).toHaveBeenCalledTimes(0);
347
- //@ts-ignore
348
- component.inputEl = { value: "" };
349
- component.handleInputBackspace();
350
- expect(mockRequestAnimationFrame).toHaveBeenCalledTimes(1);
351
- expect(mockFocus).toHaveBeenCalledTimes(1);
352
- jest.restoreAllMocks();
353
- });
354
- });
355
- describe("handles list item keys", () => {
356
- afterEach(() => {
357
- jest.restoreAllMocks();
358
- });
359
- it("handles list item click", async () => {
360
- // handleListItemClick
361
- const component = new TagInput();
362
- let fooEl = document.createElement("li");
363
- fooEl.dataset.option = "two";
364
- //@ts-ignore
365
- const event = { target: fooEl };
366
- const mockAnnounce = (component.announce = jest.fn());
367
- const mockResetInput = (component.resetInput = jest.fn());
368
- const mockAddTag = (component.addTag = jest.fn());
369
- const mockAddOption = (component.addOption = jest.fn());
370
- component.tagsList = ["one", "two"];
371
- component.handleListItemClick(event.target);
372
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
373
- expect(mockAnnounce).toHaveBeenLastCalledWith("two has already been added.");
374
- expect(mockResetInput).toHaveBeenCalledTimes(0);
375
- expect(mockAddTag).toHaveBeenCalledTimes(0);
376
- expect(mockAddOption).toHaveBeenCalledTimes(0);
377
- jest.clearAllMocks();
378
- component.tagsList = ["one"];
379
- component.handleListItemClick(event.target);
380
- expect(mockAnnounce).toHaveBeenCalledTimes(0);
381
- expect(mockResetInput).toHaveBeenCalledTimes(1);
382
- expect(mockAddTag).toHaveBeenCalledTimes(1);
383
- expect(mockAddOption).toHaveBeenCalledTimes(1);
384
- expect(component.focusedElement).toBeUndefined();
385
- jest.restoreAllMocks();
386
- });
387
- it("handles list item key down", async () => {
388
- // handleListItemKeyDown
389
- const component = new TagInput();
390
- const mockTargetClick = jest.fn();
391
- const evTarget = { click: mockTargetClick };
392
- const mockMoveDownListItem = (component.moveDownListItem = jest.fn());
393
- const mockMoveUpListItem = (component.moveUpListItem = jest.fn());
394
- const mockFocus = jest.fn();
395
- //@ts-ignore
396
- const inputEl = { focus: () => { } };
397
- // @ts-ignore
398
- component.inputEl = inputEl;
399
- const mockCloseDropdown = (component.closeDropdown = jest.fn());
400
- const mockPreventDefault = jest.fn();
401
- let event = {
402
- key: "",
403
- target: evTarget,
404
- preventDefault: mockPreventDefault,
405
- };
406
- // Enter
407
- event.key = "Enter";
408
- // @ts-ignore
409
- component.handleListItemKeyDown(event);
410
- expect(mockTargetClick).toHaveBeenCalledTimes(1);
411
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
412
- jest.clearAllMocks();
413
- // Space
414
- event.key = " ";
415
- // @ts-ignore
416
- component.handleListItemKeyDown(event);
417
- expect(mockTargetClick).toHaveBeenCalledTimes(1);
418
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
419
- jest.clearAllMocks();
420
- // ArrowDown
421
- event.key = "ArrowDown";
422
- // @ts-ignore
423
- component.handleListItemKeyDown(event);
424
- expect(mockMoveDownListItem).toHaveBeenCalledTimes(1);
425
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
426
- jest.clearAllMocks();
427
- // ArrowUp
428
- event.key = "ArrowUp";
429
- // @ts-ignore
430
- component.handleListItemKeyDown(event);
431
- expect(mockMoveUpListItem).toHaveBeenCalledTimes(1);
432
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
433
- jest.clearAllMocks();
434
- // Escape
435
- event.key = "Escape";
436
- // @ts-ignore
437
- component.inputEl = { focus: mockFocus };
438
- // @ts-ignore
439
- component.handleListItemKeyDown(event);
440
- expect(mockFocus).toHaveBeenCalledTimes(1);
441
- expect(mockCloseDropdown).toHaveBeenCalledTimes(0);
442
- expect(mockPreventDefault).toHaveBeenCalledTimes(0);
443
- jest.clearAllMocks();
444
- // @ts-ignore
445
- component.inputEl = inputEl;
446
- jest.spyOn(component, "focusedElement", "get").mockReturnValue(inputEl);
447
- // @ts-ignore
448
- component.handleListItemKeyDown(event);
449
- expect(mockFocus).toHaveBeenCalledTimes(0);
450
- expect(mockCloseDropdown).toHaveBeenCalledTimes(1);
451
- expect(mockPreventDefault).toHaveBeenCalledTimes(0);
452
- jest.restoreAllMocks();
453
- });
454
- });
455
- describe("handles tag area keys", () => {
456
- afterEach(() => {
457
- jest.restoreAllMocks();
458
- });
459
- it("handles tag area key down", async () => {
460
- // handleTagAreaKeyDown
461
- const component = new TagInput();
462
- const mockMoveLeftTag = (component.moveLeftTag = jest.fn());
463
- const mockMoveRightTag = (component.moveRightTag = jest.fn());
464
- const mockHandleTagAreaDelete = (component.handleTagAreaDelete = jest.fn());
465
- const mockPreventDefault = jest.fn();
466
- let event = { key: "", preventDefault: mockPreventDefault };
467
- // ArrowLeft
468
- event.key = "ArrowLeft";
469
- //@ts-ignore
470
- component.handleTagAreaKeyDown(event);
471
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(1);
472
- expect(mockMoveRightTag).toHaveBeenCalledTimes(0);
473
- expect(mockHandleTagAreaDelete).toHaveBeenCalledTimes(0);
474
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
475
- jest.clearAllMocks();
476
- // ArrowUp
477
- event.key = "ArrowUp";
478
- //@ts-ignore
479
- component.handleTagAreaKeyDown(event);
480
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(1);
481
- expect(mockMoveRightTag).toHaveBeenCalledTimes(0);
482
- expect(mockHandleTagAreaDelete).toHaveBeenCalledTimes(0);
483
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
484
- jest.clearAllMocks();
485
- // ArrowRight
486
- event.key = "ArrowRight";
487
- //@ts-ignore
488
- component.handleTagAreaKeyDown(event);
489
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(0);
490
- expect(mockMoveRightTag).toHaveBeenCalledTimes(1);
491
- expect(mockHandleTagAreaDelete).toHaveBeenCalledTimes(0);
492
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
493
- jest.clearAllMocks();
494
- // ArrowDown
495
- event.key = "ArrowDown";
496
- //@ts-ignore
497
- component.handleTagAreaKeyDown(event);
498
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(0);
499
- expect(mockMoveRightTag).toHaveBeenCalledTimes(1);
500
- expect(mockHandleTagAreaDelete).toHaveBeenCalledTimes(0);
501
- expect(mockPreventDefault).toHaveBeenCalledTimes(1);
502
- jest.clearAllMocks();
503
- // Backspace
504
- event.key = "Backspace";
505
- //@ts-ignore
506
- component.handleTagAreaKeyDown(event);
507
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(0);
508
- expect(mockMoveRightTag).toHaveBeenCalledTimes(0);
509
- expect(mockHandleTagAreaDelete).toHaveBeenCalledTimes(1);
510
- expect(mockPreventDefault).toHaveBeenCalledTimes(0);
511
- jest.clearAllMocks();
512
- // Delete
513
- event.key = "Delete";
514
- //@ts-ignore
515
- component.handleTagAreaKeyDown(event);
516
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(0);
517
- expect(mockMoveRightTag).toHaveBeenCalledTimes(0);
518
- expect(mockHandleTagAreaDelete).toHaveBeenCalledTimes(1);
519
- expect(mockPreventDefault).toHaveBeenCalledTimes(0);
520
- jest.clearAllMocks();
521
- jest.restoreAllMocks();
522
- });
523
- it("handles tag area delete", async () => {
524
- // handleTagAreaDelete
525
- const component = new TagInput();
526
- const mockRemoveTag = (component.removeTag = jest.fn());
527
- let element1 = document.createElement("li");
528
- element1.dataset.tag = "one";
529
- let element2 = document.createElement("li");
530
- element2.dataset.tag = "two";
531
- jest.spyOn(component, "tagEls", "get").mockReturnValue([element1, element2]);
532
- component.tagsList = ["one", "two"];
533
- const mockInputFocus = jest.fn();
534
- const mockFocusTag = (component.focusTag = jest.fn());
535
- // @ts-ignore
536
- component.inputEl = { focus: mockInputFocus };
537
- component.focusedTag = element1;
538
- component.handleTagAreaDelete();
539
- expect(mockRemoveTag).toHaveBeenCalledTimes(1);
540
- expect(component.focusedTag).toBe(element1);
541
- expect(mockInputFocus).toHaveBeenCalledTimes(0);
542
- expect(mockFocusTag).toHaveBeenCalledTimes(1);
543
- jest.clearAllMocks();
544
- component.focusedTag = element2;
545
- component.tagsList = [];
546
- component.handleTagAreaDelete();
547
- expect(mockRemoveTag).toHaveBeenCalledTimes(1);
548
- expect(mockInputFocus).toHaveBeenCalledTimes(1);
549
- expect(mockFocusTag).toHaveBeenCalledTimes(0);
550
- jest.clearAllMocks();
551
- component.focusedTag = element2;
552
- component.tagsList = ["one", "two"];
553
- component.handleTagAreaDelete();
554
- expect(mockRemoveTag).toHaveBeenCalledTimes(1);
555
- expect(mockInputFocus).toHaveBeenCalledTimes(0);
556
- expect(mockFocusTag).toHaveBeenCalledTimes(1);
557
- expect(component.focusedTag).toBe(element1);
558
- jest.clearAllMocks();
559
- component.focusedTag = null;
560
- component.handleTagAreaDelete();
561
- expect(mockRemoveTag).toHaveBeenCalledTimes(0);
562
- expect(mockInputFocus).toHaveBeenCalledTimes(0);
563
- expect(component.focusedTag).toBe(null);
564
- expect(mockFocusTag).toHaveBeenCalledTimes(0);
565
- jest.clearAllMocks();
566
- jest.restoreAllMocks();
567
- });
568
- });
569
- });
570
- describe("manages focus", () => {
571
- it("handles blur of focusable elements", () => {
572
- // handleBlur
573
- const component = new TagInput();
574
- const element1 = {};
575
- const element2 = {};
576
- const event = { relatedTarget: element1 };
577
- const mockDismissTooltip = (component.dismissTooltip = jest.fn());
578
- const mockClearCellFocus = (component.clearCellFocus = jest.fn());
579
- const mockCloseDropdown = (component.closeDropdown = jest.fn());
580
- const div = document.createElement("div");
581
- div.classList.add("focused");
582
- // @ts-ignore
583
- component.fieldWrapperEl = div;
584
- component.handleBlur(event, element1);
585
- expect(mockCloseDropdown).toHaveBeenCalledTimes(0);
586
- expect(mockDismissTooltip).toHaveBeenCalledTimes(1);
587
- expect(mockClearCellFocus).toHaveBeenCalledTimes(1);
588
- // @ts-ignore
589
- expect(component.fieldWrapperEl).toHaveClass("focused");
590
- component.handleBlur(event, element2);
591
- expect(mockCloseDropdown).toHaveBeenCalledTimes(1);
592
- expect(mockDismissTooltip).toHaveBeenCalledTimes(2);
593
- expect(mockClearCellFocus).toHaveBeenCalledTimes(2);
594
- // @ts-ignore
595
- expect(component.fieldWrapperEl).not.toHaveClass("focused");
596
- jest.restoreAllMocks();
597
- });
598
- it("handles focus of tag area", async () => {
599
- // handleTagAreaFocus
600
- const component = new TagInput();
601
- const mockCloseDropdown = (component.closeDropdown = jest.fn());
602
- const mockFocusTag = (component.focusTag = jest.fn());
603
- const liElement1 = {};
604
- const liElement2 = {};
605
- jest.spyOn(component, "tagEls", "get").mockReturnValue([liElement1]);
606
- jest.spyOn(component, "nonLockedTagEls", "get").mockReturnValue([liElement1]);
607
- component.focusedTag = null;
608
- component.handleTagAreaFocus();
609
- expect(mockCloseDropdown).toHaveBeenCalledTimes(1);
610
- expect(mockFocusTag).toHaveBeenCalledTimes(1);
611
- expect(mockFocusTag).toHaveBeenLastCalledWith(liElement1);
612
- mockCloseDropdown.mockClear();
613
- mockFocusTag.mockClear();
614
- component.focusedTag = liElement2;
615
- component.handleTagAreaFocus();
616
- expect(mockCloseDropdown).toHaveBeenCalledTimes(1);
617
- expect(mockFocusTag).toHaveBeenCalledTimes(1);
618
- expect(mockFocusTag).toHaveBeenLastCalledWith(liElement2);
619
- jest.restoreAllMocks();
620
- });
621
- it("focuses list items", async () => {
622
- // focusListItem
623
- const component = new TagInput();
624
- const mockScrollIntoView = jest.fn();
625
- const newInput = document.createElement("input");
626
- newInput.setAttribute("aria-activedescendant", "");
627
- // @ts-ignore
628
- component.inputEl = newInput;
629
- const element = document.createElement("li");
630
- element.id = "fake-id";
631
- element.scrollIntoView = mockScrollIntoView;
632
- component.focusListItem(element);
633
- expect(component.focusedListItem).toBe(element);
634
- // @ts-ignore
635
- expect(component.inputEl).toEqualAttribute("aria-activedescendant", "fake-id");
636
- expect(mockScrollIntoView).toHaveBeenCalledTimes(1);
637
- jest.restoreAllMocks();
638
- });
639
- it("focuses tags", async () => {
640
- jest.spyOn(globalFunctions, "hideTooltip").mockImplementation(() => { });
641
- // focusTag
642
- const component = new TagInput();
643
- const element = document.createElement("li");
644
- element.id = "fakeID";
645
- //@ts-ignore
646
- component.tagAreaEl = document.createElement("ul");
647
- component.focusTag(element);
648
- expect(component.focusedTag).toBe(element);
649
- //@ts-ignore
650
- expect(component.tagAreaEl).toEqualAttribute("aria-activedescendant", "fakeID");
651
- jest.restoreAllMocks();
652
- });
653
- it("clears tag focus", async () => {
654
- // clearTagFocus
655
- const component = new TagInput();
656
- const tagArea = document.createElement("ul");
657
- tagArea.setAttribute("aria-activedescendant", "fakeID");
658
- component.focusedListItem = {};
659
- //@ts-ignore
660
- component.tagAreaEl = tagArea;
661
- //@ts-ignore
662
- expect(component.tagAreaEl).toEqualAttribute("aria-activedescendant", "fakeID");
663
- component.clearTagFocus();
664
- //@ts-ignore
665
- expect(component.tagAreaEl).toEqualAttribute("aria-activedescendant", "");
666
- expect(component.focusedTag).toBeNull();
667
- });
668
- it("clears list item focus", async () => {
669
- // clearListItemFocus
670
- const component = new TagInput();
671
- const element1 = document.createElement("li");
672
- const element2 = document.createElement("li");
673
- const element3 = document.createElement("li");
674
- element1.classList.add("focused");
675
- element2.classList.add("focused");
676
- element3.classList.add("focused");
677
- jest.spyOn(component, "listItemEls", "get").mockReturnValue([element1, element2, element3]);
678
- const newInput = document.createElement("input");
679
- newInput.setAttribute("aria-activedescendant", "fake-id");
680
- // @ts-ignore
681
- component.inputEl = newInput;
682
- component.clearListItemFocus();
683
- expect(component.focusedListItem).toBeNull();
684
- component.listItemEls.forEach((el) => {
685
- expect(el.tabIndex).toBe(-1);
686
- expect(el).not.toHaveClass("focused");
687
- });
688
- // @ts-ignore
689
- expect(component.inputEl).toEqualAttribute("aria-activedescendant", "");
690
- component.focusedListItem = {};
691
- jest.spyOn(component, "listItemEls", "get").mockReturnValue([]);
692
- component.clearListItemFocus();
693
- expect(component.focusedListItem).toBeNull();
694
- });
695
- });
696
- it("moves down list items", async () => {
697
- // moveDownListItem
698
- const component = new TagInput();
699
- const element1 = {};
700
- const element2 = {};
701
- const element3 = {};
702
- // @ts-ignore
703
- element1.nextElementSibling = element2;
704
- // @ts-ignore
705
- element2.nextElementSibling = element3;
706
- const spyListItemEls = jest
707
- .spyOn(component, "listItemEls", "get")
708
- .mockReturnValue([element1, element2, element3]);
709
- const mockFocusListItem = (component.focusListItem = jest.fn());
710
- component.focusedListItem = null;
711
- component.moveDownListItem();
712
- expect(mockFocusListItem).toHaveBeenCalledTimes(1);
713
- expect(mockFocusListItem).toHaveBeenLastCalledWith(element1);
714
- mockFocusListItem.mockClear();
715
- component.focusedListItem = element1;
716
- component.moveDownListItem();
717
- expect(mockFocusListItem).toHaveBeenCalledTimes(1);
718
- expect(mockFocusListItem).toHaveBeenLastCalledWith(element2);
719
- mockFocusListItem.mockClear();
720
- component.focusedListItem = element2;
721
- component.moveDownListItem();
722
- expect(mockFocusListItem).toHaveBeenCalledTimes(1);
723
- expect(mockFocusListItem).toHaveBeenLastCalledWith(element3);
724
- mockFocusListItem.mockClear();
725
- component.focusedListItem = element3;
726
- component.moveDownListItem();
727
- expect(mockFocusListItem).toHaveBeenCalledTimes(1);
728
- expect(mockFocusListItem).toHaveBeenLastCalledWith(element1);
729
- mockFocusListItem.mockClear();
730
- spyListItemEls.mockReturnValue([]);
731
- component.moveDownListItem();
732
- expect(mockFocusListItem).toHaveBeenCalledTimes(0);
733
- mockFocusListItem.mockClear();
734
- jest.restoreAllMocks();
735
- });
736
- it("adds and removes options", async () => {
737
- // addOption, removeOption
738
- const component = new TagInput();
739
- const spyFilterCaseInsensitive = jest.spyOn(component, "filterCaseInsensitive");
740
- const optionsList = ["one", "tWo", "THREE"];
741
- const removedThree = ["one", "tWo"];
742
- // adding
743
- component.optionsList = optionsList;
744
- expect(component.addOption("WORD")).toStrictEqual([...optionsList, "WORD"]);
745
- // removing
746
- component.removeOption("three");
747
- expect(spyFilterCaseInsensitive).toHaveBeenCalledTimes(1);
748
- expect(component.optionsList).toStrictEqual(removedThree);
749
- jest.restoreAllMocks();
750
- });
751
- it("adds and removes tags", async () => {
752
- // addTag, removeTag
753
- const component = new TagInput();
754
- const spyFilterCaseInsensitive = jest.spyOn(component, "filterCaseInsensitive");
755
- const mockAnnounce = (component.announce = jest.fn());
756
- const mockRemoveOption = (component.removeOption = jest.fn());
757
- component.tagsList = ["one", "two", "three"];
758
- // adding
759
- component.addTag("three");
760
- expect(component.tagsList).toStrictEqual(["one", "two", "three"]);
761
- expect(mockAnnounce).toHaveBeenCalledTimes(0);
762
- component.addTag("four");
763
- expect(component.tagsList).toStrictEqual(["one", "two", "three", "four"]);
764
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
765
- expect(mockAnnounce).toHaveBeenLastCalledWith("four added.");
766
- mockAnnounce.mockClear();
767
- // removing
768
- component.options = "one,two,three";
769
- component.tagsList = ["one", "two", "three"];
770
- component.removeTag("two");
771
- expect(spyFilterCaseInsensitive).toHaveBeenCalledTimes(1);
772
- expect(spyFilterCaseInsensitive).toHaveBeenLastCalledWith(["one", "two", "three"], "two");
773
- expect(component.tagsList).toStrictEqual(["one", "three"]);
774
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
775
- expect(mockAnnounce).toHaveBeenLastCalledWith("two removed");
776
- expect(mockRemoveOption).toHaveBeenCalledTimes(0);
777
- spyFilterCaseInsensitive.mockClear();
778
- mockAnnounce.mockClear();
779
- mockRemoveOption.mockClear();
780
- component.options = "one,two";
781
- component.tagsList = ["one", "two", "three"];
782
- component.optionsList = ["one", "two", "three"];
783
- await component.removeTag("three");
784
- expect(spyFilterCaseInsensitive).toHaveBeenCalledTimes(1);
785
- expect(spyFilterCaseInsensitive).toHaveBeenLastCalledWith(["one", "two", "three"], "three");
786
- expect(component.tagsList).toStrictEqual(["one", "two"]);
787
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
788
- expect(mockAnnounce).toHaveBeenLastCalledWith("three removed");
789
- expect(mockRemoveOption).toHaveBeenCalledTimes(1);
790
- spyFilterCaseInsensitive.mockClear();
791
- mockAnnounce.mockClear();
792
- mockRemoveOption.mockClear();
793
- jest.restoreAllMocks();
794
- });
795
- it("removes tag on remove button click", async () => {
796
- const component = new TagInput();
797
- const mockMoveLeftTag = (component.moveLeftTag = jest.fn());
798
- const mockRemoveTag = (component.removeTag = jest.fn());
799
- component.handleRemoveButtonClick("word");
800
- expect(mockMoveLeftTag).toHaveBeenCalledTimes(1);
801
- expect(mockRemoveTag).toHaveBeenCalledTimes(1);
802
- expect(mockRemoveTag).toHaveBeenLastCalledWith("word");
803
- });
804
- it("resets the input field", async () => {
805
- // resetInput
806
- const component = new TagInput();
807
- const input = { value: "word" };
808
- // @ts-ignore
809
- component.inputEl = input;
810
- component.charCount = 20;
811
- component.resetInput();
812
- expect(component.charCount).toBe(0);
813
- // @ts-ignore
814
- expect(component.inputEl.value).toBe("");
815
- });
816
- it("closes dropdown if not target or child", async () => {
817
- // closeIfNotElOrChild
818
- const component = new TagInput();
819
- const element1 = {};
820
- const event = { target: element1 };
821
- const mockCloseDropdown = (component.closeDropdown = jest.fn());
822
- component.isExpanded = false;
823
- component.closeIfNotElOrChild(event);
824
- expect(mockCloseDropdown).toHaveBeenCalledTimes(0);
825
- jest.clearAllMocks();
826
- component.isExpanded = true;
827
- component.closeIfNotElOrChild(event);
828
- expect(mockCloseDropdown).toHaveBeenCalledTimes(1);
829
- jest.restoreAllMocks();
830
- });
831
- it("sorts case insensitively", () => {
832
- const component = new TagInput();
833
- const list = ["canteloupe", "Apple", "Banana", "Cranberry", "blueberry", "apricot", "double word", "extra"];
834
- const answer = ["Apple", "apricot", "Banana", "blueberry", "canteloupe", "Cranberry", "double word", "extra"];
835
- expect(component.sortCaseInsensitive(list)).toStrictEqual(answer);
836
- });
837
- it("debounces handling resize", () => {
838
- const component = new TagInput();
839
- const mockDebouncedUpdate = (component.debouncedUpdate = jest.fn());
840
- component.debouncedUpdate();
841
- expect(mockDebouncedUpdate).toHaveBeenCalledTimes(1);
842
- jest.restoreAllMocks();
843
- //
844
- });
845
- it("postions the input element properly", () => {
846
- const component = new TagInput();
847
- // @ts-ignore
848
- component.inputEl = document.createElement("input");
849
- // @ts-ignore
850
- component.tagAreaEl = document.createElement("ul");
851
- // @ts-ignore
852
- component.tagAreaEl.getBoundingClientRect = () => {
853
- return { right: 200 };
854
- };
855
- const element1 = {
856
- dataset: { tag: "one" },
857
- };
858
- const element2 = {
859
- dataset: { tag: "two" },
860
- };
861
- jest.spyOn(component, "tagEls", "get").mockReturnValue([element1, element2]);
862
- // last tag exists and spaceAvailable
863
- element2.getBoundingClientRect = () => {
864
- return { right: 0 };
865
- };
866
- // @ts-ignore
867
- element2.offsetTop = 10;
868
- component.positionInput();
869
- // @ts-ignore
870
- expect(component.inputEl.style.position).toBe("absolute");
871
- // @ts-ignore
872
- expect(component.inputEl.style.top).toBe("10px");
873
- // @ts-ignore
874
- // 200 - 0 - 8
875
- expect(component.inputEl.style.width).toBe("192px");
876
- // last tag exists and no spaceAvailable
877
- element2.getBoundingClientRect = () => {
878
- return { right: 180 };
879
- };
880
- // @ts-ignore
881
- element2.offsetTop = 10;
882
- component.positionInput();
883
- // @ts-ignore
884
- expect(component.inputEl.style.position).toBe("static");
885
- // @ts-ignore
886
- expect(component.inputEl.style.width).toBe("100%");
887
- // no tags exist
888
- jest.spyOn(component, "tagEls", "get").mockReturnValue([]);
889
- component.positionInput();
890
- // @ts-ignore
891
- expect(component.inputEl.style.position).toBe("static");
892
- // @ts-ignore
893
- expect(component.inputEl.style.width).toBe("100%");
894
- });
895
- it("creates CSV from list", () => {
896
- const component = new TagInput();
897
- const list1 = ["one", "two", "and three"];
898
- expect(component.listToCSV(list1)).toBe("one,two,and three");
899
- expect(component.listToCSV([])).toBe("");
900
- });
901
- describe("handles announcements", () => {
902
- it("announces live region notifications", async () => {
903
- // announce
904
- const component = new TagInput();
905
- const div = document.createElement("div");
906
- div.textContent = "";
907
- //@ts-ignore
908
- component.liveRegionEl = div;
909
- component.announce("Message");
910
- expect(component.liveRegionMessage).toBe("Message");
911
- });
912
- it("announces existing options", async () => {
913
- // announceExistingOptions
914
- const component = new TagInput();
915
- const element1 = {};
916
- const element2 = {};
917
- const mockAnnounce = (component.announce = jest.fn());
918
- jest.spyOn(component, "optionEls", "get").mockReturnValue([element1, element2]);
919
- const spyRequestAnimationFrame = jest
920
- .spyOn(window, "requestAnimationFrame")
921
- //@ts-ignore
922
- .mockImplementation((cb) => cb());
923
- component.announceExistingOptions();
924
- expect(spyRequestAnimationFrame).toHaveBeenCalledTimes(1);
925
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
926
- expect(mockAnnounce).toHaveBeenLastCalledWith("2 existing options.");
927
- jest.clearAllMocks();
928
- jest.spyOn(component, "optionEls", "get").mockReturnValue([element1]);
929
- component.announceExistingOptions();
930
- expect(spyRequestAnimationFrame).toHaveBeenCalledTimes(1);
931
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
932
- expect(mockAnnounce).toHaveBeenLastCalledWith("1 existing option.");
933
- jest.clearAllMocks();
934
- jest.spyOn(component, "optionEls", "get").mockReturnValue([]);
935
- component.announceExistingOptions();
936
- expect(spyRequestAnimationFrame).toHaveBeenCalledTimes(1);
937
- expect(mockAnnounce).toHaveBeenCalledTimes(1);
938
- expect(mockAnnounce).toHaveBeenLastCalledWith("0 existing options.");
939
- jest.restoreAllMocks();
940
- });
941
- it("announces character limits warnings", async () => {
942
- const component = new TagInput();
943
- let message = component.generateCharacterLimitWarning(0, 50);
944
- expect(message).toBe("0 of 50 characters entered.");
945
- message = component.generateCharacterLimitWarning(30, 50);
946
- expect(message).toBe("30 of 50 characters entered.");
947
- message = component.generateCharacterLimitWarning(50, 50);
948
- expect(message).toBe("50 of 50 characters entered. No additional characters will be entered.");
949
- message = component.generateCharacterLimitWarning(75, 50);
950
- expect(message).toBe("75 of 50 characters entered. No additional characters will be entered.");
951
- });
952
- });
953
- describe("generates intl messages", () => {
954
- it("generates the added tag message", () => {
955
- const component = new TagInput();
956
- expect(component.generateTagAddedMessage("word")).toBe("word added.");
957
- });
958
- it("generates the already added tag message", () => {
959
- const component = new TagInput();
960
- expect(component.generateTagAlreadyAddedMessage("word")).toBe("word has already been added.");
961
- });
962
- it("configures messages", () => {
963
- const component = new TagInput();
964
- // defaults are in place
965
- expect(component.componentMessages.addNewHelpText).toBe("Press the Enter or Comma key to add a new tag.");
966
- expect(component.componentMessages.selectionHelpText).toBe("Search and select a tag.");
967
- expect(component.componentMessages.maxTagsReached).toBe("No more tags can be added because the limit has been reached.");
968
- expect(component.componentMessages.tagAreaInstructions).toBe("tag selection. Press Backspace or Delete to remove a tag.");
969
- expect(component.componentMessages.tagsAdded).toBe("Tags added");
970
- // handles partial replacement
971
- component.messageConfig =
972
- '{"tagAreaInstructions": "tagAreaInstructions replacement", "tagsAdded": "tagsAdded replacement"}';
973
- expect(component.componentMessages.addNewHelpText).toBe("Press the Enter or Comma key to add a new tag.");
974
- expect(component.componentMessages.selectionHelpText).toBe("Search and select a tag.");
975
- expect(component.componentMessages.maxTagsReached).toBe("No more tags can be added because the limit has been reached.");
976
- expect(component.componentMessages.tagAreaInstructions).toBe("tagAreaInstructions replacement");
977
- expect(component.componentMessages.tagsAdded).toBe("tagsAdded replacement");
978
- // handles full replacement
979
- component.messageConfig =
980
- '{"addNewHelpText": "addNewHelpText replacement", "selectionHelpText": "selectionHelpText replacement", "maxTagsReached": "maxTagsReached replacement", "tagAreaInstructions": "tagAreaInstructions replacement", "tagsAdded": "tagsAdded replacement"}';
981
- expect(component.componentMessages.addNewHelpText).toBe("addNewHelpText replacement");
982
- expect(component.componentMessages.selectionHelpText).toBe("selectionHelpText replacement");
983
- expect(component.componentMessages.maxTagsReached).toBe("maxTagsReached replacement");
984
- expect(component.componentMessages.tagAreaInstructions).toBe("tagAreaInstructions replacement");
985
- expect(component.componentMessages.tagsAdded).toBe("tagsAdded replacement");
986
- });
987
- });
988
- });
989
- //type table acceptance criteria
990
- //x table is rendered under input segment
991
- //x magnifying glass icon is added for both types
992
- //x styling of the tag-limit area is updated
993
- //x add new max-tags limit reached message
994
- //x table rows are zebra-striped
995
- //x hover state is darker
996
- //x selected options have new selected style (blueish with saturated border)
997
- //x -> selected rows styling is overidden by hover styling
998
- //x locked rows have the lock icon to the left of the first column text
999
- //x selected locked tags are darker than the rest, and have lock icon instead of delete x
1000
- //x identical names have identical looking tags
1001
- //x table content scrolls while the input segment stays in place
1002
- //x entering text filters across all columns
1003
- //x when no options pass filter, message is shown
1004
- //x user can select or deselect tags by activating the row (Enter & click)
1005
- //x the text in col1 is what is displayed in the tag
1006
- //x component allows for 2-4 columns
1007
- //x component has no character count
1008
- //// Properties of wm-tag-input
1009
- //x selected-tags: in table variant, list of id of the table rows that are selected
1010
- //x tag-input-type: defaults to "dropdown", used "table" for table variant
1011
- //x col-headers: comma separated list of column headers. Required
1012
- //x col-widths: comma separated list of css width values. If a value is blank, it fills rest of available space
1013
- //x -> (defaults to a table cell with no width e.g. "50%,134px,,10%")
1014
- //x col-wrap: comma separated list of values either trunc or wrap. defaults to wrap for all columns, or wrap for all unspecified columns.
1015
- //x -> e.g. if a table has 4 columns, "trunc" is treated as "trunc,wrap,wrap,wrap"
1016
- //// Properties of wm-tag-input-row
1017
- //x id: used to link the row to the tag. the id is what is added to the selected-tags property
1018
- //x col1, col2, col3, col4: the values for each column of the row
1019
- //x locked: whether or not the row can be activated. selected and unselected rows can be locked
1020
- //x -> aria-describedby or similar indicates locked status
1021
- //x input field has combobox role
1022
- //x table has grid role
1023
- //x table has aria-multiselectable
1024
- //x rows have role row
1025
- //x cells have role gridcell
1026
- //x header cells have role columnheader
1027
- //x aria-expanded will NOT be used
1028
- //x table does not have a tab-stop
1029
- //x up, down, left and right arrows navigate the table cells
1030
- //x navigation is handled with aria-activedescendant, true focus remains on the input field
1031
- //x down arrow from the input field navigates to the first row, up arrow goes to the last row
1032
- //x while a cell is "focused" or hovered, a tooltip appears if the text is truncated
1033
- //x cell "focus" is indicated by an inwards purple shadow, like the navigation component
1034
- //x on load, pre-selected tags are handled
1035
- //x while typing, faux focus is cleared
1036
- //x if max-tags reached, make sure focus goes to list instead of input
1037
- //x tooltip disappears on scroll
1038
- // should activating a locked element sr-alert the user?
1039
- // should typing that returns no results sr-alert the user?