@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
@@ -14,11 +14,17 @@ export default {
14
14
  size: {
15
15
  control: {
16
16
  type: "select",
17
- options: SIZE_TYPES,
18
17
  },
18
+ options: Object.values(SIZE_TYPES),
19
19
  },
20
20
  icon: { control: "select", options: ICON_NAMES },
21
21
  },
22
+ parameters: {
23
+ design: {
24
+ type: "figma",
25
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17340-81934&mode=design&t=lzVrtq8lxYVJU5TB-11",
26
+ },
27
+ },
22
28
  }
23
29
 
24
30
  function Template(args) {
@@ -36,12 +42,15 @@ function Template(args) {
36
42
  max,
37
43
  minlength,
38
44
  maxlength,
45
+ step,
39
46
  disabled = false,
40
47
  required = false,
41
48
  clearable = false,
42
49
  novalidate = false,
43
50
  } = args
44
51
 
52
+ console.log(min, max)
53
+
45
54
  return html`
46
55
  <leu-input
47
56
  value=${ifDefined(value)}
@@ -56,12 +65,13 @@ function Template(args) {
56
65
  max=${ifDefined(max)}
57
66
  minlength=${ifDefined(minlength)}
58
67
  maxlength=${ifDefined(maxlength)}
68
+ step=${ifDefined(step)}
69
+ label=${label}
59
70
  ?disabled=${disabled}
60
71
  ?required=${required}
61
72
  ?clearable=${clearable}
62
73
  ?novalidate=${novalidate}
63
74
  >
64
- ${label}
65
75
  </leu-input>
66
76
  `
67
77
  }
@@ -183,3 +193,12 @@ Search.parameters = {
183
193
  },
184
194
  },
185
195
  }
196
+
197
+ export const Step = Template.bind({})
198
+ Step.args = {
199
+ label: "Tage",
200
+ type: "number",
201
+ min: "1",
202
+ max: "7",
203
+ step: "1",
204
+ }
@@ -1,15 +1,40 @@
1
1
  import { html } from "lit"
2
- import { fixture, expect } from "@open-wc/testing"
2
+ import { ifDefined } from "lit/directives/if-defined.js"
3
+ import { fixture, expect, elementUpdated } from "@open-wc/testing"
4
+ import { sendKeys } from "@web/test-runner-commands"
5
+ import { spy } from "sinon"
3
6
 
4
7
  import "../leu-input.js"
5
8
 
6
- async function defaultFixture() {
7
- return fixture(html` <leu-input> Name </leu-input> `)
9
+ async function defaultFixture(args = {}) {
10
+ return fixture(html`
11
+ <leu-input
12
+ value=${ifDefined(args.value)}
13
+ error=${ifDefined(args.error)}
14
+ pattern=${ifDefined(args.pattern)}
15
+ prefix=${ifDefined(args.prefix)}
16
+ suffix=${ifDefined(args.suffix)}
17
+ size=${ifDefined(args.size)}
18
+ icon=${ifDefined(args.icon)}
19
+ type=${ifDefined(args.type)}
20
+ min=${ifDefined(args.min)}
21
+ max=${ifDefined(args.max)}
22
+ minlength=${ifDefined(args.minlength)}
23
+ maxlength=${ifDefined(args.maxlength)}
24
+ step=${ifDefined(args.step)}
25
+ label=${args.label || "Label"}
26
+ ?disabled=${args.disabled}
27
+ ?required=${args.required}
28
+ ?clearable=${args.clearable}
29
+ ?novalidate=${args.novalidate}
30
+ >
31
+ </leu-input>
32
+ `)
8
33
  }
9
34
 
10
35
  describe("LeuInput", () => {
11
36
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-input")
37
+ const el = customElements.get("leu-input")
13
38
 
14
39
  await expect(el).not.to.be.undefined
15
40
  })
@@ -19,4 +44,407 @@ describe("LeuInput", () => {
19
44
 
20
45
  await expect(el).shadowDom.to.be.accessible()
21
46
  })
47
+
48
+ it("delegates the focus to the input element", async () => {
49
+ const el = await defaultFixture()
50
+
51
+ const input = el.shadowRoot.querySelector("input")
52
+
53
+ el.focus()
54
+
55
+ expect(el.shadowRoot.activeElement).to.equal(input)
56
+ })
57
+
58
+ it("can't be focused when disabled", async () => {
59
+ const el = await defaultFixture({ disabled: true })
60
+
61
+ el.focus()
62
+
63
+ expect(el.shadowRoot.activeElement).to.be.null
64
+ })
65
+
66
+ it("renders the defined label", async () => {
67
+ const el = await defaultFixture({ label: "Vorname" })
68
+
69
+ const label = el.shadowRoot.querySelector("label")
70
+
71
+ expect(label).to.have.text("Vorname")
72
+ })
73
+
74
+ it("has type text by default", async () => {
75
+ const el = await defaultFixture()
76
+
77
+ expect(el.type).to.equal("text")
78
+ })
79
+
80
+ it("passes the defined type to the input element", async () => {
81
+ const el = await defaultFixture({ type: "email" })
82
+
83
+ const input = el.shadowRoot.querySelector("input")
84
+
85
+ expect(input.type).to.equal("email")
86
+
87
+ el.type = "password"
88
+ await elementUpdated(el)
89
+
90
+ expect(input.type).to.equal("password")
91
+ })
92
+
93
+ it("passes the defined value to the input element", async () => {
94
+ const el = await defaultFixture({ label: "Vorname", value: "John" })
95
+
96
+ const input = el.shadowRoot.querySelector("input")
97
+
98
+ expect(input.value).to.equal("John")
99
+
100
+ el.value = "Jane"
101
+ await elementUpdated(el)
102
+
103
+ expect(input.value).to.equal("Jane")
104
+ })
105
+
106
+ it("syncs the value property of the input and the value property of the component", async () => {
107
+ const el = await defaultFixture({ label: "Vorname", value: "John" })
108
+
109
+ const input = el.shadowRoot.querySelector("input")
110
+
111
+ el.focus()
112
+
113
+ await sendKeys({ press: "Backspace" })
114
+ await sendKeys({ press: "Backspace" })
115
+ await sendKeys({ press: "Backspace" })
116
+ await sendKeys({ press: "Backspace" })
117
+ await sendKeys({ type: "Jane" })
118
+ await elementUpdated(el)
119
+
120
+ expect(el.value).to.equal("Jane")
121
+ expect(input.value).to.equal("Jane")
122
+ })
123
+
124
+ it("doesn't accept letters when type is set to number", async () => {
125
+ const el = await defaultFixture({ label: "Länge", type: "number" })
126
+
127
+ const input = el.shadowRoot.querySelector("input")
128
+
129
+ el.focus()
130
+
131
+ await sendKeys({ type: "123" })
132
+ await elementUpdated(el)
133
+
134
+ expect(el.value).to.equal("123")
135
+ expect(input.value).to.equal("123")
136
+
137
+ await sendKeys({ type: "abc" })
138
+ await elementUpdated(el)
139
+
140
+ expect(el.value).to.not.equal("123abc")
141
+ expect(input.value).to.not.equal("123abc")
142
+ })
143
+
144
+ it("fires a change event after losing focus", async () => {
145
+ const el = await defaultFixture({ label: "Vorname" })
146
+
147
+ const input = el.shadowRoot.querySelector("input")
148
+
149
+ const changeSpy = spy()
150
+ input.addEventListener("change", changeSpy)
151
+
152
+ el.focus()
153
+
154
+ await sendKeys({ type: "John" })
155
+ await sendKeys({ press: "Tab" })
156
+
157
+ expect(changeSpy).to.have.been.calledOnce
158
+ })
159
+
160
+ it("fires a input event while typing ", async () => {
161
+ const el = await defaultFixture({ label: "Vorname", maxlength: 3 })
162
+
163
+ const input = el.shadowRoot.querySelector("input")
164
+
165
+ const inputSpy = spy()
166
+ input.addEventListener("input", inputSpy)
167
+
168
+ el.focus()
169
+
170
+ await sendKeys({ type: "John" })
171
+
172
+ // Should fire 3 times, because maxlength is set to 3
173
+ expect(inputSpy).to.have.been.called.calledThrice
174
+ })
175
+
176
+ it("fires a input event while typing ", async () => {})
177
+
178
+ it("renders a prefix", async () => {
179
+ const el = await defaultFixture({ label: "Preis", prefix: "CHF" })
180
+
181
+ const prefix = el.shadowRoot.querySelector(".prefix")
182
+
183
+ expect(prefix).to.have.text("CHF")
184
+ })
185
+
186
+ it("renders a suffix", async () => {
187
+ const el = await defaultFixture({ label: "Länge", suffix: "cm" })
188
+
189
+ const suffix = el.shadowRoot.querySelector(".suffix")
190
+
191
+ expect(suffix).to.have.text("cm")
192
+ })
193
+
194
+ it("renders an icon", async () => {
195
+ const _el = await defaultFixture({ label: "Vorname", icon: "user" })
196
+ })
197
+
198
+ it("renders a clear button", async () => {
199
+ const el = await defaultFixture({ label: "Vorname", clearable: true })
200
+
201
+ let clearButton = el.shadowRoot.querySelector(".clear-button")
202
+ expect(clearButton).to.not.exist
203
+
204
+ el.focus()
205
+ await sendKeys({ type: "John" })
206
+
207
+ clearButton = el.shadowRoot.querySelector(".clear-button")
208
+ expect(clearButton).to.not.be.null
209
+ })
210
+
211
+ it("clears the value when clicking the clear button", async () => {
212
+ const el = await defaultFixture({ label: "Vorname", clearable: true })
213
+
214
+ el.focus()
215
+
216
+ await sendKeys({ type: "John" })
217
+ await elementUpdated(el)
218
+
219
+ const clearButton = el.shadowRoot.querySelector(".clear-button")
220
+ clearButton.click()
221
+
222
+ expect(el.value).to.equal("")
223
+ })
224
+
225
+ it("renders an error message when value is less than min", async () => {
226
+ const el = await defaultFixture({ label: "Länge", min: 10, type: "number" })
227
+
228
+ el.focus()
229
+
230
+ await sendKeys({ type: "5" })
231
+ await sendKeys({ press: "Tab" })
232
+ await elementUpdated(el)
233
+
234
+ const error = el.shadowRoot.querySelector(".error")
235
+
236
+ expect(error).not.to.be.null
237
+ })
238
+
239
+ it("renders an error message when value greater than max", async () => {
240
+ const el = await defaultFixture({ label: "Länge", max: 10, type: "number" })
241
+
242
+ el.focus()
243
+
244
+ await sendKeys({ type: "15" })
245
+ await sendKeys({ press: "Tab" })
246
+ await elementUpdated(el)
247
+
248
+ const error = el.shadowRoot.querySelector(".error")
249
+
250
+ expect(error).not.to.be.null
251
+ })
252
+
253
+ it("renders an error message when the value doesn't match the step", async () => {
254
+ const el = await defaultFixture({
255
+ label: "Länge",
256
+ step: 10,
257
+ type: "number",
258
+ })
259
+
260
+ el.focus()
261
+
262
+ await sendKeys({ type: "15" })
263
+ await sendKeys({ press: "Tab" })
264
+ await elementUpdated(el)
265
+
266
+ const error = el.shadowRoot.querySelector(".error")
267
+
268
+ expect(error).not.to.be.null
269
+ })
270
+
271
+ it("renders an error message when value is shorter than minlength", async () => {
272
+ const el = await defaultFixture({
273
+ label: "Vorname",
274
+ minlength: 3,
275
+ })
276
+
277
+ el.focus()
278
+
279
+ await sendKeys({ type: "Jo" })
280
+ await sendKeys({ press: "Tab" })
281
+ await elementUpdated(el)
282
+
283
+ const error = el.shadowRoot.querySelector(".error")
284
+
285
+ expect(error).not.to.be.null
286
+ })
287
+
288
+ it("renders an error message when value is longer than maxlength", async () => {
289
+ const el = await defaultFixture({
290
+ label: "Vorname",
291
+ maxlength: 10,
292
+ value: "Andrea Gabathuler",
293
+ })
294
+
295
+ el.focus()
296
+
297
+ /* Remove the selection, if there is one */
298
+ await sendKeys({ press: "ArrowRight" })
299
+ /*
300
+ * Trigger an update of the value that is too long.
301
+ * Browser won't allow to type more than maxlength.
302
+ */
303
+ await sendKeys({ press: "Backspace" })
304
+ await sendKeys({ press: "Tab" })
305
+ await elementUpdated(el)
306
+
307
+ const error = el.shadowRoot.querySelector(".error")
308
+
309
+ expect(error).not.to.be.null
310
+ })
311
+
312
+ it("renders an error message when value doesn't match pattern", async () => {
313
+ const el = await defaultFixture({
314
+ label: "Vorname",
315
+ pattern: "([A-Z]{2}-)?d{4,5}", // Pseudo zip code e.g. CH-8000 or 8000 or DE-12345
316
+ })
317
+
318
+ el.focus()
319
+
320
+ await sendKeys({ type: "123" })
321
+ await sendKeys({ press: "Tab" })
322
+ await elementUpdated(el)
323
+
324
+ let error = el.shadowRoot.querySelector(".error")
325
+ expect(error).not.to.be.null
326
+
327
+ el.value = ""
328
+ await elementUpdated(el)
329
+
330
+ el.focus()
331
+
332
+ await sendKeys({ type: "DE-987" })
333
+ await sendKeys({ press: "Tab" })
334
+ await elementUpdated(el)
335
+
336
+ error = el.shadowRoot.querySelector(".error")
337
+ expect(error).not.to.be.null
338
+ })
339
+
340
+ it("renders an error message when value is required but is empty", async () => {
341
+ const el = await defaultFixture({
342
+ label: "Vorname",
343
+ required: true,
344
+ })
345
+
346
+ el.focus()
347
+
348
+ await sendKeys({ press: "Tab" })
349
+ await elementUpdated(el)
350
+
351
+ const error = el.shadowRoot.querySelector(".error")
352
+
353
+ expect(error).not.to.be.null
354
+ })
355
+
356
+ it("renders a custom error message", async () => {
357
+ const el = await defaultFixture({
358
+ label: "Vorname",
359
+ error: "Bitte geben Sie einen Vornamen ein.",
360
+ })
361
+
362
+ el.focus()
363
+
364
+ await sendKeys({ press: "Tab" })
365
+ await elementUpdated(el)
366
+
367
+ const error = el.shadowRoot.querySelector(".error")
368
+
369
+ expect(error).to.have.trimmed.text("Bitte geben Sie einen Vornamen ein.")
370
+ })
371
+
372
+ it("resets the error message as soon as value is valid again", async () => {
373
+ const el = await defaultFixture({
374
+ label: "Vorname",
375
+ required: true,
376
+ })
377
+
378
+ el.focus()
379
+
380
+ await sendKeys({ press: "Tab" })
381
+ await elementUpdated(el)
382
+
383
+ let error = el.shadowRoot.querySelector(".error")
384
+ expect(error).not.to.be.null
385
+
386
+ el.focus()
387
+
388
+ await sendKeys({ type: "Jacqueline" })
389
+ await sendKeys({ press: "Tab" })
390
+ await elementUpdated(el)
391
+
392
+ error = el.shadowRoot.querySelector(".error")
393
+ expect(error).to.be.null
394
+ })
395
+
396
+ it("doesn't render an error message when novalidate is set", async () => {
397
+ const el = await defaultFixture({
398
+ label: "Vorname",
399
+ required: true,
400
+ novalidate: true,
401
+ })
402
+
403
+ el.focus()
404
+
405
+ await sendKeys({ press: "Tab" })
406
+ await elementUpdated(el)
407
+
408
+ const error = el.shadowRoot.querySelector(".error")
409
+
410
+ expect(error).to.be.null
411
+ })
412
+
413
+ it("shows only one 'after' element", async () => {
414
+ const el = await defaultFixture({
415
+ label: "Länge",
416
+ type: "number",
417
+ min: 50,
418
+ icon: "user",
419
+ clearable: true,
420
+ })
421
+
422
+ const getErrorIcon = () => el.shadowRoot.querySelector(".error-icon")
423
+ const getClearButton = () => el.shadowRoot.querySelector(".clear-button")
424
+ const getIcon = () => el.shadowRoot.querySelector(".icon")
425
+
426
+ /* Priority: Error > Clear > Icon */
427
+ expect(getErrorIcon()).to.be.null
428
+ expect(getClearButton()).to.be.null
429
+ expect(getIcon()).not.to.be.null
430
+
431
+ el.focus()
432
+ await sendKeys({ type: "60" })
433
+
434
+ expect(getErrorIcon()).to.be.null
435
+ expect(getClearButton()).not.to.be.null
436
+ expect(getIcon()).to.be.null
437
+
438
+ el.value = ""
439
+ await elementUpdated(el)
440
+
441
+ el.focus()
442
+ await sendKeys({ type: "40" })
443
+ await sendKeys({ press: "Tab" })
444
+ await elementUpdated(el)
445
+
446
+ expect(getErrorIcon()).not.to.be.null
447
+ expect(getClearButton()).to.be.null
448
+ expect(getIcon()).to.be.null
449
+ })
22
450
  })
@@ -1,5 +1,4 @@
1
1
  import { html, LitElement } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
3
2
  import styles from "./menu.css"
4
3
 
5
4
  /**
@@ -12,7 +11,3 @@ export class LeuMenu extends LitElement {
12
11
  return html`<slot></slot>`
13
12
  }
14
13
  }
15
-
16
- export function defineMenuElements() {
17
- defineElement("menu", LeuMenu)
18
- }
@@ -1,5 +1,7 @@
1
- import { html, LitElement, nothing } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
1
+ import { LitElement, nothing } from "lit"
2
+ import { html, unsafeStatic } from "lit/static-html.js"
3
+ import { ifDefined } from "lit/directives/if-defined.js"
4
+
3
5
  import styles from "./menu-item.css"
4
6
 
5
7
  import { Icon, ICON_NAMES } from "../icon/icon.js"
@@ -35,6 +37,8 @@ export class LeuMenuItem extends LitElement {
35
37
  active: { type: Boolean, reflect: true },
36
38
  highlighted: { type: Boolean, reflect: true },
37
39
  disabled: { type: Boolean, reflect: true },
40
+ label: { type: String, reflect: true },
41
+ href: { type: String, reflect: true },
38
42
  }
39
43
 
40
44
  constructor() {
@@ -42,8 +46,6 @@ export class LeuMenuItem extends LitElement {
42
46
 
43
47
  this.active = false
44
48
  this.disabled = false
45
- this.before = ""
46
- this.after = ""
47
49
 
48
50
  /**
49
51
  * A programmatic way to highlight the menu item like it is hovered.
@@ -65,7 +67,7 @@ export class LeuMenuItem extends LitElement {
65
67
  }
66
68
 
67
69
  renderBefore() {
68
- if (this.before !== "") {
70
+ if (this.before) {
69
71
  const content = LeuMenuItem.getIconOrText(this.before)
70
72
  return html`<span class="before">${content}</span>`
71
73
  }
@@ -74,7 +76,7 @@ export class LeuMenuItem extends LitElement {
74
76
  }
75
77
 
76
78
  renderAfter() {
77
- if (this.after !== "") {
79
+ if (this.after) {
78
80
  const content = LeuMenuItem.getIconOrText(this.after)
79
81
  return html`<span class="after">${content}</span>`
80
82
  }
@@ -82,14 +84,19 @@ export class LeuMenuItem extends LitElement {
82
84
  return nothing
83
85
  }
84
86
 
87
+ getTagName() {
88
+ return this.href ? "a" : "button"
89
+ }
90
+
85
91
  render() {
86
- return html`<button class="button" ?disabled=${this.disabled}>
87
- ${this.renderBefore()}<span class="label"><slot></slot></span
92
+ /* The eslint rules don't recognize html import from lit/static-html.js */
93
+ /* eslint-disable lit/binding-positions, lit/no-invalid-html */
94
+ return html`<${unsafeStatic(
95
+ this.getTagName()
96
+ )} class="button" href=${ifDefined(this.href)} ?disabled=${this.disabled}>
97
+ ${this.renderBefore()}<span class="label">${this.label}</span
88
98
  >${this.renderAfter()}
89
- </button>`
99
+ </${unsafeStatic(this.getTagName())}>`
100
+ /* eslint-enable lit/binding-positions, lit/no-invalid-html */
90
101
  }
91
102
  }
92
-
93
- export function defineMenuItemElements() {
94
- defineElement("menu-item", LeuMenuItem)
95
- }
@@ -1,3 +1,6 @@
1
- import { defineMenuItemElements } from "./MenuItem.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuMenuItem } from "./MenuItem.js"
2
3
 
3
- defineMenuItemElements()
4
+ export { LeuMenuItem }
5
+
6
+ defineElement("menu-item", LeuMenuItem)
@@ -1,3 +1,6 @@
1
- import { defineMenuElements } from "./Menu.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuMenu } from "./Menu.js"
2
3
 
3
- defineMenuElements()
4
+ export { LeuMenu }
5
+
6
+ defineElement("menu", LeuMenu)
@@ -7,15 +7,17 @@
7
7
  --background: var(--leu-color-black-0);
8
8
  --background-hover: var(--leu-color-black-10);
9
9
  --background-active: var(--leu-color-func-cyan);
10
- --background-disabled: var(--leu-color-black-0);
10
+ --background-disabled: var(--leu-color-black-black-0);
11
11
  --color: var(--leu-color-black-transp-60);
12
- --font-regular: var(--leu-font-regular);
13
- --font-black: var(--leu-font-black);
12
+ --color-disabled: var(--leu-color-black-transp-20);
13
+ --font-regular: var(--leu-font-family-regular);
14
+ --font-black: var(--leu-font-family-black);
14
15
 
15
- font-family: var(--chip-font-regular);
16
+ font-family: var(--leu-font-family-regular);
16
17
  }
17
18
 
18
19
  .button {
20
+ text-decoration: none;
19
21
  appearance: none;
20
22
  border: none;
21
23
  cursor: pointer;
@@ -51,6 +53,7 @@
51
53
 
52
54
  :host([disabled]) .button {
53
55
  --background: var(--background-disabled);
56
+ --color: var(--color-disabled);
54
57
  cursor: default;
55
58
  }
56
59
 
@@ -9,16 +9,24 @@ export default {
9
9
  args: {
10
10
  label: "Menu Item",
11
11
  },
12
+ parameters: {
13
+ design: {
14
+ type: "figma",
15
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17340-82208&mode=design&t=lzVrtq8lxYVJU5TB-11",
16
+ },
17
+ },
12
18
  }
13
19
 
14
20
  function Template(args) {
15
21
  return html`
16
22
  <leu-menu-item
23
+ label=${args.label}
17
24
  before=${ifDefined(args.before)}
18
25
  after=${ifDefined(args.after)}
26
+ href=${ifDefined(args.href)}
19
27
  ?active=${args.active}
20
- >${args.label}</leu-menu-item
21
- >
28
+ ?disabled=${args.disabled}
29
+ ></leu-menu-item>
22
30
  `
23
31
  }
24
32
 
@@ -34,9 +42,10 @@ IconBefore.args = {
34
42
  before: "check",
35
43
  }
36
44
 
37
- export const IconAfter = Template.bind({})
38
- IconAfter.args = {
45
+ export const IconAfterLink = Template.bind({})
46
+ IconAfterLink.args = {
39
47
  after: "arrowRight",
48
+ href: "https://www.zh.ch",
40
49
  }
41
50
 
42
51
  export const IconAndTextLabel = Template.bind({})