@statistikzh/leu 0.3.0 → 0.5.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 (295) hide show
  1. package/.github/workflows/deploy-github-pages.yaml +33 -0
  2. package/.storybook/main.js +27 -1
  3. package/.storybook/manager-head.html +1 -0
  4. package/.storybook/manager.js +9 -0
  5. package/.storybook/preview-head.html +1 -1
  6. package/.storybook/preview.js +59 -6
  7. package/.storybook/static/logo.svg +19 -0
  8. package/.storybook/theme.js +7 -0
  9. package/CHANGELOG.md +54 -0
  10. package/README.md +1 -1
  11. package/custom-elements-manifest.config.js +46 -0
  12. package/dist/Accordion.d.ts +31 -0
  13. package/dist/Accordion.d.ts.map +1 -0
  14. package/dist/Accordion.js +257 -0
  15. package/dist/Breadcrumb.d.ts +69 -0
  16. package/dist/Breadcrumb.d.ts.map +1 -0
  17. package/dist/Breadcrumb.js +392 -0
  18. package/dist/Button-da11d064.d.ts +84 -0
  19. package/dist/Button-da11d064.d.ts.map +1 -0
  20. package/dist/Button-da11d064.js +542 -0
  21. package/dist/Button.d.ts +2 -0
  22. package/dist/Button.d.ts.map +1 -0
  23. package/dist/Button.js +6 -423
  24. package/dist/ButtonGroup.d.ts +24 -0
  25. package/dist/ButtonGroup.d.ts.map +1 -0
  26. package/dist/ButtonGroup.js +72 -43
  27. package/dist/Checkbox.d.ts +13 -0
  28. package/dist/Checkbox.d.ts.map +1 -0
  29. package/dist/Checkbox.js +101 -84
  30. package/dist/CheckboxGroup.d.ts +13 -0
  31. package/dist/CheckboxGroup.d.ts.map +1 -0
  32. package/dist/CheckboxGroup.js +41 -37
  33. package/dist/Chip.d.ts +5 -0
  34. package/dist/Chip.d.ts.map +1 -0
  35. package/dist/{Chip-5f70d04f.js → Chip.js} +21 -6
  36. package/dist/ChipGroup.d.ts +28 -0
  37. package/dist/ChipGroup.d.ts.map +1 -0
  38. package/dist/ChipGroup.js +64 -10
  39. package/dist/ChipLink.d.ts +15 -0
  40. package/dist/ChipLink.d.ts.map +1 -0
  41. package/dist/ChipLink.js +4 -7
  42. package/dist/ChipRemovable.d.ts +13 -0
  43. package/dist/ChipRemovable.d.ts.map +1 -0
  44. package/dist/ChipRemovable.js +5 -8
  45. package/dist/ChipSelectable.d.ts +22 -0
  46. package/dist/ChipSelectable.d.ts.map +1 -0
  47. package/dist/ChipSelectable.js +8 -11
  48. package/dist/Dropdown.d.ts +15 -0
  49. package/dist/Dropdown.d.ts.map +1 -0
  50. package/dist/Dropdown.js +73 -26
  51. package/dist/Input.d.ts +154 -0
  52. package/dist/Input.d.ts.map +1 -0
  53. package/dist/Input.js +42 -35
  54. package/dist/Menu.d.ts +8 -0
  55. package/dist/Menu.d.ts.map +1 -0
  56. package/dist/Menu.js +2 -5
  57. package/dist/MenuItem.d.ts +21 -0
  58. package/dist/MenuItem.d.ts.map +1 -0
  59. package/dist/MenuItem.js +32 -17
  60. package/dist/Pagination.d.ts +27 -0
  61. package/dist/Pagination.d.ts.map +1 -0
  62. package/dist/Pagination.js +121 -89
  63. package/dist/Popup.d.ts +18 -0
  64. package/dist/Popup.d.ts.map +1 -0
  65. package/dist/Popup.js +215 -0
  66. package/dist/Radio.d.ts +12 -0
  67. package/dist/Radio.d.ts.map +1 -0
  68. package/dist/Radio.js +9 -8
  69. package/dist/RadioGroup.d.ts +20 -0
  70. package/dist/RadioGroup.d.ts.map +1 -0
  71. package/dist/RadioGroup.js +41 -39
  72. package/dist/ScrollTop.d.ts +19 -0
  73. package/dist/ScrollTop.d.ts.map +1 -0
  74. package/dist/ScrollTop.js +122 -0
  75. package/dist/Select.d.ts +98 -0
  76. package/dist/Select.d.ts.map +1 -0
  77. package/dist/Select.js +79 -122
  78. package/dist/Table.d.ts +48 -0
  79. package/dist/Table.d.ts.map +1 -0
  80. package/dist/Table.js +141 -121
  81. package/dist/VisuallyHidden.d.ts +8 -0
  82. package/dist/VisuallyHidden.d.ts.map +1 -0
  83. package/dist/VisuallyHidden.js +28 -0
  84. package/dist/_rollupPluginBabelHelpers-20f659f4.d.ts +3 -0
  85. package/dist/_rollupPluginBabelHelpers-20f659f4.d.ts.map +1 -0
  86. package/dist/{defineElement-ba770aed.js → _rollupPluginBabelHelpers-20f659f4.js} +1 -15
  87. package/dist/defineElement-40372b4b.d.ts +9 -0
  88. package/dist/defineElement-40372b4b.d.ts.map +1 -0
  89. package/dist/defineElement-40372b4b.js +15 -0
  90. package/dist/icon-03e86700.d.ts +11 -0
  91. package/dist/icon-03e86700.d.ts.map +1 -0
  92. package/dist/index.js.d.ts +21 -0
  93. package/dist/index.js.d.ts.map +1 -0
  94. package/dist/index.js.js +42 -0
  95. package/dist/leu-accordion.d.ts +3 -0
  96. package/dist/leu-accordion.d.ts.map +1 -0
  97. package/dist/leu-accordion.js +9 -0
  98. package/dist/leu-breadcrumb.d.ts +3 -0
  99. package/dist/leu-breadcrumb.d.ts.map +1 -0
  100. package/dist/leu-breadcrumb.js +23 -0
  101. package/dist/leu-button-group.d.ts +3 -0
  102. package/dist/leu-button-group.d.ts.map +1 -0
  103. package/dist/leu-button-group.js +6 -6
  104. package/dist/leu-button.d.ts +3 -0
  105. package/dist/leu-button.d.ts.map +1 -0
  106. package/dist/leu-button.js +7 -3
  107. package/dist/leu-checkbox-group.d.ts +3 -0
  108. package/dist/leu-checkbox-group.d.ts.map +1 -0
  109. package/dist/leu-checkbox-group.js +6 -3
  110. package/dist/leu-checkbox.d.ts +3 -0
  111. package/dist/leu-checkbox.d.ts.map +1 -0
  112. package/dist/leu-checkbox.js +6 -3
  113. package/dist/leu-chip-group.d.ts +3 -0
  114. package/dist/leu-chip-group.d.ts.map +1 -0
  115. package/dist/leu-chip-group.js +7 -3
  116. package/dist/leu-chip-link.d.ts +3 -0
  117. package/dist/leu-chip-link.d.ts.map +1 -0
  118. package/dist/leu-chip-link.js +7 -4
  119. package/dist/leu-chip-removable.d.ts +3 -0
  120. package/dist/leu-chip-removable.d.ts.map +1 -0
  121. package/dist/leu-chip-removable.js +7 -4
  122. package/dist/leu-chip-selectable.d.ts +3 -0
  123. package/dist/leu-chip-selectable.d.ts.map +1 -0
  124. package/dist/leu-chip-selectable.js +7 -4
  125. package/dist/leu-dropdown.d.ts +3 -0
  126. package/dist/leu-dropdown.d.ts.map +1 -0
  127. package/dist/leu-dropdown.js +15 -4
  128. package/dist/leu-input.d.ts +3 -0
  129. package/dist/leu-input.d.ts.map +1 -0
  130. package/dist/leu-input.js +7 -3
  131. package/dist/leu-menu-item.d.ts +3 -0
  132. package/dist/leu-menu-item.d.ts.map +1 -0
  133. package/dist/leu-menu-item.js +8 -3
  134. package/dist/leu-menu.d.ts +3 -0
  135. package/dist/leu-menu.d.ts.map +1 -0
  136. package/dist/leu-menu.js +6 -3
  137. package/dist/leu-pagination.d.ts +3 -0
  138. package/dist/leu-pagination.d.ts.map +1 -0
  139. package/dist/leu-pagination.js +12 -4
  140. package/dist/leu-popup.d.ts +3 -0
  141. package/dist/leu-popup.d.ts.map +1 -0
  142. package/dist/leu-popup.js +9 -0
  143. package/dist/leu-radio-group.d.ts +3 -0
  144. package/dist/leu-radio-group.d.ts.map +1 -0
  145. package/dist/leu-radio-group.js +6 -3
  146. package/dist/leu-radio.d.ts +3 -0
  147. package/dist/leu-radio.d.ts.map +1 -0
  148. package/dist/leu-radio.js +6 -3
  149. package/dist/leu-scroll-top.d.ts +3 -0
  150. package/dist/leu-scroll-top.d.ts.map +1 -0
  151. package/dist/leu-scroll-top.js +14 -0
  152. package/dist/leu-select.d.ts +3 -0
  153. package/dist/leu-select.d.ts.map +1 -0
  154. package/dist/leu-select.js +16 -4
  155. package/dist/leu-table.d.ts +3 -0
  156. package/dist/leu-table.d.ts.map +1 -0
  157. package/dist/leu-table.js +13 -4
  158. package/dist/leu-visually-hidden.d.ts +3 -0
  159. package/dist/leu-visually-hidden.d.ts.map +1 -0
  160. package/dist/leu-visually-hidden.js +8 -0
  161. package/dist/theme.css +386 -2
  162. package/dist/utils-65469421.d.ts +16 -0
  163. package/dist/utils-65469421.d.ts.map +1 -0
  164. package/dist/utils-65469421.js +35 -0
  165. package/index.js +3 -0
  166. package/package.json +47 -17
  167. package/postcss.config.cjs +2 -0
  168. package/rollup.config.js +21 -40
  169. package/scripts/generate-component/templates/[Name].js +0 -5
  170. package/scripts/generate-component/templates/[name].css +3 -3
  171. package/scripts/generate-component/templates/[namespace]-[name].js +5 -2
  172. package/scripts/postcss-leu-font-styles.cjs +160 -0
  173. package/src/components/accordion/Accordion.js +0 -6
  174. package/src/components/accordion/accordion.css +2 -2
  175. package/src/components/accordion/leu-accordion.js +5 -2
  176. package/src/components/accordion/stories/accordion.stories.js +8 -4
  177. package/src/components/accordion/test/accordion.test.js +95 -3
  178. package/src/components/breadcrumb/Breadcrumb.js +311 -0
  179. package/src/components/breadcrumb/breadcrumb.css +103 -0
  180. package/src/components/breadcrumb/leu-breadcrumb.js +6 -0
  181. package/src/components/breadcrumb/stories/breadcrumb.stories.js +73 -0
  182. package/src/components/breadcrumb/test/breadcrumb.test.js +141 -0
  183. package/src/components/button/Button.js +76 -20
  184. package/src/components/button/button.css +13 -5
  185. package/src/components/button/leu-button.js +5 -2
  186. package/src/components/button/stories/button.stories.js +79 -105
  187. package/src/components/button/test/button.test.js +184 -3
  188. package/src/components/button-group/ButtonGroup.js +76 -40
  189. package/src/components/button-group/leu-button-group.js +5 -2
  190. package/src/components/button-group/stories/button-group.stories.js +19 -6
  191. package/src/components/button-group/test/button-group.test.js +87 -4
  192. package/src/components/checkbox/Checkbox.js +6 -85
  193. package/src/components/checkbox/CheckboxGroup.js +8 -38
  194. package/src/components/checkbox/checkbox-group.css +29 -0
  195. package/src/components/checkbox/checkbox.css +76 -0
  196. package/src/components/checkbox/leu-checkbox-group.js +5 -2
  197. package/src/components/checkbox/leu-checkbox.js +5 -2
  198. package/src/components/checkbox/stories/checkbox-group.stories.js +44 -21
  199. package/src/components/checkbox/stories/checkbox.stories.js +7 -1
  200. package/src/components/checkbox/test/checkbox-group.test.js +124 -0
  201. package/src/components/checkbox/test/checkbox.test.js +72 -59
  202. package/src/components/chip/Chip.js +1 -0
  203. package/src/components/chip/ChipGroup.js +42 -7
  204. package/src/components/chip/ChipLink.js +1 -6
  205. package/src/components/chip/ChipRemovable.js +2 -7
  206. package/src/components/chip/ChipSelectable.js +5 -10
  207. package/src/components/chip/chip-group.css +12 -2
  208. package/src/components/chip/chip.css +14 -3
  209. package/src/components/chip/exports.js +4 -10
  210. package/src/components/chip/leu-chip-group.js +5 -2
  211. package/src/components/chip/leu-chip-link.js +5 -2
  212. package/src/components/chip/leu-chip-removable.js +5 -2
  213. package/src/components/chip/leu-chip-selectable.js +5 -2
  214. package/src/components/chip/stories/chip-group.stories.js +110 -44
  215. package/src/components/chip/stories/chip-link.stories.js +16 -4
  216. package/src/components/chip/stories/chip-removable.stories.js +15 -4
  217. package/src/components/chip/stories/chip-selectable.stories.js +13 -3
  218. package/src/components/chip/test/chip-group.test.js +124 -0
  219. package/src/components/chip/test/chip-link.test.js +58 -0
  220. package/src/components/chip/test/chip-removable.test.js +79 -0
  221. package/src/components/chip/test/chip-selectable.test.js +95 -0
  222. package/src/components/chip/test/chip.test.js +1 -1
  223. package/src/components/dropdown/Dropdown.js +72 -24
  224. package/src/components/dropdown/dropdown.css +1 -2
  225. package/src/components/dropdown/leu-dropdown.js +5 -2
  226. package/src/components/dropdown/stories/dropdown.stories.js +11 -5
  227. package/src/components/dropdown/test/dropdown.test.js +6 -6
  228. package/src/components/icon/icon.js +1 -1
  229. package/src/components/icon/test/icon.test.js +66 -0
  230. package/src/components/input/Input.js +25 -28
  231. package/src/components/input/input.css +11 -8
  232. package/src/components/input/leu-input.js +5 -2
  233. package/src/components/input/stories/input.stories.js +21 -2
  234. package/src/components/input/test/input.test.js +432 -4
  235. package/src/components/menu/Menu.js +0 -5
  236. package/src/components/menu/MenuItem.js +20 -13
  237. package/src/components/menu/leu-menu-item.js +5 -2
  238. package/src/components/menu/leu-menu.js +5 -2
  239. package/src/components/menu/menu-item.css +7 -4
  240. package/src/components/menu/stories/menu-item.stories.js +13 -4
  241. package/src/components/menu/stories/menu.stories.js +11 -5
  242. package/src/components/menu/test/menu-item.test.js +180 -0
  243. package/src/components/menu/test/menu.test.js +10 -2
  244. package/src/components/pagination/Pagination.js +118 -99
  245. package/src/components/pagination/leu-pagination.js +5 -2
  246. package/src/components/pagination/pagination.css +6 -1
  247. package/src/components/pagination/stories/pagination.stories.js +30 -9
  248. package/src/components/pagination/test/pagination.test.js +191 -5
  249. package/src/components/popup/Popup.js +200 -0
  250. package/src/components/popup/leu-popup.js +6 -0
  251. package/src/components/popup/popup.css +27 -0
  252. package/src/components/popup/stories/popup.stories.js +58 -0
  253. package/src/components/popup/test/popup.test.js +29 -0
  254. package/src/components/radio/Radio.js +2 -6
  255. package/src/components/radio/RadioGroup.js +6 -38
  256. package/src/components/radio/leu-radio-group.js +5 -2
  257. package/src/components/radio/leu-radio.js +5 -2
  258. package/src/components/radio/radio-group.css +29 -0
  259. package/src/components/radio/radio.css +1 -1
  260. package/src/components/radio/stories/radio-group.stories.js +38 -19
  261. package/src/components/radio/stories/radio.stories.js +7 -1
  262. package/src/components/radio/test/radio-group.test.js +86 -0
  263. package/src/components/radio/test/radio.test.js +108 -17
  264. package/src/components/scroll-top/ScrollTop.js +87 -0
  265. package/src/components/scroll-top/leu-scroll-top.js +6 -0
  266. package/src/components/scroll-top/scroll-top.css +34 -0
  267. package/src/components/scroll-top/stories/scroll-top.stories.js +217 -0
  268. package/src/components/scroll-top/test/scroll-top.test.js +22 -0
  269. package/src/components/select/Select.js +58 -37
  270. package/src/components/select/leu-select.js +5 -2
  271. package/src/components/select/select.css +15 -15
  272. package/src/components/select/stories/select.stories.js +15 -168
  273. package/src/components/select/test/fixtures.js +162 -0
  274. package/src/components/select/test/select.test.js +236 -12
  275. package/src/components/table/Table.js +43 -118
  276. package/src/components/table/leu-table.js +5 -2
  277. package/src/components/table/stories/table.stories.js +20 -10
  278. package/src/components/table/table.css +99 -0
  279. package/src/components/table/test/table.test.js +1 -1
  280. package/src/components/visually-hidden/VisuallyHidden.js +13 -0
  281. package/src/components/visually-hidden/leu-visually-hidden.js +6 -0
  282. package/src/components/visually-hidden/stories/visually-hidden.stories.js +22 -0
  283. package/src/components/visually-hidden/test/visually-hidden.test.js +36 -0
  284. package/src/components/visually-hidden/visually-hidden.css +10 -0
  285. package/src/lib/defineElement.js +1 -1
  286. package/src/lib/hasSlotController.js +5 -3
  287. package/src/lib/utils.js +35 -0
  288. package/src/styles/custom-properties.css +6 -2
  289. package/src/styles/font-definitions.json +202 -0
  290. package/stylelint.config.mjs +2 -0
  291. package/tsconfig.build.json +21 -0
  292. package/tsconfig.json +16 -0
  293. package/{web-dev-server-storybook.config.mjs → web-dev-server.config.mjs} +1 -2
  294. package/web-test-runner.config.mjs +15 -2
  295. package/dist/index.js +0 -26
@@ -1,22 +1,208 @@
1
1
  import { html } from "lit"
2
- import { fixture, expect } from "@open-wc/testing"
2
+ import { fixture, expect, elementUpdated } from "@open-wc/testing"
3
+ import { ifDefined } from "lit/directives/if-defined.js"
4
+ import { sendKeys } from "@web/test-runner-commands"
5
+ import { spy } from "sinon"
3
6
 
4
7
  import "../leu-pagination.js"
5
8
 
6
- async function defaultFixture() {
7
- return fixture(html` <leu-pagination></leu-button>`)
9
+ async function defaultFixture(args = {}) {
10
+ return fixture(html`<leu-pagination
11
+ numOfItems=${ifDefined(args.numOfItems)}
12
+ itemsPerPage=${ifDefined(args.itemsPerPage)}
13
+ defaultPage=${ifDefined(args.defaultPage)}
14
+ >
15
+ </leu-pagination>`)
8
16
  }
9
17
 
10
18
  describe("LeuPagination", () => {
11
19
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-pagination")
20
+ const el = customElements.get("leu-pagination")
13
21
 
14
22
  await expect(el).not.to.be.undefined
15
23
  })
16
24
 
17
25
  it("passes the a11y audit", async () => {
18
- const el = await defaultFixture()
26
+ const el = await defaultFixture({
27
+ numOfItems: 98,
28
+ itemsPerPage: 7,
29
+ defaultPage: 1,
30
+ })
19
31
 
20
32
  await expect(el).shadowDom.to.be.accessible()
21
33
  })
34
+
35
+ it("disables the previous button on the first page", async () => {
36
+ const el = await defaultFixture({
37
+ numOfItems: 98,
38
+ itemsPerPage: 7,
39
+ defaultPage: 1,
40
+ })
41
+
42
+ const previous = el.shadowRoot.querySelectorAll("leu-button")[0]
43
+
44
+ expect(previous).to.have.attribute("disabled")
45
+ })
46
+
47
+ it("disables the next button on the last page", async () => {
48
+ const el = await defaultFixture({
49
+ numOfItems: 98,
50
+ itemsPerPage: 7,
51
+ defaultPage: 14,
52
+ })
53
+
54
+ const next = el.shadowRoot.querySelectorAll("leu-button")[1]
55
+
56
+ expect(next).to.have.attribute("disabled")
57
+ })
58
+
59
+ it("renders the correct number of available pages", async () => {
60
+ const el = await defaultFixture({
61
+ numOfItems: 98,
62
+ itemsPerPage: 7,
63
+ defaultPage: 1,
64
+ })
65
+
66
+ const label = el.shadowRoot.querySelectorAll(".label")
67
+
68
+ expect(label).to.have.trimmed.text("von 14")
69
+ })
70
+
71
+ it("displays the current page", async () => {
72
+ const el = await defaultFixture({
73
+ numOfItems: 98,
74
+ itemsPerPage: 7,
75
+ defaultPage: 2,
76
+ })
77
+
78
+ const input = el.shadowRoot.querySelector("input")
79
+
80
+ expect(input.value).to.equal("2")
81
+ })
82
+
83
+ it("increments the page with a click on the next button", async () => {
84
+ const el = await defaultFixture({
85
+ numOfItems: 98,
86
+ itemsPerPage: 7,
87
+ defaultPage: 1,
88
+ })
89
+
90
+ const next = el.shadowRoot.querySelectorAll("leu-button")[1]
91
+
92
+ next.click()
93
+ await elementUpdated(el)
94
+
95
+ expect(el.page).to.equal(2)
96
+ })
97
+
98
+ it("decrements the page with a click on the prev button", async () => {
99
+ const el = await defaultFixture({
100
+ numOfItems: 98,
101
+ itemsPerPage: 7,
102
+ defaultPage: 10,
103
+ })
104
+
105
+ const next = el.shadowRoot.querySelectorAll("leu-button")[0]
106
+
107
+ next.click()
108
+ await elementUpdated(el)
109
+
110
+ expect(el.page).to.equal(9)
111
+ })
112
+
113
+ it("increments the page with pressing the arrow up key", async () => {
114
+ const el = await defaultFixture({
115
+ numOfItems: 98,
116
+ itemsPerPage: 7,
117
+ defaultPage: 1,
118
+ })
119
+
120
+ el.focus()
121
+
122
+ await sendKeys({
123
+ press: "ArrowUp",
124
+ })
125
+ await elementUpdated(el)
126
+
127
+ expect(el.page).to.equal(2)
128
+ })
129
+
130
+ it("decrements the page with pressing the arrow down key", async () => {
131
+ const el = await defaultFixture({
132
+ numOfItems: 98,
133
+ itemsPerPage: 7,
134
+ defaultPage: 13,
135
+ })
136
+
137
+ el.focus()
138
+
139
+ await sendKeys({
140
+ press: "ArrowDown",
141
+ })
142
+ await elementUpdated(el)
143
+
144
+ expect(el.page).to.equal(12)
145
+ })
146
+
147
+ it("bounds the page to the max and min values", async () => {
148
+ const el = await defaultFixture({
149
+ numOfItems: 50,
150
+ itemsPerPage: 10,
151
+ defaultPage: 6,
152
+ })
153
+
154
+ const input = el.shadowRoot.querySelector("input")
155
+
156
+ expect(el.page).to.equal(5)
157
+ expect(input.value).to.equal("5")
158
+
159
+ el.page = 0
160
+ await elementUpdated(el)
161
+
162
+ expect(el.page).to.equal(1)
163
+ expect(input.value).to.equal("1")
164
+
165
+ el.focus()
166
+
167
+ await sendKeys({
168
+ press: "ArrowDown",
169
+ })
170
+ await elementUpdated(el)
171
+
172
+ expect(el.page).to.equal(1)
173
+ expect(input.value).to.equal("1")
174
+
175
+ await sendKeys({ press: "ArrowUp" })
176
+ await sendKeys({ press: "ArrowUp" })
177
+ await sendKeys({ press: "ArrowUp" })
178
+ await sendKeys({ press: "ArrowUp" })
179
+ await sendKeys({ press: "ArrowUp" })
180
+
181
+ expect(el.page).to.equal(5)
182
+ expect(input.value).to.equal("5")
183
+ })
184
+
185
+ it("fires a leu:pagechange event", async () => {
186
+ const el = await defaultFixture({
187
+ numOfItems: 98,
188
+ itemsPerPage: 7,
189
+ page: 14,
190
+ })
191
+
192
+ const eventSpy = spy()
193
+ el.addEventListener("leu:pagechange", eventSpy)
194
+
195
+ const prevButton = el.shadowRoot.querySelectorAll("leu-button")[0]
196
+ const nextButton = el.shadowRoot.querySelectorAll("leu-button")[1]
197
+
198
+ nextButton.click()
199
+ prevButton.click()
200
+
201
+ el.focus()
202
+
203
+ await sendKeys({ press: "ArrowUp" })
204
+ await sendKeys({ press: "ArrowDown" })
205
+
206
+ expect(eventSpy).to.have.been.called.callCount(4)
207
+ })
22
208
  })
@@ -0,0 +1,200 @@
1
+ import { html, LitElement } from "lit"
2
+ import {
3
+ autoUpdate,
4
+ computePosition,
5
+ flip,
6
+ shift,
7
+ size,
8
+ } from "@floating-ui/dom"
9
+ import styles from "./popup.css"
10
+
11
+ /**
12
+ * @tagname leu-popup
13
+ */
14
+ export class LeuPopup extends LitElement {
15
+ static styles = styles
16
+
17
+ static shadowRootOptions = {
18
+ ...LitElement.shadowRootOptions,
19
+ delegatesFocus: true,
20
+ }
21
+
22
+ static properties = {
23
+ anchor: {},
24
+ active: { type: Boolean },
25
+ /**
26
+ * @type {"top"|"top-start"|"top-end"|"bottom"|"bottom-start"|"bottom-end"|"left"|"left-start"|"left-end"|"right"|"right-start"|"right-end"}
27
+ */
28
+ placement: { type: String },
29
+ flip: { type: Boolean },
30
+ shift: { type: Boolean },
31
+ shiftPadding: { type: Number },
32
+ /** @type {"width" | "height" | "both"} */
33
+ matchSize: { type: String },
34
+ /** @type {"width" | "height" | "both"} */
35
+ autoSize: { type: String },
36
+ autoSizePadding: { type: Number },
37
+ }
38
+
39
+ constructor() {
40
+ super()
41
+
42
+ this.anchorEl = null
43
+ this.cleanup = undefined
44
+ this.flip = false
45
+ this.shift = false
46
+ }
47
+
48
+ disconnectedCallback() {
49
+ super.disconnectedCallback()
50
+ this.stop()
51
+ }
52
+
53
+ updated(changedProperties) {
54
+ if (changedProperties.has("active")) {
55
+ if (this.active) {
56
+ this.start()
57
+ } else {
58
+ this.stop()
59
+ }
60
+ }
61
+
62
+ if (changedProperties.has("anchor")) {
63
+ this.handleAnchorChange()
64
+ }
65
+
66
+ if (this.active) {
67
+ this.reposition()
68
+ }
69
+ }
70
+
71
+ get popupEl() {
72
+ return this.renderRoot?.querySelector(".popup") ?? null
73
+ }
74
+
75
+ start() {
76
+ if (!this.anchorEl || !this.active) return
77
+
78
+ this.cleanup = autoUpdate(this.anchorEl, this.popupEl, () => {
79
+ this.reposition()
80
+ })
81
+ }
82
+
83
+ stop() {
84
+ this.cleanup?.()
85
+
86
+ this.style.removeProperty("--auto-size-available-width")
87
+ this.style.removeProperty("--auto-size-available-height")
88
+ }
89
+
90
+ reposition() {
91
+ if (!this.anchorEl || !this.popupEl || !this.active) return
92
+
93
+ const middleware = []
94
+
95
+ if (this.matchSize) {
96
+ middleware.push(
97
+ size({
98
+ apply: ({ rects }) => {
99
+ const matchWidth =
100
+ this.matchSize === "width" || this.matchSize === "both"
101
+ const matchHeight =
102
+ this.matchSize === "height" || this.matchSize === "both"
103
+ this.popupEl.style.width = matchWidth
104
+ ? `${rects.reference.width}px`
105
+ : ""
106
+ this.popupEl.style.height = matchHeight
107
+ ? `${rects.reference.height}px`
108
+ : ""
109
+ },
110
+ })
111
+ )
112
+ } else {
113
+ // Cleanup styles if we're not matching width/height
114
+ this.popupEl.style.width = ""
115
+ this.popupEl.style.height = ""
116
+ }
117
+
118
+ if (this.flip) {
119
+ middleware.push(flip())
120
+ }
121
+
122
+ if (this.shift) {
123
+ middleware.push(shift({ padding: this.shiftPadding }))
124
+ }
125
+
126
+ if (this.autoSize) {
127
+ middleware.push(
128
+ size({
129
+ padding: this.autoSizePadding,
130
+ apply: ({ availableWidth, availableHeight }) => {
131
+ const setMaxWidth =
132
+ this.autoSize === "width" || this.autoSize === "both"
133
+ const setMaxHeight =
134
+ this.autoSize === "height" || this.autoSize === "both"
135
+
136
+ if (setMaxHeight) {
137
+ this.style.setProperty(
138
+ "--auto-size-available-height",
139
+ `${availableHeight}px`
140
+ )
141
+ } else {
142
+ this.style.removeProperty("--auto-size-available-height")
143
+ }
144
+
145
+ if (setMaxWidth) {
146
+ this.style.setProperty(
147
+ "--auto-size-available-width",
148
+ `${availableWidth}px`
149
+ )
150
+ } else {
151
+ this.style.removeProperty("--auto-size-available-width")
152
+ }
153
+ },
154
+ })
155
+ )
156
+ } else {
157
+ // Cleanup styles if we're not auto-sizing
158
+ this.style.removeProperty("--auto-size-available-width")
159
+ this.style.removeProperty("--auto-size-available-height")
160
+ }
161
+
162
+ computePosition(this.anchorEl, this.popupEl, {
163
+ placement: this.placement,
164
+ middleware,
165
+ }).then(({ x, y }) => {
166
+ Object.assign(this.popupEl.style, {
167
+ left: `${x}px`,
168
+ top: `${y}px`,
169
+ })
170
+ })
171
+ }
172
+
173
+ handleAnchorChange() {
174
+ if (this.anchor && typeof this.anchor === "string") {
175
+ const root = this.getRootNode()
176
+ this.anchorEl = root.getElementById(this.anchor)
177
+ } else if (this.anchor instanceof HTMLElement) {
178
+ this.anchorEl = this.anchor
179
+ } else {
180
+ this.anchorEl = this.querySelector("[slot=anchor]")
181
+ }
182
+
183
+ if (this.anchorEl instanceof HTMLSlotElement) {
184
+ ;[this.anchorEl] = this.anchorEl.assignedElements({ flatten: true })
185
+ }
186
+
187
+ if (this.anchorEl) {
188
+ this.start()
189
+ }
190
+ }
191
+
192
+ render() {
193
+ return html`
194
+ <slot name="anchor" @slotchange=${this.handleAnchorChange}></slot>
195
+ <div class="popup">
196
+ <slot> </slot>
197
+ </div>
198
+ `
199
+ }
200
+ }
@@ -0,0 +1,6 @@
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuPopup } from "./Popup.js"
3
+
4
+ export { LeuPopup }
5
+
6
+ defineElement("popup", LeuPopup)
@@ -0,0 +1,27 @@
1
+ :host,
2
+ :host * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ :host {
7
+ --popup-font-regular: var(--leu-font-family-regular);
8
+ --popup-font-black: var(--leu-font-family-black);
9
+
10
+ font-family: var(--popup-font-regular);
11
+ }
12
+
13
+ .popup {
14
+ display: none;
15
+
16
+ max-width: var(--auto-size-available-width, none);
17
+ max-height: var(--auto-size-available-height, none);
18
+
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ }
23
+
24
+ /* TODO: Should visibility be a matter of the popup component? */
25
+ :host([active]) .popup {
26
+ display: block;
27
+ }
@@ -0,0 +1,58 @@
1
+ import { html } from "lit"
2
+ import { styleMap } from "lit/directives/style-map.js"
3
+ import { ifDefined } from "lit/directives/if-defined.js"
4
+ import "../leu-popup.js"
5
+ import "../../button/leu-button.js"
6
+
7
+ export default {
8
+ title: "Popup",
9
+ component: "leu-popup",
10
+ argTypes: {
11
+ placement: {
12
+ control: {
13
+ type: "select",
14
+ },
15
+ options: [
16
+ "top",
17
+ "top-start",
18
+ "top-end",
19
+ "bottom",
20
+ "bottom-start",
21
+ "bottom-end",
22
+ "left",
23
+ "left-start",
24
+ "left-end",
25
+ "right",
26
+ "right-start",
27
+ "right-end",
28
+ ],
29
+ },
30
+ },
31
+ }
32
+
33
+ const popupStyles = {
34
+ background: "white",
35
+ border: "1px solid var(--leu-color-black-40)",
36
+ padding: "0.5rem",
37
+ boxShadow: "var(--leu-box-shadow-regular)",
38
+ }
39
+
40
+ function Template(args = {}) {
41
+ return html`<leu-popup
42
+ ?active=${args.active}
43
+ ?flip=${args.flip}
44
+ ?shift=${args.shift}
45
+ placement=${ifDefined(args.placement)}
46
+ >
47
+ <leu-button slot="anchor">Open popup</leu-button>
48
+ <div style=${styleMap(popupStyles)}>Popup content</div>
49
+ </leu-popup>`
50
+ }
51
+
52
+ export const Regular = Template.bind({})
53
+ Regular.args = {
54
+ active: true,
55
+ placement: "bottom-start",
56
+ flip: true,
57
+ shift: true,
58
+ }
@@ -0,0 +1,29 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect } from "@open-wc/testing"
3
+
4
+ import "../leu-popup.js"
5
+
6
+ async function defaultFixture() {
7
+ return fixture(
8
+ html`
9
+ <leu-popup
10
+ ><div slot="anchor"></div>
11
+ <p>Popup content</p></leu-popup
12
+ >
13
+ `
14
+ )
15
+ }
16
+
17
+ describe("LeuPopup", () => {
18
+ it("is a defined element", async () => {
19
+ const el = await customElements.get("leu-popup")
20
+
21
+ await expect(el).not.to.be.undefined
22
+ })
23
+
24
+ it("passes the a11y audit", async () => {
25
+ const el = await defaultFixture()
26
+
27
+ await expect(el).shadowDom.to.be.accessible()
28
+ })
29
+ })
@@ -1,5 +1,4 @@
1
1
  import { html, LitElement } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
3
2
  import styles from "./radio.css"
4
3
 
5
4
  /**
@@ -19,6 +18,7 @@ export class LeuRadio extends LitElement {
19
18
  identifier: { type: String, reflect: true },
20
19
  value: { type: String, reflect: true },
21
20
  name: { type: String, reflect: true },
21
+ label: { type: String, reflect: true },
22
22
  }
23
23
 
24
24
  constructor() {
@@ -51,11 +51,7 @@ export class LeuRadio extends LitElement {
51
51
  ?disabled=${this.disabled}
52
52
  .value=${this.value}
53
53
  />
54
- <label for=${this.identifier} class="label"><slot></slot></label>
54
+ <label for=${this.identifier} class="label">${this.label}</label>
55
55
  `
56
56
  }
57
57
  }
58
-
59
- export function defineRadioElements() {
60
- defineElement("radio", LeuRadio)
61
- }
@@ -1,45 +1,17 @@
1
- import { html, css, LitElement } from "lit"
1
+ import { html, LitElement } from "lit"
2
2
  import { classMap } from "lit/directives/class-map.js"
3
- import { defineElement } from "../../lib/defineElement.js"
3
+
4
+ import styles from "./radio-group.css"
4
5
 
5
6
  /**
6
7
  * @tagname leu-radio-group
7
8
  */
8
9
  export class LeuRadioGroup extends LitElement {
9
- static styles = css`
10
- :host {
11
- --group-font-regular: var(--leu-font-regular);
12
- --group-font-black: var(--leu-font-black);
13
-
14
- font-family: var(--group-font-regular);
15
- }
16
-
17
- .fieldset {
18
- display: flex;
19
- align-items: flex-start;
20
- flex-wrap: wrap;
21
- gap: 0.5rem 1rem;
22
-
23
- border: none;
24
- padding: 0;
25
- }
26
-
27
- .fieldset--vertical {
28
- flex-direction: column;
29
- gap: 1rem;
30
- }
31
-
32
- .legend {
33
- font-family: var(--group-font-black);
34
- font-size: 1.125rem;
35
- line-height: 1.5;
36
-
37
- margin-bottom: 0.5rem;
38
- }
39
- `
10
+ static styles = styles
40
11
 
41
12
  static properties = {
42
13
  orientation: { type: String, reflect: true },
14
+ label: { type: String, reflect: true },
43
15
  }
44
16
 
45
17
  constructor() {
@@ -181,13 +153,9 @@ export class LeuRadioGroup extends LitElement {
181
153
 
182
154
  return html`
183
155
  <fieldset class=${classMap(fieldsetClasses)}>
184
- <legend class="legend"><slot name="legend"></slot></legend>
156
+ <legend class="legend">${this.label}</legend>
185
157
  <slot @slotchange=${this.handleSlotChange}></slot>
186
158
  </fieldset>
187
159
  `
188
160
  }
189
161
  }
190
-
191
- export function defineRadioGroupElements() {
192
- defineElement("radio-group", LeuRadioGroup)
193
- }
@@ -1,3 +1,6 @@
1
- import { defineRadioGroupElements } from "./RadioGroup.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuRadioGroup } from "./RadioGroup.js"
2
3
 
3
- defineRadioGroupElements()
4
+ export { LeuRadioGroup }
5
+
6
+ defineElement("radio-group", LeuRadioGroup)
@@ -1,3 +1,6 @@
1
- import { defineRadioElements } from "./Radio.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuRadio } from "./Radio.js"
2
3
 
3
- defineRadioElements()
4
+ export { LeuRadio }
5
+
6
+ defineElement("radio", LeuRadio)
@@ -0,0 +1,29 @@
1
+ :host {
2
+ --group-font-regular: var(--leu-font-family-regular);
3
+ --group-font-black: var(--leu-font-family-black);
4
+
5
+ font-family: var(--group-font-regular);
6
+ }
7
+
8
+ .fieldset {
9
+ display: flex;
10
+ align-items: flex-start;
11
+ flex-wrap: wrap;
12
+ gap: 0.5rem 1rem;
13
+
14
+ border: none;
15
+ padding: 0;
16
+ }
17
+
18
+ .fieldset--vertical {
19
+ flex-direction: column;
20
+ gap: 1rem;
21
+ }
22
+
23
+ .legend {
24
+ font-family: var(--group-font-black);
25
+ font-size: 1.125rem;
26
+ line-height: 1.5;
27
+
28
+ margin-bottom: 0.5rem;
29
+ }
@@ -6,7 +6,7 @@
6
6
  --radio-label-color: var(--leu-color-black-100);
7
7
  --radio-label-color-disabled: var(--radio-color-disabled);
8
8
 
9
- --radio-font-regular: var(--leu-font-regular);
9
+ --radio-font-regular: var(--leu-font-family-regular);
10
10
 
11
11
  display: inline-flex;
12
12
  align-items: flex-start;