@oslokommune/punkt-elements 13.5.4 → 13.5.5

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ og skriver commits ca etter [Conventional Commits](https://conventionalcommits.o
5
5
 
6
6
  ---
7
7
 
8
+ ## [13.5.5](https://github.com/oslokommune/punkt/compare/13.5.4...13.5.5) (2025-09-09)
9
+
10
+ ### ⚠ BREAKING CHANGES
11
+ Ingen
12
+
13
+ ### Features
14
+ Ingen
15
+
16
+ ### Bug Fixes
17
+ Ingen
18
+
19
+ ### Chores
20
+ Ingen
21
+
22
+ ---
23
+
24
+
8
25
  ## [13.5.4](https://github.com/oslokommune/punkt/compare/13.5.3...13.5.4) (2025-09-09)
9
26
 
10
27
  ### ⚠ BREAKING CHANGES
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("./alert-7rUOhlNi.cjs"),l=require("./accordionitem-Csh7iSVG.cjs"),d=require("./backlink-JbBNi3qg.cjs"),b=require("./button-B8rdtaHB.cjs"),k=require("./calendar-32W9p9uc.cjs"),m=require("./card-DBlFf1ry.cjs"),g=require("./combobox-DjO0RMUB.cjs"),h=require("./consent-hYeFWNFr.cjs"),f=require("./checkbox-Gn7Wtk9h.cjs"),t=require("./element-6DBpyGQm.cjs"),y=require("./pkt-slot-controller-BzddBp7z.cjs"),s=require("./ref-iJtiv3o2.cjs"),O=require("./class-map-BBG2gMX4.cjs"),j=require("./datepicker-CmTrG5GE.cjs"),q=require("./helptext-CzQX6YVE.cjs"),x=require("./heading-CNycsyMj.cjs"),C=require("./icon-B_ryAy4Q.cjs"),v=require("./input-wrapper-CZ-a00V7.cjs"),S=require("./link-Cjl0xwSq.cjs"),$=require("./linkcard-DqIvb54H.cjs"),L=require("./loader-DNidjwH-.cjs"),_=require("./messagebox-CjPtPPrW.cjs"),A=require("./modal-CRtxhCaP.cjs"),B=require("./progressbar-DhMBXkww.cjs"),p=require("./radiobutton-CdT6v1oq.cjs"),T=require("./tag-Bbs0U_Au.cjs"),I=require("./textarea-CPXsMFUq.cjs"),M=require("./textinput-aNI5kibM.cjs"),R=require("./select-Dkl0KhGW.cjs");var H=Object.defineProperty,w=Object.getOwnPropertyDescriptor,o=(a,e,r,i)=>{for(var n=i>1?void 0:i?w(e,r):e,u=a.length-1,c;u>=0;u--)(c=a[u])&&(n=(i?c(e,r,n):c(n))||n);return i&&n&&H(e,r,n),n};exports.PktComponent=class extends t.PktElement{constructor(){super(),this.string="",this.strings=[],this.darkmode=!1,this._list=[],this.defaultSlot=s.e(),this.namedSlot=s.e(),this.slotController=new y.PktSlotController(this,this.defaultSlot,this.namedSlot)}connectedCallback(){this.strings.length&&this.strings.forEach(e=>{this._list.push(e.toUpperCase())}),super.connectedCallback()}render(){const e={"pkt-component":!0,"pkt-component--has-list":this.strings.length>0,"pkt-darkmode":this.darkmode};return t.x`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("./alert-7rUOhlNi.cjs"),l=require("./accordionitem-Csh7iSVG.cjs"),d=require("./backlink-JbBNi3qg.cjs"),b=require("./button-B8rdtaHB.cjs"),k=require("./calendar-32W9p9uc.cjs"),m=require("./card-DBlFf1ry.cjs"),g=require("./combobox-DjO0RMUB.cjs"),h=require("./consent-hYeFWNFr.cjs"),f=require("./checkbox-Gn7Wtk9h.cjs"),t=require("./element-6DBpyGQm.cjs"),y=require("./pkt-slot-controller-BzddBp7z.cjs"),s=require("./ref-iJtiv3o2.cjs"),O=require("./class-map-BBG2gMX4.cjs"),j=require("./datepicker-CmTrG5GE.cjs"),q=require("./helptext-CzQX6YVE.cjs"),x=require("./heading-CNycsyMj.cjs"),C=require("./icon-B_ryAy4Q.cjs"),v=require("./input-wrapper-CZ-a00V7.cjs"),S=require("./link-Cjl0xwSq.cjs"),$=require("./linkcard-DqIvb54H.cjs"),L=require("./loader-DNidjwH-.cjs"),_=require("./messagebox-CjPtPPrW.cjs"),A=require("./modal-CRtxhCaP.cjs"),B=require("./progressbar-DhMBXkww.cjs"),p=require("./radiobutton-CdT6v1oq.cjs"),T=require("./tag-Bbs0U_Au.cjs"),I=require("./textarea-CPXsMFUq.cjs"),M=require("./textinput-aNI5kibM.cjs"),R=require("./select-CD6Zn8YH.cjs");var H=Object.defineProperty,w=Object.getOwnPropertyDescriptor,o=(a,e,r,i)=>{for(var n=i>1?void 0:i?w(e,r):e,u=a.length-1,c;u>=0;u--)(c=a[u])&&(n=(i?c(e,r,n):c(n))||n);return i&&n&&H(e,r,n),n};exports.PktComponent=class extends t.PktElement{constructor(){super(),this.string="",this.strings=[],this.darkmode=!1,this._list=[],this.defaultSlot=s.e(),this.namedSlot=s.e(),this.slotController=new y.PktSlotController(this,this.defaultSlot,this.namedSlot)}connectedCallback(){this.strings.length&&this.strings.forEach(e=>{this._list.push(e.toUpperCase())}),super.connectedCallback()}render(){const e={"pkt-component":!0,"pkt-component--has-list":this.strings.length>0,"pkt-darkmode":this.darkmode};return t.x`
2
2
  <div class="${O.e(e)}">
3
3
  <h1 class="pkt-txt-28">${this.string}</h1>
4
4
 
package/dist/pkt-index.js CHANGED
@@ -27,7 +27,7 @@ import { P as ht, P as ct } from "./radiobutton-CWxiIVfA.js";
27
27
  import { P as ut } from "./tag-DyXzTY68.js";
28
28
  import { P as vt } from "./textarea-BZL8Mkm0.js";
29
29
  import { P as bt } from "./textinput-DjPhmmkB.js";
30
- import { P as $t } from "./select-CJS_CIKv.js";
30
+ import { P as $t } from "./select-dcaJHvmR.js";
31
31
  var g = Object.defineProperty, v = Object.getOwnPropertyDescriptor, s = (t, e, i, a) => {
32
32
  for (var r = a > 1 ? void 0 : a ? v(e, i) : e, p = t.length - 1, l; p >= 0; p--)
33
33
  (l = t[p]) && (r = (a ? l(e, i, r) : l(r)) || r);
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./select-Dkl0KhGW.cjs"),t=e.PktSelect;Object.defineProperty(exports,"PktSelect",{enumerable:!0,get:()=>e.PktSelect});exports.default=t;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./select-CD6Zn8YH.cjs"),t=e.PktSelect;Object.defineProperty(exports,"PktSelect",{enumerable:!0,get:()=>e.PktSelect});exports.default=t;
@@ -1,4 +1,4 @@
1
- import { P as t } from "./select-CJS_CIKv.js";
1
+ import { P as t } from "./select-dcaJHvmR.js";
2
2
  const a = t;
3
3
  export {
4
4
  t as PktSelect,
@@ -1,4 +1,4 @@
1
- "use strict";const r=require("./element-6DBpyGQm.cjs"),p=require("./state-DPobt-Yz.cjs"),h=require("./ref-iJtiv3o2.cjs"),l=require("./if-defined-Cni-RHLS.cjs"),d=require("./input-element-C4xJoM-X.cjs"),c=require("./pkt-options-controller-CiuBG6Lt.cjs"),v=require("./pkt-slot-controller-BzddBp7z.cjs");require("./input-wrapper-CZ-a00V7.cjs");var f=Object.defineProperty,b=Object.getOwnPropertyDescriptor,a=(u,t,e,s)=>{for(var i=s>1?void 0:s?b(t,e):t,o=u.length-1,n;o>=0;o--)(n=u[o])&&(i=(s?n(t,e,i):n(i))||i);return s&&i&&f(t,e,i),i};exports.PktSelect=class extends d.PktInputElement{constructor(){super(),this.inputRef=h.e(),this.helptextSlot=h.e(),this.options=[],this.value="",this._options=[],this.selectedIndex=-1,this.selectedOptions=void 0,this.optionsController=new c.PktOptionsSlotController(this),this.slotController=new v.PktSlotController(this,this.helptextSlot),this.slotController.skipOptions=!0}connectedCallback(){super.connectedCallback();const t=this.options.length>0,e=this.optionsController.nodes.length&&this.optionsController.nodes.length>0;!t&&e?this.optionsController.nodes.forEach(s=>{const i={value:s.hasAttribute("value")?s.getAttribute("value")??"":s.textContent??"",label:s.textContent||s.getAttribute("value")||"",disabled:s.hasAttribute("disabled"),selected:s.hasAttribute("selected"),hidden:s.hasAttribute("data-hidden")};s.getAttribute("selected")&&!this.value&&(this.value=i.value),this._options.push(i)}):(this._options=this.options,this._options.forEach(s=>{s.selected&&!this.value&&(this.value=s.value)}))}add(t,e){var s,i,o;(s=this.inputRef.value)==null||s.add(t,e),this._options.push({value:t.value||t.text,label:t.text||t.value,selected:t.selected,disabled:t.disabled}),t.selected&&(this.value=t.value||t.text,this.selectedIndex=this.returnNumberOrNull((i=this.inputRef.value)==null?void 0:i.selectedIndex),this.selectedOptions=(o=this.inputRef.value)==null?void 0:o.selectedOptions),this.requestUpdate()}remove(t){var e,s;typeof t=="number"&&(this.selectedIndex===t&&(this.value=((e=this._options[0])==null?void 0:e.value)||""),(s=this.inputRef.value)==null||s.remove(t))}item(t){var e;return(e=this.inputRef.value)==null?void 0:e.item(t)}namedItem(t){var e;return(e=this.inputRef.value)==null?void 0:e.namedItem(t)}showPicker(){var t;(t=this.inputRef.value)==null||t.showPicker()}attributeChangedCallback(t,e,s){var i,o;t==="options"&&(this._options=s?JSON.parse(s):[]),t==="value"&&this.value!==e&&(this.selectedIndex=this.touched?this.returnNumberOrNull((i=this.inputRef.value)==null?void 0:i.selectedIndex):this.options.findIndex(n=>n.value===s),this.selectedOptions=(o=this.inputRef.value)==null?void 0:o.selectedOptions,this.valueChanged(s,e)),super.attributeChangedCallback(t,e,s)}update(t){var e,s;super.update(t),t.has("value")&&this.value!==t.get("value")&&(this.selectedIndex=this.touched?this.returnNumberOrNull((e=this.inputRef.value)==null?void 0:e.selectedIndex):this.options.findIndex(i=>i.value===this.value),this.selectedOptions=(s=this.inputRef.value)==null?void 0:s.selectedOptions,this.valueChanged(this.value,t.get("value"))),t.has("id")&&!this.name&&this.id&&(this.name=this.id)}firstUpdated(t){var e;super.firstUpdated(t),this.options.length&&(this._options=this.options),!this.value&&this._options.length>0?(this.value=this._options[0].value,this.selectedIndex=0):this.selectedIndex=this._options.findIndex(s=>s.value===this.value),this.selectedOptions=(e=this.inputRef.value)==null?void 0:e.selectedOptions}render(){const t=`pkt-input ${this.fullwidth?"pkt-input--fullwidth":""}`;return r.x`
1
+ "use strict";const h=require("./element-6DBpyGQm.cjs"),p=require("./state-DPobt-Yz.cjs"),r=require("./ref-iJtiv3o2.cjs"),l=require("./if-defined-Cni-RHLS.cjs"),d=require("./input-element-C4xJoM-X.cjs"),c=require("./pkt-options-controller-CiuBG6Lt.cjs"),v=require("./pkt-slot-controller-BzddBp7z.cjs");require("./input-wrapper-CZ-a00V7.cjs");var f=Object.defineProperty,b=Object.getOwnPropertyDescriptor,a=(u,t,e,s)=>{for(var i=s>1?void 0:s?b(t,e):t,o=u.length-1,n;o>=0;o--)(n=u[o])&&(i=(s?n(t,e,i):n(i))||i);return s&&i&&f(t,e,i),i};exports.PktSelect=class extends d.PktInputElement{constructor(){super(),this.inputRef=r.e(),this.helptextSlot=r.e(),this.options=[],this.value="",this._options=[],this.selectedIndex=-1,this.selectedOptions=void 0,this.optionsController=new c.PktOptionsSlotController(this),this.slotController=new v.PktSlotController(this,this.helptextSlot),this.slotController.skipOptions=!0}connectedCallback(){super.connectedCallback();const t=this.options.length>0,e=this.optionsController.nodes.length&&this.optionsController.nodes.length>0;!t&&e?this.optionsController.nodes.forEach(s=>{const i={value:s.hasAttribute("value")?s.getAttribute("value")??"":s.textContent??"",label:s.textContent||s.getAttribute("value")||"",disabled:s.hasAttribute("disabled"),selected:s.hasAttribute("selected"),hidden:s.hasAttribute("data-hidden")};s.getAttribute("selected")&&!this.value&&(this.value=i.value),this._options.push(i)}):(this._options=this.options,this._options.forEach(s=>{s.selected&&!this.value&&(this.value=s.value)}))}add(t,e){var s,i,o;(s=this.inputRef.value)==null||s.add(t,e),this._options.push({value:t.value||t.text,label:t.text||t.value,selected:t.selected,disabled:t.disabled}),t.selected&&(this.value=t.value||t.text,this.selectedIndex=this.returnNumberOrNull((i=this.inputRef.value)==null?void 0:i.selectedIndex),this.selectedOptions=(o=this.inputRef.value)==null?void 0:o.selectedOptions),this.requestUpdate()}remove(t){var e,s;typeof t=="number"&&(this.selectedIndex===t&&(this.value=((e=this._options[0])==null?void 0:e.value)||""),(s=this.inputRef.value)==null||s.remove(t))}item(t){var e;return(e=this.inputRef.value)==null?void 0:e.item(t)}namedItem(t){var e;return(e=this.inputRef.value)==null?void 0:e.namedItem(t)}showPicker(){var t;(t=this.inputRef.value)==null||t.showPicker()}attributeChangedCallback(t,e,s){var i,o;t==="options"&&(this._options=s?JSON.parse(s):[]),t==="value"&&this.value!==e&&(this.selectedIndex=this.touched?this.returnNumberOrNull((i=this.inputRef.value)==null?void 0:i.selectedIndex):this.options.findIndex(n=>n.value===s),this.selectedOptions=(o=this.inputRef.value)==null?void 0:o.selectedOptions,this.valueChanged(s,e)),super.attributeChangedCallback(t,e,s)}update(t){var e,s;super.update(t),t.has("options")&&(this._options=this.options,this.requestUpdate("_options"),!this.value&&this._options.length>0&&(this.value=this._options[0].value,this.selectedIndex=0)),t.has("value")&&this.value!==t.get("value")&&(this.selectedIndex=this.touched?this.returnNumberOrNull((e=this.inputRef.value)==null?void 0:e.selectedIndex):this.options.findIndex(i=>i.value===this.value),this.selectedOptions=(s=this.inputRef.value)==null?void 0:s.selectedOptions,this.valueChanged(this.value,t.get("value"))),t.has("id")&&!this.name&&this.id&&(this.name=this.id)}firstUpdated(t){var e;super.firstUpdated(t),this.options.length&&(this._options=this.options),!this.value&&this._options.length>0?(this.value=this._options[0].value,this.selectedIndex=0):this.selectedIndex=this._options.findIndex(s=>s.value===this.value),this.selectedOptions=(e=this.inputRef.value)==null?void 0:e.selectedOptions}render(){const t=`pkt-input ${this.fullwidth?"pkt-input--fullwidth":""}`;return h.x`
2
2
  <pkt-input-wrapper
3
3
  ?counter=${this.counter}
4
4
  ?disabled=${this.disabled}
@@ -33,17 +33,17 @@
33
33
  @input=${e=>{this.onInput(),e.stopImmediatePropagation()}}
34
34
  @focus=${e=>{this.onFocus(),e.stopImmediatePropagation()}}
35
35
  @blur=${e=>{this.onBlur(),e.stopImmediatePropagation()}}
36
- ${h.n(this.inputRef)}
36
+ ${r.n(this.inputRef)}
37
37
  >
38
- ${this._options.length>0&&this._options.map(e=>r.x`<option
39
- value=${e.value}
40
- ?selected=${this.value==e.value||e.selected}
41
- ?disabled=${e.disabled}
42
- ?hidden=${e.hidden}
43
- >
44
- ${e.label}
45
- </option>`)}
38
+ ${this._options.length>0?this._options.map(e=>h.x`<option
39
+ value=${e.value}
40
+ ?selected=${this.value==e.value||e.selected}
41
+ ?disabled=${e.disabled}
42
+ ?hidden=${e.hidden}
43
+ >
44
+ ${e.label}
45
+ </option>`):""}
46
46
  </select>
47
- <div class="pkt-contents" ${h.n(this.helptextSlot)} name="helptext" slot="helptext"></div>
47
+ <div class="pkt-contents" ${r.n(this.helptextSlot)} name="helptext" slot="helptext"></div>
48
48
  </pkt-input-wrapper>
49
- `}returnNumberOrNull(t){return t==null||isNaN(t)?null:t}};a([r.n({type:Array})],exports.PktSelect.prototype,"options",2);a([r.n({type:String})],exports.PktSelect.prototype,"value",2);a([p.r()],exports.PktSelect.prototype,"_options",2);exports.PktSelect=a([r.t("pkt-select")],exports.PktSelect);
49
+ `}returnNumberOrNull(t){return t==null||isNaN(t)?null:t}};a([h.n({type:Array})],exports.PktSelect.prototype,"options",2);a([h.n({type:String})],exports.PktSelect.prototype,"value",2);a([p.r()],exports.PktSelect.prototype,"_options",2);exports.PktSelect=a([h.t("pkt-select")],exports.PktSelect);
@@ -7,11 +7,11 @@ import { P as $ } from "./pkt-options-controller-CZO1nxZ8.js";
7
7
  import { P as x } from "./pkt-slot-controller-BPGj-LC5.js";
8
8
  import "./input-wrapper-Dr__Sxql.js";
9
9
  var m = Object.defineProperty, g = Object.getOwnPropertyDescriptor, n = (t, e, s, i) => {
10
- for (var l = i > 1 ? void 0 : i ? g(e, s) : e, a = t.length - 1, r; a >= 0; a--)
11
- (r = t[a]) && (l = (i ? r(e, s, l) : r(l)) || l);
10
+ for (var l = i > 1 ? void 0 : i ? g(e, s) : e, h = t.length - 1, r; h >= 0; h--)
11
+ (r = t[h]) && (l = (i ? r(e, s, l) : r(l)) || l);
12
12
  return i && l && m(e, s, l), l;
13
13
  };
14
- let h = class extends b {
14
+ let a = class extends b {
15
15
  constructor() {
16
16
  super(), this.inputRef = u(), this.helptextSlot = u(), this.options = [], this.value = "", this._options = [], this.selectedIndex = -1, this.selectedOptions = void 0, this.optionsController = new $(this), this.slotController = new x(this, this.helptextSlot), this.slotController.skipOptions = !0;
17
17
  }
@@ -64,11 +64,11 @@ let h = class extends b {
64
64
  }
65
65
  attributeChangedCallback(t, e, s) {
66
66
  var i, l;
67
- t === "options" && (this._options = s ? JSON.parse(s) : []), t === "value" && this.value !== e && (this.selectedIndex = this.touched ? this.returnNumberOrNull((i = this.inputRef.value) == null ? void 0 : i.selectedIndex) : this.options.findIndex((a) => a.value === s), this.selectedOptions = (l = this.inputRef.value) == null ? void 0 : l.selectedOptions, this.valueChanged(s, e)), super.attributeChangedCallback(t, e, s);
67
+ t === "options" && (this._options = s ? JSON.parse(s) : []), t === "value" && this.value !== e && (this.selectedIndex = this.touched ? this.returnNumberOrNull((i = this.inputRef.value) == null ? void 0 : i.selectedIndex) : this.options.findIndex((h) => h.value === s), this.selectedOptions = (l = this.inputRef.value) == null ? void 0 : l.selectedOptions, this.valueChanged(s, e)), super.attributeChangedCallback(t, e, s);
68
68
  }
69
69
  update(t) {
70
70
  var e, s;
71
- super.update(t), t.has("value") && this.value !== t.get("value") && (this.selectedIndex = this.touched ? this.returnNumberOrNull((e = this.inputRef.value) == null ? void 0 : e.selectedIndex) : this.options.findIndex((i) => i.value === this.value), this.selectedOptions = (s = this.inputRef.value) == null ? void 0 : s.selectedOptions, this.valueChanged(this.value, t.get("value"))), t.has("id") && !this.name && this.id && (this.name = this.id);
71
+ super.update(t), t.has("options") && (this._options = this.options, this.requestUpdate("_options"), !this.value && this._options.length > 0 && (this.value = this._options[0].value, this.selectedIndex = 0)), t.has("value") && this.value !== t.get("value") && (this.selectedIndex = this.touched ? this.returnNumberOrNull((e = this.inputRef.value) == null ? void 0 : e.selectedIndex) : this.options.findIndex((i) => i.value === this.value), this.selectedOptions = (s = this.inputRef.value) == null ? void 0 : s.selectedOptions, this.valueChanged(this.value, t.get("value"))), t.has("id") && !this.name && this.id && (this.name = this.id);
72
72
  }
73
73
  firstUpdated(t) {
74
74
  var e;
@@ -121,16 +121,16 @@ let h = class extends b {
121
121
  }}
122
122
  ${d(this.inputRef)}
123
123
  >
124
- ${this._options.length > 0 && this._options.map(
124
+ ${this._options.length > 0 ? this._options.map(
125
125
  (e) => p`<option
126
- value=${e.value}
127
- ?selected=${this.value == e.value || e.selected}
128
- ?disabled=${e.disabled}
129
- ?hidden=${e.hidden}
130
- >
131
- ${e.label}
132
- </option>`
133
- )}
126
+ value=${e.value}
127
+ ?selected=${this.value == e.value || e.selected}
128
+ ?disabled=${e.disabled}
129
+ ?hidden=${e.hidden}
130
+ >
131
+ ${e.label}
132
+ </option>`
133
+ ) : ""}
134
134
  </select>
135
135
  <div class="pkt-contents" ${d(this.helptextSlot)} name="helptext" slot="helptext"></div>
136
136
  </pkt-input-wrapper>
@@ -142,16 +142,16 @@ let h = class extends b {
142
142
  };
143
143
  n([
144
144
  c({ type: Array })
145
- ], h.prototype, "options", 2);
145
+ ], a.prototype, "options", 2);
146
146
  n([
147
147
  c({ type: String })
148
- ], h.prototype, "value", 2);
148
+ ], a.prototype, "value", 2);
149
149
  n([
150
150
  f()
151
- ], h.prototype, "_options", 2);
152
- h = n([
151
+ ], a.prototype, "_options", 2);
152
+ a = n([
153
153
  v("pkt-select")
154
- ], h);
154
+ ], a);
155
155
  export {
156
- h as P
156
+ a as P
157
157
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-elements",
3
- "version": "13.5.4",
3
+ "version": "13.5.5",
4
4
  "description": "Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo",
5
5
  "homepage": "https://punkt.oslo.kommune.no",
6
6
  "author": "Team Designsystem, Oslo Origo",
@@ -73,5 +73,5 @@
73
73
  "url": "https://github.com/oslokommune/punkt/issues"
74
74
  },
75
75
  "license": "MIT",
76
- "gitHead": "aed226fa38fe77f28a9bc5d0380b194f0f90a64a"
76
+ "gitHead": "f65d3c81c0543cc360a34fe346b4e0585deb46ac"
77
77
  }
@@ -0,0 +1,195 @@
1
+ import '@testing-library/jest-dom'
2
+ import { axe, toHaveNoViolations } from 'jest-axe'
3
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
4
+ import { CustomElementFor } from '../../tests/component-registry'
5
+ import { type IPktProgressbar } from './progressbar'
6
+ import './progressbar'
7
+
8
+ export interface ProgressbarTestConfig extends Partial<IPktProgressbar>, BaseTestConfig {}
9
+
10
+ export const createProgressbarTest = async (config: ProgressbarTestConfig = {}) => {
11
+ const { container, element } = await createElementTest<
12
+ CustomElementFor<'pkt-progressbar'>,
13
+ ProgressbarTestConfig
14
+ >('pkt-progressbar', config)
15
+
16
+ return {
17
+ container,
18
+ progressbar: element,
19
+ }
20
+ }
21
+
22
+ expect.extend(toHaveNoViolations)
23
+
24
+ afterEach(() => {
25
+ document.body.innerHTML = ''
26
+ })
27
+
28
+ describe('PktProgressbar', () => {
29
+ describe('Rendering and basic functionality', () => {
30
+ test('renders without errors', async () => {
31
+ const { progressbar } = await createProgressbarTest()
32
+
33
+ expect(progressbar).toBeInTheDocument()
34
+ expect(progressbar).toBeTruthy()
35
+ })
36
+
37
+ test('renders with progress value', async () => {
38
+ const { progressbar } = await createProgressbarTest({ valueCurrent: 50 })
39
+
40
+ expect(progressbar.valueCurrent).toBe(50)
41
+ expect(progressbar.getAttribute('role')).toBe('progressbar')
42
+ })
43
+ })
44
+
45
+ describe('Properties and attributes', () => {
46
+ test('applies default properties correctly', async () => {
47
+ const { progressbar } = await createProgressbarTest()
48
+
49
+ expect(progressbar.valueCurrent).toBe(0)
50
+ expect(progressbar.valueMin).toBe(0)
51
+ expect(progressbar.valueMax).toBe(100)
52
+ expect(progressbar.skin).toBe('dark-blue')
53
+ expect(progressbar.role).toBe('progressbar')
54
+ })
55
+
56
+ test('sets properties correctly', async () => {
57
+ const { progressbar } = await createProgressbarTest({
58
+ valueCurrent: 75,
59
+ valueMin: 10,
60
+ valueMax: 200,
61
+ skin: 'green',
62
+ title: 'Loading',
63
+ })
64
+
65
+ expect(progressbar.valueCurrent).toBe(75)
66
+ expect(progressbar.valueMin).toBe(10)
67
+ expect(progressbar.valueMax).toBe(200)
68
+ expect(progressbar.skin).toBe('green')
69
+ expect(progressbar.title).toBe('Loading')
70
+ })
71
+ })
72
+
73
+ describe('Skin variants', () => {
74
+ test('applies dark-blue skin correctly', async () => {
75
+ const { progressbar } = await createProgressbarTest({ skin: 'dark-blue' })
76
+
77
+ const barElement = progressbar.querySelector('.pkt-progressbar__bar')
78
+ expect(barElement?.classList.contains('pkt-progressbar__bar--dark-blue')).toBe(true)
79
+ })
80
+
81
+ test('applies green skin correctly', async () => {
82
+ const { progressbar } = await createProgressbarTest({ skin: 'green' })
83
+
84
+ const barElement = progressbar.querySelector('.pkt-progressbar__bar')
85
+ expect(barElement?.classList.contains('pkt-progressbar__bar--green')).toBe(true)
86
+ })
87
+
88
+ test('applies red skin correctly', async () => {
89
+ const { progressbar } = await createProgressbarTest({ skin: 'red' })
90
+
91
+ const barElement = progressbar.querySelector('.pkt-progressbar__bar')
92
+ expect(barElement?.classList.contains('pkt-progressbar__bar--red')).toBe(true)
93
+ })
94
+ })
95
+
96
+ describe('Status display', () => {
97
+ test('shows percentage when statusType is percentage', async () => {
98
+ const { progressbar } = await createProgressbarTest({
99
+ valueCurrent: 50,
100
+ statusType: 'percentage',
101
+ })
102
+
103
+ const statusElement = progressbar.querySelector('.pkt-progressbar__status')
104
+ expect(statusElement?.textContent).toContain('50%')
105
+ })
106
+
107
+ test('shows fraction when statusType is fraction', async () => {
108
+ const { progressbar } = await createProgressbarTest({
109
+ valueCurrent: 25,
110
+ valueMax: 50,
111
+ statusType: 'fraction',
112
+ })
113
+
114
+ const statusElement = progressbar.querySelector('.pkt-progressbar__status')
115
+ expect(statusElement?.textContent?.trim()).toContain('25 av 50')
116
+ })
117
+
118
+ test('hides status when statusType is none', async () => {
119
+ const { progressbar } = await createProgressbarTest({
120
+ valueCurrent: 50,
121
+ statusType: 'none',
122
+ })
123
+
124
+ const statusElement = progressbar.querySelector('.pkt-progressbar__status')
125
+ expect(statusElement).not.toBeInTheDocument()
126
+ })
127
+ })
128
+
129
+ describe('Title functionality', () => {
130
+ test('renders title when provided', async () => {
131
+ const { progressbar } = await createProgressbarTest({ title: 'Loading progress' })
132
+
133
+ const titleElement = progressbar.querySelector('.pkt-progressbar__title')
134
+ expect(titleElement).toBeInTheDocument()
135
+ expect(titleElement?.textContent).toBe('Loading progress')
136
+ })
137
+
138
+ test('does not render title when empty', async () => {
139
+ const { progressbar } = await createProgressbarTest()
140
+
141
+ const titleElement = progressbar.querySelector('.pkt-progressbar__title')
142
+ expect(titleElement).not.toBeInTheDocument()
143
+ })
144
+ })
145
+
146
+ describe('ARIA and accessibility', () => {
147
+ test('has correct ARIA attributes', async () => {
148
+ const { progressbar } = await createProgressbarTest({
149
+ valueCurrent: 60,
150
+ ariaLabel: 'File upload progress',
151
+ })
152
+
153
+ expect(progressbar.getAttribute('aria-valuemin')).toBe('0')
154
+ expect(progressbar.getAttribute('aria-valuemax')).toBe('100')
155
+ expect(progressbar.getAttribute('aria-valuenow')).toBe('60')
156
+ expect(progressbar.getAttribute('aria-label')).toBe('File upload progress')
157
+ })
158
+
159
+ test('updates aria-valuenow when value changes', async () => {
160
+ const { progressbar } = await createProgressbarTest({ valueCurrent: 30 })
161
+
162
+ progressbar.valueCurrent = 80
163
+ await progressbar.updateComplete
164
+
165
+ expect(progressbar.getAttribute('aria-valuenow')).toBe('80')
166
+ })
167
+ })
168
+
169
+ describe('Role variants', () => {
170
+ test('uses progressbar role by default', async () => {
171
+ const { progressbar } = await createProgressbarTest()
172
+
173
+ expect(progressbar.getAttribute('role')).toBe('progressbar')
174
+ })
175
+
176
+ test('uses meter role when specified', async () => {
177
+ const { progressbar } = await createProgressbarTest({ role: 'meter' })
178
+
179
+ expect(progressbar.getAttribute('role')).toBe('meter')
180
+ })
181
+ })
182
+
183
+ describe('Accessibility', () => {
184
+ test('progressbar is accessible', async () => {
185
+ const { container } = await createProgressbarTest({
186
+ title: 'Upload progress',
187
+ valueCurrent: 50,
188
+ ariaLabel: 'File upload progress',
189
+ })
190
+
191
+ const results = await axe(container)
192
+ expect(results).toHaveNoViolations()
193
+ })
194
+ })
195
+ })
@@ -0,0 +1,272 @@
1
+ import '@testing-library/jest-dom'
2
+ import { axe, toHaveNoViolations } from 'jest-axe'
3
+ import { fireEvent } from '@testing-library/dom'
4
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
5
+ import { CustomElementFor } from '../../tests/component-registry'
6
+ import './radiobutton'
7
+
8
+ export interface RadioButtonTestConfig extends BaseTestConfig {
9
+ value?: string
10
+ checkHelptext?: string | null
11
+ defaultChecked?: boolean
12
+ hasTile?: boolean
13
+ checked?: boolean | string | null
14
+ type?: string
15
+ tagText?: string | null
16
+ optionalTag?: boolean
17
+ optionalText?: string
18
+ requiredTag?: boolean
19
+ requiredText?: string
20
+
21
+ id?: string
22
+ name?: string
23
+ label?: string
24
+ disabled?: boolean
25
+ readonly?: boolean
26
+ required?: boolean
27
+ hasError?: boolean
28
+ inline?: boolean
29
+ ariaDescribedBy?: string | null
30
+ ariaLabelledby?: string | null
31
+ }
32
+
33
+ export const createRadioButtonTest = async (config: RadioButtonTestConfig = {}) => {
34
+ const { container, element } = await createElementTest<
35
+ CustomElementFor<'pkt-radiobutton'>,
36
+ RadioButtonTestConfig
37
+ >('pkt-radiobutton', config)
38
+
39
+ if (config.label) {
40
+ element.label = config.label
41
+ await element.updateComplete
42
+ }
43
+
44
+ return {
45
+ container,
46
+ radiobutton: element,
47
+ }
48
+ }
49
+
50
+ expect.extend(toHaveNoViolations)
51
+
52
+ describe('pkt-radiobutton', () => {
53
+ afterEach(() => {
54
+ document.body.innerHTML = ''
55
+ })
56
+
57
+ test('renders with basic properties', async () => {
58
+ const { radiobutton } = await createRadioButtonTest({
59
+ id: 'test',
60
+ name: 'test',
61
+ value: 'test',
62
+ })
63
+
64
+ expect(radiobutton).toBeInTheDocument()
65
+ expect(radiobutton.tagName).toBe('PKT-RADIOBUTTON')
66
+ })
67
+
68
+ test('renders input element with correct attributes', async () => {
69
+ const { radiobutton } = await createRadioButtonTest({
70
+ id: 'test',
71
+ name: 'test',
72
+ value: 'test',
73
+ })
74
+
75
+ const input = radiobutton.querySelector('input[type="radio"]') as HTMLInputElement
76
+ expect(input).toBeInTheDocument()
77
+ expect(input.type).toBe('radio')
78
+ expect(input.id).toBe('test-internal')
79
+ expect(input.name).toBe('test-internal')
80
+ })
81
+
82
+ test('displays label correctly', async () => {
83
+ const { radiobutton } = await createRadioButtonTest({
84
+ id: 'test',
85
+ label: 'Test Radio',
86
+ })
87
+
88
+ const label = radiobutton.querySelector('label')
89
+ expect(label).toBeInTheDocument()
90
+ expect(label?.textContent?.trim()).toContain('Test Radio')
91
+ expect(label?.getAttribute('for')).toBe('test-internal')
92
+ })
93
+
94
+ test('handles checked state correctly', async () => {
95
+ const { radiobutton } = await createRadioButtonTest()
96
+
97
+ expect(radiobutton.checked).toBe(null)
98
+
99
+ radiobutton.checked = true
100
+ await radiobutton.updateComplete
101
+
102
+ const input = radiobutton.querySelector('input') as HTMLInputElement
103
+ expect(input.checked).toBe(true)
104
+ expect(radiobutton.checked).toBe(true)
105
+ })
106
+
107
+ test('updates checked state on user interaction', async () => {
108
+ const { radiobutton } = await createRadioButtonTest({
109
+ label: 'Test Option',
110
+ })
111
+
112
+ const input = radiobutton.querySelector('input') as HTMLInputElement
113
+ expect(input.checked).toBe(false)
114
+ expect(radiobutton.touched).toBe(false)
115
+
116
+ fireEvent.click(input)
117
+ await radiobutton.updateComplete
118
+
119
+ expect(input.checked).toBe(true)
120
+ expect(radiobutton.touched).toBe(true)
121
+ })
122
+
123
+ test('handles disabled state correctly', async () => {
124
+ const { radiobutton } = await createRadioButtonTest({
125
+ disabled: true,
126
+ })
127
+
128
+ const input = radiobutton.querySelector('input') as HTMLInputElement
129
+ expect(input.disabled).toBe(true)
130
+
131
+ const label = radiobutton.querySelector('.pkt-input-check__input-label')
132
+ expect(label).toHaveClass('pkt-input-check__input-label--disabled')
133
+ })
134
+
135
+ test('renders tile variant correctly', async () => {
136
+ const { radiobutton } = await createRadioButtonTest({
137
+ hasTile: true,
138
+ })
139
+
140
+ const container = radiobutton.querySelector('.pkt-input-check__input')
141
+ expect(container).toHaveClass('pkt-input-check__input--tile')
142
+ })
143
+
144
+ test('renders disabled tile variant correctly', async () => {
145
+ const { radiobutton } = await createRadioButtonTest({
146
+ hasTile: true,
147
+ disabled: true,
148
+ })
149
+
150
+ const container = radiobutton.querySelector('.pkt-input-check__input')
151
+ expect(container).toHaveClass('pkt-input-check__input--tile-disabled')
152
+ })
153
+
154
+ test('renders helptext when provided', async () => {
155
+ const { radiobutton } = await createRadioButtonTest({
156
+ checkHelptext: 'This is helpful information',
157
+ })
158
+
159
+ const helptext = radiobutton.querySelector('.pkt-input-check__input-helptext')
160
+ expect(helptext).toBeInTheDocument()
161
+ expect(helptext?.textContent).toBe('This is helpful information')
162
+ })
163
+
164
+ test('handles error state correctly', async () => {
165
+ const { radiobutton } = await createRadioButtonTest({
166
+ hasError: true,
167
+ })
168
+
169
+ const input = radiobutton.querySelector('input')
170
+ expect(input).toHaveClass('pkt-input-check__input-checkbox--error')
171
+ })
172
+
173
+ test('renders tag text when provided', async () => {
174
+ const { radiobutton } = await createRadioButtonTest({
175
+ tagText: 'Tag',
176
+ })
177
+
178
+ const tag = radiobutton.querySelector('.pkt-tag--gray')
179
+ expect(tag).toBeInTheDocument()
180
+ expect(tag?.textContent).toBe('Tag')
181
+ })
182
+
183
+ test('renders optional tag when enabled', async () => {
184
+ const { radiobutton } = await createRadioButtonTest({
185
+ optionalTag: true,
186
+ })
187
+
188
+ const tag = radiobutton.querySelector('.pkt-tag--blue-light')
189
+ expect(tag).toBeInTheDocument()
190
+ expect(tag?.textContent).toBe('Valgfritt')
191
+ })
192
+
193
+ test('renders required tag when enabled', async () => {
194
+ const { radiobutton } = await createRadioButtonTest({
195
+ requiredTag: true,
196
+ })
197
+
198
+ const tag = radiobutton.querySelector('.pkt-tag--beige')
199
+ expect(tag).toBeInTheDocument()
200
+ expect(tag?.textContent).toBe('Må fylles ut')
201
+ })
202
+
203
+ test('manages focus events correctly', async () => {
204
+ const { radiobutton } = await createRadioButtonTest()
205
+
206
+ const focusSpy = jest.fn()
207
+ const blurSpy = jest.fn()
208
+ radiobutton.addEventListener('focus', focusSpy)
209
+ radiobutton.addEventListener('blur', blurSpy)
210
+
211
+ const input = radiobutton.querySelector('input') as HTMLInputElement
212
+
213
+ fireEvent.focus(input)
214
+ expect(focusSpy).toHaveBeenCalled()
215
+
216
+ fireEvent.blur(input)
217
+ expect(blurSpy).toHaveBeenCalled()
218
+ })
219
+
220
+ test('works with radio button groups', async () => {
221
+ const container = document.createElement('form')
222
+ document.body.appendChild(container)
223
+
224
+ const radio1Container = document.createElement('div')
225
+ radio1Container.innerHTML = '<pkt-radiobutton name="group" value="1"></pkt-radiobutton>'
226
+ container.appendChild(radio1Container)
227
+ await customElements.whenDefined('pkt-radiobutton')
228
+ const radio1 = radio1Container.querySelector(
229
+ 'pkt-radiobutton',
230
+ ) as CustomElementFor<'pkt-radiobutton'>
231
+ radio1.label = 'Option 1'
232
+ await radio1.updateComplete
233
+
234
+ const radio2Container = document.createElement('div')
235
+ radio2Container.innerHTML = '<pkt-radiobutton name="group" value="2"></pkt-radiobutton>'
236
+ container.appendChild(radio2Container)
237
+ const radio2 = radio2Container.querySelector(
238
+ 'pkt-radiobutton',
239
+ ) as CustomElementFor<'pkt-radiobutton'>
240
+ radio2.label = 'Option 2'
241
+ await radio2.updateComplete
242
+
243
+ const input1 = radio1.querySelector('input') as HTMLInputElement
244
+ const input2 = radio2.querySelector('input') as HTMLInputElement
245
+
246
+ expect(input1.checked).toBe(false)
247
+ expect(input2.checked).toBe(false)
248
+
249
+ fireEvent.click(input1)
250
+ await radio1.updateComplete
251
+
252
+ expect(input1.checked).toBe(true)
253
+ expect(radio1.touched).toBe(true)
254
+
255
+ fireEvent.click(input2)
256
+ await radio2.updateComplete
257
+
258
+ expect(input2.checked).toBe(true)
259
+ expect(radio2.touched).toBe(true)
260
+
261
+ document.body.removeChild(container)
262
+ })
263
+
264
+ test('meets accessibility standards', async () => {
265
+ const { radiobutton } = await createRadioButtonTest({
266
+ label: 'Accessible Radio Button',
267
+ })
268
+
269
+ const results = await axe(radiobutton)
270
+ expect(results).toHaveNoViolations()
271
+ })
272
+ })
@@ -0,0 +1,227 @@
1
+ import '@testing-library/jest-dom'
2
+ import { axe, toHaveNoViolations } from 'jest-axe'
3
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
4
+ import { CustomElementFor } from '../../tests/component-registry'
5
+ import { type IPktSelect, type TSelectOption } from './select'
6
+ import './select'
7
+
8
+ export interface SelectTestConfig extends Partial<IPktSelect>, BaseTestConfig {
9
+ id?: string
10
+ label?: string
11
+ disabled?: boolean
12
+ required?: boolean
13
+ hasError?: boolean
14
+ errorMessage?: string
15
+ helptext?: string
16
+ fullwidth?: boolean
17
+ ariaLabelledby?: string | null
18
+ ariaDescribedBy?: string | null
19
+ counter?: boolean
20
+ inline?: boolean
21
+ useWrapper?: boolean | string
22
+ }
23
+
24
+ export const createSelectTest = async (config: SelectTestConfig = {}) => {
25
+ const { options, ...htmlConfig } = config
26
+
27
+ const { container, element } = await createElementTest<
28
+ CustomElementFor<'pkt-select'>,
29
+ Omit<SelectTestConfig, 'options'>
30
+ >('pkt-select', htmlConfig)
31
+
32
+ if (options) {
33
+ element.options = options
34
+ await element.updateComplete
35
+ }
36
+
37
+ return {
38
+ container,
39
+ select: element,
40
+ }
41
+ }
42
+
43
+ expect.extend(toHaveNoViolations)
44
+
45
+ describe('pkt-select', () => {
46
+ afterEach(() => {
47
+ document.body.innerHTML = ''
48
+ })
49
+
50
+ test('renders with basic properties', async () => {
51
+ const { select } = await createSelectTest({
52
+ label: 'Test Select',
53
+ id: 'test-select',
54
+ })
55
+
56
+ expect(select).toBeInTheDocument()
57
+ expect(select.getAttribute('id')).toBe('test-select')
58
+ expect(select.label).toBe('Test Select')
59
+ })
60
+
61
+ test('displays correct label in input wrapper', async () => {
62
+ const { select } = await createSelectTest({
63
+ label: 'Choose Option',
64
+ })
65
+
66
+ const inputWrapper = select.querySelector('pkt-input-wrapper')
67
+ expect(inputWrapper).toBeInTheDocument()
68
+ expect(inputWrapper?.getAttribute('label')).toBe('Choose Option')
69
+ })
70
+
71
+ test('renders select element with correct attributes', async () => {
72
+ const { select } = await createSelectTest({
73
+ id: 'my-select',
74
+ disabled: true,
75
+ value: 'option2',
76
+ })
77
+
78
+ const selectElement = select.querySelector('select')
79
+ expect(selectElement).toBeInTheDocument()
80
+ expect(selectElement?.getAttribute('id')).toBe('my-select-input')
81
+ expect(selectElement?.hasAttribute('disabled')).toBe(true)
82
+ expect(selectElement?.getAttribute('value')).toBe('option2')
83
+ })
84
+
85
+ test('handles options array property', async () => {
86
+ const options: TSelectOption[] = [
87
+ { value: '1', label: 'Option 1' },
88
+ { value: '2', label: 'Option 2' },
89
+ { value: '3', label: 'Option 3', disabled: true },
90
+ ]
91
+
92
+ const { select } = await createSelectTest({ options })
93
+
94
+ const optionElements = select.querySelectorAll('option')
95
+ expect(optionElements).toHaveLength(3)
96
+
97
+ expect(optionElements[0].value).toBe('1')
98
+ expect(optionElements[0].textContent?.trim()).toBe('Option 1')
99
+
100
+ expect(optionElements[2].hasAttribute('disabled')).toBe(true)
101
+ })
102
+
103
+ test('updates value on selection', async () => {
104
+ const options: TSelectOption[] = [
105
+ { value: 'a', label: 'Option A' },
106
+ { value: 'b', label: 'Option B' },
107
+ ]
108
+
109
+ const { select } = await createSelectTest({ options })
110
+
111
+ const selectElement = select.querySelector('select') as HTMLSelectElement
112
+
113
+ expect(selectElement.options.length).toBe(2)
114
+ expect(selectElement.options[0].value).toBe('a')
115
+ expect(selectElement.options[1].value).toBe('b')
116
+
117
+ expect(select.value).toBe('a')
118
+
119
+ selectElement.selectedIndex = 1
120
+ selectElement.dispatchEvent(new Event('change', { bubbles: true }))
121
+ await select.updateComplete
122
+
123
+ expect(select.value).toBe('b')
124
+ expect(select.touched).toBe(true)
125
+ })
126
+
127
+ test('handles error state correctly', async () => {
128
+ const { select } = await createSelectTest({
129
+ hasError: true,
130
+ errorMessage: 'Selection required',
131
+ })
132
+
133
+ const inputWrapper = select.querySelector('pkt-input-wrapper')
134
+ expect(inputWrapper?.hasAttribute('hasError')).toBe(true)
135
+ expect(inputWrapper?.getAttribute('errorMessage')).toBe('Selection required')
136
+
137
+ const selectElement = select.querySelector('select')
138
+ expect(selectElement?.getAttribute('aria-invalid')).toBe('true')
139
+ })
140
+
141
+ test('renders helptext when provided', async () => {
142
+ const { select } = await createSelectTest({
143
+ helptext: 'Choose your preferred option',
144
+ })
145
+
146
+ const inputWrapper = select.querySelector('pkt-input-wrapper')
147
+ expect(inputWrapper?.getAttribute('helptext')).toBe('Choose your preferred option')
148
+ })
149
+
150
+ test('handles fullwidth styling', async () => {
151
+ const { select } = await createSelectTest({
152
+ fullwidth: true,
153
+ })
154
+
155
+ const selectElement = select.querySelector('select')
156
+ expect(selectElement?.className).toContain('pkt-input--fullwidth')
157
+ })
158
+
159
+ test('manages focus events correctly', async () => {
160
+ const { select } = await createSelectTest()
161
+
162
+ const focusSpy = jest.spyOn(select, 'onFocus')
163
+ const blurSpy = jest.spyOn(select, 'onBlur')
164
+
165
+ const selectElement = select.querySelector('select') as HTMLSelectElement
166
+
167
+ selectElement.dispatchEvent(new FocusEvent('focus'))
168
+ expect(focusSpy).toHaveBeenCalled()
169
+
170
+ selectElement.dispatchEvent(new FocusEvent('blur'))
171
+ expect(blurSpy).toHaveBeenCalled()
172
+ })
173
+
174
+ test('passes through accessibility attributes', async () => {
175
+ const { select } = await createSelectTest({
176
+ ariaLabelledby: 'external-label',
177
+ ariaDescribedBy: 'external-description',
178
+ })
179
+
180
+ const selectElement = select.querySelector('select')
181
+ expect(selectElement?.getAttribute('aria-labelledby')).toBe('external-label')
182
+
183
+ const inputWrapper = select.querySelector('pkt-input-wrapper')
184
+ expect(inputWrapper?.getAttribute('ariaDescribedBy')).toBe('external-description')
185
+ })
186
+
187
+ test('handles input wrapper properties', async () => {
188
+ const { select } = await createSelectTest({
189
+ counter: true,
190
+ inline: true,
191
+ })
192
+
193
+ const inputWrapper = select.querySelector('pkt-input-wrapper')
194
+ expect(inputWrapper?.hasAttribute('counter')).toBe(true)
195
+ expect(inputWrapper?.hasAttribute('inline')).toBe(true)
196
+ })
197
+
198
+ test('handles useWrapper false', async () => {
199
+ const { select } = await createSelectTest()
200
+
201
+ // Set useWrapper property directly on the element
202
+ select.useWrapper = false
203
+ await select.updateComplete
204
+
205
+ // When useWrapper is false, verify the behavior
206
+ expect(select.useWrapper).toBe(false)
207
+
208
+ // Confirm that there's a label with class pkt-sr-only in the markup
209
+ const srOnlyLabel = select.querySelector('label.pkt-sr-only')
210
+ expect(srOnlyLabel).toBeInTheDocument()
211
+ })
212
+
213
+ test('meets accessibility standards', async () => {
214
+ const options: TSelectOption[] = [
215
+ { value: '1', label: 'Option 1' },
216
+ { value: '2', label: 'Option 2' },
217
+ ]
218
+
219
+ const { select } = await createSelectTest({
220
+ label: 'Accessible Select',
221
+ options,
222
+ })
223
+
224
+ const results = await axe(select)
225
+ expect(results).toHaveNoViolations()
226
+ })
227
+ })
@@ -150,6 +150,16 @@ export class PktSelect extends PktInputElement implements IPktSelect {
150
150
 
151
151
  update(changedProperties: PropertyValues) {
152
152
  super.update(changedProperties)
153
+ if (changedProperties.has('options')) {
154
+ this._options = this.options
155
+ this.requestUpdate('_options')
156
+
157
+ // If no value is set and we have options, set to first option
158
+ if (!this.value && this._options.length > 0) {
159
+ this.value = this._options[0].value
160
+ this.selectedIndex = 0
161
+ }
162
+ }
153
163
  if (changedProperties.has('value') && this.value !== changedProperties.get('value')) {
154
164
  this.selectedIndex = this.touched
155
165
  ? this.returnNumberOrNull(this.inputRef.value?.selectedIndex)
@@ -230,18 +240,19 @@ export class PktSelect extends PktInputElement implements IPktSelect {
230
240
  }}
231
241
  ${ref(this.inputRef)}
232
242
  >
233
- ${this._options.length > 0 &&
234
- this._options.map(
235
- (option) =>
236
- html`<option
237
- value=${option.value}
238
- ?selected=${this.value == option.value || option.selected}
239
- ?disabled=${option.disabled}
240
- ?hidden=${option.hidden}
241
- >
242
- ${option.label}
243
- </option>`,
244
- )}
243
+ ${this._options.length > 0
244
+ ? this._options.map(
245
+ (option) =>
246
+ html`<option
247
+ value=${option.value}
248
+ ?selected=${this.value == option.value || option.selected}
249
+ ?disabled=${option.disabled}
250
+ ?hidden=${option.hidden}
251
+ >
252
+ ${option.label}
253
+ </option>`,
254
+ )
255
+ : ''}
245
256
  </select>
246
257
  <div class="pkt-contents" ${ref(this.helptextSlot)} name="helptext" slot="helptext"></div>
247
258
  </pkt-input-wrapper>