@statistikzh/leu 0.14.4 → 0.15.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 (148) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +17 -0
  3. package/dist/Accordion.js +2 -30
  4. package/dist/Button.d.ts +56 -68
  5. package/dist/Button.js +74 -52
  6. package/dist/ButtonGroup.d.ts +9 -9
  7. package/dist/ButtonGroup.js +30 -20
  8. package/dist/ChartWrapper.d.ts +38 -0
  9. package/dist/ChartWrapper.js +164 -0
  10. package/dist/Checkbox.d.ts +6 -21
  11. package/dist/Checkbox.js +17 -9
  12. package/dist/CheckboxGroup.d.ts +13 -14
  13. package/dist/CheckboxGroup.js +18 -11
  14. package/dist/Chip.js +1 -1
  15. package/dist/ChipGroup.js +1 -1
  16. package/dist/ChipLink.js +1 -1
  17. package/dist/ChipRemovable.js +1 -1
  18. package/dist/ChipSelectable.js +1 -1
  19. package/dist/Dialog.js +1 -1
  20. package/dist/Dropdown.js +3 -1
  21. package/dist/Icon.js +1 -1
  22. package/dist/Input.js +1 -1
  23. package/dist/{LeuElement-x8UlIDDl.js → LeuElement-B84x5CPL.js} +1 -1
  24. package/dist/Menu.js +1 -1
  25. package/dist/MenuItem.js +1 -1
  26. package/dist/Pagination.d.ts +40 -28
  27. package/dist/Pagination.js +39 -14
  28. package/dist/Placeholder.d.ts +27 -0
  29. package/dist/Placeholder.js +90 -0
  30. package/dist/Popup.js +1 -1
  31. package/dist/Radio.d.ts +6 -21
  32. package/dist/Radio.js +17 -9
  33. package/dist/RadioGroup.d.ts +28 -23
  34. package/dist/RadioGroup.js +29 -16
  35. package/dist/Range.js +1 -1
  36. package/dist/ScrollTop.d.ts +0 -1
  37. package/dist/ScrollTop.js +3 -1
  38. package/dist/Select.js +3 -1
  39. package/dist/Spinner.js +1 -1
  40. package/dist/Table.d.ts +0 -1
  41. package/dist/Table.js +3 -1
  42. package/dist/VisuallyHidden.js +1 -1
  43. package/dist/_tslib-CNEFicEt.js +30 -0
  44. package/dist/components/button/Button.d.ts +55 -67
  45. package/dist/components/button/Button.d.ts.map +1 -1
  46. package/dist/components/button/stories/button.stories.d.ts.map +1 -1
  47. package/dist/components/button-group/ButtonGroup.d.ts +9 -9
  48. package/dist/components/button-group/ButtonGroup.d.ts.map +1 -1
  49. package/dist/components/chart-wrapper/ChartWrapper.d.ts +34 -0
  50. package/dist/components/chart-wrapper/ChartWrapper.d.ts.map +1 -0
  51. package/dist/components/chart-wrapper/leu-chart-wrapper.d.ts +3 -0
  52. package/dist/components/chart-wrapper/leu-chart-wrapper.d.ts.map +1 -0
  53. package/dist/components/chart-wrapper/stories/chart-wrapper.stories.d.ts +47 -0
  54. package/dist/components/chart-wrapper/stories/chart-wrapper.stories.d.ts.map +1 -0
  55. package/dist/components/chart-wrapper/test/chart-wrapper.test.d.ts +2 -0
  56. package/dist/components/chart-wrapper/test/chart-wrapper.test.d.ts.map +1 -0
  57. package/dist/components/checkbox/Checkbox.d.ts +6 -21
  58. package/dist/components/checkbox/Checkbox.d.ts.map +1 -1
  59. package/dist/components/checkbox/CheckboxGroup.d.ts +13 -14
  60. package/dist/components/checkbox/CheckboxGroup.d.ts.map +1 -1
  61. package/dist/components/icon/stories/icon.stories.d.ts +10 -0
  62. package/dist/components/icon/stories/icon.stories.d.ts.map +1 -1
  63. package/dist/components/pagination/Pagination.d.ts +40 -27
  64. package/dist/components/pagination/Pagination.d.ts.map +1 -1
  65. package/dist/components/pagination/stories/pagination.stories.d.ts +10 -2
  66. package/dist/components/pagination/stories/pagination.stories.d.ts.map +1 -1
  67. package/dist/components/placeholder/Placeholder.d.ts +23 -0
  68. package/dist/components/placeholder/Placeholder.d.ts.map +1 -0
  69. package/dist/components/placeholder/leu-placeholder.d.ts +3 -0
  70. package/dist/components/placeholder/leu-placeholder.d.ts.map +1 -0
  71. package/dist/components/placeholder/stories/placeholder.stories.d.ts +27 -0
  72. package/dist/components/placeholder/stories/placeholder.stories.d.ts.map +1 -0
  73. package/dist/components/placeholder/test/placeholder.test.d.ts +2 -0
  74. package/dist/components/placeholder/test/placeholder.test.d.ts.map +1 -0
  75. package/dist/components/radio/Radio.d.ts +6 -21
  76. package/dist/components/radio/Radio.d.ts.map +1 -1
  77. package/dist/components/radio/RadioGroup.d.ts +28 -23
  78. package/dist/components/radio/RadioGroup.d.ts.map +1 -1
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +5 -2
  82. package/dist/leu-accordion.js +2 -1
  83. package/dist/leu-button-group.js +8 -1
  84. package/dist/leu-button.d.ts +0 -1
  85. package/dist/leu-button.js +3 -1
  86. package/dist/leu-chart-wrapper.d.ts +6 -0
  87. package/dist/leu-chart-wrapper.js +11 -0
  88. package/dist/leu-checkbox-group.js +5 -1
  89. package/dist/leu-checkbox.js +3 -1
  90. package/dist/leu-chip-group.js +1 -1
  91. package/dist/leu-chip-link.js +1 -1
  92. package/dist/leu-chip-removable.js +1 -1
  93. package/dist/leu-chip-selectable.js +1 -1
  94. package/dist/leu-dialog.js +1 -1
  95. package/dist/leu-dropdown.js +3 -1
  96. package/dist/leu-icon.js +1 -1
  97. package/dist/leu-input.js +1 -1
  98. package/dist/leu-menu-item.js +1 -1
  99. package/dist/leu-menu.js +1 -1
  100. package/dist/leu-pagination.d.ts +0 -1
  101. package/dist/leu-pagination.js +3 -1
  102. package/dist/leu-placeholder.d.ts +4 -0
  103. package/dist/leu-placeholder.js +7 -0
  104. package/dist/leu-popup.js +1 -1
  105. package/dist/leu-radio-group.js +4 -1
  106. package/dist/leu-radio.js +3 -1
  107. package/dist/leu-range.js +1 -1
  108. package/dist/leu-scroll-top.d.ts +0 -1
  109. package/dist/leu-scroll-top.js +3 -1
  110. package/dist/leu-select.js +3 -1
  111. package/dist/leu-spinner.js +1 -1
  112. package/dist/leu-table.d.ts +0 -1
  113. package/dist/leu-table.js +3 -1
  114. package/dist/leu-visually-hidden.js +1 -1
  115. package/dist/lib/a11y.d.ts +2 -2
  116. package/dist/theme.css +18 -18
  117. package/dist/vscode.html-custom-data.json +95 -25
  118. package/dist/vue/index.d.ts +86 -46
  119. package/dist/web-types.json +222 -80
  120. package/package.json +1 -1
  121. package/release-please-config.json +1 -2
  122. package/scripts/generate-component/templates/stories/[name].stories.ts +17 -4
  123. package/scripts/postcss-leu-font-styles.cjs +10 -10
  124. package/src/components/button/Button.ts +95 -79
  125. package/src/components/button/stories/button.stories.ts +5 -6
  126. package/src/components/button-group/ButtonGroup.ts +18 -13
  127. package/src/components/chart-wrapper/ChartWrapper.ts +78 -0
  128. package/src/components/chart-wrapper/chart-wrapper.css +87 -0
  129. package/src/components/chart-wrapper/leu-chart-wrapper.ts +5 -0
  130. package/src/components/chart-wrapper/stories/chart-wrapper.stories.ts +58 -0
  131. package/src/components/chart-wrapper/test/chart-wrapper.test.ts +77 -0
  132. package/src/components/checkbox/Checkbox.ts +13 -15
  133. package/src/components/checkbox/CheckboxGroup.ts +20 -16
  134. package/src/components/icon/stories/icon.stories.ts +27 -0
  135. package/src/components/pagination/Pagination.ts +45 -32
  136. package/src/components/pagination/stories/pagination.stories.ts +28 -17
  137. package/src/components/placeholder/Placeholder.ts +33 -0
  138. package/src/components/placeholder/leu-placeholder.ts +5 -0
  139. package/src/components/placeholder/placeholder.css +59 -0
  140. package/src/components/placeholder/stories/placeholder.stories.ts +34 -0
  141. package/src/components/placeholder/test/placeholder.test.ts +31 -0
  142. package/src/components/radio/Radio.ts +13 -15
  143. package/src/components/radio/RadioGroup.ts +42 -28
  144. package/src/docs/contributing.mdx +1 -1
  145. package/src/index.ts +1 -0
  146. package/src/lib/a11y.ts +2 -2
  147. package/src/styles/theme.css +89 -1
  148. package/src/styles/custom-properties.css +0 -89
@@ -1,38 +1,42 @@
1
1
  import { html } from "lit"
2
2
  import { classMap } from "lit/directives/class-map.js"
3
+ import { property } from "lit/decorators.js"
3
4
 
4
5
  import { LeuElement } from "../../lib/LeuElement.js"
5
6
 
6
7
  import styles from "./checkbox-group.css"
8
+ import { LeuCheckbox } from "./Checkbox.js"
7
9
 
8
10
  /**
11
+ *
12
+ * @slot - Place the checkboxes inside the default slot.
9
13
  * @tagname leu-checkbox-group
10
14
  */
11
15
  export class LeuCheckboxGroup extends LeuElement {
12
16
  static styles = [LeuElement.styles, styles]
13
17
 
14
- static properties = {
15
- orientation: { type: String, reflect: true },
16
- label: { type: String, reflect: true },
17
- }
18
+ /**
19
+ * Defines how the checkboxes should be aligned.
20
+ */
21
+ @property({ type: String, reflect: true })
22
+ orientation: "horizontal" | "vertical" = "horizontal"
18
23
 
19
- constructor() {
20
- super()
21
- /** @type {"horizontal" | "vertical"} */
22
- this.orientation = "horizontal"
23
- this.items = []
24
- }
24
+ /**
25
+ * The label of the checkbox group
26
+ */
27
+ @property({ type: String, reflect: true })
28
+ label?: string
29
+
30
+ private items: LeuCheckbox[] = []
25
31
 
26
32
  get value() {
27
33
  return this.items.filter((i) => i.checked).map((i) => i.value)
28
34
  }
29
35
 
30
- handleSlotChange() {
31
- this.handleItems()
32
- }
33
-
34
- handleItems() {
35
- this.items = Array.from(this.querySelectorAll(":scope > *:not([slot])"))
36
+ private handleSlotChange() {
37
+ this.items = Array.from(
38
+ this.querySelectorAll(":scope > *:not([slot])"),
39
+ ).filter((el) => el instanceof LeuCheckbox)
36
40
  }
37
41
 
38
42
  render() {
@@ -45,3 +45,30 @@ Colored.args = {
45
45
  size: 24,
46
46
  color: "#d93c1a",
47
47
  }
48
+
49
+ export const Overview = {
50
+ render: ({ color, size }) => html`
51
+ <ul
52
+ style="font-family: var(--leu-font-family-regular); color: var(--leu-color-black-60); list-style: none; margin: 0; padding: 0;"
53
+ >
54
+ ${Object.keys(paths).map(
55
+ (name) =>
56
+ html`<li
57
+ style="display: flex; align-items: center; margin-bottom: 1rem; gap: 1rem;"
58
+ >
59
+ <leu-icon
60
+ style="color: ${color}; ${size
61
+ ? `--leu-icon-size: ${size}px`
62
+ : ""};"
63
+ name=${ifDefined(name)}
64
+ ></leu-icon>
65
+ ${name}
66
+ </li>`,
67
+ )}
68
+ </ul>
69
+ `,
70
+ args: {
71
+ size: 24,
72
+ color: "#000000",
73
+ },
74
+ }
@@ -1,5 +1,6 @@
1
1
  import { html } from "lit"
2
2
  import { live } from "lit/directives/live.js"
3
+ import { property, state } from "lit/decorators.js"
3
4
 
4
5
  import { LeuElement } from "../../lib/LeuElement.js"
5
6
  import { LeuButton } from "../button/Button.js"
@@ -30,32 +31,30 @@ export class LeuPagination extends LeuElement {
30
31
  delegatesFocus: true,
31
32
  }
32
33
 
33
- static properties = {
34
- defaultPage: { type: Number, reflect: true },
35
- itemsPerPage: { type: Number, reflect: true },
36
- numOfItems: { type: Number, reflect: true },
37
- _page: { state: true },
38
- }
39
-
40
- constructor() {
41
- super()
34
+ @property({ type: Number, reflect: true })
35
+ defaultPage?: number
42
36
 
43
- /** @type {Number} */
44
- this.numOfItems = 1
37
+ /**
38
+ * Number of items per page.
39
+ */
40
+ @property({ type: Number, reflect: true })
41
+ itemsPerPage: number = 1
45
42
 
46
- /** @type {Number} */
47
- this.itemsPerPage = 1
43
+ /**
44
+ * Total number of items to paginate.
45
+ */
46
+ @property({ type: Number, reflect: true })
47
+ numOfItems: number = 1
48
48
 
49
- /**
50
- * Internal page state that contains an
51
- * already clamped page number. Should only
52
- * be accessed through the `page` getter and
53
- * setter.
54
- * @type {Number}
55
- * @internal
56
- */
57
- this._page = 1
58
- }
49
+ /**
50
+ * Internal page state that contains an
51
+ * already clamped page number. Should only
52
+ * be accessed through the `page` getter and
53
+ * setter.
54
+ * @internal
55
+ */
56
+ @state()
57
+ private _page: number = 1
59
58
 
60
59
  attributeChangedCallback(name, oldVal, newVal) {
61
60
  super.attributeChangedCallback(name, oldVal, newVal)
@@ -65,6 +64,11 @@ export class LeuPagination extends LeuElement {
65
64
  }
66
65
  }
67
66
 
67
+ /**
68
+ * The current page number. This is a 1-based index.
69
+ * When setting this value, it will be clamped
70
+ * to the range of valid pages.
71
+ */
68
72
  get page() {
69
73
  return this._page
70
74
  }
@@ -73,10 +77,19 @@ export class LeuPagination extends LeuElement {
73
77
  this._page = this._clampPage(page)
74
78
  }
75
79
 
80
+ /**
81
+ * The index of the first item on the current page.
82
+ */
76
83
  get startIndex() {
77
84
  return (this.page - 1) * this.itemsPerPage
78
85
  }
79
86
 
87
+ /**
88
+ * The index of the last item on the current page.
89
+ * This is exclusive, meaning it is one past the last item.
90
+ *
91
+ * @todo This value should be inclusive, meaning it should be the index of the last item on the page.
92
+ */
80
93
  get endIndex() {
81
94
  return Math.min(this.startIndex + this.itemsPerPage, this.numOfItems)
82
95
  }
@@ -85,19 +98,19 @@ export class LeuPagination extends LeuElement {
85
98
  return Math.ceil(this.numOfItems / this.itemsPerPage)
86
99
  }
87
100
 
88
- _isFirstPage() {
101
+ private _isFirstPage() {
89
102
  return this.page === MIN_PAGE
90
103
  }
91
104
 
92
- _isLastPage() {
105
+ private _isLastPage() {
93
106
  return this.page === this._maxPage
94
107
  }
95
108
 
96
- _clampPage(page) {
109
+ private _clampPage(page: number) {
97
110
  return Math.min(Math.max(page, MIN_PAGE), this._maxPage)
98
111
  }
99
112
 
100
- _updatePage(page) {
113
+ private _updatePage(page) {
101
114
  const prevPage = this.page
102
115
  this.page = this._clampPage(page)
103
116
 
@@ -115,18 +128,18 @@ export class LeuPagination extends LeuElement {
115
128
  }
116
129
  }
117
130
 
118
- _handleChange(event) {
131
+ private _handleChange(event: Event & { target: HTMLInputElement }) {
119
132
  this._updatePage(parseInt(event.target.value, 10) || 0)
120
133
  }
121
134
 
122
- _handleInput(event) {
135
+ private _handleInput(event: InputEvent & { target: HTMLInputElement }) {
123
136
  if (event.target.value !== "") {
124
137
  event.preventDefault()
125
138
  this._handleChange(event)
126
139
  }
127
140
  }
128
141
 
129
- _handleKeyDown(event) {
142
+ private _handleKeyDown(event: KeyboardEvent & { target: HTMLInputElement }) {
130
143
  if (event.key === "ArrowUp") {
131
144
  event.preventDefault()
132
145
  this._updatePage(this.page + 1)
@@ -158,7 +171,7 @@ export class LeuPagination extends LeuElement {
158
171
  <leu-button
159
172
  variant="secondary"
160
173
  label="Vorherige Seite"
161
- @click=${(_) => {
174
+ @click=${() => {
162
175
  this._updatePage(this.page - 1)
163
176
  }}
164
177
  ?disabled=${this._isFirstPage()}
@@ -167,7 +180,7 @@ export class LeuPagination extends LeuElement {
167
180
  <leu-button
168
181
  variant="secondary"
169
182
  label="Nächste Seite"
170
- @click=${(_) => {
183
+ @click=${() => {
171
184
  this._updatePage(this.page + 1)
172
185
  }}
173
186
  ?disabled=${this._isLastPage()}
@@ -1,11 +1,20 @@
1
1
  import { html } from "lit"
2
- import { action } from "@storybook/addon-actions"
3
-
4
- import "../leu-pagination.js"
2
+ import { ifDefined } from "lit/directives/if-defined.js"
5
3
 
6
4
  // https://stackoverflow.com/questions/72566428/storybook-angular-how-to-dynamically-update-args-from-the-template
7
5
  import { UPDATE_STORY_ARGS } from "@storybook/core-events" // eslint-disable-line
8
- import { ifDefined } from "lit/directives/if-defined.js"
6
+ import { action, HandlerFunction } from "@storybook/addon-actions"
7
+ import { Meta, StoryObj } from "@storybook/web-components"
8
+
9
+ import "../leu-pagination.js"
10
+ import { LeuPagination } from "../Pagination.js"
11
+
12
+ interface ExtraArgs {
13
+ onPageChange: HandlerFunction
14
+ }
15
+
16
+ type StoryArgs = LeuPagination & ExtraArgs
17
+ type Story = StoryObj<StoryArgs>
9
18
 
10
19
  function updateStorybookArgss(id, args) {
11
20
  const channel = window.__STORYBOOK_ADDONS_CHANNEL__
@@ -69,13 +78,13 @@ export default {
69
78
  url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17341-82468&mode=design&t=lzVrtq8lxYVJU5TB-11",
70
79
  },
71
80
  },
72
- }
81
+ } satisfies Meta<StoryArgs>
73
82
 
74
- function Template(
75
- { startIndex, endIndex, onPageChange, itemsPerPage, defaultPage },
76
- { id },
77
- ) {
78
- return html`
83
+ const Template: Story = {
84
+ render: (
85
+ { startIndex, endIndex, onPageChange, itemsPerPage, defaultPage },
86
+ { id },
87
+ ) => html`
79
88
  ${items
80
89
  .slice(startIndex, endIndex)
81
90
  .map((item) => html`<div>${item.label}</div>`)}
@@ -92,13 +101,15 @@ function Template(
92
101
  }}
93
102
  >
94
103
  </leu-pagination>
95
- `
104
+ `,
96
105
  }
97
106
 
98
- export const Regular = Template.bind({})
99
- Regular.args = {
100
- startIndex: 0,
101
- endIndex: 5,
102
- itemsPerPage: 5,
103
- // defaultPage: 2,
107
+ export const Regular: Story = {
108
+ ...Template,
109
+ args: {
110
+ startIndex: 0,
111
+ endIndex: 5,
112
+ itemsPerPage: 5,
113
+ // defaultPage: 2,
114
+ },
104
115
  }
@@ -0,0 +1,33 @@
1
+ import { html } from "lit"
2
+
3
+ import { LeuElement } from "../../lib/LeuElement.js"
4
+
5
+ import styles from "./placeholder.css"
6
+
7
+ /**
8
+ * @summary * A placeholder to display when no content is available.
9
+ * @tagname leu-placeholder
10
+ * @slot title - The placeholders title. Use a heading tag (h1-6) depeneding on your context.
11
+ * @slot description - A description of the placeholder. Content is wrapped in a `<p>` tag by the component.
12
+ * @slot cta - A call to action button like "Reload" or "Create". Add a single `<leu-button>`.
13
+ *
14
+ * @todo Add pending state with a skeleton.
15
+ */
16
+ export class LeuPlaceholder extends LeuElement {
17
+ static styles = [LeuElement.styles, styles]
18
+
19
+ static shadowRootOptions = {
20
+ ...LeuElement.shadowRootOptions,
21
+ delegatesFocus: true,
22
+ }
23
+
24
+ render() {
25
+ return html`
26
+ <div class="placeholder">
27
+ <slot class="placeholder__title" name="title"></slot>
28
+ <p><slot class="placeholder__description" name="description"></slot></p>
29
+ <slot name="cta"></slot>
30
+ </div>
31
+ `
32
+ }
33
+ }
@@ -0,0 +1,5 @@
1
+ import { LeuPlaceholder } from "./Placeholder.js"
2
+
3
+ export { LeuPlaceholder }
4
+
5
+ LeuPlaceholder.define("leu-placeholder")
@@ -0,0 +1,59 @@
1
+ @import url("../../styles/custom-media.css");
2
+
3
+ :host {
4
+ --placeholder-font-regular: var(--leu-font-family-regular);
5
+ --placeholder-font-black: var(--leu-font-family-black);
6
+
7
+ --placeholder-border-color: var(--leu-color-black-20);
8
+
9
+ font-family: var(--placeholder-font-regular);
10
+ }
11
+
12
+ .placeholder {
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: center;
16
+ text-align: center;
17
+
18
+ border: 2px dashed var(--placeholder-border-color);
19
+ border-radius: 0.25rem;
20
+
21
+ padding: 2rem 1.5rem;
22
+
23
+ @media (--viewport-regular) {
24
+ padding: 2.5rem 2rem;
25
+ }
26
+
27
+ @media (--viewport-xlarge) {
28
+ padding: 3.5rem 2.5rem;
29
+ }
30
+ }
31
+
32
+ .placeholder__title {
33
+ display: block;
34
+ }
35
+
36
+ .placeholder__title::slotted(:where(h1, h2, h3, h4, h5, h6)) {
37
+ font: var(--leu-t-curve-regular-black-font);
38
+ color: var(--leu-color-black-100);
39
+ margin: 0 0 0.5rem;
40
+ }
41
+
42
+ .placeholder__description {
43
+ font: var(--leu-t-curve-small-regular-font);
44
+ color: var(--leu-color-black-60);
45
+
46
+ margin-bottom: 0.75rem;
47
+
48
+ @media (--viewport-small) {
49
+ margin-bottom: 2rem;
50
+ }
51
+
52
+ @media (--viewport-regular) {
53
+ margin-bottom: 1.25rem;
54
+ }
55
+
56
+ @media (--viewport-xlarge) {
57
+ margin-bottom: 1.5rem;
58
+ }
59
+ }
@@ -0,0 +1,34 @@
1
+ import { Meta, StoryObj } from "@storybook/web-components"
2
+ import { html } from "lit"
3
+
4
+ import "../../button/leu-button.js"
5
+ import "../../icon/leu-icon.js"
6
+ import "../leu-placeholder.js"
7
+ import { LeuPlaceholder } from "../Placeholder.js"
8
+
9
+ type StoryArgs = LeuPlaceholder
10
+ type Story = StoryObj<StoryArgs>
11
+
12
+ export default {
13
+ title: "Components/Placeholder",
14
+ component: "leu-placeholder",
15
+ } satisfies Meta<StoryArgs>
16
+
17
+ const Template: Story = {
18
+ render: (_args) =>
19
+ html` <leu-placeholder>
20
+ <h2 slot="title">Keine Ergebnisse zu «Regoin Zürich» gefunden.</h2>
21
+ <p slot="description">
22
+ Überprüfen Sie die Schreibweise der eingegebenen Wörter. Versuchen Sie
23
+ andere Stichwörter. Versuchen Sie allgemeinere Stichwörter.
24
+ </p>
25
+ <leu-button slot="cta"> Suche zurücksetzen </leu-button>
26
+ </leu-placeholder>`,
27
+ }
28
+
29
+ export const Regular = {
30
+ ...Template,
31
+ args: {
32
+ // Add default args here
33
+ },
34
+ }
@@ -0,0 +1,31 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect } from "@open-wc/testing"
3
+
4
+ import "../leu-placeholder.js"
5
+
6
+ async function defaultFixture() {
7
+ return fixture(
8
+ html`<leu-placeholder>
9
+ <h2 slot="title">Keine Ergebnisse zu «Regoin Zürich» gefunden.</h2>
10
+ <p slot="description">
11
+ Überprüfen Sie die Schreibweise der eingegebenen Wörter. Versuchen Sie
12
+ andere Stichwörter. Versuchen Sie allgemeinere Stichwörter.
13
+ </p>
14
+ <leu-button slot="cta"> Suche zurücksetzen </leu-button>
15
+ </leu-placeholder>`,
16
+ )
17
+ }
18
+
19
+ describe("LeuPlaceholder", () => {
20
+ it("is a defined element", async () => {
21
+ const el = customElements.get("leu-placeholder")
22
+
23
+ expect(el).not.to.be.undefined
24
+ })
25
+
26
+ it("passes the a11y audit", async () => {
27
+ const el = await defaultFixture()
28
+
29
+ await expect(el).shadowDom.to.be.accessible()
30
+ })
31
+ })
@@ -1,4 +1,5 @@
1
1
  import { html } from "lit"
2
+ import { property } from "lit/decorators.js"
2
3
 
3
4
  import { LeuElement } from "../../lib/LeuElement.js"
4
5
 
@@ -16,29 +17,26 @@ export class LeuRadio extends LeuElement {
16
17
  delegatesFocus: true,
17
18
  }
18
19
 
19
- static properties = {
20
- checked: { type: Boolean, reflect: true },
21
- disabled: { type: Boolean, reflect: true },
22
- value: { type: String, reflect: true },
23
- name: { type: String, reflect: true },
24
- }
20
+ @property({ type: Boolean, reflect: true })
21
+ checked: boolean = false
25
22
 
26
- constructor() {
27
- super()
28
- this.checked = false
29
- this.disabled = false
30
- this.name = ""
31
- this.value = ""
32
- }
23
+ @property({ type: Boolean, reflect: true })
24
+ disabled: boolean = false
25
+
26
+ @property({ type: String, reflect: true })
27
+ value: string = ""
28
+
29
+ @property({ type: String, reflect: true })
30
+ name: string = ""
33
31
 
34
- handleChange(event) {
32
+ private handleChange(event: Event & { target: HTMLInputElement }) {
35
33
  this.checked = event.target.checked
36
34
 
37
35
  const customEvent = new CustomEvent(event.type, event)
38
36
  this.dispatchEvent(customEvent)
39
37
  }
40
38
 
41
- handleInput(event) {
39
+ private handleInput(event: InputEvent & { target: HTMLInputElement }) {
42
40
  this.checked = event.target.checked
43
41
  }
44
42
 
@@ -1,28 +1,39 @@
1
1
  import { html } from "lit"
2
2
  import { classMap } from "lit/directives/class-map.js"
3
+ import { property } from "lit/decorators.js"
3
4
 
4
5
  import { LeuElement } from "../../lib/LeuElement.js"
5
6
 
6
7
  import styles from "./radio-group.css"
8
+ import { LeuRadio } from "./Radio.js"
7
9
 
8
10
  /**
11
+ * @summary Handles a group of radio buttons, allowing only one to be selected at a time. It provides keyboard navigation and manages focus within the group.
12
+ * @slot - Place the radio buttons inside the default slot.
9
13
  * @tagname leu-radio-group
10
14
  */
11
15
  export class LeuRadioGroup extends LeuElement {
12
16
  static styles = [LeuElement.styles, styles]
13
17
 
14
- static properties = {
15
- orientation: { type: String, reflect: true },
16
- label: { type: String, reflect: true },
17
- }
18
+ /**
19
+ * Defines how the radio buttons should be aligned.
20
+ */
21
+ @property({ type: String, reflect: true })
22
+ orientation: "horizontal" | "vertical" = "horizontal"
18
23
 
19
- constructor() {
20
- super()
21
- /** @type {"horizontal" | "vertical"} */
22
- this.orientation = "horizontal"
23
- this._currentIndex = 0
24
- this.items = []
25
- }
24
+ /**
25
+ * The label of the radio group
26
+ */
27
+ @property({ type: String, reflect: true })
28
+ label?: string
29
+
30
+ /**
31
+ * Index of the radio button that would be focused
32
+ * when the focus moves into the group.
33
+ */
34
+ private currentIndex = 0
35
+
36
+ private items: LeuRadio[] = []
26
37
 
27
38
  get value() {
28
39
  const checkedValues = this.items
@@ -42,7 +53,7 @@ export class LeuRadioGroup extends LeuElement {
42
53
  this.removeEventListeners()
43
54
  }
44
55
 
45
- addEventListeners() {
56
+ private addEventListeners() {
46
57
  /**
47
58
  * It is technically possible to add an event listener to the host element
48
59
  * before it is connected to the dom. In that case the outside event listener would
@@ -55,21 +66,21 @@ export class LeuRadioGroup extends LeuElement {
55
66
  this.addEventListener("keydown", this.handleKeyDown)
56
67
  }
57
68
 
58
- removeEventListeners() {
69
+ private removeEventListeners() {
59
70
  this.removeEventListener("input", this.handleInput, { capture: true })
60
71
  this.removeEventListener("focusin", this.handleFocusIn)
61
72
  this.removeEventListener("keydown", this.handleKeyDown)
62
73
  }
63
74
 
64
- handleSlotChange() {
75
+ private handleSlotChange() {
65
76
  this.handleItems()
66
77
  }
67
78
 
68
- handleFocusIn = (e) => {
69
- this._currentIndex = this.items.indexOf(e.target)
79
+ private handleFocusIn = (e: FocusEvent & { target: LeuRadio }) => {
80
+ this.currentIndex = this.items.indexOf(e.target)
70
81
  }
71
82
 
72
- handleKeyDown = (e) => {
83
+ private handleKeyDown = (e: KeyboardEvent & { target: LeuRadio }) => {
73
84
  const currentIndex = this.items.indexOf(e.target)
74
85
 
75
86
  switch (e.key) {
@@ -93,19 +104,19 @@ export class LeuRadioGroup extends LeuElement {
93
104
  this.setTabIndex()
94
105
  }
95
106
 
96
- handleInput = (e) => {
107
+ private handleInput = (e: InputEvent & { target: LeuRadio }) => {
97
108
  this.items.forEach((item) => {
98
109
  item.checked = item === e.target // eslint-disable-line no-param-reassign
99
110
  })
100
111
  }
101
112
 
102
- selectItem(selectingItem) {
113
+ private selectItem(selectingItem: LeuRadio) {
103
114
  this.items.forEach((item) => {
104
115
  item.checked = item === selectingItem // eslint-disable-line no-param-reassign
105
116
  })
106
117
  }
107
118
 
108
- selectNextItem(startingIndex, direction) {
119
+ private selectNextItem(startingIndex: number, direction: -1 | 1) {
109
120
  let selected = false
110
121
 
111
122
  for (let index = 0; index < this.items.length; index += 1) {
@@ -124,23 +135,26 @@ export class LeuRadioGroup extends LeuElement {
124
135
  }
125
136
  }
126
137
 
127
- setTabIndex() {
138
+ private setTabIndex() {
128
139
  this.items.forEach((item, index) => {
129
- if (index === this._currentIndex) {
130
- item.tabIndex = "0" // eslint-disable-line no-param-reassign
140
+ if (index === this.currentIndex) {
141
+ item.tabIndex = 0 // eslint-disable-line no-param-reassign
131
142
  } else {
132
- item.tabIndex = "-1" // eslint-disable-line no-param-reassign
143
+ item.tabIndex = -1 // eslint-disable-line no-param-reassign
133
144
  }
134
145
  })
135
146
  }
136
147
 
137
- handleItems() {
138
- this.items = Array.from(this.querySelectorAll(":scope > *:not([slot])"))
148
+ private handleItems() {
149
+ this.items = Array.from(
150
+ this.querySelectorAll(":scope > *:not([slot])"),
151
+ ).filter((el) => el instanceof LeuRadio)
152
+
139
153
  this.initializeIndex()
140
154
  this.setTabIndex()
141
155
  }
142
156
 
143
- initializeIndex() {
157
+ private initializeIndex() {
144
158
  const index = this.items.findIndex(
145
159
  (item) => item.hasAttribute("checked") && !item.hasAttribute("disabled"),
146
160
  )
@@ -148,7 +162,7 @@ export class LeuRadioGroup extends LeuElement {
148
162
  (item) => !item.hasAttribute("disabled"),
149
163
  )
150
164
 
151
- this._currentIndex = index >= 0 ? index : nextEnabledIndex
165
+ this.currentIndex = index >= 0 ? index : nextEnabledIndex
152
166
  }
153
167
 
154
168
  render() {