@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,58 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect } from "@open-wc/testing"
3
+
4
+ import "../leu-chip-link.js"
5
+
6
+ async function defaultFixture() {
7
+ return fixture(
8
+ html`
9
+ <leu-chip-link label="Daten" href="https://zh.ch/daten"></leu-chip-link>
10
+ `
11
+ )
12
+ }
13
+
14
+ describe("LeuChipLink", () => {
15
+ it("is a defined element", async () => {
16
+ const el = customElements.get("leu-chip-link")
17
+
18
+ await expect(el).not.to.be.undefined
19
+ })
20
+
21
+ it("passes the a11y audit", async () => {
22
+ const el = await defaultFixture()
23
+
24
+ await expect(el).shadowDom.to.be.accessible()
25
+ })
26
+
27
+ it("renders the label", async () => {
28
+ const el = await defaultFixture()
29
+
30
+ const link = el.shadowRoot.querySelector("a")
31
+
32
+ expect(link).to.have.trimmed.text("Daten")
33
+ })
34
+
35
+ it("renders a link element", async () => {
36
+ const el = await defaultFixture()
37
+
38
+ expect(el.shadowRoot.querySelector("a")).to.exist
39
+ })
40
+
41
+ it("passes the href attribute to the link", async () => {
42
+ const el = await defaultFixture()
43
+
44
+ expect(el.shadowRoot.querySelector("a").getAttribute("href")).to.equal(
45
+ "https://zh.ch/daten"
46
+ )
47
+ })
48
+
49
+ it("delegates the focus to the a element", async () => {
50
+ const el = await defaultFixture()
51
+
52
+ el.focus()
53
+
54
+ expect(el.shadowRoot.querySelector("a")).to.equal(
55
+ el.shadowRoot.activeElement
56
+ )
57
+ })
58
+ })
@@ -0,0 +1,79 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect, oneEvent } from "@open-wc/testing"
3
+ import { sendKeys } from "@web/test-runner-commands"
4
+
5
+ import { iconPaths } from "../../icon/icon.js"
6
+
7
+ import "../leu-chip-group.js"
8
+
9
+ async function defaultFixture() {
10
+ return fixture(
11
+ html` <leu-chip-removable label="Daten"></leu-chip-removable> `
12
+ )
13
+ }
14
+
15
+ describe("LeuChipRemovable", () => {
16
+ it("is a defined element", async () => {
17
+ const el = customElements.get("leu-chip-removable")
18
+
19
+ await expect(el).not.to.be.undefined
20
+ })
21
+
22
+ it("passes the a11y audit", async () => {
23
+ const el = await defaultFixture()
24
+
25
+ await expect(el).shadowDom.to.be.accessible()
26
+ })
27
+
28
+ it("renders the label", async () => {
29
+ const el = await defaultFixture()
30
+
31
+ const button = el.shadowRoot.querySelector("button")
32
+
33
+ expect(button).to.have.trimmed.text("Daten")
34
+ })
35
+
36
+ it("renders a button element", async () => {
37
+ const el = await defaultFixture()
38
+
39
+ expect(el.shadowRoot.querySelector("button")).to.exist
40
+ })
41
+
42
+ it("renders a remove icon", async () => {
43
+ const el = await defaultFixture()
44
+
45
+ const iconPath = el.shadowRoot.querySelector("svg path")
46
+
47
+ expect(iconPath.getAttribute("d")).to.equal(iconPaths.close)
48
+ })
49
+
50
+ it("delegates the focus to the button element", async () => {
51
+ const el = await defaultFixture()
52
+
53
+ el.focus()
54
+
55
+ expect(el.shadowRoot.querySelector("button")).to.equal(
56
+ el.shadowRoot.activeElement
57
+ )
58
+ })
59
+
60
+ it("fires a remove event when the button is clicked", async () => {
61
+ const el = await defaultFixture()
62
+ const button = el.shadowRoot.querySelector("button")
63
+
64
+ setTimeout(() => button.click())
65
+ const event = await oneEvent(el, "remove")
66
+
67
+ expect(event).to.exist
68
+ })
69
+
70
+ it("fires a remove event when the enter key is pressed", async () => {
71
+ const el = await defaultFixture()
72
+
73
+ el.focus()
74
+ setTimeout(() => sendKeys({ press: "Enter" }))
75
+ const event = await oneEvent(el, "remove")
76
+
77
+ expect(event).to.exist
78
+ })
79
+ })
@@ -0,0 +1,95 @@
1
+ import { html } from "lit"
2
+ import { ifDefined } from "lit/directives/if-defined.js"
3
+ import { fixture, expect, oneEvent } from "@open-wc/testing"
4
+ import { sendKeys } from "@web/test-runner-commands"
5
+
6
+ import "../leu-chip-selectable.js"
7
+
8
+ async function defaultFixture(args = {}) {
9
+ return fixture(
10
+ html`
11
+ <leu-chip-selectable
12
+ label="Publikationen"
13
+ value="Publikationen"
14
+ variant=${ifDefined(args.variant)}
15
+ ?selected=${args.selected}
16
+ ></leu-chip-selectable>
17
+ `
18
+ )
19
+ }
20
+
21
+ describe("LeuChipSelectable", () => {
22
+ it("is a defined element", async () => {
23
+ const el = customElements.get("leu-chip-selectable")
24
+
25
+ await expect(el).not.to.be.undefined
26
+ })
27
+
28
+ it("passes the a11y audit", async () => {
29
+ const el = await defaultFixture()
30
+
31
+ await expect(el).shadowDom.to.be.accessible()
32
+ })
33
+
34
+ it("renders the label", async () => {
35
+ const el = await defaultFixture()
36
+
37
+ const button = el.shadowRoot.querySelector("button")
38
+
39
+ expect(button).to.have.trimmed.text("Publikationen")
40
+ })
41
+
42
+ it("renders a button element", async () => {
43
+ const el = await defaultFixture()
44
+
45
+ expect(el.shadowRoot.querySelector("button")).to.exist
46
+ })
47
+
48
+ it("delegates the focus to the button element", async () => {
49
+ const el = await defaultFixture()
50
+
51
+ el.focus()
52
+
53
+ expect(el.shadowRoot.querySelector("button")).to.equal(
54
+ el.shadowRoot.activeElement
55
+ )
56
+ })
57
+
58
+ it("fires a input event when the button is clicked", async () => {
59
+ const el = await defaultFixture()
60
+ const button = el.shadowRoot.querySelector("button")
61
+
62
+ setTimeout(() => button.click())
63
+ const event = await oneEvent(el, "input")
64
+
65
+ expect(event).to.exist
66
+ })
67
+
68
+ it("fires a input event when the enter key is pressed", async () => {
69
+ const el = await defaultFixture()
70
+
71
+ el.focus()
72
+ setTimeout(() => sendKeys({ press: "Enter" }))
73
+ const event = await oneEvent(el, "input")
74
+
75
+ expect(event).to.exist
76
+ })
77
+
78
+ it("removes the selected state when the button is clicked", async () => {
79
+ const el = await defaultFixture({ selected: true })
80
+ const button = el.shadowRoot.querySelector("button")
81
+
82
+ button.click()
83
+
84
+ expect(el.selected).to.be.false
85
+ })
86
+
87
+ it("doesn't remove the selected state of a selected radio chip", async () => {
88
+ const el = await defaultFixture({ variant: "radio", selected: true })
89
+
90
+ const button = el.shadowRoot.querySelector("button")
91
+ button.click()
92
+
93
+ expect(el.selected).to.be.true
94
+ })
95
+ })
@@ -9,7 +9,7 @@ async function defaultFixture() {
9
9
 
10
10
  describe("LeuChipLink", () => {
11
11
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-chip-link")
12
+ const el = customElements.get("leu-chip-link")
13
13
 
14
14
  await expect(el).not.to.be.undefined
15
15
  })
@@ -1,10 +1,10 @@
1
1
  import { html, LitElement } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
3
2
  import styles from "./dropdown.css"
4
3
 
5
- import { defineButtonElements } from "../button/Button.js"
6
- import { defineMenuElements } from "../menu/Menu.js"
7
- import { defineMenuItemElements } from "../menu/MenuItem.js"
4
+ import "../button/leu-button.js"
5
+ import "../menu/leu-menu.js"
6
+ import "../menu/leu-menu-item.js"
7
+ import "../popup/leu-popup.js"
8
8
 
9
9
  /**
10
10
  * @tagname leu-dropdown
@@ -22,34 +22,62 @@ export class LeuDropdown extends LitElement {
22
22
 
23
23
  this.label = ""
24
24
  this.expanded = false
25
+ this.menuItems = []
25
26
  }
26
27
 
27
- handleClick() {
28
+ disconnectedCallback() {
29
+ super.disconnectedCallback()
30
+ this._removeMenuItemListeners()
31
+ }
32
+
33
+ _handleSlotChange() {
34
+ this._removeMenuItemListeners()
35
+ this.menuItems = [...this.querySelectorAll("leu-menu > leu-menu-item")]
36
+
37
+ this.menuItems.forEach((item) =>
38
+ item.addEventListener("click", this._handleMenuItemClick)
39
+ )
40
+ }
41
+
42
+ _removeMenuItemListeners() {
43
+ this.menuItems.forEach((item) => {
44
+ item.removeEventListener("click", this._handleMenuItemClick)
45
+ })
46
+ }
47
+
48
+ _handleMenuItemClick = () => {
49
+ this.expanded = false
50
+ }
51
+
52
+ _handleToggleClick() {
28
53
  this.expanded = !this.expanded
29
54
  }
30
55
 
31
56
  render() {
32
57
  return html`
33
- <leu-button
34
- icon="download"
35
- variant="ghost"
36
- label=${this.label}
37
- expanded=${this.expanded ? "open" : "closed"}
38
- aria-expanded=${this.expanded ? "true" : "false"}
39
- aria-controls="content"
58
+ <leu-popup
40
59
  ?active=${this.expanded}
41
- @click=${this.handleClick}
42
- ></leu-button>
43
- <div id="content" class="content" ?hidden=${!this.expanded}>
44
- <slot></slot>
45
- </div>
60
+ placement="bottom-start"
61
+ shift
62
+ shiftPadding="8"
63
+ autoSize="width"
64
+ autoSizePadding="8"
65
+ >
66
+ <leu-button
67
+ slot="anchor"
68
+ icon="download"
69
+ variant="ghost"
70
+ label=${this.label}
71
+ expanded=${this.expanded ? "open" : "closed"}
72
+ aria-expanded=${this.expanded ? "true" : "false"}
73
+ aria-controls="content"
74
+ ?active=${this.expanded}
75
+ @click=${this._handleToggleClick}
76
+ ></leu-button>
77
+ <div id="content" class="content" ?hidden=${!this.expanded}>
78
+ <slot @slotchange=${this._handleSlotChange}></slot>
79
+ </div>
80
+ </leu-popup>
46
81
  `
47
82
  }
48
83
  }
49
-
50
- export function defineDropdownElements() {
51
- defineElement("dropdown", LeuDropdown)
52
- defineButtonElements()
53
- defineMenuElements()
54
- defineMenuItemElements()
55
- }
@@ -12,6 +12,5 @@
12
12
  box-shadow: var(--leu-box-shadow-short);
13
13
  border-radius: 2px;
14
14
 
15
- position: absolute;
16
- top: calc(100% + 0.5rem);
15
+ margin-top: 0.5rem;
17
16
  }
@@ -1,3 +1,6 @@
1
- import { defineDropdownElements } from "./Dropdown.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuDropdown } from "./Dropdown.js"
2
3
 
3
- defineDropdownElements()
4
+ export { LeuDropdown }
5
+
6
+ defineElement("dropdown", LeuDropdown)
@@ -4,17 +4,23 @@ import "../leu-dropdown.js"
4
4
  export default {
5
5
  title: "Dropdown",
6
6
  component: "leu-dropdown",
7
+ parameters: {
8
+ design: {
9
+ type: "figma",
10
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=136815-217650&mode=design&t=lzVrtq8lxYVJU5TB-11",
11
+ },
12
+ },
7
13
  }
8
14
 
9
15
  function Template({ label, expanded }) {
10
16
  return html` <leu-dropdown label=${label} ?expanded=${expanded}>
11
17
  <leu-menu>
12
- <leu-menu-item>Als CSV Tabelle</leu-menu-item>
13
- <leu-menu-item>Als XLS Tabelle</leu-menu-item>
18
+ <leu-menu-item label="Als CSV Tabelle"></leu-menu-item>
19
+ <leu-menu-item label="Als XLS Tabelle"></leu-menu-item>
14
20
  <hr />
15
- <leu-menu-item>Als PNG exportieren</leu-menu-item>
16
- <leu-menu-item>Als SVG exportieren</leu-menu-item>
17
- <leu-menu-item>Als PDF exportieren</leu-menu-item>
21
+ <leu-menu-item label="Als PNG exportieren"></leu-menu-item>
22
+ <leu-menu-item label="Als SVG exportieren"></leu-menu-item>
23
+ <leu-menu-item label="Als PDF exportieren" disabled></leu-menu-item>
18
24
  </leu-menu>
19
25
  </leu-dropdown>`
20
26
  }
@@ -6,19 +6,19 @@ import "../leu-dropdown.js"
6
6
  async function defaultFixture() {
7
7
  return fixture(html` <leu-dropdown label="Download">
8
8
  <leu-menu>
9
- <leu-menu-item>Als CSV Tabelle</leu-menu-item>
10
- <leu-menu-item>Als XLS Tabelle</leu-menu-item>
9
+ <leu-menu-item label="Als CSV Tabelle"></leu-menu-item>
10
+ <leu-menu-item label="Als XLS Tabelle"></leu-menu-item>
11
11
  <hr />
12
- <leu-menu-item>Als PNG exportieren</leu-menu-item>
13
- <leu-menu-item>Als SVG exportieren</leu-menu-item>
14
- <leu-menu-item>Als PDF exportieren</leu-menu-item>
12
+ <leu-menu-item label="Als PNG exportieren"></leu-menu-item>
13
+ <leu-menu-item label="Als SVG exportieren"></leu-menu-item>
14
+ <leu-menu-item label="Als PDF exportieren"></leu-menu-item>
15
15
  </leu-menu>
16
16
  </leu-dropdown>`)
17
17
  }
18
18
 
19
19
  describe("LeuDropdown", () => {
20
20
  it("is a defined element", async () => {
21
- const el = await customElements.get("leu-dropdown")
21
+ const el = customElements.get("leu-dropdown")
22
22
 
23
23
  await expect(el).not.to.be.undefined
24
24
  })
@@ -5,7 +5,7 @@ import { html, svg } from "lit"
5
5
  // - click icon and then export as svg on the right drawer
6
6
  // - copy svg path (multiple with space between)
7
7
  // - update @param {TYPE} with console.log() below
8
- const iconPaths = {
8
+ export const iconPaths = {
9
9
  addNew:
10
10
  "M12 1C5.926 1 1 5.925 1 12C1 18.075 5.926 23 12 23C18.074 23 23 18.075 23 12C23 5.925 18.074 1 12 1ZM17 13H13V17H11V13H7V11H11V7H13V11H17V13Z",
11
11
  angleDropDown: "M17 9.5L12 14.5L7 9.5L17 9.5Z",
@@ -0,0 +1,66 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect } from "@open-wc/testing"
3
+
4
+ import { Icon, iconPaths } from "../icon.js"
5
+
6
+ async function defaultFixture(args = {}) {
7
+ const { name = "edit", size } = args
8
+ return fixture(html`${Icon(name, size)} `)
9
+ }
10
+
11
+ describe("Icon", () => {
12
+ it("passes the a11y audit", async () => {
13
+ const el = await defaultFixture()
14
+
15
+ await expect(el).shadowDom.to.be.accessible()
16
+ })
17
+
18
+ it("renders an svg html element", async () => {
19
+ const el = await defaultFixture()
20
+
21
+ expect(el).to.have.tagName("svg")
22
+ })
23
+
24
+ it("renders a single path", async () => {
25
+ const el = await defaultFixture()
26
+
27
+ expect(el.querySelectorAll("path")).to.have.lengthOf(1)
28
+ })
29
+
30
+ it("renders the correct path", async () => {
31
+ const el = await defaultFixture({ name: "share" })
32
+
33
+ const d = el.querySelector("path").getAttribute("d")
34
+
35
+ expect(d).to.equal(iconPaths.share)
36
+ })
37
+
38
+ it("has the size 24x24 by default", async () => {
39
+ const el = await defaultFixture()
40
+
41
+ const width = el.getAttribute("width")
42
+ const height = el.getAttribute("height")
43
+
44
+ expect(width).to.equal("24")
45
+ expect(height).to.equal("24")
46
+ })
47
+
48
+ it("has the size 16x16", async () => {
49
+ const el = await defaultFixture({ size: "16" })
50
+
51
+ const width = el.getAttribute("width")
52
+ const height = el.getAttribute("height")
53
+
54
+ expect(width).to.equal("16")
55
+ expect(height).to.equal("16")
56
+ })
57
+
58
+ it("has always a square size", async () => {
59
+ const el = await defaultFixture({ size: "564" })
60
+
61
+ const width = el.getAttribute("width")
62
+ const height = el.getAttribute("height")
63
+
64
+ expect(width).to.equal(height)
65
+ })
66
+ })
@@ -1,10 +1,10 @@
1
1
  import { html, LitElement, nothing } from "lit"
2
2
  import { classMap } from "lit/directives/class-map.js"
3
3
  import { ifDefined } from "lit/directives/if-defined.js"
4
+ import { live } from "lit/directives/live.js"
4
5
  import { createRef, ref } from "lit/directives/ref.js"
5
6
 
6
7
  import { Icon } from "../icon/icon.js"
7
- import { defineElement } from "../../lib/defineElement.js"
8
8
 
9
9
  import styles from "./input.css"
10
10
 
@@ -117,19 +117,9 @@ export class LeuInput extends LitElement {
117
117
  this.required = false
118
118
  this.clearable = false
119
119
 
120
- this.value = ""
121
- this.name = ""
122
- this.error = ""
123
-
124
- this.label = ""
125
- this.prefix = ""
126
- this.suffix = ""
127
-
128
120
  /** @type {keyof typeof SIZE_TYPES} */
129
121
  this.size = SIZE_TYPES.REGULAR
130
122
 
131
- this.icon = ""
132
-
133
123
  this.type = "text"
134
124
  this._validity = null
135
125
  this.validationMessages = {}
@@ -201,7 +191,9 @@ export class LeuInput extends LitElement {
201
191
  * @returns {void}
202
192
  */
203
193
  handleChange(event) {
204
- this.value = event.target.value
194
+ if (event.target.validity.valid) {
195
+ this.value = event.target.value
196
+ }
205
197
 
206
198
  const customEvent = new CustomEvent(event.type, event)
207
199
  this.dispatchEvent(customEvent)
@@ -217,6 +209,12 @@ export class LeuInput extends LitElement {
217
209
  */
218
210
  handleInput(event) {
219
211
  this.value = event.target.value
212
+
213
+ const customEvent = new CustomEvent("input", {
214
+ bubbles: true,
215
+ composed: true,
216
+ })
217
+ this.dispatchEvent(customEvent)
220
218
  }
221
219
 
222
220
  /**
@@ -348,7 +346,7 @@ export class LeuInput extends LitElement {
348
346
  return html`<div class="error-icon">${Icon("caution")}</div>`
349
347
  }
350
348
 
351
- if (this.clearable && this.value !== "") {
349
+ if (this.clearable && this.value) {
352
350
  return html`<button
353
351
  class="clear-button"
354
352
  @click=${this.clear}
@@ -359,7 +357,7 @@ export class LeuInput extends LitElement {
359
357
  </button>`
360
358
  }
361
359
 
362
- if (this.icon !== "") {
360
+ if (this.icon) {
363
361
  return html`<div class="icon">${Icon(this.icon)}</div>`
364
362
  }
365
363
 
@@ -367,7 +365,7 @@ export class LeuInput extends LitElement {
367
365
  }
368
366
 
369
367
  isInvalid() {
370
- if (this.error !== "") {
368
+ if (this.error) {
371
369
  return true
372
370
  }
373
371
 
@@ -381,7 +379,7 @@ export class LeuInput extends LitElement {
381
379
 
382
380
  const inputWrapperClasses = {
383
381
  "input-wrapper": true,
384
- "input-wrapper--empty": this.value === "",
382
+ "input-wrapper--empty": !this.value,
385
383
  "input-wrapper--invalid": isInvalid,
386
384
  }
387
385
 
@@ -403,20 +401,20 @@ export class LeuInput extends LitElement {
403
401
  @invalid=${this.handleInvalid}
404
402
  ?disabled=${this.disabled}
405
403
  ?required=${this.required}
404
+ .value=${live(this.value ?? "")}
406
405
  pattern=${ifDefined(this.pattern)}
407
406
  min=${ifDefined(this.min)}
408
407
  max=${ifDefined(this.max)}
409
408
  maxlength=${ifDefined(this.maxlength)}
410
409
  minlength=${ifDefined(this.minlength)}
411
- .value=${this.value}
412
410
  ref=${ref(this._inputRef)}
413
411
  aria-invalid=${isInvalid}
414
412
  />
415
- <label for="input-${this.getId()}" class="label"><slot></slot></label>
416
- ${this.prefix !== ""
413
+ <label for="input-${this.getId()}" class="label">${this.label}</label>
414
+ ${this.prefix
417
415
  ? html`<div class="prefix" .aria-hidden=${true}>${this.prefix}</div>`
418
416
  : nothing}
419
- ${this.suffix !== ""
417
+ ${this.suffix
420
418
  ? html`<div class="suffix" .aria-hidden=${true}>${this.suffix}</div>`
421
419
  : nothing}
422
420
  ${this.renderAfterContent()}
@@ -425,7 +423,3 @@ export class LeuInput extends LitElement {
425
423
  `
426
424
  }
427
425
  }
428
-
429
- export function defineInputElements() {
430
- defineElement("input", LeuInput)
431
- }
@@ -115,6 +115,7 @@
115
115
  }
116
116
 
117
117
  /* is size regular AND (has focus OR contains a value) */
118
+ .input-wrapper--invalid .label,
118
119
  :host(:not([size])) .label,
119
120
  :host([size="regular"]) .label {
120
121
  top: calc(0.75rem - var(--input-border-width));
@@ -138,18 +139,20 @@
138
139
  content: "*";
139
140
  }
140
141
 
142
+ /* is not disabled AND has focus AND is empty */
143
+ :host(:not([disabled], :focus-within)) .input-wrapper--empty .label {
144
+ --input-label-color: var(--input-label-color-empty);
145
+ }
146
+
141
147
  /* is empty AND has no focus */
142
- :host(:not(:focus-within)) .input-wrapper--empty .label {
148
+ :host(:not(:focus-within))
149
+ .input-wrapper--empty:not(.input-wrapper--invalid)
150
+ .label {
143
151
  font-family: var(--input-font-regular);
144
152
  font-size: 1rem;
145
153
  top: calc(1.5rem - var(--input-border-width));
146
154
  }
147
155
 
148
- /* is not disabled AND has focus AND is empty */
149
- :host(:not([disabled], :focus-within)) .input-wrapper--empty .label {
150
- --input-label-color: var(--input-label-color-empty);
151
- }
152
-
153
156
  /* is size small AND has no focus AND is empty */
154
157
  :host(:not(:focus-within)[size="small"]) .input-wrapper--empty .label {
155
158
  top: calc(0.75rem - var(--input-border-width));
@@ -1,3 +1,6 @@
1
- import { defineInputElements } from "./Input.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuInput } from "./Input.js"
2
3
 
3
- defineInputElements()
4
+ export { LeuInput }
5
+
6
+ defineElement("input", LeuInput)