@statistikzh/leu 0.5.1 → 0.7.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 (236) hide show
  1. package/.husky/commit-msg +0 -3
  2. package/.husky/pre-commit +0 -3
  3. package/CHANGELOG.md +54 -0
  4. package/dist/Accordion.d.ts +10 -9
  5. package/dist/Accordion.d.ts.map +1 -1
  6. package/dist/Accordion.js +12 -11
  7. package/dist/Breadcrumb.d.ts +4 -4
  8. package/dist/Breadcrumb.d.ts.map +1 -1
  9. package/dist/Breadcrumb.js +28 -24
  10. package/dist/{Button-5326c982.d.ts → Button-7370f901.d.ts} +10 -11
  11. package/dist/Button-7370f901.d.ts.map +1 -0
  12. package/dist/{Button-5326c982.js → Button-7370f901.js} +57 -67
  13. package/dist/Button.d.ts +1 -1
  14. package/dist/Button.js +3 -3
  15. package/dist/ButtonGroup.d.ts +2 -2
  16. package/dist/ButtonGroup.d.ts.map +1 -1
  17. package/dist/ButtonGroup.js +3 -3
  18. package/dist/Checkbox.d.ts +4 -3
  19. package/dist/Checkbox.d.ts.map +1 -1
  20. package/dist/Checkbox.js +14 -17
  21. package/dist/CheckboxGroup.d.ts +2 -2
  22. package/dist/CheckboxGroup.d.ts.map +1 -1
  23. package/dist/CheckboxGroup.js +4 -4
  24. package/dist/Chip.d.ts +2 -2
  25. package/dist/Chip.d.ts.map +1 -1
  26. package/dist/Chip.js +23 -28
  27. package/dist/ChipGroup.d.ts +16 -8
  28. package/dist/ChipGroup.d.ts.map +1 -1
  29. package/dist/ChipGroup.js +39 -9
  30. package/dist/ChipLink.d.ts +2 -1
  31. package/dist/ChipLink.d.ts.map +1 -1
  32. package/dist/ChipLink.js +4 -7
  33. package/dist/ChipRemovable.d.ts +0 -2
  34. package/dist/ChipRemovable.d.ts.map +1 -1
  35. package/dist/ChipRemovable.js +8 -11
  36. package/dist/ChipSelectable.d.ts +12 -2
  37. package/dist/ChipSelectable.d.ts.map +1 -1
  38. package/dist/ChipSelectable.js +24 -26
  39. package/dist/Dropdown.d.ts +9 -5
  40. package/dist/Dropdown.d.ts.map +1 -1
  41. package/dist/Dropdown.js +68 -32
  42. package/dist/Icon.d.ts +116 -0
  43. package/dist/Icon.d.ts.map +1 -0
  44. package/dist/{icon-03e86700.js → Icon.js} +61 -32
  45. package/dist/Input.d.ts +13 -17
  46. package/dist/Input.d.ts.map +1 -1
  47. package/dist/Input.js +33 -24
  48. package/dist/LeuElement-ba5ea33d.d.ts +7 -0
  49. package/dist/LeuElement-ba5ea33d.d.ts.map +1 -0
  50. package/dist/{_rollupPluginBabelHelpers-20f659f4.js → LeuElement-ba5ea33d.js} +20 -1
  51. package/dist/Menu.d.ts +24 -2
  52. package/dist/Menu.d.ts.map +1 -1
  53. package/dist/Menu.js +120 -3
  54. package/dist/MenuItem.d.ts +28 -11
  55. package/dist/MenuItem.d.ts.map +1 -1
  56. package/dist/MenuItem.js +110 -63
  57. package/dist/Pagination.d.ts +10 -3
  58. package/dist/Pagination.d.ts.map +1 -1
  59. package/dist/Pagination.js +24 -21
  60. package/dist/Popup.d.ts +21 -3
  61. package/dist/Popup.d.ts.map +1 -1
  62. package/dist/Popup.js +44 -17
  63. package/dist/Radio.d.ts +4 -2
  64. package/dist/Radio.d.ts.map +1 -1
  65. package/dist/Radio.js +9 -14
  66. package/dist/RadioGroup.d.ts +2 -2
  67. package/dist/RadioGroup.d.ts.map +1 -1
  68. package/dist/RadioGroup.js +20 -11
  69. package/dist/ScrollTop.d.ts +2 -2
  70. package/dist/ScrollTop.d.ts.map +1 -1
  71. package/dist/ScrollTop.js +10 -8
  72. package/dist/Select.d.ts +75 -37
  73. package/dist/Select.d.ts.map +1 -1
  74. package/dist/Select.js +279 -181
  75. package/dist/Table.d.ts +2 -6
  76. package/dist/Table.d.ts.map +1 -1
  77. package/dist/Table.js +16 -16
  78. package/dist/VisuallyHidden.d.ts +2 -2
  79. package/dist/VisuallyHidden.d.ts.map +1 -1
  80. package/dist/VisuallyHidden.js +3 -3
  81. package/dist/index.d.ts +2 -2
  82. package/dist/index.js +5 -14
  83. package/dist/leu-accordion.d.ts.map +1 -1
  84. package/dist/leu-accordion.js +2 -3
  85. package/dist/leu-breadcrumb.d.ts.map +1 -1
  86. package/dist/leu-breadcrumb.js +4 -10
  87. package/dist/leu-button-group.d.ts.map +1 -1
  88. package/dist/leu-button-group.js +2 -3
  89. package/dist/leu-button.d.ts +1 -1
  90. package/dist/leu-button.d.ts.map +1 -1
  91. package/dist/leu-button.js +4 -5
  92. package/dist/leu-checkbox-group.d.ts.map +1 -1
  93. package/dist/leu-checkbox-group.js +2 -3
  94. package/dist/leu-checkbox.d.ts.map +1 -1
  95. package/dist/leu-checkbox.js +3 -4
  96. package/dist/leu-chip-group.d.ts.map +1 -1
  97. package/dist/leu-chip-group.js +2 -3
  98. package/dist/leu-chip-link.d.ts.map +1 -1
  99. package/dist/leu-chip-link.js +2 -3
  100. package/dist/leu-chip-removable.d.ts.map +1 -1
  101. package/dist/leu-chip-removable.js +3 -4
  102. package/dist/leu-chip-selectable.d.ts.map +1 -1
  103. package/dist/leu-chip-selectable.js +2 -3
  104. package/dist/leu-dropdown.d.ts.map +1 -1
  105. package/dist/leu-dropdown.js +5 -10
  106. package/dist/leu-icon.d.ts +3 -0
  107. package/dist/leu-icon.d.ts.map +1 -0
  108. package/dist/leu-icon.js +7 -0
  109. package/dist/leu-input.d.ts.map +1 -1
  110. package/dist/leu-input.js +3 -4
  111. package/dist/leu-menu-item.d.ts.map +1 -1
  112. package/dist/leu-menu-item.js +3 -5
  113. package/dist/leu-menu.d.ts.map +1 -1
  114. package/dist/leu-menu.js +5 -3
  115. package/dist/leu-pagination.d.ts.map +1 -1
  116. package/dist/leu-pagination.js +4 -7
  117. package/dist/leu-popup.d.ts.map +1 -1
  118. package/dist/leu-popup.js +2 -3
  119. package/dist/leu-radio-group.d.ts.map +1 -1
  120. package/dist/leu-radio-group.js +2 -3
  121. package/dist/leu-radio.d.ts.map +1 -1
  122. package/dist/leu-radio.js +2 -3
  123. package/dist/leu-scroll-top.d.ts.map +1 -1
  124. package/dist/leu-scroll-top.js +4 -6
  125. package/dist/leu-select.d.ts.map +1 -1
  126. package/dist/leu-select.js +5 -13
  127. package/dist/leu-table.d.ts.map +1 -1
  128. package/dist/leu-table.js +4 -8
  129. package/dist/leu-visually-hidden.d.ts.map +1 -1
  130. package/dist/leu-visually-hidden.js +2 -3
  131. package/dist/theme.css +2 -0
  132. package/dist/vscode.html-custom-data.json +124 -74
  133. package/dist/vue/index.d.ts +83 -67
  134. package/dist/web-types.json +256 -142
  135. package/package.json +9 -12
  136. package/scripts/generate-component/templates/[Name].js +6 -3
  137. package/scripts/generate-component/templates/test/[name].test.js +1 -1
  138. package/src/components/accordion/Accordion.js +13 -10
  139. package/src/components/accordion/leu-accordion.js +1 -2
  140. package/src/components/breadcrumb/Breadcrumb.js +31 -18
  141. package/src/components/breadcrumb/leu-breadcrumb.js +1 -2
  142. package/src/components/button/Button.js +45 -71
  143. package/src/components/button/button.css +11 -9
  144. package/src/components/button/leu-button.js +1 -2
  145. package/src/components/button/stories/button.stories.js +60 -19
  146. package/src/components/button/test/button.test.js +26 -63
  147. package/src/components/button-group/ButtonGroup.js +4 -2
  148. package/src/components/button-group/leu-button-group.js +1 -2
  149. package/src/components/checkbox/Checkbox.js +17 -11
  150. package/src/components/checkbox/CheckboxGroup.js +6 -3
  151. package/src/components/checkbox/leu-checkbox-group.js +1 -2
  152. package/src/components/checkbox/leu-checkbox.js +1 -2
  153. package/src/components/checkbox/stories/checkbox-group.stories.js +10 -26
  154. package/src/components/checkbox/stories/checkbox.stories.js +2 -7
  155. package/src/components/checkbox/test/checkbox-group.test.js +6 -21
  156. package/src/components/checkbox/test/checkbox.test.js +1 -12
  157. package/src/components/chip/Chip.js +5 -4
  158. package/src/components/chip/ChipGroup.js +38 -8
  159. package/src/components/chip/ChipLink.js +3 -7
  160. package/src/components/chip/ChipRemovable.js +8 -11
  161. package/src/components/chip/ChipSelectable.js +23 -27
  162. package/src/components/chip/chip.css +19 -20
  163. package/src/components/chip/leu-chip-group.js +1 -2
  164. package/src/components/chip/leu-chip-link.js +1 -2
  165. package/src/components/chip/leu-chip-removable.js +1 -2
  166. package/src/components/chip/leu-chip-selectable.js +1 -2
  167. package/src/components/chip/stories/chip-group.stories.js +6 -9
  168. package/src/components/chip/stories/chip-link.stories.js +3 -5
  169. package/src/components/chip/stories/chip-removable.stories.js +3 -4
  170. package/src/components/chip/stories/chip-selectable.stories.js +3 -3
  171. package/src/components/chip/test/chip-group.test.js +82 -30
  172. package/src/components/chip/test/chip-link.test.js +2 -6
  173. package/src/components/chip/test/chip-removable.test.js +4 -10
  174. package/src/components/chip/test/chip-selectable.test.js +10 -12
  175. package/src/components/dropdown/Dropdown.js +79 -26
  176. package/src/components/dropdown/leu-dropdown.js +1 -2
  177. package/src/components/dropdown/stories/dropdown.stories.js +30 -7
  178. package/src/components/dropdown/test/dropdown.test.js +5 -5
  179. package/src/components/icon/Icon.js +55 -0
  180. package/src/components/icon/icon.css +6 -0
  181. package/src/components/icon/leu-icon.js +5 -0
  182. package/src/components/icon/{icon.js → paths.js} +4 -37
  183. package/src/components/icon/stories/icon.stories.js +47 -0
  184. package/src/components/icon/test/icon.test.js +23 -40
  185. package/src/components/input/Input.js +31 -20
  186. package/src/components/input/input.css +4 -2
  187. package/src/components/input/leu-input.js +1 -2
  188. package/src/components/input/stories/input.stories.js +5 -5
  189. package/src/components/input/test/input.test.js +22 -0
  190. package/src/components/menu/Menu.js +143 -2
  191. package/src/components/menu/MenuItem.js +104 -52
  192. package/src/components/menu/leu-menu-item.js +1 -2
  193. package/src/components/menu/leu-menu.js +1 -2
  194. package/src/components/menu/menu-item.css +11 -4
  195. package/src/components/menu/stories/menu-item.stories.js +15 -4
  196. package/src/components/menu/stories/menu.stories.js +34 -7
  197. package/src/components/menu/test/menu-item.test.js +88 -82
  198. package/src/components/menu/test/menu.test.js +101 -8
  199. package/src/components/pagination/Pagination.js +27 -18
  200. package/src/components/pagination/leu-pagination.js +1 -2
  201. package/src/components/popup/Popup.js +39 -16
  202. package/src/components/popup/leu-popup.js +1 -2
  203. package/src/components/popup/popup.css +1 -0
  204. package/src/components/radio/Radio.js +12 -7
  205. package/src/components/radio/RadioGroup.js +18 -12
  206. package/src/components/radio/leu-radio-group.js +1 -2
  207. package/src/components/radio/leu-radio.js +1 -2
  208. package/src/components/radio/stories/radio-group.stories.js +5 -19
  209. package/src/components/radio/stories/radio.stories.js +2 -7
  210. package/src/components/radio/test/radio-group.test.js +6 -9
  211. package/src/components/radio/test/radio.test.js +3 -13
  212. package/src/components/scroll-top/ScrollTop.js +15 -5
  213. package/src/components/scroll-top/leu-scroll-top.js +1 -2
  214. package/src/components/select/Select.js +279 -175
  215. package/src/components/select/leu-select.js +1 -2
  216. package/src/components/select/select.css +20 -12
  217. package/src/components/select/stories/select.stories.js +16 -2
  218. package/src/components/select/test/select.test.js +191 -37
  219. package/src/components/table/Table.js +15 -9
  220. package/src/components/table/leu-table.js +1 -2
  221. package/src/components/table/table.css +3 -1
  222. package/src/components/visually-hidden/VisuallyHidden.js +6 -2
  223. package/src/components/visually-hidden/leu-visually-hidden.js +1 -2
  224. package/src/lib/LeuElement.js +23 -0
  225. package/src/lib/a11y.js +26 -0
  226. package/src/styles/custom-properties.css +2 -0
  227. package/web-test-runner.config.mjs +2 -0
  228. package/dist/Button-5326c982.d.ts.map +0 -1
  229. package/dist/_rollupPluginBabelHelpers-20f659f4.d.ts +0 -3
  230. package/dist/_rollupPluginBabelHelpers-20f659f4.d.ts.map +0 -1
  231. package/dist/defineElement-40372b4b.d.ts +0 -9
  232. package/dist/defineElement-40372b4b.d.ts.map +0 -1
  233. package/dist/defineElement-40372b4b.js +0 -15
  234. package/dist/icon-03e86700.d.ts +0 -11
  235. package/dist/icon-03e86700.d.ts.map +0 -1
  236. package/src/lib/defineElement.js +0 -13
@@ -1,6 +1,8 @@
1
1
  import { html } from "lit"
2
2
  import "../leu-menu.js"
3
3
  import "../leu-menu-item.js"
4
+ import "../../icon/leu-icon.js"
5
+ import { ifDefined } from "lit/directives/if-defined.js"
4
6
 
5
7
  export default {
6
8
  title: "Menu",
@@ -11,16 +13,41 @@ export default {
11
13
  url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17340-82208&mode=design&t=lzVrtq8lxYVJU5TB-11",
12
14
  },
13
15
  },
16
+ argTypes: {
17
+ selects: {
18
+ control: "select",
19
+ options: ["single", "multiple"],
20
+ },
21
+ role: {
22
+ control: "select",
23
+ options: ["menu", "listbox"],
24
+ },
25
+ },
14
26
  }
15
27
 
16
- function Template() {
17
- return html` <leu-menu>
18
- <leu-menu-item label="Menu Item 1" before="EMPTY"></leu-menu-item>
19
- <leu-menu-item label="Menu Item 2" before="check" active></leu-menu-item>
20
- <leu-menu-item label="Menu Item 3" before="EMPTY"></leu-menu-item>
28
+ function Template(args) {
29
+ return html` <leu-menu
30
+ role=${ifDefined(args.role)}
31
+ selects=${ifDefined(args.selects)}
32
+ >
33
+ <leu-menu-item
34
+ ><leu-icon slot="before"></leu-icon>Menu Item 1</leu-menu-item
35
+ >
36
+ <leu-menu-item active
37
+ ><leu-icon slot="before" name="check"></leu-icon>Menu Item
38
+ 2</leu-menu-item
39
+ >
40
+ <leu-menu-item
41
+ ><leu-icon slot="before"></leu-icon>Menu Item 3</leu-menu-item
42
+ >
21
43
  <hr />
22
- <leu-menu-item label="Menu Item 3" before="pin" after="CH"></leu-menu-item>
23
- <leu-menu-item label="Menu Item 4"></leu-menu-item>
44
+ <leu-menu-item
45
+ ><leu-icon slot="before" name="pin"></leu-icon>Menu Item 3<span
46
+ slot="after"
47
+ >CH</span
48
+ ></leu-menu-item
49
+ >
50
+ <leu-menu-item>Menu Item 4</leu-menu-item>
24
51
  </leu-menu>`
25
52
  }
26
53
 
@@ -1,20 +1,31 @@
1
1
  import { html } from "lit"
2
- import { fixture, expect, oneEvent } from "@open-wc/testing"
2
+ import { fixture, expect, oneEvent, elementUpdated } from "@open-wc/testing"
3
3
  import { ifDefined } from "lit/directives/if-defined.js"
4
4
  import { spy } from "sinon"
5
5
 
6
+ import "../leu-menu.js"
6
7
  import "../leu-menu-item.js"
7
8
 
8
9
  async function defaultFixture(args = {}) {
9
10
  return fixture(html`
10
11
  <leu-menu-item
11
- label=${args.label}
12
- before=${ifDefined(args.before)}
13
- after=${ifDefined(args.after)}
14
12
  href=${ifDefined(args.href)}
13
+ componentRole=${ifDefined(args.componentRole)}
14
+ value=${ifDefined(args.value)}
15
15
  ?active=${args.active}
16
16
  ?disabled=${args.disabled}
17
- ></leu-menu-item>
17
+ ?tabbable=${args.tabbable}
18
+ >
19
+ ${args.label}
20
+ </leu-menu-item>
21
+ `)
22
+ }
23
+
24
+ async function wrappedFixture(args = {}) {
25
+ return fixture(html`
26
+ <leu-menu role=${ifDefined(args.menuRole)}>
27
+ ${await defaultFixture(args)}
28
+ </leu-menu>
18
29
  `)
19
30
  }
20
31
 
@@ -26,26 +37,25 @@ describe("LeuMenuItem", () => {
26
37
  })
27
38
 
28
39
  it("passes the a11y audit", async () => {
29
- const el = await defaultFixture({ label: "Download" })
40
+ const el = await wrappedFixture({ label: "Download" })
30
41
 
31
- await expect(el).shadowDom.to.be.accessible()
42
+ await expect(el).dom.to.be.accessible()
32
43
  })
33
44
 
34
45
  it("passes the a11y audit with a link", async () => {
35
- const el = await defaultFixture({
46
+ const el = await wrappedFixture({
36
47
  label: "Download",
37
48
  href: "https://zh.ch",
49
+ menuRole: "none",
38
50
  })
39
51
 
40
- await expect(el).shadowDom.to.be.accessible()
52
+ await expect(el).dom.to.be.accessible()
41
53
  })
42
54
 
43
55
  it("renders a label", async () => {
44
56
  const el = await defaultFixture({ label: "Download" })
45
57
 
46
- const button = el.shadowRoot.querySelector("button")
47
-
48
- expect(button).to.have.trimmed.text("Download")
58
+ expect(el).to.have.trimmed.text("Download")
49
59
  })
50
60
 
51
61
  it("renders a button", async () => {
@@ -66,90 +76,61 @@ describe("LeuMenuItem", () => {
66
76
 
67
77
  expect(link).to.exist
68
78
  expect(link).to.have.attribute("href", "https://zh.ch")
69
- expect(link).to.have.trimmed.text("Kanton Zürich")
70
- })
71
-
72
- it("renders a before icon", async () => {
73
- const el = await defaultFixture({ label: "Download", before: "download" })
74
-
75
- const before = el.shadowRoot.querySelector(".before")
76
- expect(before).to.exist
77
-
78
- expect(el).shadowDom.to.equal(
79
- "<button class='button'><span class='before'></span><span class='label'>Download</span></button>"
80
- )
79
+ expect(el).to.have.trimmed.text("Kanton Zürich")
81
80
  })
82
81
 
83
- it("renders a before label", async () => {
84
- const el = await defaultFixture({ label: "Download", before: "DE" })
85
-
86
- const before = el.shadowRoot.querySelector(".before")
87
- expect(before).to.exist
88
- expect(before).to.have.trimmed.text("DE")
89
-
90
- expect(el).shadowDom.to.equal(
91
- "<button class='button'><span class='before'>DE</span><span class='label'>Download</span></button>"
92
- )
93
- })
94
-
95
- it("renders a before placeholder", async () => {
96
- const el = await defaultFixture({ label: "Download", before: "EMPTY" })
97
-
98
- const before = el.shadowRoot.querySelector(".before")
99
- expect(before).to.exist
100
- expect(before).to.not.have.trimmed.text()
101
-
102
- const iconPlaceholder = before.querySelector(".icon-placeholder")
103
- expect(iconPlaceholder).to.exist
104
-
105
- expect(el).shadowDom.to.equal(
106
- "<button class='button'><span class='before'><div class='icon-placeholder'></div></span><span class='label'>Download</span></button>"
107
- )
108
- })
109
-
110
- it("renders a after icon", async () => {
111
- const el = await defaultFixture({ label: "Download", after: "download" })
112
-
113
- const after = el.shadowRoot.querySelector(".after")
114
- expect(after).to.exist
82
+ it("sets the aria-disabled attribute to the button", async () => {
83
+ const el = await defaultFixture({ label: "Download", disabled: true })
115
84
 
116
- expect(el).shadowDom.to.equal(
117
- "<button class='button'><span class='label'>Download</span><span class='after'></span></button>"
118
- )
85
+ const button = el.shadowRoot.querySelector("button")
86
+ expect(button).to.have.attribute("aria-disabled", "true")
119
87
  })
120
88
 
121
- it("renders a after label", async () => {
122
- const el = await defaultFixture({ label: "Download", after: "DE" })
123
-
124
- const after = el.shadowRoot.querySelector(".after")
125
- expect(after).to.exist
126
- expect(after).to.have.trimmed.text("DE")
89
+ it("sets the defined role on the button", async () => {
90
+ const el = await defaultFixture({
91
+ label: "Download",
92
+ })
127
93
 
128
- expect(el).shadowDom.to.equal(
129
- "<button class='button'><span class='label'>Download</span><span class='after'>DE</span></button>"
130
- )
131
- })
94
+ const button = el.shadowRoot.querySelector("button")
95
+ expect(button).to.have.attribute("role", "menuitem")
132
96
 
133
- it("renders a after placeholder", async () => {
134
- const el = await defaultFixture({ label: "Download", after: "EMPTY" })
97
+ el.componentRole = "menuitemcheckbox"
98
+ await elementUpdated(el)
99
+ expect(button).to.have.attribute("role", "menuitemcheckbox")
135
100
 
136
- const after = el.shadowRoot.querySelector(".after")
137
- expect(after).to.exist
138
- expect(after).to.not.have.trimmed.text()
101
+ el.componentRole = "menuitemradio"
102
+ await elementUpdated(el)
103
+ expect(button).to.have.attribute("role", "menuitemradio")
139
104
 
140
- const iconPlaceholder = after.querySelector(".icon-placeholder")
141
- expect(iconPlaceholder).to.exist
105
+ el.componentRole = "option"
106
+ await elementUpdated(el)
107
+ expect(button).to.have.attribute("role", "option")
142
108
 
143
- expect(el).shadowDom.to.equal(
144
- "<button class='button'><span class='label'>Download</span><span class='after'><div class='icon-placeholder'></div></span></button>"
145
- )
109
+ el.componentRole = "none"
110
+ await elementUpdated(el)
111
+ expect(button).to.not.have.attribute("role")
146
112
  })
147
113
 
148
- it("passes the disabled attribute to the button", async () => {
149
- const el = await defaultFixture({ label: "Download", disabled: true })
114
+ it("adds either the aria-checked or aria-selected attribute to the button when the item is active", async () => {
115
+ const el = await defaultFixture({
116
+ label: "Download",
117
+ componentRole: "option",
118
+ active: true,
119
+ })
150
120
 
151
121
  const button = el.shadowRoot.querySelector("button")
152
- expect(button).to.have.attribute("disabled")
122
+ expect(button).to.have.attribute("aria-selected", "true")
123
+ expect(button).not.to.have.attribute("aria-checked")
124
+
125
+ el.componentRole = "menuitemcheckbox"
126
+ await elementUpdated(el)
127
+ expect(button).to.have.attribute("aria-checked", "true")
128
+ expect(button).not.to.have.attribute("aria-selected")
129
+
130
+ el.componentRole = "menuitemradio"
131
+ await elementUpdated(el)
132
+ expect(button).to.have.attribute("aria-checked", "true")
133
+ expect(button).not.to.have.attribute("aria-selected")
153
134
  })
154
135
 
155
136
  it("lets the click event bubble up", async () => {
@@ -177,4 +158,29 @@ describe("LeuMenuItem", () => {
177
158
 
178
159
  expect(clickSpy).to.have.not.been.called
179
160
  })
161
+
162
+ it("reflects the tabbable property as tabindex to the button", async () => {
163
+ const el = await defaultFixture({ label: "Download", tabbable: true })
164
+
165
+ const button = el.shadowRoot.querySelector("button")
166
+ expect(button).to.have.attribute("tabindex", "0")
167
+
168
+ el.tabbable = false
169
+ await elementUpdated(el)
170
+ expect(button).to.have.attribute("tabindex", "-1")
171
+
172
+ el.tabbable = undefined
173
+ await elementUpdated(el)
174
+ expect(button).to.not.have.attribute("tabindex")
175
+ })
176
+
177
+ it("returns the value or label when getValue is called", async () => {
178
+ const el = await defaultFixture({ label: "Download " })
179
+
180
+ expect(el.getValue()).to.equal("Download")
181
+
182
+ el.value = "download-01"
183
+
184
+ expect(el.getValue()).to.equal("download-01")
185
+ })
180
186
  })
@@ -1,17 +1,35 @@
1
1
  import { html } from "lit"
2
+ import { ifDefined } from "lit/directives/if-defined.js"
2
3
  import { fixture, expect } from "@open-wc/testing"
4
+ import { sendKeys } from "@web/test-runner-commands"
3
5
 
4
6
  import "../leu-menu.js"
5
7
  import "../leu-menu-item.js"
8
+ import "../../icon/leu-icon.js"
6
9
 
7
- async function defaultFixture() {
8
- return fixture(html` <leu-menu>
9
- <leu-menu-item label="Menu Item 1" before="EMPTY"></leu-menu-item>
10
- <leu-menu-item label="Menu Item 2" before="check" active></leu-menu-item>
11
- <leu-menu-item label="Menu Item 3" before="EMPTY"></leu-menu-item>
10
+ async function defaultFixture(args = {}) {
11
+ return fixture(html` <leu-menu
12
+ role=${ifDefined(args.role)}
13
+ selects=${ifDefined(args.selects)}
14
+ >
15
+ <leu-menu-item
16
+ ><leu-icon slot="before"></leu-icon>Menu Item 1</leu-menu-item
17
+ >
18
+ <leu-menu-item active
19
+ ><leu-icon slot="before" name="check"></leu-icon>Menu Item
20
+ 2</leu-menu-item
21
+ >
22
+ <leu-menu-item
23
+ ><leu-icon slot="before"></leu-icon>Menu Item 3</leu-menu-item
24
+ >
12
25
  <hr />
13
- <leu-menu-item label="Menu Item 3" before="pin" after="CH"></leu-menu-item>
14
- <leu-menu-item label="Menu Item 4"></leu-menu-item>
26
+ <leu-menu-item
27
+ ><leu-icon name="pin" slot="before"></leu-icon>Menu Item 3<slot
28
+ name="after"
29
+ >CH</slot
30
+ ></leu-menu-item
31
+ >
32
+ <leu-menu-item>Menu Item 4</leu-menu-item>
15
33
  </leu-menu>`)
16
34
  }
17
35
 
@@ -25,6 +43,81 @@ describe("LeuMenu", () => {
25
43
  it("passes the a11y audit", async () => {
26
44
  const el = await defaultFixture()
27
45
 
28
- await expect(el).shadowDom.to.be.accessible()
46
+ await expect(el).dom.to.be.accessible()
47
+ })
48
+
49
+ it("sets 'menu' as the default role", async () => {
50
+ const el = await defaultFixture()
51
+
52
+ expect(el.getAttribute("role")).to.equal("menu")
53
+ })
54
+
55
+ it("sets 'menuitem' as the default role for menu items", async () => {
56
+ const el = await defaultFixture()
57
+
58
+ const menuItems = el.querySelectorAll("leu-menu-item")
59
+
60
+ menuItems.forEach((menuItem) => {
61
+ expect(menuItem.componentRole).to.equal("menuitem")
62
+ })
63
+ })
64
+
65
+ it("sets 'menuitemradio' as the role for menu items when only one item can be selected", async () => {
66
+ const el = await defaultFixture({ selects: "single" })
67
+
68
+ const menuItems = el.querySelectorAll("leu-menu-item")
69
+
70
+ menuItems.forEach((menuItem) => {
71
+ expect(menuItem.componentRole).to.equal("menuitemradio")
72
+ })
73
+ })
74
+
75
+ it("sets 'menuitemcheckbox' as the role for menu items when multiple items can be selected", async () => {
76
+ const el = await defaultFixture({ selects: "multiple" })
77
+
78
+ const menuItems = el.querySelectorAll("leu-menu-item")
79
+
80
+ menuItems.forEach((menuItem) => {
81
+ expect(menuItem.componentRole).to.equal("menuitemcheckbox")
82
+ })
83
+ })
84
+
85
+ it("sets 'option' as the role for menu items when the menu role is 'listbox'", async () => {
86
+ const el = await defaultFixture({ role: "listbox" })
87
+
88
+ const menuItems = el.querySelectorAll("leu-menu-item")
89
+
90
+ menuItems.forEach((menuItem) => {
91
+ expect(menuItem.componentRole).to.equal("option")
92
+ })
93
+ })
94
+
95
+ it("moves the focus when the arrow keys are pressed", async () => {
96
+ const el = await defaultFixture()
97
+
98
+ const menuItems = Array.from(el.querySelectorAll("leu-menu-item"))
99
+
100
+ await sendKeys({ press: "Tab" })
101
+ expect(document.activeElement).to.equal(menuItems[0])
102
+
103
+ await sendKeys({ press: "ArrowDown" })
104
+ await sendKeys({ press: "ArrowDown" })
105
+
106
+ expect(document.activeElement).to.equal(menuItems[2])
107
+
108
+ await sendKeys({ press: "ArrowUp" })
109
+ await sendKeys({ press: "ArrowUp" })
110
+ await sendKeys({ press: "ArrowUp" })
111
+
112
+ expect(document.activeElement).to.equal(menuItems.at(-1))
113
+
114
+ await sendKeys({ press: "Home" })
115
+ expect(document.activeElement).to.equal(menuItems[0])
116
+
117
+ await sendKeys({ press: "End" })
118
+ expect(document.activeElement).to.equal(menuItems.at(-1))
119
+
120
+ await sendKeys({ press: "ArrowDown" })
121
+ expect(document.activeElement).to.equal(menuItems[0])
29
122
  })
30
123
  })
@@ -1,24 +1,33 @@
1
- import { html, LitElement } from "lit"
1
+ import { html } from "lit"
2
2
  import { live } from "lit/directives/live.js"
3
3
 
4
- import "../button/leu-button.js"
5
- import styles from "./pagination.css"
4
+ import { LeuElement } from "../../lib/LeuElement.js"
5
+ import { LeuButton } from "../button/Button.js"
6
+ import { LeuVisuallyHidden } from "../visually-hidden/VisuallyHidden.js"
7
+ import { LeuIcon } from "../icon/Icon.js"
6
8
 
7
- import "../visually-hidden/leu-visually-hidden.js"
9
+ // @ts-ignore
10
+ import styles from "./pagination.css"
8
11
 
9
12
  const MIN_PAGE = 1
10
13
 
11
14
  /**
12
15
  * @tagname leu-pagination
13
16
  */
14
- export class LeuPagination extends LitElement {
17
+ export class LeuPagination extends LeuElement {
18
+ static dependencies = {
19
+ "leu-button": LeuButton,
20
+ "leu-icon": LeuIcon,
21
+ "leu-visually-hidden": LeuVisuallyHidden,
22
+ }
23
+
15
24
  static styles = styles
16
25
 
17
26
  /**
18
27
  * @internal
19
28
  */
20
29
  static shadowRootOptions = {
21
- ...LitElement.shadowRootOptions,
30
+ ...LeuElement.shadowRootOptions,
22
31
  delegatesFocus: true,
23
32
  }
24
33
 
@@ -26,15 +35,6 @@ export class LeuPagination extends LitElement {
26
35
  defaultPage: { type: Number, reflect: true },
27
36
  itemsPerPage: { type: Number, reflect: true },
28
37
  numOfItems: { type: Number, reflect: true },
29
-
30
- /**
31
- * Internal page state that contains an
32
- * already clamped page number. Should only
33
- * be accessed through the `page` getter and
34
- * setter.
35
- * @type {Number}
36
- * @internal
37
- */
38
38
  _page: { state: true },
39
39
  }
40
40
 
@@ -43,9 +43,18 @@ export class LeuPagination extends LitElement {
43
43
 
44
44
  /** @type {Number} */
45
45
  this.numOfItems = 1
46
+
46
47
  /** @type {Number} */
47
48
  this.itemsPerPage = 1
48
- /** @type {Number} */
49
+
50
+ /**
51
+ * Internal page state that contains an
52
+ * already clamped page number. Should only
53
+ * be accessed through the `page` getter and
54
+ * setter.
55
+ * @type {Number}
56
+ * @internal
57
+ */
49
58
  this._page = 1
50
59
  }
51
60
 
@@ -148,22 +157,22 @@ export class LeuPagination extends LitElement {
148
157
  <div class="label">von ${this._maxPage}</div>
149
158
  <div class="button-group">
150
159
  <leu-button
151
- icon="angleLeft"
152
160
  variant="secondary"
153
161
  label="Vorherige Seite"
154
162
  @click=${(_) => {
155
163
  this._updatePage(this.page - 1)
156
164
  }}
157
165
  ?disabled=${this._isFirstPage()}
166
+ ><leu-icon name="angleLeft"></leu-icon
158
167
  ></leu-button>
159
168
  <leu-button
160
- icon="angleRight"
161
169
  variant="secondary"
162
170
  label="Nächste Seite"
163
171
  @click=${(_) => {
164
172
  this._updatePage(this.page + 1)
165
173
  }}
166
174
  ?disabled=${this._isLastPage()}
175
+ ><leu-icon name="angleRight"></leu-icon
167
176
  ></leu-button>
168
177
  </div>
169
178
  `
@@ -1,6 +1,5 @@
1
- import { defineElement } from "../../lib/defineElement.js"
2
1
  import { LeuPagination } from "./Pagination.js"
3
2
 
4
3
  export { LeuPagination }
5
4
 
6
- defineElement("pagination", LeuPagination)
5
+ LeuPagination.define("leu-pagination")
@@ -1,4 +1,4 @@
1
- import { html, LitElement } from "lit"
1
+ import { html } from "lit"
2
2
  import {
3
3
  autoUpdate,
4
4
  computePosition,
@@ -6,34 +6,37 @@ import {
6
6
  shift,
7
7
  size,
8
8
  } from "@floating-ui/dom"
9
+
10
+ import { LeuElement } from "../../lib/LeuElement.js"
11
+
12
+ // @ts-ignore
9
13
  import styles from "./popup.css"
10
14
 
15
+ /**
16
+ * @typedef {"top"|"top-start"|"top-end"|"bottom"|"bottom-start"|"bottom-end"|"left"|"left-start"|"left-end"|"right"|"right-start"|"right-end"} Placement
17
+ */
18
+
11
19
  /**
12
20
  * @tagname leu-popup
13
21
  */
14
- export class LeuPopup extends LitElement {
22
+ export class LeuPopup extends LeuElement {
15
23
  static styles = styles
16
24
 
17
25
  static shadowRootOptions = {
18
- ...LitElement.shadowRootOptions,
26
+ ...LeuElement.shadowRootOptions,
19
27
  delegatesFocus: true,
20
28
  }
21
29
 
22
30
  static properties = {
23
31
  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 },
32
+ active: { type: Boolean, reflect: true },
33
+ placement: { type: String, reflect: true },
34
+ flip: { type: Boolean, reflect: true },
35
+ shift: { type: Boolean, reflect: true },
36
+ shiftPadding: { type: Number, reflect: true },
37
+ matchSize: { type: String, reflect: true },
38
+ autoSize: { type: String, reflect: true },
39
+ autoSizePadding: { type: Number, reflect: true },
37
40
  }
38
41
 
39
42
  constructor() {
@@ -43,6 +46,23 @@ export class LeuPopup extends LitElement {
43
46
  this.cleanup = undefined
44
47
  this.flip = false
45
48
  this.shift = false
49
+
50
+ this.active = false
51
+
52
+ /** @type {Placement} */
53
+ this.placement = undefined
54
+
55
+ /** @type {"width" | "height" | "both"} */
56
+ this.matchSize = undefined
57
+
58
+ /** @type {"width" | "height" | "both"} */
59
+ this.autoSize = undefined
60
+
61
+ this.shiftPadding = 0
62
+ this.autoSizePadding = 0
63
+
64
+ /** @type {string | HTMLElement} */
65
+ this.anchor = undefined
46
66
  }
47
67
 
48
68
  disconnectedCallback() {
@@ -68,6 +88,9 @@ export class LeuPopup extends LitElement {
68
88
  }
69
89
  }
70
90
 
91
+ /**
92
+ * @returns {HTMLElement | null}
93
+ */
71
94
  get popupEl() {
72
95
  return this.renderRoot?.querySelector(".popup") ?? null
73
96
  }
@@ -1,6 +1,5 @@
1
- import { defineElement } from "../../lib/defineElement.js"
2
1
  import { LeuPopup } from "./Popup.js"
3
2
 
4
3
  export { LeuPopup }
5
4
 
6
- defineElement("popup", LeuPopup)
5
+ LeuPopup.define("leu-popup")
@@ -19,6 +19,7 @@
19
19
  position: absolute;
20
20
  top: 0;
21
21
  left: 0;
22
+ z-index: var(--leu-z-index-popup);
22
23
  }
23
24
 
24
25
  /* TODO: Should visibility be a matter of the popup component? */