@statistikzh/leu 0.3.0 → 0.4.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 (153) hide show
  1. package/.github/workflows/deploy-github-pages.yaml +33 -0
  2. package/.storybook/main.js +27 -1
  3. package/.storybook/manager-head.html +1 -0
  4. package/.storybook/manager.js +9 -0
  5. package/.storybook/preview-head.html +1 -1
  6. package/.storybook/preview.js +59 -5
  7. package/.storybook/static/logo.svg +19 -0
  8. package/.storybook/theme.js +7 -0
  9. package/CHANGELOG.md +30 -0
  10. package/README.md +1 -1
  11. package/dist/Button.js +15 -18
  12. package/dist/ButtonGroup.js +5 -7
  13. package/dist/Checkbox.js +101 -84
  14. package/dist/CheckboxGroup.js +41 -37
  15. package/dist/{Chip-5f70d04f.js → Chip-dac7337d.js} +5 -1
  16. package/dist/ChipGroup.js +2 -5
  17. package/dist/ChipLink.js +4 -7
  18. package/dist/ChipRemovable.js +4 -7
  19. package/dist/ChipSelectable.js +4 -7
  20. package/dist/Dropdown.js +55 -26
  21. package/dist/Input.js +29 -28
  22. package/dist/Menu.js +2 -5
  23. package/dist/MenuItem.js +30 -15
  24. package/dist/Pagination.js +54 -54
  25. package/dist/Radio.js +7 -6
  26. package/dist/RadioGroup.js +41 -39
  27. package/dist/Select.js +55 -43
  28. package/dist/Table.js +137 -120
  29. package/dist/{defineElement-ba770aed.js → _rollupPluginBabelHelpers-20f659f4.js} +1 -15
  30. package/dist/defineElement-47d4f665.js +15 -0
  31. package/dist/index.js +29 -19
  32. package/dist/leu-button-group.js +7 -3
  33. package/dist/leu-button.js +6 -3
  34. package/dist/leu-checkbox-group.js +6 -3
  35. package/dist/leu-checkbox.js +6 -3
  36. package/dist/leu-chip-group.js +6 -3
  37. package/dist/leu-chip-link.js +7 -4
  38. package/dist/leu-chip-removable.js +7 -4
  39. package/dist/leu-chip-selectable.js +7 -4
  40. package/dist/leu-dropdown.js +13 -3
  41. package/dist/leu-input.js +7 -3
  42. package/dist/leu-menu-item.js +8 -3
  43. package/dist/leu-menu.js +6 -3
  44. package/dist/leu-pagination.js +8 -3
  45. package/dist/leu-popup-4bf6f1f4.js +216 -0
  46. package/dist/leu-radio-group.js +6 -3
  47. package/dist/leu-radio.js +6 -3
  48. package/dist/leu-select.js +14 -3
  49. package/dist/leu-table.js +9 -3
  50. package/package.json +24 -12
  51. package/scripts/generate-component/templates/[Name].js +0 -5
  52. package/scripts/generate-component/templates/[name].css +1 -1
  53. package/scripts/generate-component/templates/[namespace]-[name].js +5 -2
  54. package/src/components/accordion/Accordion.js +0 -6
  55. package/src/components/accordion/leu-accordion.js +5 -2
  56. package/src/components/accordion/stories/accordion.stories.js +7 -4
  57. package/src/components/accordion/test/accordion.test.js +92 -2
  58. package/src/components/breadcrumb/Breadcrumb.js +310 -0
  59. package/src/components/breadcrumb/breadcrumb.css +114 -0
  60. package/src/components/breadcrumb/leu-breadcrumb.js +6 -0
  61. package/src/components/breadcrumb/stories/breadcrumb.stories.js +73 -0
  62. package/src/components/breadcrumb/test/breadcrumb.test.js +141 -0
  63. package/src/components/button/Button.js +10 -15
  64. package/src/components/button/button.css +3 -3
  65. package/src/components/button/leu-button.js +5 -2
  66. package/src/components/button/stories/button.stories.js +58 -37
  67. package/src/components/button/test/button.test.js +112 -2
  68. package/src/components/button-group/ButtonGroup.js +1 -7
  69. package/src/components/button-group/leu-button-group.js +5 -2
  70. package/src/components/button-group/stories/button-group.stories.js +6 -0
  71. package/src/components/button-group/test/button-group.test.js +79 -3
  72. package/src/components/checkbox/Checkbox.js +6 -85
  73. package/src/components/checkbox/CheckboxGroup.js +8 -38
  74. package/src/components/checkbox/checkbox-group.css +29 -0
  75. package/src/components/checkbox/checkbox.css +76 -0
  76. package/src/components/checkbox/leu-checkbox-group.js +5 -2
  77. package/src/components/checkbox/leu-checkbox.js +5 -2
  78. package/src/components/checkbox/stories/checkbox-group.stories.js +44 -21
  79. package/src/components/checkbox/stories/checkbox.stories.js +7 -1
  80. package/src/components/checkbox/test/checkbox-group.test.js +124 -0
  81. package/src/components/checkbox/test/checkbox.test.js +72 -59
  82. package/src/components/chip/Chip.js +1 -0
  83. package/src/components/chip/ChipGroup.js +0 -5
  84. package/src/components/chip/ChipLink.js +1 -6
  85. package/src/components/chip/ChipRemovable.js +1 -6
  86. package/src/components/chip/ChipSelectable.js +1 -6
  87. package/src/components/chip/exports.js +4 -10
  88. package/src/components/chip/leu-chip-group.js +5 -2
  89. package/src/components/chip/leu-chip-link.js +5 -2
  90. package/src/components/chip/leu-chip-removable.js +5 -2
  91. package/src/components/chip/leu-chip-selectable.js +5 -2
  92. package/src/components/chip/stories/chip-group.stories.js +18 -6
  93. package/src/components/chip/stories/chip-link.stories.js +16 -4
  94. package/src/components/chip/stories/chip-removable.stories.js +15 -4
  95. package/src/components/chip/stories/chip-selectable.stories.js +13 -3
  96. package/src/components/chip/test/chip-group.test.js +124 -0
  97. package/src/components/chip/test/chip-link.test.js +58 -0
  98. package/src/components/chip/test/chip-removable.test.js +79 -0
  99. package/src/components/chip/test/chip-selectable.test.js +95 -0
  100. package/src/components/chip/test/chip.test.js +1 -1
  101. package/src/components/dropdown/Dropdown.js +52 -24
  102. package/src/components/dropdown/dropdown.css +1 -2
  103. package/src/components/dropdown/leu-dropdown.js +5 -2
  104. package/src/components/dropdown/stories/dropdown.stories.js +11 -5
  105. package/src/components/dropdown/test/dropdown.test.js +6 -6
  106. package/src/components/icon/icon.js +1 -1
  107. package/src/components/icon/test/icon.test.js +66 -0
  108. package/src/components/input/Input.js +18 -24
  109. package/src/components/input/input.css +9 -6
  110. package/src/components/input/leu-input.js +5 -2
  111. package/src/components/input/stories/input.stories.js +8 -2
  112. package/src/components/input/test/input.test.js +431 -4
  113. package/src/components/menu/Menu.js +0 -5
  114. package/src/components/menu/MenuItem.js +20 -13
  115. package/src/components/menu/leu-menu-item.js +5 -2
  116. package/src/components/menu/leu-menu.js +5 -2
  117. package/src/components/menu/menu-item.css +5 -2
  118. package/src/components/menu/stories/menu-item.stories.js +13 -4
  119. package/src/components/menu/stories/menu.stories.js +11 -5
  120. package/src/components/menu/test/menu-item.test.js +180 -0
  121. package/src/components/menu/test/menu.test.js +10 -2
  122. package/src/components/pagination/Pagination.js +53 -65
  123. package/src/components/pagination/leu-pagination.js +5 -2
  124. package/src/components/pagination/stories/pagination.stories.js +17 -9
  125. package/src/components/pagination/test/pagination.test.js +191 -5
  126. package/src/components/popup/Popup.js +200 -0
  127. package/src/components/popup/leu-popup.js +6 -0
  128. package/src/components/popup/popup.css +27 -0
  129. package/src/components/popup/stories/popup.stories.js +58 -0
  130. package/src/components/popup/test/popup.test.js +29 -0
  131. package/src/components/radio/Radio.js +2 -6
  132. package/src/components/radio/RadioGroup.js +6 -38
  133. package/src/components/radio/leu-radio-group.js +5 -2
  134. package/src/components/radio/leu-radio.js +5 -2
  135. package/src/components/radio/radio-group.css +29 -0
  136. package/src/components/radio/stories/radio-group.stories.js +38 -19
  137. package/src/components/radio/stories/radio.stories.js +7 -1
  138. package/src/components/radio/test/radio-group.test.js +86 -0
  139. package/src/components/radio/test/radio.test.js +108 -17
  140. package/src/components/select/Select.js +34 -31
  141. package/src/components/select/leu-select.js +5 -2
  142. package/src/components/select/select.css +13 -13
  143. package/src/components/select/stories/select.stories.js +15 -168
  144. package/src/components/select/test/fixtures.js +162 -0
  145. package/src/components/select/test/select.test.js +236 -12
  146. package/src/components/table/Table.js +43 -118
  147. package/src/components/table/leu-table.js +5 -2
  148. package/src/components/table/stories/table.stories.js +20 -10
  149. package/src/components/table/table.css +99 -0
  150. package/src/components/table/test/table.test.js +1 -1
  151. package/src/lib/utils.js +17 -0
  152. package/{web-dev-server-storybook.config.mjs → web-dev-server.config.mjs} +1 -2
  153. package/web-test-runner.config.mjs +15 -2
@@ -0,0 +1,310 @@
1
+ import { html, LitElement, nothing } from "lit"
2
+ import { createRef, ref } from "lit/directives/ref.js"
3
+ import { classMap } from "lit/directives/class-map.js"
4
+
5
+ import styles from "./breadcrumb.css"
6
+ import { Icon } from "../icon/icon.js"
7
+ import "../menu/leu-menu.js"
8
+ import "../menu/leu-menu-item.js"
9
+ import "../popup/leu-popup.js"
10
+ import { debounce } from "../../lib/utils.js"
11
+
12
+ /**
13
+ * A Breadcrumb Navigation.
14
+ *
15
+ * The breadcrumbs can be displayed in two different layouts.
16
+ * Only the back link (the last item / parent of the current page)
17
+ * is displayed when…
18
+ * - … the width of the container is smaller
19
+ * than the BACK_ONLY_BREAKPOINT.
20
+ * - … less then two breadcrumb items could be displayed
21
+ * without overflowing the container.
22
+ *
23
+ * Otherwise as many items as possible are displayed in an inline list
24
+ * without overflowing the container. The remaining items are displayed
25
+ * in a dropdown menu.
26
+ *
27
+ * In order to determine the exact numbers of items that have to be
28
+ * hidden inside the dropdown, all of them have to be rendered first.
29
+ * 1. Render all items
30
+ * 2. Calculate (measure) the number of items that can be displayed
31
+ * without overflowing the container.
32
+ * 3. Updating the state (_hiddeItems) which will trigger a rerender
33
+ * 4. Render the items again with the new state.
34
+ *
35
+ * This results in multiple updates scheduled one after another. Lit
36
+ * will also print a waring in the console beacause of that.
37
+ * It's no a nice behaviour but the only one that works without
38
+ * having duplicate and hidden markup to derive the sizes from that.
39
+ *
40
+ *
41
+ * @prop {Array} items - Object array with { label, href }
42
+ * @prop {Boolean} inverted - invert color on dark background
43
+ *
44
+ * @tagname leu-breadcrumb
45
+ */
46
+ export class LeuBreadcrumb extends LitElement {
47
+ static styles = styles
48
+
49
+ static properties = {
50
+ items: { type: Array },
51
+ inverted: { type: Boolean, reflect: true },
52
+
53
+ _hiddenItems: { state: true },
54
+ _showBackOnly: { state: true },
55
+ _isRecalculating: { state: true },
56
+ _isDropdownOpen: { state: true },
57
+ }
58
+
59
+ static BACK_ONLY_BREAKPOINT = 320
60
+
61
+ constructor() {
62
+ super()
63
+ /** @type {Array} */
64
+ this.items = []
65
+ /** @type {Boolean} - will be used on dark Background */
66
+ this.inverted = false
67
+
68
+ /** @internal */
69
+ this._containerRef = createRef()
70
+ /** @internal */
71
+ this._hiddenItems = 0
72
+ /** @internal */
73
+ this._showBackOnly = null
74
+ /** @internal */
75
+ this._lastContainerWidth = null
76
+ /**
77
+ * @internal
78
+ * Forces the toggle button to be rendered
79
+ * so that all possible inline items will be measured.
80
+ * */
81
+ this._isRecalculating = true
82
+ /** @internal */
83
+ this._isDropdownOpen = false
84
+
85
+ this.resizeObserver = new ResizeObserver(
86
+ debounce(() => {
87
+ this._handleResize()
88
+ }, 500)
89
+ )
90
+ }
91
+
92
+ firstUpdated() {
93
+ this.resizeObserver.observe(this._containerRef.value)
94
+ }
95
+
96
+ async updated(changedProperties) {
97
+ if (changedProperties.has("items")) {
98
+ this._hiddenItems = 0
99
+ this._isRecalculating = true
100
+ await this.updateComplete
101
+ this._checkWidth()
102
+ }
103
+ }
104
+
105
+ disconnectedCallback() {
106
+ super.disconnectedCallback()
107
+
108
+ window.removeEventListener("click", this._closeDropdown)
109
+ this.resizeObserver.disconnect()
110
+ }
111
+
112
+ /** @internal */
113
+ get _listItems() {
114
+ return this.items.toSpliced(1, this._hiddenItems)
115
+ }
116
+
117
+ /** @internal */
118
+ get _dropdownItems() {
119
+ return this.items.slice(1, 1 + this._hiddenItems)
120
+ }
121
+
122
+ _handleResize = async () => {
123
+ const containerOffsetWidth = this._containerRef.value.offsetWidth
124
+ const sizeIsGrowing = containerOffsetWidth > this._lastContainerWidth
125
+ this._lastContainerWidth = containerOffsetWidth
126
+
127
+ /**
128
+ * Show only the back link (parent of the current page)
129
+ * when the width of the container is smaller than the BACK_ONLY_BREAKPOINT
130
+ */
131
+ if (containerOffsetWidth <= LeuBreadcrumb.BACK_ONLY_BREAKPOINT) {
132
+ this._showBackOnly = true
133
+ this._isRecalculating = false
134
+ return
135
+ }
136
+
137
+ this._showBackOnly = false
138
+
139
+ /**
140
+ * In order to calculate how many items can be displayed
141
+ * when the container is growing, all items have to
142
+ * be marked as displayed (_hiddenItems = 0) and
143
+ * rendered.
144
+ */
145
+ if (sizeIsGrowing && this._hiddenItems > 0) {
146
+ this._hiddenItems = 0
147
+ this._isRecalculating = true
148
+ await this.updateComplete
149
+ }
150
+
151
+ this._checkWidth()
152
+ }
153
+
154
+ /**
155
+ * Calculate the number of items that can be displayed
156
+ * without overflowing the container.
157
+ * @internal
158
+ * @returns {void}
159
+ */
160
+ _checkWidth() {
161
+ const containerOffsetWidth = this._containerRef.value.offsetWidth
162
+ const containerScrollWidth = this._containerRef.value.scrollWidth
163
+ this._lastContainerWidth = containerOffsetWidth
164
+
165
+ /** When the container is not overflowing, nothing has to be done */
166
+ if (containerOffsetWidth === containerScrollWidth) {
167
+ this._isRecalculating = false
168
+ return
169
+ }
170
+
171
+ const listItems = this._containerRef.value.querySelectorAll(
172
+ "li:not([data-dropdown-toggle])"
173
+ )
174
+ const listItemWidths = [...listItems].map((o) => o.offsetWidth)
175
+
176
+ let hiddenItems = 0
177
+ let hiddenItemsWidth = 0
178
+
179
+ /**
180
+ * Remove item by item until the sum of the remaining items
181
+ * is smaller than the width of the container.
182
+ * The first item will not be removed.
183
+ */
184
+ while (
185
+ hiddenItems < listItemWidths.length &&
186
+ containerOffsetWidth < containerScrollWidth - hiddenItemsWidth
187
+ ) {
188
+ hiddenItems += 1
189
+
190
+ hiddenItemsWidth = listItemWidths
191
+ .slice(1, 1 + hiddenItems)
192
+ .reduce((sum, itemWidth) => sum + itemWidth, 0)
193
+ }
194
+
195
+ this._hiddenItems += hiddenItems
196
+ this._isRecalculating = false
197
+ }
198
+
199
+ /** @internal */
200
+ _handleDropdownToggle = (e) => {
201
+ e.stopPropagation()
202
+
203
+ this._isDropdownOpen = !this._isDropdownOpen
204
+
205
+ if (this._isDropdownOpen) {
206
+ window.addEventListener("click", this._closeDropdown)
207
+ } else {
208
+ window.removeEventListener("click", this._closeDropdown)
209
+ }
210
+ }
211
+
212
+ _closeDropdown = () => {
213
+ this._isDropdownOpen = false
214
+ window.removeEventListener("click", this._closeDropdown)
215
+ }
216
+
217
+ /**
218
+ * Render the dropdown menu
219
+ * @returns
220
+ */
221
+ renderDropdown() {
222
+ if (this._dropdownItems.length === 0 && !this._isRecalculating)
223
+ return nothing
224
+
225
+ return html`
226
+ <li class="breadcrumbs__item" data-dropdown-toggle>
227
+ <span class="breadcrumbs__icon">${Icon("angleRight")}</span>
228
+ <leu-popup
229
+ ?active=${this._isDropdownOpen}
230
+ placement="bottom-start"
231
+ shift
232
+ shiftPadding="8"
233
+ autoSize="width"
234
+ autoSizePadding="8"
235
+ >
236
+ <button
237
+ slot="anchor"
238
+ class="menu"
239
+ @click=${this._handleDropdownToggle}
240
+ tabindex="0"
241
+ >
242
+ &hellip;
243
+ </button>
244
+ <div class="dropdown">
245
+ ${html`
246
+ <leu-menu>
247
+ ${this._dropdownItems.map(
248
+ (item) =>
249
+ html`
250
+ <leu-menu-item
251
+ label=${item.label}
252
+ href=${item.href}
253
+ ></leu-menu-item>
254
+ `
255
+ )}
256
+ </leu-menu>
257
+ `}
258
+ </div>
259
+ </leu-popup>
260
+ </li>
261
+ `
262
+ }
263
+
264
+ render() {
265
+ if (this.items.length < 2) return nothing
266
+
267
+ const parentItem = this.items[this.items.length - 2]
268
+
269
+ const showBackOnly =
270
+ this._showBackOnly || this.items.length - this._hiddenItems < 2
271
+
272
+ const wrapperClasses = {
273
+ breadcrumbs: true,
274
+ "breadcrumbs--back-only": showBackOnly,
275
+ }
276
+
277
+ return html`
278
+ <nav class=${classMap(wrapperClasses)}>
279
+ <h2 class="visuallyhidden">Sie sind hier:</h2>
280
+ <ol class="breadcrumbs__list" ref=${ref(this._containerRef)}>
281
+ ${showBackOnly
282
+ ? html` <li class="breadcrumbs__item breadcrumbs__item--back">
283
+ <span class="breadcrumbs__icon">${Icon("arrowLeft")}</span>
284
+ <a class="breadcrumbs__link" href=${parentItem.href}
285
+ >${parentItem.label}</a
286
+ >
287
+ </li>`
288
+ : this._listItems.map(
289
+ (item, index, list) =>
290
+ html`
291
+ <li class="breadcrumbs__item">
292
+ ${index > 0
293
+ ? html`<span class="breadcrumbs__icon"
294
+ >${Icon("angleRight")}</span
295
+ >` // First list item doesn't have an arrow
296
+ : nothing}
297
+ ${index === list.length - 1
298
+ ? item.label // Last list item doesn't contain a link
299
+ : html`<a class="breadcrumbs__link" href=${item.href}
300
+ >${item.label}</a
301
+ >`}
302
+ </li>
303
+ ${index === 0 ? this.renderDropdown() : nothing}
304
+ `
305
+ )}
306
+ </ol>
307
+ </nav>
308
+ `
309
+ }
310
+ }
@@ -0,0 +1,114 @@
1
+ :host,
2
+ :host * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ :host {
7
+ --breadcrumb-font-regular: var(--leu-font-regular);
8
+ --breadcrumb-font-black: var(--leu-font-black);
9
+
10
+ font-family: var(--breadcrumb-font-regular);
11
+ line-height: 1.5;
12
+ color: var(--leu-color-black-100);
13
+ }
14
+
15
+ :host([inverted]) {
16
+ color: var(--leu-color-black-0);
17
+ }
18
+
19
+ .breadcrumbs__list {
20
+ display: flex;
21
+ align-items: center;
22
+ list-style-type: none;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+
27
+ .breadcrumbs--back-only .breadcrumbs__list {
28
+ overflow: hidden;
29
+ }
30
+
31
+ .breadcrumbs__item,
32
+ .breadcrumbs__icon {
33
+ align-items: center;
34
+ display: flex;
35
+ min-height: 1.875rem;
36
+ white-space: nowrap;
37
+ }
38
+
39
+ .breadcrumbs__item:first-child:not(.breadcrumbs__item--back) {
40
+ font-family: var(--breadcrumb-font-black);
41
+ }
42
+
43
+ .breadcrumbs__item--back {
44
+ max-width: 100%;
45
+ }
46
+
47
+ .breadcrumbs__link {
48
+ color: inherit;
49
+ text-decoration: none;
50
+ transition: color 0.1s ease;
51
+ white-space: nowrap;
52
+ }
53
+
54
+ .breadcrumbs__item--back .breadcrumbs__link {
55
+ overflow: hidden;
56
+ text-overflow: ellipsis;
57
+ margin-right: 0.25rem;
58
+ }
59
+
60
+ .menu {
61
+ background: none;
62
+ color: inherit;
63
+ cursor: pointer;
64
+ border: 2px solid transparent;
65
+ }
66
+
67
+ .menu:focus-visible {
68
+ outline: 2px solid var(--leu-color-func-cyan);
69
+ outline-offset: 2px;
70
+ }
71
+
72
+ .dropdown {
73
+ background-color: var(--leu-color-black-0);
74
+ box-shadow: var(--leu-box-shadow-short);
75
+ }
76
+
77
+ .visuallyhidden {
78
+ clip: rect(0 0 0 0);
79
+ border: 0;
80
+ height: 1px;
81
+ margin: -1px !important;
82
+ overflow: hidden;
83
+ padding: 0 !important;
84
+ position: absolute;
85
+ width: 1px;
86
+ }
87
+
88
+ .breadcrumbs {
89
+ font-size: 1rem;
90
+ }
91
+
92
+ @media (width >= 320px) {
93
+ .breadcrumbs {
94
+ font-size: calc(2.5vw + 8px);
95
+ }
96
+ }
97
+
98
+ @media (width >= 400px) {
99
+ .breadcrumbs {
100
+ font-size: 18px;
101
+ }
102
+ }
103
+
104
+ @media (width >= 1024px) {
105
+ .breadcrumbs {
106
+ font-size: calc(0.7813vw + 10px);
107
+ }
108
+ }
109
+
110
+ @media (width >= 1280px) {
111
+ .breadcrumbs {
112
+ font-size: 20px;
113
+ }
114
+ }
@@ -0,0 +1,6 @@
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuBreadcrumb } from "./Breadcrumb.js"
3
+
4
+ export { LeuBreadcrumb }
5
+
6
+ defineElement("breadcrumb", LeuBreadcrumb)
@@ -0,0 +1,73 @@
1
+ import { html } from "lit"
2
+ import "../leu-breadcrumb.js"
3
+
4
+ export default {
5
+ title: "Breadcrumb",
6
+ component: "leu-breadcrumb",
7
+ parameters: {
8
+ design: {
9
+ type: "figma",
10
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=18100-258351&mode=design&t=lzVrtq8lxYVJU5TB-11",
11
+ },
12
+ html: {
13
+ root: "[data-root]",
14
+ },
15
+ },
16
+ }
17
+
18
+ function Template({ items, inverted }) {
19
+ return html`
20
+ <div
21
+ style=${inverted ? "background: var(--leu-color-accent-blue);" : ""}
22
+ data-root
23
+ >
24
+ <leu-breadcrumb .items=${items} ?inverted=${inverted}></leu-breadcrumb>
25
+ </div>
26
+ <button
27
+ @click=${() => {
28
+ document.getElementsByTagName("leu-breadcrumb")[0].items = [
29
+ { label: "Kanton Zürich", href: "https://zh.ch" },
30
+ { label: "Bildung", href: "https://www.zh.ch/de/bildung.html" },
31
+ {
32
+ label: "Schulen",
33
+ href: "https://www.zh.ch/de/bildung/schulen.html",
34
+ },
35
+ {
36
+ label: "Volksschule",
37
+ href: "https://www.zh.ch/de/bildung/schulen/volksschule.html",
38
+ },
39
+ ]
40
+ }}
41
+ style="margin-top:50px;"
42
+ >
43
+ update items
44
+ </button>
45
+ `
46
+ }
47
+
48
+ export const Regular = Template.bind({})
49
+ Regular.argTypes = {
50
+ _allListElementWidths: { table: { disable: true } },
51
+ _visible: { table: { disable: true } },
52
+ _small: { table: { disable: true } },
53
+ _resizeListenerFunction: { table: { disable: true } },
54
+ }
55
+ Regular.args = {
56
+ items: [
57
+ { label: "Kanton Zürich", href: "https://zh.ch" },
58
+ { label: "Gesundheit", href: "https://zh.ch/de/gesundheit.html" },
59
+ {
60
+ label: "Lebensmittel & Gebrauchsgegenstände",
61
+ href: "https://zh.ch/de/gesundheit/lebensmittel-gebrauchsgegenstaende.html",
62
+ },
63
+ {
64
+ label: "Lebensmittel",
65
+ href: "https://zh.ch/de/gesundheit/lebensmittel-gebrauchsgegenstaende/lebensmittel.html",
66
+ },
67
+ {
68
+ label: "Trinkwasser",
69
+ href: "https://zh.ch/de/gesundheit/lebensmittel-gebrauchsgegenstaende/lebensmittel.html",
70
+ },
71
+ ],
72
+ inverted: true,
73
+ }
@@ -0,0 +1,141 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect, aTimeout } from "@open-wc/testing"
3
+ import { setViewport } from "@web/test-runner-commands"
4
+
5
+ import "../leu-breadcrumb.js"
6
+
7
+ const items = [
8
+ { label: "Kanton Zürich", href: "https://zh.ch" },
9
+ { label: "Gesundheit", href: "https://zh.ch/de/gesundheit.html" },
10
+ {
11
+ label: "Lebensmittel & Gebrauchsgegenstände",
12
+ href: "https://zh.ch/de/gesundheit/lebensmittel-gebrauchsgegenstaende.html",
13
+ },
14
+ {
15
+ label: "Lebensmittel",
16
+ href: "https://zh.ch/de/gesundheit/lebensmittel-gebrauchsgegenstaende/lebensmittel.html",
17
+ },
18
+ {
19
+ label: "Trinkwasser",
20
+ href: "https://zh.ch/de/gesundheit/lebensmittel-gebrauchsgegenstaende/lebensmittel.html",
21
+ },
22
+ ]
23
+
24
+ async function defaultFixture(args = {}) {
25
+ return fixture(
26
+ html` <leu-breadcrumb .items="${args.items ?? items}"></leu-breadcrumb> `
27
+ )
28
+ }
29
+
30
+ describe("LeuBreadcrumb", () => {
31
+ it("is a defined element", async () => {
32
+ const el = customElements.get("leu-breadcrumb")
33
+
34
+ expect(el).not.to.be.undefined
35
+ })
36
+
37
+ it("passes the a11y audit", async () => {
38
+ const el = await defaultFixture()
39
+ await expect(el).to.be.accessible()
40
+ })
41
+
42
+ it("renders a list of items", async () => {
43
+ await setViewport({ width: 1024, height: 1024 })
44
+ const el = await defaultFixture()
45
+
46
+ const itemEls = el.shadowRoot.querySelectorAll("li")
47
+
48
+ expect(itemEls[0]).to.have.trimmed.text(items[0].label)
49
+ expect(itemEls[0].querySelector("a")).to.have.attribute(
50
+ "href",
51
+ items[0].href
52
+ )
53
+ expect(itemEls[1]).to.have.trimmed.text(items[1].label)
54
+ expect(itemEls[1].querySelector("a")).to.have.attribute(
55
+ "href",
56
+ items[1].href
57
+ )
58
+ expect(itemEls[2]).to.have.trimmed.text(items[2].label)
59
+ expect(itemEls[2].querySelector("a")).to.have.attribute(
60
+ "href",
61
+ items[2].href
62
+ )
63
+ expect(itemEls[3]).to.have.trimmed.text(items[3].label)
64
+ expect(itemEls[3].querySelector("a")).to.have.attribute(
65
+ "href",
66
+ items[3].href
67
+ )
68
+
69
+ expect(itemEls[4]).to.have.trimmed.text(items[4].label)
70
+ expect(itemEls[4].querySelector("a")).to.not.exist
71
+ })
72
+
73
+ it("hides the overflowing items when shrinking the viewport", async () => {
74
+ await setViewport({ width: 1024, height: 1024 })
75
+ const el = await defaultFixture()
76
+
77
+ let itemEls = el.shadowRoot.querySelectorAll("li")
78
+ expect(itemEls.length).to.equal(5)
79
+
80
+ await setViewport({ width: 768, height: 1024 })
81
+ await aTimeout(600)
82
+ await el.updateComplete
83
+ itemEls = el.shadowRoot.querySelectorAll("li")
84
+
85
+ expect(itemEls.length).to.equal(4)
86
+ })
87
+
88
+ it("shows all the items when viewport is enlarged", async () => {
89
+ await setViewport({ width: 768, height: 1024 })
90
+ const el = await defaultFixture()
91
+
92
+ let itemEls = el.shadowRoot.querySelectorAll("li")
93
+ expect(itemEls.length).to.equal(4)
94
+
95
+ await setViewport({ width: 1024, height: 1024 })
96
+ await aTimeout(600)
97
+ await el.updateComplete
98
+ itemEls = el.shadowRoot.querySelectorAll("li")
99
+
100
+ expect(itemEls.length).to.equal(5)
101
+ })
102
+
103
+ it("only shows the first item when the viewport is too small", async () => {
104
+ await setViewport({ width: 240, height: 1024 })
105
+ const el = await defaultFixture()
106
+
107
+ const itemEls = el.shadowRoot.querySelectorAll("li")
108
+ expect(itemEls.length).to.equal(1)
109
+
110
+ expect(itemEls[0]).to.have.trimmed.text(items[3].label)
111
+ expect(itemEls[0].querySelector("a")).to.have.attribute(
112
+ "href",
113
+ items[3].href
114
+ )
115
+ })
116
+
117
+ it("shows a dropdown toggle when items are hidden", async () => {
118
+ await setViewport({ width: 768, height: 1024 })
119
+ const el = await defaultFixture()
120
+
121
+ const dropdownToggle = el.shadowRoot.querySelector("li:nth-child(2) button")
122
+ expect(dropdownToggle).to.exist
123
+ expect(dropdownToggle).to.have.trimmed.text("…")
124
+ })
125
+
126
+ it("shows a dropdown when the toggle is clicked", async () => {
127
+ await setViewport({ width: 768, height: 1024 })
128
+ const el = await defaultFixture()
129
+
130
+ const dropdownToggle = el.shadowRoot.querySelector("li:nth-child(2) button")
131
+ dropdownToggle.click()
132
+
133
+ await el.updateComplete
134
+
135
+ const dropdown = el.shadowRoot.querySelectorAll("leu-menu")
136
+ expect(dropdown).to.exist
137
+
138
+ const dropdownItems = el.shadowRoot.querySelectorAll("leu-menu-item")
139
+ expect(dropdownItems.length).to.equal(2)
140
+ })
141
+ })