@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,180 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect, oneEvent } from "@open-wc/testing"
3
+ import { ifDefined } from "lit/directives/if-defined.js"
4
+ import { spy } from "sinon"
5
+
6
+ import "../leu-menu-item.js"
7
+
8
+ async function defaultFixture(args = {}) {
9
+ return fixture(html`
10
+ <leu-menu-item
11
+ label=${args.label}
12
+ before=${ifDefined(args.before)}
13
+ after=${ifDefined(args.after)}
14
+ href=${ifDefined(args.href)}
15
+ ?active=${args.active}
16
+ ?disabled=${args.disabled}
17
+ ></leu-menu-item>
18
+ `)
19
+ }
20
+
21
+ describe("LeuMenuItem", () => {
22
+ it("is a defined element", async () => {
23
+ const el = customElements.get("leu-menu-item")
24
+
25
+ await expect(el).not.to.be.undefined
26
+ })
27
+
28
+ it("passes the a11y audit", async () => {
29
+ const el = await defaultFixture({ label: "Download" })
30
+
31
+ await expect(el).shadowDom.to.be.accessible()
32
+ })
33
+
34
+ it("passes the a11y audit with a link", async () => {
35
+ const el = await defaultFixture({
36
+ label: "Download",
37
+ href: "https://zh.ch",
38
+ })
39
+
40
+ await expect(el).shadowDom.to.be.accessible()
41
+ })
42
+
43
+ it("renders a label", async () => {
44
+ const el = await defaultFixture({ label: "Download" })
45
+
46
+ const button = el.shadowRoot.querySelector("button")
47
+
48
+ expect(button).to.have.trimmed.text("Download")
49
+ })
50
+
51
+ it("renders a button", async () => {
52
+ const el = await defaultFixture({ label: "Download" })
53
+
54
+ const button = el.shadowRoot.querySelector("button")
55
+
56
+ expect(button).to.exist
57
+ })
58
+
59
+ it("renders a link", async () => {
60
+ const el = await defaultFixture({
61
+ label: "Kanton Zürich",
62
+ href: "https://zh.ch",
63
+ })
64
+
65
+ const link = el.shadowRoot.querySelector("a")
66
+
67
+ expect(link).to.exist
68
+ 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
+ )
81
+ })
82
+
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
115
+
116
+ expect(el).shadowDom.to.equal(
117
+ "<button class='button'><span class='label'>Download</span><span class='after'></span></button>"
118
+ )
119
+ })
120
+
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")
127
+
128
+ expect(el).shadowDom.to.equal(
129
+ "<button class='button'><span class='label'>Download</span><span class='after'>DE</span></button>"
130
+ )
131
+ })
132
+
133
+ it("renders a after placeholder", async () => {
134
+ const el = await defaultFixture({ label: "Download", after: "EMPTY" })
135
+
136
+ const after = el.shadowRoot.querySelector(".after")
137
+ expect(after).to.exist
138
+ expect(after).to.not.have.trimmed.text()
139
+
140
+ const iconPlaceholder = after.querySelector(".icon-placeholder")
141
+ expect(iconPlaceholder).to.exist
142
+
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
+ )
146
+ })
147
+
148
+ it("passes the disabled attribute to the button", async () => {
149
+ const el = await defaultFixture({ label: "Download", disabled: true })
150
+
151
+ const button = el.shadowRoot.querySelector("button")
152
+ expect(button).to.have.attribute("disabled")
153
+ })
154
+
155
+ it("lets the click event bubble up", async () => {
156
+ const el = await defaultFixture({ label: "Download" })
157
+
158
+ const button = el.shadowRoot.querySelector("button")
159
+
160
+ setTimeout(() => {
161
+ button.click()
162
+ })
163
+
164
+ const event = await oneEvent(el, "click")
165
+
166
+ expect(event).to.exist
167
+ })
168
+
169
+ it("does not let the click event bubble up when disabled", async () => {
170
+ const el = await defaultFixture({ label: "Download", disabled: true })
171
+
172
+ const button = el.shadowRoot.querySelector("button")
173
+ const clickSpy = spy()
174
+ button.addEventListener("click", clickSpy)
175
+
176
+ el.click()
177
+
178
+ expect(clickSpy).to.have.not.been.called
179
+ })
180
+ })
@@ -2,14 +2,22 @@ import { html } from "lit"
2
2
  import { fixture, expect } from "@open-wc/testing"
3
3
 
4
4
  import "../leu-menu.js"
5
+ import "../leu-menu-item.js"
5
6
 
6
7
  async function defaultFixture() {
7
- return fixture(html` <leu-menu /> `)
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>
12
+ <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>
15
+ </leu-menu>`)
8
16
  }
9
17
 
10
18
  describe("LeuMenu", () => {
11
19
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-menu")
20
+ const el = customElements.get("leu-menu")
13
21
 
14
22
  await expect(el).not.to.be.undefined
15
23
  })
@@ -1,7 +1,7 @@
1
1
  import { html, LitElement } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
3
- import { defineButtonElements } from "../button/Button.js"
2
+ import { live } from "lit/directives/live.js"
4
3
 
4
+ import "../button/leu-button.js"
5
5
  import styles from "./pagination.css"
6
6
 
7
7
  const MIN_PAGE = 1
@@ -12,16 +12,22 @@ const MIN_PAGE = 1
12
12
  export class LeuPagination extends LitElement {
13
13
  static styles = styles
14
14
 
15
+ /**
16
+ * @internal
17
+ */
18
+ static shadowRootOptions = {
19
+ ...LitElement.shadowRootOptions,
20
+ delegatesFocus: true,
21
+ }
22
+
15
23
  static events = {
16
24
  range: {},
17
25
  }
18
26
 
19
27
  static properties = {
20
28
  page: { type: Number, reflect: true },
21
- itemsOnAPage: { type: Number, reflect: true },
22
- dataLength: { type: Number, reflect: true },
23
-
24
- _minPage: { type: Number, state: true },
29
+ itemsPerPage: { type: Number, reflect: true },
30
+ numOfItems: { type: Number, reflect: true },
25
31
  }
26
32
 
27
33
  constructor() {
@@ -29,45 +35,55 @@ export class LeuPagination extends LitElement {
29
35
  /** @type {number} */
30
36
  this.page = 1
31
37
  /** @type {number} */
32
- this.dataLength = 0
38
+ this.numOfItems = 1
33
39
  /** @type {number} */
34
- this.itemsOnAPage = 30
40
+ this.itemsPerPage = 1
35
41
  }
36
42
 
37
43
  get maxPage() {
38
- return Math.ceil(this.dataLength / this.itemsOnAPage)
44
+ return Math.ceil(this.numOfItems / this.itemsPerPage)
39
45
  }
40
46
 
41
47
  get firstPage() {
42
- return this.page === MIN_PAGE
48
+ return this.boundPage === MIN_PAGE
43
49
  }
44
50
 
45
51
  get lastPage() {
46
- return this.page === this.maxPage
52
+ return this.boundPage === this.maxPage
47
53
  }
48
54
 
49
- holdInRange(value) {
50
- return Math.min(Math.max(value, MIN_PAGE), this.maxPage)
55
+ /**
56
+ * The boundPage getter is necessary to ensure that the current page (this.page) is always within the valid range of pages.
57
+ * It prevents the page number from going below the minimum page limit (MIN_PAGE) or above the maximum page limit (this.maxPage).
58
+ * This is important for the correct functioning of the pagination system, as it prevents users from navigating to non-existent pages.
59
+ *
60
+ * @returns {number}
61
+ */
62
+ get boundPage() {
63
+ return Math.min(Math.max(this.page, MIN_PAGE), this.maxPage)
51
64
  }
52
65
 
53
66
  numberUpdate(number) {
54
- this.page = this.holdInRange(number)
55
-
56
- const min = (this.page - 1) * this.itemsOnAPage
57
- const max = Math.min(min + this.itemsOnAPage, this.dataLength)
58
- this.dispatchEvent(
59
- new CustomEvent("range-updated", {
60
- detail: {
61
- min,
62
- max,
63
- },
64
- bubbles: false,
65
- })
66
- )
67
+ const prevPage = this.page
68
+ this.page = number
69
+
70
+ if (this.page !== prevPage) {
71
+ const startIndex = (this.boundPage - 1) * this.itemsPerPage
72
+ const endIndex = Math.min(startIndex + this.itemsPerPage, this.numOfItems)
73
+ this.dispatchEvent(
74
+ new CustomEvent("leu:pagechange", {
75
+ detail: {
76
+ startIndex,
77
+ endIndex,
78
+ page: this.boundPage,
79
+ },
80
+ bubbles: false,
81
+ })
82
+ )
83
+ }
67
84
  }
68
85
 
69
86
  change(event) {
70
- // target.value = this.page // eslint-disable-line
71
87
  this.numberUpdate(parseInt(event.target.value, 10) || 0)
72
88
  }
73
89
 
@@ -79,46 +95,23 @@ export class LeuPagination extends LitElement {
79
95
  }
80
96
 
81
97
  keydown(event) {
82
- const specialKeys = [
83
- "ArrowUp",
84
- "ArrowDown",
85
- "ArrowLeft",
86
- "ArrowRight",
87
- "Backspace",
88
- "Enter",
89
- "Tab",
90
- ]
91
- const numberKeys = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
92
- if (!numberKeys.includes(event.key) && !specialKeys.includes(event.key)) {
98
+ if (event.key === "ArrowUp") {
93
99
  event.preventDefault()
94
- } else {
95
- if (event.key === "ArrowUp") {
96
- event.preventDefault()
97
- this.numberUpdate(this.page + 1)
98
- }
99
- if (event.key === "ArrowDown") {
100
- event.preventDefault()
101
- this.numberUpdate(this.page - 1)
102
- }
100
+ this.numberUpdate(this.boundPage + 1)
103
101
  }
104
- }
105
-
106
- firstUpdated() {
107
- this.numberUpdate(this.page)
108
- }
109
-
110
- requestUpdate(name, oldValue, newValue) {
111
- if (name === "itemsOnAPage") {
112
- this.numberUpdate(this.page)
102
+ if (event.key === "ArrowDown") {
103
+ event.preventDefault()
104
+ this.numberUpdate(this.boundPage - 1)
113
105
  }
114
- return super.requestUpdate(name, oldValue, newValue)
115
106
  }
116
107
 
117
108
  render() {
118
109
  return html`
119
110
  <input
120
111
  class="input"
121
- .value=${this.page}
112
+ min=${MIN_PAGE}
113
+ max=${this.maxPage}
114
+ .value=${live(this.boundPage.toString())}
122
115
  @input=${this.input}
123
116
  @change=${this.change}
124
117
  @keydown=${this.keydown}
@@ -129,7 +122,7 @@ export class LeuPagination extends LitElement {
129
122
  icon="angleLeft"
130
123
  variant="secondary"
131
124
  @click=${(_) => {
132
- this.numberUpdate(this.page - 1)
125
+ this.numberUpdate(this.boundPage - 1)
133
126
  }}
134
127
  ?disabled=${this.firstPage}
135
128
  ></leu-button>
@@ -137,7 +130,7 @@ export class LeuPagination extends LitElement {
137
130
  icon="angleRight"
138
131
  variant="secondary"
139
132
  @click=${(_) => {
140
- this.numberUpdate(this.page + 1)
133
+ this.numberUpdate(this.boundPage + 1)
141
134
  }}
142
135
  ?disabled=${this.lastPage}
143
136
  style="margin-left:4px;"
@@ -145,8 +138,3 @@ export class LeuPagination extends LitElement {
145
138
  `
146
139
  }
147
140
  }
148
-
149
- export function definePaginationElements() {
150
- defineButtonElements()
151
- defineElement("pagination", LeuPagination)
152
- }
@@ -1,3 +1,6 @@
1
- import { definePaginationElements } from "./Pagination.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuPagination } from "./Pagination.js"
2
3
 
3
- definePaginationElements()
4
+ export { LeuPagination }
5
+
6
+ defineElement("pagination", LeuPagination)
@@ -56,18 +56,26 @@ const items = [
56
56
  export default {
57
57
  title: "Pagination",
58
58
  component: "leu-pagination",
59
+ parameters: {
60
+ design: {
61
+ type: "figma",
62
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17341-82468&mode=design&t=lzVrtq8lxYVJU5TB-11",
63
+ },
64
+ },
59
65
  }
60
66
 
61
- function Template({ min, max }, { id }) {
67
+ function Template({ startIndex, endIndex }, { id }) {
62
68
  return html`
63
- ${items.slice(min, max).map((item) => html`<div>${item.label}</div>`)}
69
+ ${items
70
+ .slice(startIndex, endIndex)
71
+ .map((item) => html`<div>${item.label}</div>`)}
64
72
  <leu-pagination
65
- dataLength=${items.length}
66
- itemsOnAPage="5"
67
- @range-updated=${(e) => {
73
+ numOfItems=${items.length}
74
+ itemsPerPage="5"
75
+ @leu:pagechange=${(e) => {
68
76
  updateStorybookArgss(id, {
69
- min: e.detail.min,
70
- max: e.detail.max,
77
+ startIndex: e.detail.startIndex,
78
+ endIndex: e.detail.endIndex,
71
79
  })
72
80
  }}
73
81
  >
@@ -77,6 +85,6 @@ function Template({ min, max }, { id }) {
77
85
 
78
86
  export const Regular = Template.bind({})
79
87
  Regular.args = {
80
- min: 0,
81
- max: 5,
88
+ startIndex: 0,
89
+ endIndex: 5,
82
90
  }
@@ -1,22 +1,208 @@
1
1
  import { html } from "lit"
2
- import { fixture, expect } from "@open-wc/testing"
2
+ import { fixture, expect, elementUpdated } from "@open-wc/testing"
3
+ import { ifDefined } from "lit/directives/if-defined.js"
4
+ import { sendKeys } from "@web/test-runner-commands"
5
+ import { spy } from "sinon"
3
6
 
4
7
  import "../leu-pagination.js"
5
8
 
6
- async function defaultFixture() {
7
- return fixture(html` <leu-pagination></leu-button>`)
9
+ async function defaultFixture(args = {}) {
10
+ return fixture(html`<leu-pagination
11
+ numOfItems=${ifDefined(args.numOfItems)}
12
+ itemsPerPage=${ifDefined(args.itemsPerPage)}
13
+ page=${ifDefined(args.page)}
14
+ >
15
+ </leu-pagination>`)
8
16
  }
9
17
 
10
18
  describe("LeuPagination", () => {
11
19
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-pagination")
20
+ const el = customElements.get("leu-pagination")
13
21
 
14
22
  await expect(el).not.to.be.undefined
15
23
  })
16
24
 
17
25
  it("passes the a11y audit", async () => {
18
- const el = await defaultFixture()
26
+ const el = await defaultFixture({
27
+ numOfItems: 98,
28
+ itemsPerPage: 7,
29
+ page: 1,
30
+ })
19
31
 
20
32
  await expect(el).shadowDom.to.be.accessible()
21
33
  })
34
+
35
+ it("disables the previous button on the first page", async () => {
36
+ const el = await defaultFixture({
37
+ numOfItems: 98,
38
+ itemsPerPage: 7,
39
+ page: 1,
40
+ })
41
+
42
+ const previous = el.shadowRoot.querySelectorAll("leu-button")[0]
43
+
44
+ expect(previous).to.have.attribute("disabled")
45
+ })
46
+
47
+ it("disables the next button on the last page", async () => {
48
+ const el = await defaultFixture({
49
+ numOfItems: 98,
50
+ itemsPerPage: 7,
51
+ page: 14,
52
+ })
53
+
54
+ const next = el.shadowRoot.querySelectorAll("leu-button")[1]
55
+
56
+ expect(next).to.have.attribute("disabled")
57
+ })
58
+
59
+ it("renders the correct number of available pages", async () => {
60
+ const el = await defaultFixture({
61
+ numOfItems: 98,
62
+ itemsPerPage: 7,
63
+ page: 1,
64
+ })
65
+
66
+ const label = el.shadowRoot.querySelectorAll(".label")
67
+
68
+ expect(label).to.have.trimmed.text("von 14")
69
+ })
70
+
71
+ it("displays the current page", async () => {
72
+ const el = await defaultFixture({
73
+ numOfItems: 98,
74
+ itemsPerPage: 7,
75
+ page: 2,
76
+ })
77
+
78
+ const input = el.shadowRoot.querySelector("input")
79
+
80
+ expect(input.value).to.equal("2")
81
+ })
82
+
83
+ it("increments the page with a click on the next button", async () => {
84
+ const el = await defaultFixture({
85
+ numOfItems: 98,
86
+ itemsPerPage: 7,
87
+ page: 1,
88
+ })
89
+
90
+ const next = el.shadowRoot.querySelectorAll("leu-button")[1]
91
+
92
+ next.click()
93
+ await elementUpdated(el)
94
+
95
+ expect(el.page).to.equal(2)
96
+ })
97
+
98
+ it("decrements the page with a click on the prev button", async () => {
99
+ const el = await defaultFixture({
100
+ numOfItems: 98,
101
+ itemsPerPage: 7,
102
+ page: 10,
103
+ })
104
+
105
+ const next = el.shadowRoot.querySelectorAll("leu-button")[0]
106
+
107
+ next.click()
108
+ await elementUpdated(el)
109
+
110
+ expect(el.page).to.equal(9)
111
+ })
112
+
113
+ it("increments the page with pressing the arrow up key", async () => {
114
+ const el = await defaultFixture({
115
+ numOfItems: 98,
116
+ itemsPerPage: 7,
117
+ page: 1,
118
+ })
119
+
120
+ el.focus()
121
+
122
+ await sendKeys({
123
+ press: "ArrowUp",
124
+ })
125
+ await elementUpdated(el)
126
+
127
+ expect(el.page).to.equal(2)
128
+ })
129
+
130
+ it("decrements the page with pressing the arrow down key", async () => {
131
+ const el = await defaultFixture({
132
+ numOfItems: 98,
133
+ itemsPerPage: 7,
134
+ page: 13,
135
+ })
136
+
137
+ el.focus()
138
+
139
+ await sendKeys({
140
+ press: "ArrowDown",
141
+ })
142
+ await elementUpdated(el)
143
+
144
+ expect(el.page).to.equal(12)
145
+ })
146
+
147
+ it("bounds the page to the max and min values", async () => {
148
+ const el = await defaultFixture({
149
+ numOfItems: 50,
150
+ itemsPerPage: 10,
151
+ page: 6,
152
+ })
153
+
154
+ const input = el.shadowRoot.querySelector("input")
155
+
156
+ expect(el.boundPage).to.equal(5)
157
+ expect(input.value).to.equal("5")
158
+
159
+ el.page = 0
160
+ await elementUpdated(el)
161
+
162
+ expect(el.boundPage).to.equal(1)
163
+ expect(input.value).to.equal("1")
164
+
165
+ el.focus()
166
+
167
+ await sendKeys({
168
+ press: "ArrowDown",
169
+ })
170
+ await elementUpdated(el)
171
+
172
+ expect(el.boundPage).to.equal(1)
173
+ expect(input.value).to.equal("1")
174
+
175
+ await sendKeys({ press: "ArrowUp" })
176
+ await sendKeys({ press: "ArrowUp" })
177
+ await sendKeys({ press: "ArrowUp" })
178
+ await sendKeys({ press: "ArrowUp" })
179
+ await sendKeys({ press: "ArrowUp" })
180
+
181
+ expect(el.boundPage).to.equal(5)
182
+ expect(input.value).to.equal("5")
183
+ })
184
+
185
+ it("fires a leu:pagechange event", async () => {
186
+ const el = await defaultFixture({
187
+ numOfItems: 98,
188
+ itemsPerPage: 7,
189
+ page: 14,
190
+ })
191
+
192
+ const eventSpy = spy()
193
+ el.addEventListener("leu:pagechange", eventSpy)
194
+
195
+ const prevButton = el.shadowRoot.querySelectorAll("leu-button")[0]
196
+ const nextButton = el.shadowRoot.querySelectorAll("leu-button")[1]
197
+
198
+ nextButton.click()
199
+ prevButton.click()
200
+
201
+ el.focus()
202
+
203
+ await sendKeys({ press: "ArrowUp" })
204
+ await sendKeys({ press: "ArrowDown" })
205
+
206
+ expect(eventSpy).to.have.been.called.callCount(4)
207
+ })
22
208
  })