@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,15 +1,15 @@
1
1
  import { html } from "lit"
2
- import { fixture, expect } from "@open-wc/testing"
2
+ import { fixture, expect, elementUpdated, oneEvent } from "@open-wc/testing"
3
3
 
4
4
  import "../leu-button.js"
5
5
 
6
6
  async function defaultFixture() {
7
- return fixture(html` <leu-button label="button"></leu-button>`)
7
+ return fixture(html` <leu-button>button</leu-button>`)
8
8
  }
9
9
 
10
10
  describe("LeuButton", () => {
11
11
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-button")
12
+ const el = customElements.get("leu-button")
13
13
 
14
14
  await expect(el).not.to.be.undefined
15
15
  })
@@ -19,4 +19,185 @@ describe("LeuButton", () => {
19
19
 
20
20
  await expect(el).shadowDom.to.be.accessible()
21
21
  })
22
+
23
+ it("passes the a11y audit with no visible text", async () => {
24
+ const el = await fixture(
25
+ html` <leu-button icon="download" label="sichern"></leu-button>`
26
+ )
27
+
28
+ await expect(el).shadowDom.to.be.accessible()
29
+ })
30
+
31
+ it("renders the label", async () => {
32
+ const el = await fixture(html` <leu-button>Sichern</leu-button>`)
33
+
34
+ expect(el).to.have.trimmed.text("Sichern")
35
+ })
36
+
37
+ it("sets the aria-label attribute", async () => {
38
+ const el = await fixture(
39
+ html` <leu-button
40
+ icon="download"
41
+ label="Dokument herunterladen"
42
+ ></leu-button>`
43
+ )
44
+ const button = el.shadowRoot.querySelector("button")
45
+
46
+ expect(button).to.have.attribute("aria-label", "Dokument herunterladen")
47
+ })
48
+
49
+ it("renders the icon at the correct position", async () => {
50
+ const el = await fixture(
51
+ html` <leu-button icon="addNew">Sichern</leu-button>`
52
+ )
53
+
54
+ const button = el.shadowRoot.querySelector("button")
55
+
56
+ expect(button).dom.to.equal(
57
+ "<button><div><svg><path /></svg></div><slot></slot></div>",
58
+ { ignoreAttributes: ["d", "class", "type"] }
59
+ )
60
+
61
+ el.iconPosition = "after"
62
+
63
+ await elementUpdated(el)
64
+
65
+ expect(button).dom.to.equal(
66
+ "<button><slot></slot><div><svg><path /></svg></div></div>",
67
+ { ignoreAttributes: ["d", "class", "type"] }
68
+ )
69
+ })
70
+
71
+ it("renders the icon at the correct size", async () => {
72
+ const el = await fixture(
73
+ html` <leu-button icon="addNew">Sichern</leu-button>`
74
+ )
75
+
76
+ const button = el.shadowRoot.querySelector("button")
77
+
78
+ expect(button).dom.to.equal(
79
+ "<button><div><svg width='24' height='24'><path /></svg></div><slot></slot></div>",
80
+ { ignoreAttributes: ["d", "class", "type"] }
81
+ )
82
+
83
+ el.size = "small"
84
+
85
+ await elementUpdated(el)
86
+
87
+ expect(button).dom.to.equal(
88
+ "<button><div><svg width='16' height='16'><path /></svg></div><slot></slot></div>",
89
+ { ignoreAttributes: ["d", "class", "type"] }
90
+ )
91
+ })
92
+
93
+ it("renders the expanded icon only when the variant is ghost", async () => {
94
+ const el = await fixture(
95
+ html` <leu-button icon="addNew" variant="ghost" expanded="true"
96
+ >Sichern</leu-button
97
+ >`
98
+ )
99
+
100
+ const button = el.shadowRoot.querySelector("button")
101
+
102
+ expect(button).dom.to.equal(
103
+ "<button class='ghost regular'><div class='icon-wrapper icon-wrapper--before'><svg width='24' height='24'><path /></svg></div><slot></slot><div class='icon-wrapper icon-wrapper--expanded'><svg width='24' height='24'><path /></svg></div></div>",
104
+ { ignoreAttributes: ["d", "type", "width", "height"] }
105
+ )
106
+
107
+ el.variant = "primary"
108
+
109
+ await elementUpdated(el)
110
+
111
+ expect(button).dom.to.equal(
112
+ "<button class='primary regular'><div class='icon-wrapper icon-wrapper--before'><svg width='24' height='24'><path /></svg></div><slot></slot></div>",
113
+ { ignoreAttributes: ["d", "type"] }
114
+ )
115
+ })
116
+
117
+ it("sets the dissabled attrbiute", async () => {
118
+ const el = await fixture(
119
+ html` <leu-button
120
+ icon="addNew"
121
+ label="Sichern"
122
+ variant="ghost"
123
+ expanded="true"
124
+ disabled
125
+ ></leu-button>`
126
+ )
127
+
128
+ const button = el.shadowRoot.querySelector("button")
129
+
130
+ expect(button).to.have.attribute("disabled")
131
+
132
+ el.disabled = false
133
+ await elementUpdated(el)
134
+
135
+ expect(button).to.not.have.attribute("disabled")
136
+ })
137
+
138
+ it("reflects the role attribute", async () => {
139
+ const el = await fixture(
140
+ html` <leu-button
141
+ icon="addNew"
142
+ variant="ghost"
143
+ componentRole="menuitemradio"
144
+ >Sichern</leu-button
145
+ >`
146
+ )
147
+
148
+ const button = el.shadowRoot.querySelector("button")
149
+
150
+ expect(button).to.have.attribute("role", "menuitemradio")
151
+ })
152
+
153
+ it("sets the either checked or selected attribute depending on the role", async () => {
154
+ const el = await fixture(
155
+ html` <leu-button
156
+ icon="addNew"
157
+ variant="ghost"
158
+ componentRole="menuitemradio"
159
+ active
160
+ >Sichern</leu-button
161
+ >`
162
+ )
163
+
164
+ const button = el.shadowRoot.querySelector("button")
165
+
166
+ expect(button).to.have.attribute("aria-checked", "true")
167
+ expect(button).to.not.have.attribute("aria-selected")
168
+
169
+ el.componentRole = "tab"
170
+
171
+ await elementUpdated(el)
172
+
173
+ expect(button).to.have.attribute("aria-selected", "true")
174
+ expect(button).to.not.have.attribute("aria-checked")
175
+
176
+ el.componentRole = "checkbox"
177
+ el.active = false
178
+
179
+ await elementUpdated(el)
180
+
181
+ expect(button).to.have.attribute("aria-checked", "false")
182
+ expect(button).to.not.have.attribute("aria-selected")
183
+
184
+ el.componentRole = undefined
185
+ el.active = true
186
+
187
+ await elementUpdated(el)
188
+
189
+ expect(button).to.not.have.attribute("aria-checked")
190
+ expect(button).to.not.have.attribute("aria-selected")
191
+ })
192
+
193
+ it("dispatches the click event", async () => {
194
+ const el = await fixture(html` <leu-button>Sichern</leu-button>`)
195
+ const button = el.shadowRoot.querySelector("button")
196
+
197
+ setTimeout(() => button.click())
198
+
199
+ const event = await oneEvent(el, "click")
200
+
201
+ expect(event).to.exist
202
+ })
22
203
  })
@@ -1,63 +1,99 @@
1
1
  import { html, LitElement } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
2
+ // @ts-ignore
3
3
  import styles from "./button-group.css"
4
- import { defineButtonElements } from "../button/Button.js"
5
4
 
6
5
  /**
7
6
  * @tagname leu-button-group
7
+ * @slot - Slot for the buttons
8
+ * @prop {string} value - The value of the currenty selected (active) button
9
+ * @fires input - When the value of the group changes by clicking a button
8
10
  */
9
11
  export class LeuButtonGroup extends LitElement {
10
12
  static styles = styles
11
13
 
12
- static properties = {
13
- items: { type: Array, reflect: true },
14
- value: { type: String, reflect: true },
15
- }
16
-
17
14
  constructor() {
18
15
  super()
19
- /** @type {Array} */
20
- this.items = []
21
- /** @type {string} */
22
- this.value = null
16
+
17
+ this._items = []
18
+ }
19
+
20
+ /**
21
+ * @param {import("../button/Button").LeuButton} button
22
+ * @returns {string}
23
+ */
24
+ static getButtonValue(button) {
25
+ return button.getAttribute("value") ?? button.innerText.trim()
26
+ }
27
+
28
+ get value() {
29
+ const activeButton = this._items.find((item) => item.active)
30
+ return activeButton ? LeuButtonGroup.getButtonValue(activeButton) : null
31
+ }
32
+
33
+ set value(newValue) {
34
+ this._items.forEach((item) => {
35
+ /* eslint-disable no-param-reassign */
36
+ item.active = LeuButtonGroup.getButtonValue(item) === newValue
37
+ /* eslint-enable no-param-reassign */
38
+ })
23
39
  }
24
40
 
25
- _setValue(newValue) {
26
- this.value = newValue
41
+ _handleSlotChange() {
42
+ /**
43
+ * Remove all event listeners that were added before.
44
+ * Just because a slotchange event was fired, it doesn't mean that all of the
45
+ * children of the slot have changed.
46
+ */
47
+ this._items.forEach((item) => {
48
+ item.removeEventListener("click", this._handleButtonClick)
49
+ })
27
50
 
28
- this.dispatchEvent(
29
- new CustomEvent("input", {
30
- bubbles: true,
31
- composed: true,
32
- detail: { value: newValue },
33
- })
34
- )
51
+ const slot = this.shadowRoot.querySelector("slot")
52
+ this._items = slot.assignedElements({ flatten: true })
53
+
54
+ let foundActiveButtonBefore = false
55
+
56
+ this._items.forEach((item) => {
57
+ /* eslint-disable no-param-reassign */
58
+ item.addEventListener("click", () => this._handleButtonClick(item))
59
+ item.componentRole = "menuitemradio"
60
+
61
+ /**
62
+ * In case there are multiple active buttons
63
+ * only the first one will be kept active.
64
+ */
65
+ if (item.active && foundActiveButtonBefore) {
66
+ item.active = false
67
+ } else if (item.active) {
68
+ foundActiveButtonBefore = true
69
+ }
70
+
71
+ /* eslint-enable no-param-reassign */
72
+ })
73
+ }
74
+
75
+ /**
76
+ * @param {import("../button/Button").LeuButton} button
77
+ */
78
+ _handleButtonClick(button) {
79
+ if (!button.active) {
80
+ this.value = LeuButtonGroup.getButtonValue(button)
81
+
82
+ this.dispatchEvent(
83
+ new CustomEvent("input", {
84
+ bubbles: true,
85
+ composed: true,
86
+ detail: { value: LeuButtonGroup.getButtonValue(button) },
87
+ })
88
+ )
89
+ }
35
90
  }
36
91
 
37
92
  render() {
38
93
  return html`
39
94
  <div role="menubar" class="group">
40
- ${this.items.map(
41
- (item) =>
42
- html`
43
- <leu-button
44
- label=${item}
45
- variant=${this.value === item ? "primary" : "secondary"}
46
- @click=${() => {
47
- this._setValue(item)
48
- }}
49
- role="menuitemradio"
50
- aria-checked=${this.value === item}
51
- >
52
- </leu-button>
53
- `
54
- )}
95
+ <slot @slotchange=${this._handleSlotChange}></slot>
55
96
  </div>
56
97
  `
57
98
  }
58
99
  }
59
-
60
- export function defineButtonGroupElements() {
61
- defineButtonElements()
62
- defineElement("button-group", LeuButtonGroup)
63
- }
@@ -1,3 +1,6 @@
1
- import { defineButtonGroupElements } from "./ButtonGroup.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuButtonGroup } from "./ButtonGroup.js"
2
3
 
3
- defineButtonGroupElements()
4
+ export { LeuButtonGroup }
5
+
6
+ defineElement("button-group", LeuButtonGroup)
@@ -1,5 +1,6 @@
1
1
  import { html } from "lit"
2
2
  import "../leu-button-group.js"
3
+ import "../../button/leu-button.js"
3
4
 
4
5
  // https://stackoverflow.com/questions/72566428/storybook-angular-how-to-dynamically-update-args-from-the-template
5
6
  import { UPDATE_STORY_ARGS } from "@storybook/core-events" // eslint-disable-line
@@ -14,24 +15,36 @@ function updateStorybookArgss(id, args) {
14
15
  export default {
15
16
  title: "ButtonGroup",
16
17
  component: "leu-button-group",
18
+ parameters: {
19
+ design: {
20
+ type: "figma",
21
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=18180-165796&mode=design&t=lzVrtq8lxYVJU5TB-11",
22
+ },
23
+ },
17
24
  }
18
25
 
19
26
  function Template({ items, value }, { id }) {
20
- return html`
21
- <leu-button-group
22
- .items=${items}
27
+ return html` <leu-button-group
23
28
  .value=${value}
24
- @click=${(event) => {
29
+ @input=${(event) => {
25
30
  updateStorybookArgss(id, {
26
31
  value: event.target.value,
27
32
  })
28
33
  }}
29
34
  >
35
+ ${items.map(
36
+ (i) =>
37
+ html`<leu-button
38
+ variant="secondary"
39
+ ?active=${value === i}
40
+ value=${`${i}-attr`}
41
+ >${i}
42
+ </leu-button>`
43
+ )}
30
44
  </leu-button-group>
31
45
  <br />
32
46
  <br />
33
- <pre>value = '${value}'</pre>
34
- `
47
+ <pre>value = '${value}'</pre>`
35
48
  }
36
49
 
37
50
  export const Regular = Template.bind({})
@@ -1,15 +1,28 @@
1
1
  import { html } from "lit"
2
- import { fixture, expect } from "@open-wc/testing"
2
+ import {
3
+ fixture,
4
+ expect,
5
+ oneEvent,
6
+ elementUpdated,
7
+ aTimeout,
8
+ } from "@open-wc/testing"
3
9
 
4
10
  import "../leu-button-group.js"
11
+ import "../../button/leu-button.js"
5
12
 
6
13
  async function defaultFixture() {
7
- return fixture(html` <leu-button-group /> `)
14
+ return fixture(html`
15
+ <leu-button-group>
16
+ <leu-button variant="secondary" value="Eins">Eins</leu-button>
17
+ <leu-button variant="secondary" value="Zweierlei">Zwei</leu-button>
18
+ <leu-button variant="secondary">Drei</leu-button>
19
+ </leu-button-group>
20
+ `)
8
21
  }
9
22
 
10
23
  describe("LeuButtonGroup", () => {
11
24
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-button-group")
25
+ const el = customElements.get("leu-button-group")
13
26
 
14
27
  await expect(el).not.to.be.undefined
15
28
  })
@@ -17,6 +30,76 @@ describe("LeuButtonGroup", () => {
17
30
  it("passes the a11y audit", async () => {
18
31
  const el = await defaultFixture()
19
32
 
20
- await expect(el).shadowDom.to.be.accessible()
33
+ await expect(el).to.be.accessible()
34
+ })
35
+
36
+ it("has no value by default", async () => {
37
+ const el = await defaultFixture()
38
+
39
+ await expect(el.value).to.be.null
40
+ })
41
+
42
+ it("has the correct value after clicking a button", async () => {
43
+ const el = await defaultFixture()
44
+
45
+ const buttons = Array.from(el.querySelectorAll("leu-button"))
46
+
47
+ setTimeout(() => buttons[1].click())
48
+ await oneEvent(el, "input")
49
+ await expect(el.value).to.equal("Zweierlei")
50
+
51
+ setTimeout(() => buttons[0].click())
52
+ await oneEvent(el, "input")
53
+ await expect(el.value).to.equal("Eins")
54
+
55
+ setTimeout(() => buttons[2].click())
56
+ await oneEvent(el, "input")
57
+ await expect(el.value).to.equal("Drei")
58
+
59
+ // Should not change after clicking the same button again
60
+ setTimeout(() => buttons[2].click())
61
+ await aTimeout(100) // There is no event to wait for so
62
+ await expect(el.value).to.equal("Drei")
63
+ })
64
+
65
+ it("sets the active attribute on the active button", async () => {
66
+ const el = await defaultFixture()
67
+ el.value = "Zweierlei"
68
+ await elementUpdated(el)
69
+
70
+ const buttons = el.querySelectorAll("leu-button")
71
+
72
+ await expect(buttons[0].active).to.be.false
73
+ await expect(buttons[1].active).to.be.true
74
+ await expect(buttons[2].active).to.be.false
75
+
76
+ buttons[0].click()
77
+
78
+ await expect(buttons[0].variant).to.equal("secondary")
79
+ await expect(buttons[1].variant).to.equal("secondary")
80
+ await expect(buttons[2].variant).to.equal("secondary")
81
+ })
82
+
83
+ it("sets the menuitemradio role on the buttons", async () => {
84
+ const el = await defaultFixture()
85
+ const buttons = el.querySelectorAll("leu-button")
86
+
87
+ await expect(buttons[0].componentRole).to.equal("menuitemradio")
88
+ await expect(buttons[1].componentRole).to.equal("menuitemradio")
89
+ await expect(buttons[2].componentRole).to.equal("menuitemradio")
90
+ })
91
+
92
+ it("dispatches an input event when the value changes", async () => {
93
+ const el = await defaultFixture()
94
+ el.value = "Drei"
95
+ await elementUpdated(el)
96
+
97
+ const buttons = el.querySelectorAll("leu-button")
98
+
99
+ setTimeout(() => buttons[0].click())
100
+
101
+ const inputEvent = await oneEvent(el, "input")
102
+
103
+ await expect(inputEvent.detail.value).to.be.equal("Eins")
21
104
  })
22
105
  })
@@ -1,89 +1,13 @@
1
- import { html, css, LitElement } from "lit"
1
+ import { html, LitElement } from "lit"
2
2
  import { Icon } from "../icon/icon.js"
3
- import { defineElement } from "../../lib/defineElement.js"
3
+
4
+ import styles from "./checkbox.css"
4
5
 
5
6
  /**
6
7
  * @tagname leu-checkbox
7
8
  */
8
9
  export class LeuCheckbox extends LitElement {
9
- static styles = css`
10
- :host {
11
- --checkbox-color: var(--leu-color-black-40);
12
- --checkbox-color-disabled: var(--leu-color-black-20);
13
- --checkbox-color-focus: var(--leu-color-func-cyan);
14
-
15
- --checkbox-label-color: var(--leu-color-black-100);
16
- --checkbox-label-color-disabled: var(--checkbox-color-disabled);
17
-
18
- --checkbox-tick-color: var(--leu-color-black-0);
19
-
20
- --checkbox-font-regular: var(--leu-font-regular);
21
-
22
- position: relative;
23
-
24
- display: inline-flex;
25
- align-items: flex-start;
26
- gap: 0.5rem;
27
-
28
- font-family: var(--checkbox-font-regular);
29
- }
30
-
31
- .checkbox {
32
- --_length: 1.5rem;
33
- appearance: none;
34
- cursor: pointer;
35
-
36
- width: var(--_length);
37
- height: var(--_length);
38
- margin: 0;
39
-
40
- border: 2px solid var(--checkbox-color);
41
- border-radius: 2px;
42
-
43
- flex: 1 0 var(--_length);
44
-
45
- display: grid;
46
- place-items: center;
47
- }
48
-
49
- .checkbox:checked {
50
- background-color: var(--checkbox-color);
51
- }
52
-
53
- .checkbox:is(:hover, :checked, :focus) {
54
- --checkbox-color: var(--checkbox-color-focus);
55
- }
56
-
57
- .checkbox:focus-visible {
58
- outline: 2px solid var(--checkbox-color-focus);
59
- outline-offset: 2px;
60
- }
61
-
62
- .checkbox:disabled {
63
- --checkbox-color: var(--checkbox-color-disabled);
64
- cursor: not-allowed;
65
- }
66
-
67
- .label {
68
- cursor: pointer;
69
- color: var(--checkbox-label-color);
70
- font-size: 1rem;
71
- line-height: 1.5;
72
- }
73
-
74
- .checkbox:disabled + .label {
75
- --checkbox-label-color: var(--checkbox-label-color-disabled);
76
- cursor: not-allowed;
77
- }
78
-
79
- .icon {
80
- position: absolute;
81
- top: 0;
82
- left: 0;
83
- color: var(--checkbox-tick-color);
84
- pointer-events: none;
85
- }
86
- `
10
+ static styles = styles
87
11
 
88
12
  static shadowRootOptions = {
89
13
  ...LitElement.shadowRootOptions,
@@ -96,6 +20,7 @@ export class LeuCheckbox extends LitElement {
96
20
  identifier: { type: String, reflect: true },
97
21
  value: { type: String, reflect: true },
98
22
  name: { type: String, reflect: true },
23
+ label: { type: String, reflect: true },
99
24
  }
100
25
 
101
26
  constructor() {
@@ -130,12 +55,8 @@ export class LeuCheckbox extends LitElement {
130
55
  ?disabled=${this.disabled}
131
56
  .value=${this.value}
132
57
  />
133
- <label for=${this.identifier} class="label"><slot></slot></label>
58
+ <label for=${this.identifier} class="label">${this.label}</label>
134
59
  <div class="icon">${this.checkIcon}</div>
135
60
  `
136
61
  }
137
62
  }
138
-
139
- export function defineCheckboxElements() {
140
- defineElement("checkbox", LeuCheckbox)
141
- }
@@ -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 "./checkbox-group.css"
4
5
 
5
6
  /**
6
7
  * @tagname leu-checkbox-group
7
8
  */
8
9
  export class LeuCheckboxGroup 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() {
@@ -68,13 +40,11 @@ export class LeuCheckboxGroup extends LitElement {
68
40
 
69
41
  return html`
70
42
  <fieldset class=${classMap(fieldsetClasses)}>
71
- <legend class="legend"><slot name="legend"></slot></legend>
43
+ ${this.label
44
+ ? html`<legend class="legend">${this.label}</legend>`
45
+ : html``}
72
46
  <slot @slotchange=${this.handleSlotChange}></slot>
73
47
  </fieldset>
74
48
  `
75
49
  }
76
50
  }
77
-
78
- export function defineCheckboxGroupElements() {
79
- defineElement("checkbox-group", LeuCheckboxGroup)
80
- }