@statistikzh/leu 0.2.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 +43 -0
  10. package/README.md +1 -1
  11. package/dist/Button.js +39 -30
  12. package/dist/ButtonGroup.js +5 -7
  13. package/dist/Checkbox.js +107 -88
  14. package/dist/CheckboxGroup.js +43 -38
  15. package/dist/{Chip-389013ff.js → Chip-dac7337d.js} +7 -2
  16. package/dist/ChipGroup.js +4 -6
  17. package/dist/ChipLink.js +6 -8
  18. package/dist/ChipRemovable.js +4 -7
  19. package/dist/ChipSelectable.js +10 -10
  20. package/dist/Dropdown.js +57 -27
  21. package/dist/Input.js +59 -43
  22. package/dist/Menu.js +2 -5
  23. package/dist/MenuItem.js +34 -17
  24. package/dist/Pagination.js +57 -55
  25. package/dist/Radio.js +13 -10
  26. package/dist/RadioGroup.js +43 -40
  27. package/dist/Select.js +57 -44
  28. package/dist/Table.js +147 -125
  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 +29 -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 +3 -9
  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 +22 -27
  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 +9 -89
  73. package/src/components/checkbox/CheckboxGroup.js +9 -39
  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 +2 -1
  83. package/src/components/chip/ChipGroup.js +1 -6
  84. package/src/components/chip/ChipLink.js +2 -8
  85. package/src/components/chip/ChipRemovable.js +1 -6
  86. package/src/components/chip/ChipSelectable.js +4 -9
  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 +53 -25
  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 +33 -39
  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 +22 -15
  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 +5 -10
  132. package/src/components/radio/RadioGroup.js +7 -39
  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 +35 -32
  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 +48 -123
  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
@@ -1,5 +1,7 @@
1
- import { html, LitElement, nothing } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
1
+ import { LitElement, nothing } from "lit"
2
+ import { html, unsafeStatic } from "lit/static-html.js"
3
+ import { ifDefined } from "lit/directives/if-defined.js"
4
+
3
5
  import styles from "./menu-item.css"
4
6
 
5
7
  import { Icon, ICON_NAMES } from "../icon/icon.js"
@@ -25,16 +27,18 @@ export class LeuMenuItem extends LitElement {
25
27
  * If no icon with this value is found, it will be displayed as text.
26
28
  * If the value is "EMPTY", an empty placeholder with the size of an icon will be displayed.
27
29
  */
28
- before: { type: String },
30
+ before: { type: String, reflect: true },
29
31
  /**
30
32
  * Can be either an icon name or a text
31
33
  * If no icon with this value is found, it will be displayed as text
32
34
  * If the value is "EMPTY", an empty placeholder with the size of an icon will be displayed.
33
35
  */
34
- after: { type: String },
36
+ after: { type: String, reflect: true },
35
37
  active: { type: Boolean, reflect: true },
36
38
  highlighted: { type: Boolean, reflect: true },
37
39
  disabled: { type: Boolean, reflect: true },
40
+ label: { type: String, reflect: true },
41
+ href: { type: String, reflect: true },
38
42
  }
39
43
 
40
44
  constructor() {
@@ -42,8 +46,6 @@ export class LeuMenuItem extends LitElement {
42
46
 
43
47
  this.active = false
44
48
  this.disabled = false
45
- this.before = ""
46
- this.after = ""
47
49
 
48
50
  /**
49
51
  * A programmatic way to highlight the menu item like it is hovered.
@@ -65,7 +67,7 @@ export class LeuMenuItem extends LitElement {
65
67
  }
66
68
 
67
69
  renderBefore() {
68
- if (this.before !== "") {
70
+ if (this.before) {
69
71
  const content = LeuMenuItem.getIconOrText(this.before)
70
72
  return html`<span class="before">${content}</span>`
71
73
  }
@@ -74,7 +76,7 @@ export class LeuMenuItem extends LitElement {
74
76
  }
75
77
 
76
78
  renderAfter() {
77
- if (this.after !== "") {
79
+ if (this.after) {
78
80
  const content = LeuMenuItem.getIconOrText(this.after)
79
81
  return html`<span class="after">${content}</span>`
80
82
  }
@@ -82,14 +84,19 @@ export class LeuMenuItem extends LitElement {
82
84
  return nothing
83
85
  }
84
86
 
87
+ getTagName() {
88
+ return this.href ? "a" : "button"
89
+ }
90
+
85
91
  render() {
86
- return html`<button class="button" ?disabled=${this.disabled}>
87
- ${this.renderBefore()}<span class="label"><slot></slot></span
92
+ /* The eslint rules don't recognize html import from lit/static-html.js */
93
+ /* eslint-disable lit/binding-positions, lit/no-invalid-html */
94
+ return html`<${unsafeStatic(
95
+ this.getTagName()
96
+ )} class="button" href=${ifDefined(this.href)} ?disabled=${this.disabled}>
97
+ ${this.renderBefore()}<span class="label">${this.label}</span
88
98
  >${this.renderAfter()}
89
- </button>`
99
+ </${unsafeStatic(this.getTagName())}>`
100
+ /* eslint-enable lit/binding-positions, lit/no-invalid-html */
90
101
  }
91
102
  }
92
-
93
- export function defineMenuItemElements() {
94
- defineElement("menu-item", LeuMenuItem)
95
- }
@@ -1,3 +1,6 @@
1
- import { defineMenuItemElements } from "./MenuItem.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuMenuItem } from "./MenuItem.js"
2
3
 
3
- defineMenuItemElements()
4
+ export { LeuMenuItem }
5
+
6
+ defineElement("menu-item", LeuMenuItem)
@@ -1,3 +1,6 @@
1
- import { defineMenuElements } from "./Menu.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuMenu } from "./Menu.js"
2
3
 
3
- defineMenuElements()
4
+ export { LeuMenu }
5
+
6
+ defineElement("menu", LeuMenu)
@@ -7,15 +7,17 @@
7
7
  --background: var(--leu-color-black-0);
8
8
  --background-hover: var(--leu-color-black-10);
9
9
  --background-active: var(--leu-color-func-cyan);
10
- --background-disabled: var(--leu-color-black-0);
10
+ --background-disabled: var(--leu-color-black-black-0);
11
11
  --color: var(--leu-color-black-transp-60);
12
+ --color-disabled: var(--leu-color-black-transp-20);
12
13
  --font-regular: var(--leu-font-regular);
13
14
  --font-black: var(--leu-font-black);
14
15
 
15
- font-family: var(--chip-font-regular);
16
+ font-family: var(--leu-font-regular);
16
17
  }
17
18
 
18
19
  .button {
20
+ text-decoration: none;
19
21
  appearance: none;
20
22
  border: none;
21
23
  cursor: pointer;
@@ -51,6 +53,7 @@
51
53
 
52
54
  :host([disabled]) .button {
53
55
  --background: var(--background-disabled);
56
+ --color: var(--color-disabled);
54
57
  cursor: default;
55
58
  }
56
59
 
@@ -9,16 +9,24 @@ export default {
9
9
  args: {
10
10
  label: "Menu Item",
11
11
  },
12
+ parameters: {
13
+ design: {
14
+ type: "figma",
15
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17340-82208&mode=design&t=lzVrtq8lxYVJU5TB-11",
16
+ },
17
+ },
12
18
  }
13
19
 
14
20
  function Template(args) {
15
21
  return html`
16
22
  <leu-menu-item
23
+ label=${args.label}
17
24
  before=${ifDefined(args.before)}
18
25
  after=${ifDefined(args.after)}
26
+ href=${ifDefined(args.href)}
19
27
  ?active=${args.active}
20
- >${args.label}</leu-menu-item
21
- >
28
+ ?disabled=${args.disabled}
29
+ ></leu-menu-item>
22
30
  `
23
31
  }
24
32
 
@@ -34,9 +42,10 @@ IconBefore.args = {
34
42
  before: "check",
35
43
  }
36
44
 
37
- export const IconAfter = Template.bind({})
38
- IconAfter.args = {
45
+ export const IconAfterLink = Template.bind({})
46
+ IconAfterLink.args = {
39
47
  after: "arrowRight",
48
+ href: "https://www.zh.ch",
40
49
  }
41
50
 
42
51
  export const IconAndTextLabel = Template.bind({})
@@ -5,16 +5,22 @@ import "../leu-menu-item.js"
5
5
  export default {
6
6
  title: "Menu",
7
7
  component: "leu-menu",
8
+ parameters: {
9
+ design: {
10
+ type: "figma",
11
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17340-82208&mode=design&t=lzVrtq8lxYVJU5TB-11",
12
+ },
13
+ },
8
14
  }
9
15
 
10
16
  function Template() {
11
17
  return html` <leu-menu>
12
- <leu-menu-item before="EMPTY">Menu Item 1</leu-menu-item>
13
- <leu-menu-item before="check" active>Menu Item 2</leu-menu-item>
14
- <leu-menu-item before="EMPTY">Menu Item 3</leu-menu-item>
18
+ <leu-menu-item label="Menu Item 1" before="EMPTY"></leu-menu-item>
19
+ <leu-menu-item label="Menu Item 2" before="check" active></leu-menu-item>
20
+ <leu-menu-item label="Menu Item 3" before="EMPTY"></leu-menu-item>
15
21
  <hr />
16
- <leu-menu-item before="pin" after="CH">Menu Item 3</leu-menu-item>
17
- <leu-menu-item>Menu Item 4</leu-menu-item>
22
+ <leu-menu-item label="Menu Item 3" before="pin" after="CH"></leu-menu-item>
23
+ <leu-menu-item label="Menu Item 4"></leu-menu-item>
18
24
  </leu-menu>`
19
25
  }
20
26
 
@@ -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 },
22
- dataLength: { type: Number },
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
  }