@statistikzh/leu 0.4.0 → 0.5.1

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 (220) hide show
  1. package/.storybook/preview.js +1 -2
  2. package/CHANGELOG.md +32 -0
  3. package/custom-elements-manifest.config.js +46 -0
  4. package/dist/Accordion.d.ts +31 -0
  5. package/dist/Accordion.d.ts.map +1 -0
  6. package/dist/Accordion.js +257 -0
  7. package/dist/Breadcrumb.d.ts +69 -0
  8. package/dist/Breadcrumb.d.ts.map +1 -0
  9. package/dist/Breadcrumb.js +392 -0
  10. package/dist/Button-5326c982.d.ts +84 -0
  11. package/dist/Button-5326c982.d.ts.map +1 -0
  12. package/dist/Button-5326c982.js +555 -0
  13. package/dist/Button.d.ts +2 -0
  14. package/dist/Button.d.ts.map +1 -0
  15. package/dist/Button.js +6 -420
  16. package/dist/ButtonGroup.d.ts +24 -0
  17. package/dist/ButtonGroup.d.ts.map +1 -0
  18. package/dist/ButtonGroup.js +70 -39
  19. package/dist/Checkbox.d.ts +13 -0
  20. package/dist/Checkbox.d.ts.map +1 -0
  21. package/dist/Checkbox.js +2 -2
  22. package/dist/CheckboxGroup.d.ts +13 -0
  23. package/dist/CheckboxGroup.d.ts.map +1 -0
  24. package/dist/CheckboxGroup.js +3 -3
  25. package/dist/Chip.d.ts +5 -0
  26. package/dist/Chip.d.ts.map +1 -0
  27. package/dist/{Chip-dac7337d.js → Chip.js} +16 -5
  28. package/dist/ChipGroup.d.ts +28 -0
  29. package/dist/ChipGroup.d.ts.map +1 -0
  30. package/dist/ChipGroup.js +62 -5
  31. package/dist/ChipLink.d.ts +15 -0
  32. package/dist/ChipLink.d.ts.map +1 -0
  33. package/dist/ChipLink.js +1 -1
  34. package/dist/ChipRemovable.d.ts +13 -0
  35. package/dist/ChipRemovable.d.ts.map +1 -0
  36. package/dist/ChipRemovable.js +2 -2
  37. package/dist/ChipSelectable.d.ts +22 -0
  38. package/dist/ChipSelectable.d.ts.map +1 -0
  39. package/dist/ChipSelectable.js +5 -5
  40. package/dist/Dropdown.d.ts +15 -0
  41. package/dist/Dropdown.d.ts.map +1 -0
  42. package/dist/Dropdown.js +30 -7
  43. package/dist/Input.d.ts +154 -0
  44. package/dist/Input.d.ts.map +1 -0
  45. package/dist/Input.js +13 -7
  46. package/dist/Menu.d.ts +8 -0
  47. package/dist/Menu.d.ts.map +1 -0
  48. package/dist/MenuItem.d.ts +21 -0
  49. package/dist/MenuItem.d.ts.map +1 -0
  50. package/dist/MenuItem.js +3 -3
  51. package/dist/Pagination.d.ts +27 -0
  52. package/dist/Pagination.d.ts.map +1 -0
  53. package/dist/Pagination.js +93 -61
  54. package/dist/Popup.d.ts +18 -0
  55. package/dist/Popup.d.ts.map +1 -0
  56. package/dist/{leu-popup-4bf6f1f4.js → Popup.js} +4 -5
  57. package/dist/Radio.d.ts +12 -0
  58. package/dist/Radio.d.ts.map +1 -0
  59. package/dist/Radio.js +2 -2
  60. package/dist/RadioGroup.d.ts +20 -0
  61. package/dist/RadioGroup.d.ts.map +1 -0
  62. package/dist/RadioGroup.js +3 -3
  63. package/dist/ScrollTop.d.ts +19 -0
  64. package/dist/ScrollTop.d.ts.map +1 -0
  65. package/dist/ScrollTop.js +122 -0
  66. package/dist/Select.d.ts +98 -0
  67. package/dist/Select.d.ts.map +1 -0
  68. package/dist/Select.js +27 -82
  69. package/dist/Table.d.ts +48 -0
  70. package/dist/Table.d.ts.map +1 -0
  71. package/dist/Table.js +7 -4
  72. package/dist/VisuallyHidden.d.ts +8 -0
  73. package/dist/VisuallyHidden.d.ts.map +1 -0
  74. package/dist/VisuallyHidden.js +28 -0
  75. package/dist/_rollupPluginBabelHelpers-20f659f4.d.ts +3 -0
  76. package/dist/_rollupPluginBabelHelpers-20f659f4.d.ts.map +1 -0
  77. package/dist/defineElement-40372b4b.d.ts +9 -0
  78. package/dist/defineElement-40372b4b.d.ts.map +1 -0
  79. package/dist/{defineElement-47d4f665.js → defineElement-40372b4b.js} +1 -1
  80. package/dist/icon-03e86700.d.ts +11 -0
  81. package/dist/icon-03e86700.d.ts.map +1 -0
  82. package/dist/index.d.ts +21 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +14 -8
  85. package/dist/leu-accordion.d.ts +3 -0
  86. package/dist/leu-accordion.d.ts.map +1 -0
  87. package/dist/leu-accordion.js +9 -0
  88. package/dist/leu-breadcrumb.d.ts +3 -0
  89. package/dist/leu-breadcrumb.d.ts.map +1 -0
  90. package/dist/leu-breadcrumb.js +23 -0
  91. package/dist/leu-button-group.d.ts +3 -0
  92. package/dist/leu-button-group.d.ts.map +1 -0
  93. package/dist/leu-button-group.js +1 -5
  94. package/dist/leu-button.d.ts +3 -0
  95. package/dist/leu-button.d.ts.map +1 -0
  96. package/dist/leu-button.js +3 -2
  97. package/dist/leu-checkbox-group.d.ts +3 -0
  98. package/dist/leu-checkbox-group.d.ts.map +1 -0
  99. package/dist/leu-checkbox-group.js +1 -1
  100. package/dist/leu-checkbox.d.ts +3 -0
  101. package/dist/leu-checkbox.d.ts.map +1 -0
  102. package/dist/leu-checkbox.js +1 -1
  103. package/dist/leu-chip-group.d.ts +3 -0
  104. package/dist/leu-chip-group.d.ts.map +1 -0
  105. package/dist/leu-chip-group.js +2 -1
  106. package/dist/leu-chip-link.d.ts +3 -0
  107. package/dist/leu-chip-link.d.ts.map +1 -0
  108. package/dist/leu-chip-link.js +2 -2
  109. package/dist/leu-chip-removable.d.ts +3 -0
  110. package/dist/leu-chip-removable.d.ts.map +1 -0
  111. package/dist/leu-chip-removable.js +2 -2
  112. package/dist/leu-chip-selectable.d.ts +3 -0
  113. package/dist/leu-chip-selectable.d.ts.map +1 -0
  114. package/dist/leu-chip-selectable.js +2 -2
  115. package/dist/leu-dropdown.d.ts +3 -0
  116. package/dist/leu-dropdown.d.ts.map +1 -0
  117. package/dist/leu-dropdown.js +5 -4
  118. package/dist/leu-input.d.ts +3 -0
  119. package/dist/leu-input.d.ts.map +1 -0
  120. package/dist/leu-input.js +1 -1
  121. package/dist/leu-menu-item.d.ts +3 -0
  122. package/dist/leu-menu-item.d.ts.map +1 -0
  123. package/dist/leu-menu-item.js +1 -1
  124. package/dist/leu-menu.d.ts +3 -0
  125. package/dist/leu-menu.d.ts.map +1 -0
  126. package/dist/leu-menu.js +1 -1
  127. package/dist/leu-pagination.d.ts +3 -0
  128. package/dist/leu-pagination.d.ts.map +1 -0
  129. package/dist/leu-pagination.js +5 -2
  130. package/dist/leu-popup.d.ts +3 -0
  131. package/dist/leu-popup.d.ts.map +1 -0
  132. package/dist/leu-popup.js +9 -0
  133. package/dist/leu-radio-group.d.ts +3 -0
  134. package/dist/leu-radio-group.d.ts.map +1 -0
  135. package/dist/leu-radio-group.js +1 -1
  136. package/dist/leu-radio.d.ts +3 -0
  137. package/dist/leu-radio.d.ts.map +1 -0
  138. package/dist/leu-radio.js +1 -1
  139. package/dist/leu-scroll-top.d.ts +3 -0
  140. package/dist/leu-scroll-top.d.ts.map +1 -0
  141. package/dist/leu-scroll-top.js +14 -0
  142. package/dist/leu-select.d.ts +3 -0
  143. package/dist/leu-select.d.ts.map +1 -0
  144. package/dist/leu-select.js +4 -3
  145. package/dist/leu-table.d.ts +3 -0
  146. package/dist/leu-table.d.ts.map +1 -0
  147. package/dist/leu-table.js +5 -2
  148. package/dist/leu-visually-hidden.d.ts +3 -0
  149. package/dist/leu-visually-hidden.d.ts.map +1 -0
  150. package/dist/leu-visually-hidden.js +8 -0
  151. package/dist/theme.css +386 -2
  152. package/dist/utils-65469421.d.ts +16 -0
  153. package/dist/utils-65469421.d.ts.map +1 -0
  154. package/dist/utils-65469421.js +35 -0
  155. package/dist/vscode.html-custom-data.json +579 -0
  156. package/dist/vue/index.d.ts +678 -0
  157. package/dist/web-types.json +1076 -0
  158. package/index.js +3 -0
  159. package/package.json +30 -12
  160. package/postcss.config.cjs +2 -0
  161. package/rollup.config.js +21 -40
  162. package/scripts/generate-component/templates/[name].css +2 -2
  163. package/scripts/postcss-leu-font-styles.cjs +160 -0
  164. package/src/components/accordion/accordion.css +2 -2
  165. package/src/components/accordion/stories/accordion.stories.js +2 -1
  166. package/src/components/accordion/test/accordion.test.js +4 -2
  167. package/src/components/breadcrumb/Breadcrumb.js +2 -1
  168. package/src/components/breadcrumb/breadcrumb.css +2 -13
  169. package/src/components/button/Button.js +69 -8
  170. package/src/components/button/button.css +23 -2
  171. package/src/components/button/stories/button.stories.js +43 -90
  172. package/src/components/button/test/button.test.js +90 -19
  173. package/src/components/button-group/ButtonGroup.js +76 -34
  174. package/src/components/button-group/stories/button-group.stories.js +13 -6
  175. package/src/components/button-group/test/button-group.test.js +38 -31
  176. package/src/components/checkbox/checkbox-group.css +2 -2
  177. package/src/components/checkbox/checkbox.css +1 -1
  178. package/src/components/chip/ChipGroup.js +42 -2
  179. package/src/components/chip/ChipRemovable.js +1 -1
  180. package/src/components/chip/ChipSelectable.js +4 -4
  181. package/src/components/chip/chip-group.css +12 -2
  182. package/src/components/chip/chip.css +14 -3
  183. package/src/components/chip/stories/chip-group.stories.js +100 -46
  184. package/src/components/chip/test/chip-removable.test.js +3 -3
  185. package/src/components/dropdown/Dropdown.js +24 -3
  186. package/src/components/dropdown/dropdown.css +4 -0
  187. package/src/components/input/Input.js +7 -4
  188. package/src/components/input/input.css +2 -2
  189. package/src/components/input/stories/input.stories.js +13 -0
  190. package/src/components/input/test/input.test.js +1 -0
  191. package/src/components/menu/menu-item.css +3 -3
  192. package/src/components/pagination/Pagination.js +91 -60
  193. package/src/components/pagination/pagination.css +6 -1
  194. package/src/components/pagination/stories/pagination.stories.js +15 -2
  195. package/src/components/pagination/test/pagination.test.js +15 -15
  196. package/src/components/popup/popup.css +2 -2
  197. package/src/components/popup/stories/popup.stories.js +1 -1
  198. package/src/components/radio/radio-group.css +2 -2
  199. package/src/components/radio/radio.css +1 -1
  200. package/src/components/scroll-top/ScrollTop.js +87 -0
  201. package/src/components/scroll-top/leu-scroll-top.js +6 -0
  202. package/src/components/scroll-top/scroll-top.css +34 -0
  203. package/src/components/scroll-top/stories/scroll-top.stories.js +217 -0
  204. package/src/components/scroll-top/test/scroll-top.test.js +22 -0
  205. package/src/components/select/Select.js +24 -6
  206. package/src/components/select/select.css +2 -2
  207. package/src/components/table/table.css +2 -2
  208. package/src/components/visually-hidden/VisuallyHidden.js +13 -0
  209. package/src/components/visually-hidden/leu-visually-hidden.js +6 -0
  210. package/src/components/visually-hidden/stories/visually-hidden.stories.js +22 -0
  211. package/src/components/visually-hidden/test/visually-hidden.test.js +36 -0
  212. package/src/components/visually-hidden/visually-hidden.css +10 -0
  213. package/src/lib/defineElement.js +1 -1
  214. package/src/lib/hasSlotController.js +5 -3
  215. package/src/lib/utils.js +21 -3
  216. package/src/styles/custom-properties.css +6 -2
  217. package/src/styles/font-definitions.json +202 -0
  218. package/stylelint.config.mjs +2 -0
  219. package/tsconfig.build.json +21 -0
  220. package/tsconfig.json +16 -0
@@ -10,11 +10,8 @@ import {
10
10
  BUTTON_EXPANDED_OPTIONS,
11
11
  } from "../Button.js"
12
12
 
13
- function copyContent(params) {
14
- const string = `<leu-button${Object.values(params)
15
- .filter((o) => o)
16
- .join("")}>\n</leu-button>`
17
- navigator.clipboard.writeText(string)
13
+ function copyContent(e) {
14
+ navigator.clipboard.writeText(e.target.outerHTML.replace(/<!--.*?-->/g, ""))
18
15
  }
19
16
 
20
17
  export default {
@@ -31,47 +28,24 @@ export default {
31
28
  },
32
29
  }
33
30
 
34
- function Template({
35
- label,
36
- round,
37
- size,
38
- active,
39
- inverted,
40
- variant,
41
- disabled,
42
- icon,
43
- iconPosition,
44
- type,
45
- expanded,
46
- }) {
47
- const params = {
48
- label: label ? ` label="${label}"` : undefined,
49
- size: size === "small" ? ' size="small"' : undefined,
50
- variant: variant !== "primary" ? ` variant="${variant}"` : undefined,
51
- icon: icon ? ` icon="${icon}"` : undefined,
52
- iconPosition: iconPosition ? ` icon="${iconPosition}"` : undefined,
53
- round: round ? " round" : undefined,
54
- active: active ? " active" : undefined,
55
- disabled: disabled ? " disabled" : undefined,
56
- inverted: inverted ? " inverted" : undefined,
57
- expanded: expanded ? ` expanded="${expanded}"` : undefined,
58
- }
31
+ function Template(args = {}) {
59
32
  const component = html`
60
33
  <div data-root>
61
34
  <leu-button
62
- label=${ifDefined(label)}
63
- size=${ifDefined(size)}
64
- variant=${ifDefined(variant)}
65
- icon=${ifDefined(icon)}
66
- iconPosition=${ifDefined(iconPosition)}
67
- type=${ifDefined(type)}
68
- expanded=${ifDefined(expanded)}
69
- ?round=${round}
70
- ?active=${active}
71
- ?inverted=${inverted}
72
- ?disabled=${disabled}
73
- @click=${() => copyContent(params)}
35
+ content=${ifDefined(args.content)}
36
+ size=${ifDefined(args.size)}
37
+ variant=${ifDefined(args.variant)}
38
+ icon=${ifDefined(args.icon)}
39
+ iconPosition=${ifDefined(args.iconPosition)}
40
+ type=${ifDefined(args.type)}
41
+ expanded=${ifDefined(args.expanded)}
42
+ ?round=${args.round}
43
+ ?active=${args.active}
44
+ ?inverted=${args.inverted}
45
+ ?disabled=${args.disabled}
46
+ @click=${copyContent}
74
47
  >
48
+ ${args.content}
75
49
  </leu-button>
76
50
  </div>
77
51
  <br />
@@ -85,7 +59,7 @@ function Template({
85
59
  }
86
60
  </style>
87
61
  <div
88
- style="${inverted
62
+ style="${args.inverted
89
63
  ? "background:var(--leu-color-accent-blue); color: var(--leu-color-white-transp-90);"
90
64
  : ""}padding:40px;"
91
65
  >
@@ -96,7 +70,7 @@ function Template({
96
70
 
97
71
  export const Regular = Template.bind({})
98
72
  Regular.argTypes = {
99
- label: { type: "string" },
73
+ content: { type: "string" },
100
74
  icon: { control: "select", options: ICON_NAMES },
101
75
  iconPosition: { control: "select", options: ["before", "after"] },
102
76
  type: { control: "radio", options: BUTTON_TYPES },
@@ -105,7 +79,7 @@ Regular.argTypes = {
105
79
  expanded: { control: "radio", options: BUTTON_EXPANDED_OPTIONS },
106
80
  }
107
81
  Regular.args = {
108
- label: "Click Mich...",
82
+ content: "Click Mich...",
109
83
  round: false,
110
84
  disabled: false,
111
85
  active: false,
@@ -119,18 +93,18 @@ Regular.args = {
119
93
  }
120
94
 
121
95
  const items = [
122
- { label: "Normal" },
123
- { label: "Active", active: true },
124
- { label: "Disabled", disabled: true },
96
+ { content: "Normal" },
97
+ { content: "Active", active: true },
98
+ { content: "Disabled", disabled: true },
125
99
 
126
- { label: "Normal", icon: "calendar" },
127
- { label: "Active", icon: "calendar", active: true },
128
- { label: "Disabled", icon: "calendar", disabled: true },
100
+ { content: "Normal", icon: "calendar" },
101
+ { content: "Active", icon: "calendar", active: true },
102
+ { content: "Disabled", icon: "calendar", disabled: true },
129
103
 
130
- { label: "Normal", icon: "calendar", iconPosition: "after" },
131
- { label: "Active", icon: "calendar", iconPosition: "after", active: true },
104
+ { content: "Normal", icon: "calendar", iconPosition: "after" },
105
+ { content: "Active", icon: "calendar", iconPosition: "after", active: true },
132
106
  {
133
- label: "Disabled",
107
+ content: "Disabled",
134
108
  icon: "calendar",
135
109
  iconPosition: "after",
136
110
  disabled: true,
@@ -146,18 +120,18 @@ const items = [
146
120
  ]
147
121
 
148
122
  const ghostItems = [
149
- { label: "Normal", icon: "calendar" },
150
- { label: "Active", icon: "calendar", active: true },
151
- { label: "Disabled", icon: "calendar", disabled: true },
123
+ { content: "Normal", icon: "calendar" },
124
+ { content: "Active", icon: "calendar", active: true },
125
+ { content: "Disabled", icon: "calendar", disabled: true },
152
126
 
153
- { label: "Normal", icon: "calendar", expanded: "closed" },
154
- { label: "Active", icon: "calendar", active: true, expanded: "closed" },
155
- { label: "Disabled", icon: "calendar", disabled: true, expanded: "closed" },
127
+ { content: "Normal", icon: "calendar", expanded: "closed" },
128
+ { content: "Active", icon: "calendar", active: true, expanded: "closed" },
129
+ { content: "Disabled", icon: "calendar", disabled: true, expanded: "closed" },
156
130
 
157
- { label: "Normal", icon: "calendar", iconPosition: "after" },
158
- { label: "Active", icon: "calendar", iconPosition: "after", active: true },
131
+ { content: "Normal", icon: "calendar", iconPosition: "after" },
132
+ { content: "Active", icon: "calendar", iconPosition: "after", active: true },
159
133
  {
160
- label: "Disabled",
134
+ content: "Disabled",
161
135
  icon: "calendar",
162
136
  iconPosition: "after",
163
137
  disabled: true,
@@ -289,30 +263,8 @@ function TemplateOverview() {
289
263
  html`
290
264
  <div>
291
265
  <div class=${classMap({ table: true })} data-root>
292
- ${size.items.map((item) => {
293
- const params = {
294
- label: item.label
295
- ? ` label="${item.label}"`
296
- : undefined,
297
- size:
298
- size.size === "small" ? ' size="small"' : undefined,
299
- variant:
300
- group.variant !== "primary"
301
- ? ` variant="${group.variant}"`
302
- : undefined,
303
- icon: item.icon ? ` icon="${item.icon}"` : undefined,
304
- iconPosition: item.iconPosition
305
- ? ` iconPosition="${item.iconPosition}"`
306
- : undefined,
307
- round: item.round ? " round" : undefined,
308
- active: item.active ? " active" : undefined,
309
- disabled: item.disabled ? " disabled" : undefined,
310
- inverted: group.inverted ? " inverted" : undefined,
311
- expanded: item.expanded
312
- ? ` expanded="${item.expanded}"`
313
- : undefined,
314
- }
315
- return html`
266
+ ${size.items.map(
267
+ (item) => html`
316
268
  <leu-button
317
269
  label=${ifDefined(item.label)}
318
270
  size=${ifDefined(size.size)}
@@ -324,11 +276,12 @@ function TemplateOverview() {
324
276
  ?active=${item.active}
325
277
  ?disabled=${item.disabled}
326
278
  ?inverted=${group.inverted}
327
- @click=${() => copyContent(params)}
279
+ @click=${copyContent}
328
280
  >
281
+ ${item.content}
329
282
  </leu-button>
330
283
  `
331
- })}
284
+ )}
332
285
  </div>
333
286
  </div>
334
287
  `
@@ -341,7 +294,7 @@ function TemplateOverview() {
341
294
 
342
295
  export const Overview = TemplateOverview.bind({})
343
296
  Overview.argTypes = {
344
- label: { table: { disable: true } },
297
+ content: { table: { disable: true } },
345
298
  icon: { table: { disable: true } },
346
299
  iconPosition: { table: { disable: true } },
347
300
  size: { table: { disable: true } },
@@ -4,7 +4,7 @@ import { fixture, expect, elementUpdated, oneEvent } from "@open-wc/testing"
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", () => {
@@ -20,22 +20,41 @@ describe("LeuButton", () => {
20
20
  await expect(el).shadowDom.to.be.accessible()
21
21
  })
22
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
+
23
31
  it("renders the label", async () => {
24
- const el = await fixture(html` <leu-button label="Sichern"></leu-button>`)
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
+ )
25
44
  const button = el.shadowRoot.querySelector("button")
26
45
 
27
- expect(button).to.have.trimmed.text("Sichern")
46
+ expect(button).to.have.attribute("aria-label", "Dokument herunterladen")
28
47
  })
29
48
 
30
49
  it("renders the icon at the correct position", async () => {
31
50
  const el = await fixture(
32
- html` <leu-button icon="addNew" label="Sichern"></leu-button>`
51
+ html` <leu-button icon="addNew">Sichern</leu-button>`
33
52
  )
34
53
 
35
54
  const button = el.shadowRoot.querySelector("button")
36
55
 
37
56
  expect(button).dom.to.equal(
38
- "<button><div><svg><path /></svg></div>Sichern</div>",
57
+ "<button><div><svg><path /></svg></div><slot></slot></div>",
39
58
  { ignoreAttributes: ["d", "class", "type"] }
40
59
  )
41
60
 
@@ -44,20 +63,20 @@ describe("LeuButton", () => {
44
63
  await elementUpdated(el)
45
64
 
46
65
  expect(button).dom.to.equal(
47
- "<button>Sichern<div><svg><path /></svg></div></div>",
66
+ "<button><slot></slot><div><svg><path /></svg></div></div>",
48
67
  { ignoreAttributes: ["d", "class", "type"] }
49
68
  )
50
69
  })
51
70
 
52
71
  it("renders the icon at the correct size", async () => {
53
72
  const el = await fixture(
54
- html` <leu-button icon="addNew" label="Sichern"></leu-button>`
73
+ html` <leu-button icon="addNew">Sichern</leu-button>`
55
74
  )
56
75
 
57
76
  const button = el.shadowRoot.querySelector("button")
58
77
 
59
78
  expect(button).dom.to.equal(
60
- "<button><div><svg width='24' height='24'><path /></svg></div>Sichern</div>",
79
+ "<button><div><svg width='24' height='24'><path /></svg></div><slot></slot></div>",
61
80
  { ignoreAttributes: ["d", "class", "type"] }
62
81
  )
63
82
 
@@ -66,25 +85,22 @@ describe("LeuButton", () => {
66
85
  await elementUpdated(el)
67
86
 
68
87
  expect(button).dom.to.equal(
69
- "<button><div><svg width='16' height='16'><path /></svg></div>Sichern</div>",
88
+ "<button><div><svg width='16' height='16'><path /></svg></div><slot></slot></div>",
70
89
  { ignoreAttributes: ["d", "class", "type"] }
71
90
  )
72
91
  })
73
92
 
74
93
  it("renders the expanded icon only when the variant is ghost", async () => {
75
94
  const el = await fixture(
76
- html` <leu-button
77
- icon="addNew"
78
- label="Sichern"
79
- variant="ghost"
80
- expanded="open"
81
- ></leu-button>`
95
+ html` <leu-button icon="addNew" variant="ghost" expanded="true"
96
+ >Sichern</leu-button
97
+ >`
82
98
  )
83
99
 
84
100
  const button = el.shadowRoot.querySelector("button")
85
101
 
86
102
  expect(button).dom.to.equal(
87
- "<button class='ghost normal'><div class='icon-wrapper icon-wrapper--before'><svg width='24' height='24'><path /></svg></div>Sichern<div class='icon-wrapper icon-wrapper--expanded'><svg width='24' height='24'><path /></svg></div></div>",
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>",
88
104
  { ignoreAttributes: ["d", "type", "width", "height"] }
89
105
  )
90
106
 
@@ -93,7 +109,7 @@ describe("LeuButton", () => {
93
109
  await elementUpdated(el)
94
110
 
95
111
  expect(button).dom.to.equal(
96
- "<button class='primary normal'><div class='icon-wrapper icon-wrapper--before'><svg width='24' height='24'><path /></svg></div>Sichern</div>",
112
+ "<button class='primary regular'><div class='icon-wrapper icon-wrapper--before'><svg width='24' height='24'><path /></svg></div><slot></slot></div>",
97
113
  { ignoreAttributes: ["d", "type"] }
98
114
  )
99
115
  })
@@ -104,7 +120,7 @@ describe("LeuButton", () => {
104
120
  icon="addNew"
105
121
  label="Sichern"
106
122
  variant="ghost"
107
- expanded="open"
123
+ expanded="true"
108
124
  disabled
109
125
  ></leu-button>`
110
126
  )
@@ -119,8 +135,63 @@ describe("LeuButton", () => {
119
135
  expect(button).to.not.have.attribute("disabled")
120
136
  })
121
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
+
122
193
  it("dispatches the click event", async () => {
123
- const el = await fixture(html` <leu-button label="Sichern"></leu-button>`)
194
+ const el = await fixture(html` <leu-button>Sichern</leu-button>`)
124
195
  const button = el.shadowRoot.querySelector("button")
125
196
 
126
197
  setTimeout(() => button.click())
@@ -1,56 +1,98 @@
1
1
  import { html, LitElement } from "lit"
2
+ // @ts-ignore
2
3
  import styles from "./button-group.css"
3
- import "../button/leu-button.js"
4
4
 
5
5
  /**
6
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
7
10
  */
8
11
  export class LeuButtonGroup extends LitElement {
9
12
  static styles = styles
10
13
 
11
- static properties = {
12
- items: { type: Array, reflect: true },
13
- value: { type: String, reflect: true },
14
- }
15
-
16
14
  constructor() {
17
15
  super()
18
- /** @type {Array} */
19
- this.items = []
20
- /** @type {string} */
21
- 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
+ })
39
+ }
40
+
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
+ })
50
+
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
+ })
22
73
  }
23
74
 
24
- _setValue(newValue) {
25
- this.value = newValue
75
+ /**
76
+ * @param {import("../button/Button").LeuButton} button
77
+ */
78
+ _handleButtonClick(button) {
79
+ if (!button.active) {
80
+ this.value = LeuButtonGroup.getButtonValue(button)
26
81
 
27
- this.dispatchEvent(
28
- new CustomEvent("input", {
29
- bubbles: true,
30
- composed: true,
31
- detail: { value: newValue },
32
- })
33
- )
82
+ this.dispatchEvent(
83
+ new CustomEvent("input", {
84
+ bubbles: true,
85
+ composed: true,
86
+ detail: { value: LeuButtonGroup.getButtonValue(button) },
87
+ })
88
+ )
89
+ }
34
90
  }
35
91
 
36
92
  render() {
37
93
  return html`
38
94
  <div role="menubar" class="group">
39
- ${this.items.map(
40
- (item) =>
41
- html`
42
- <leu-button
43
- label=${item}
44
- variant=${this.value === item ? "primary" : "secondary"}
45
- @click=${() => {
46
- this._setValue(item)
47
- }}
48
- role="menuitemradio"
49
- aria-checked=${this.value === item}
50
- >
51
- </leu-button>
52
- `
53
- )}
95
+ <slot @slotchange=${this._handleSlotChange}></slot>
54
96
  </div>
55
97
  `
56
98
  }
@@ -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
@@ -23,21 +24,27 @@ export default {
23
24
  }
24
25
 
25
26
  function Template({ items, value }, { id }) {
26
- return html`
27
- <leu-button-group
28
- .items=${items}
27
+ return html` <leu-button-group
29
28
  .value=${value}
30
- @click=${(event) => {
29
+ @input=${(event) => {
31
30
  updateStorybookArgss(id, {
32
31
  value: event.target.value,
33
32
  })
34
33
  }}
35
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
+ )}
36
44
  </leu-button-group>
37
45
  <br />
38
46
  <br />
39
- <pre>value = '${value}'</pre>
40
- `
47
+ <pre>value = '${value}'</pre>`
41
48
  }
42
49
 
43
50
  export const Regular = Template.bind({})