@justeattakeaway/pie-textarea 0.5.0 → 0.6.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.
@@ -34,7 +34,7 @@
34
34
  "type": {
35
35
  "text": "DefaultProps"
36
36
  },
37
- "default": "{\n disabled: false,\n size: 'medium',\n resize: 'auto',\n value: '',\n autoFocus: false,\n readonly: false,\n required: false,\n}",
37
+ "default": "{\n disabled: false,\n size: 'medium',\n resize: 'auto',\n label: '',\n value: '',\n autoFocus: false,\n readonly: false,\n required: false,\n}",
38
38
  "description": "Default values for optional properties that have default fallback values in the component."
39
39
  }
40
40
  ],
@@ -108,6 +108,21 @@
108
108
  "privacy": "public",
109
109
  "attribute": "resize"
110
110
  },
111
+ {
112
+ "kind": "field",
113
+ "name": "label",
114
+ "privacy": "public",
115
+ "attribute": "label"
116
+ },
117
+ {
118
+ "kind": "field",
119
+ "name": "maxLength",
120
+ "type": {
121
+ "text": "TextareaProps['maxLength']"
122
+ },
123
+ "privacy": "public",
124
+ "attribute": "maxLength"
125
+ },
111
126
  {
112
127
  "kind": "field",
113
128
  "name": "readonly",
@@ -203,6 +218,11 @@
203
218
  "name": "handleResize",
204
219
  "privacy": "private"
205
220
  },
221
+ {
222
+ "kind": "method",
223
+ "name": "restrictInputLength",
224
+ "privacy": "private"
225
+ },
206
226
  {
207
227
  "kind": "field",
208
228
  "name": "handleInput",
@@ -224,6 +244,25 @@
224
244
  "kind": "field",
225
245
  "name": "handleKeyDown",
226
246
  "privacy": "private"
247
+ },
248
+ {
249
+ "kind": "method",
250
+ "name": "renderLabel",
251
+ "parameters": [
252
+ {
253
+ "name": "label",
254
+ "type": {
255
+ "text": "string"
256
+ }
257
+ },
258
+ {
259
+ "name": "maxLength",
260
+ "optional": true,
261
+ "type": {
262
+ "text": "number"
263
+ }
264
+ }
265
+ ]
227
266
  }
228
267
  ],
229
268
  "events": [
@@ -259,6 +298,17 @@
259
298
  "name": "resize",
260
299
  "fieldName": "resize"
261
300
  },
301
+ {
302
+ "name": "label",
303
+ "fieldName": "label"
304
+ },
305
+ {
306
+ "name": "maxLength",
307
+ "type": {
308
+ "text": "TextareaProps['maxLength']"
309
+ },
310
+ "fieldName": "maxLength"
311
+ },
262
312
  {
263
313
  "name": "readonly",
264
314
  "fieldName": "readonly"
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { CSSResult } from 'lit';
3
3
  import type { FormControlInterface } from '@justeattakeaway/pie-webc-core';
4
4
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
5
5
  import type { LitElement } from 'lit';
6
+ import type { nothing } from 'lit';
6
7
  import type { PropertyValues } from 'lit';
7
8
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
8
9
  import type { TemplateResult } from 'lit-html';
@@ -10,7 +11,7 @@ import type { TemplateResult } from 'lit-html';
10
11
  /**
11
12
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
12
13
  */
13
- declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete'>>;
14
+ declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength'>>;
14
15
 
15
16
  /**
16
17
  * Default values for optional properties that have default fallback values in the component.
@@ -32,6 +33,8 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
32
33
  disabled: boolean;
33
34
  size: "small" | "medium" | "large";
34
35
  resize: "auto" | "manual";
36
+ label: string;
37
+ maxLength: TextareaProps['maxLength'];
35
38
  readonly: boolean;
36
39
  autoFocus: boolean;
37
40
  required: boolean;
@@ -58,6 +61,7 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
58
61
  formResetCallback(): void;
59
62
  protected firstUpdated(): void;
60
63
  private handleResize;
64
+ private restrictInputLength;
61
65
  protected updated(changedProperties: PropertyValues<this>): void;
62
66
  /**
63
67
  * Handles data processing in response to the input event. The native input event is left to bubble up.
@@ -67,6 +71,7 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
67
71
  private handleChange;
68
72
  private handleKeyDown;
69
73
  disconnectedCallback(): void;
74
+ renderLabel(label: string, maxLength?: number): TemplateResult<1> | typeof nothing;
70
75
  render(): TemplateResult<1>;
71
76
  static styles: CSSResult;
72
77
  }
@@ -120,6 +125,15 @@ export declare interface TextareaProps {
120
125
  * If true, the textarea is required to have a value before submitting the form. If there is no value, then the component validity state will be invalid.
121
126
  */
122
127
  required?: boolean;
128
+ /**
129
+ * The label text for the textarea field.
130
+ */
131
+ label?: string;
132
+ /**
133
+ * The maximum number of characters allowed in the textarea field.
134
+ * If the `label` property is not set, this property will have no effect.
135
+ */
136
+ maxLength?: number;
123
137
  }
124
138
 
125
139
  export { }
package/dist/index.js CHANGED
@@ -1,125 +1,127 @@
1
- import { LitElement as F, html as q, unsafeCSS as B } from "lit";
2
- import { property as p, query as N } from "lit/decorators.js";
3
- import { ifDefined as W } from "lit/directives/if-defined.js";
4
- import { live as V } from "lit/directives/live.js";
5
- import { FormControlMixin as A, RtlMixin as K, wrapNativeEvent as G, validPropertyValues as I, defineCustomElement as H } from "@justeattakeaway/pie-webc-core";
6
- var m = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
7
- function U(e) {
8
- return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
1
+ import { LitElement as w, html as I, nothing as B, unsafeCSS as N } from "lit";
2
+ import { property as h, query as V } from "lit/decorators.js";
3
+ import { live as A } from "lit/directives/live.js";
4
+ import { FormControlMixin as K, RtlMixin as G, wrapNativeEvent as H, validPropertyValues as F, defineCustomElement as U } from "@justeattakeaway/pie-webc-core";
5
+ import { ifDefined as O } from "lit/directives/if-defined.js";
6
+ import "@justeattakeaway/pie-form-label";
7
+ var z = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
8
+ function X(t) {
9
+ return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
9
10
  }
10
- var R = "Expected a function", w = 0 / 0, X = "[object Symbol]", J = /^\s+|\s+$/g, Q = /^[-+]0x[0-9a-f]+$/i, Y = /^0b[01]+$/i, Z = /^0o[0-7]+$/i, ee = parseInt, te = typeof m == "object" && m && m.Object === Object && m, ae = typeof self == "object" && self && self.Object === Object && self, re = te || ae || Function("return this")(), ie = Object.prototype, ne = ie.toString, oe = Math.max, se = Math.min, j = function() {
11
- return re.Date.now();
11
+ var R = "Expected a function", C = 0 / 0, J = "[object Symbol]", Q = /^\s+|\s+$/g, Y = /^[-+]0x[0-9a-f]+$/i, Z = /^0b[01]+$/i, ee = /^0o[0-7]+$/i, te = parseInt, ae = typeof z == "object" && z && z.Object === Object && z, re = typeof self == "object" && self && self.Object === Object && self, ie = ae || re || Function("return this")(), ne = Object.prototype, oe = ne.toString, se = Math.max, le = Math.min, L = function() {
12
+ return ie.Date.now();
12
13
  };
13
- function le(e, t, a) {
14
- var n, r, d, l, o, h, x = 0, T = !1, v = !1, z = !0;
15
- if (typeof e != "function")
14
+ function de(t, e, a) {
15
+ var r, i, u, l, s, p, g = 0, m = !1, v = !1, _ = !0;
16
+ if (typeof t != "function")
16
17
  throw new TypeError(R);
17
- t = C(t) || 0, y(a) && (T = !!a.leading, v = "maxWait" in a, d = v ? oe(C(a.maxWait) || 0, t) : d, z = "trailing" in a ? !!a.trailing : z);
18
- function k(i) {
19
- var f = n, g = r;
20
- return n = r = void 0, x = i, l = e.apply(g, f), l;
18
+ e = W(e) || 0, k(a) && (m = !!a.leading, v = "maxWait" in a, u = v ? se(W(a.maxWait) || 0, e) : u, _ = "trailing" in a ? !!a.trailing : _);
19
+ function $(n) {
20
+ var f = r, b = i;
21
+ return r = i = void 0, g = n, l = t.apply(b, f), l;
21
22
  }
22
- function D(i) {
23
- return x = i, o = setTimeout(b, t), T ? k(i) : l;
23
+ function D(n) {
24
+ return g = n, s = setTimeout(y, e), m ? $(n) : l;
24
25
  }
25
- function P(i) {
26
- var f = i - h, g = i - x, $ = t - f;
27
- return v ? se($, d - g) : $;
26
+ function P(n) {
27
+ var f = n - p, b = n - g, E = e - f;
28
+ return v ? le(E, u - b) : E;
28
29
  }
29
- function E(i) {
30
- var f = i - h, g = i - x;
31
- return h === void 0 || f >= t || f < 0 || v && g >= d;
30
+ function S(n) {
31
+ var f = n - p, b = n - g;
32
+ return p === void 0 || f >= e || f < 0 || v && b >= u;
32
33
  }
33
- function b() {
34
- var i = j();
35
- if (E(i))
36
- return S(i);
37
- o = setTimeout(b, P(i));
34
+ function y() {
35
+ var n = L();
36
+ if (S(n))
37
+ return T(n);
38
+ s = setTimeout(y, P(n));
38
39
  }
39
- function S(i) {
40
- return o = void 0, z && n ? k(i) : (n = r = void 0, l);
41
- }
42
- function L() {
43
- o !== void 0 && clearTimeout(o), x = 0, n = h = r = o = void 0;
40
+ function T(n) {
41
+ return s = void 0, _ && r ? $(n) : (r = i = void 0, l);
44
42
  }
45
43
  function M() {
46
- return o === void 0 ? l : S(j());
44
+ s !== void 0 && clearTimeout(s), g = 0, r = p = i = s = void 0;
45
+ }
46
+ function q() {
47
+ return s === void 0 ? l : T(L());
47
48
  }
48
- function _() {
49
- var i = j(), f = E(i);
50
- if (n = arguments, r = this, h = i, f) {
51
- if (o === void 0)
52
- return D(h);
49
+ function j() {
50
+ var n = L(), f = S(n);
51
+ if (r = arguments, i = this, p = n, f) {
52
+ if (s === void 0)
53
+ return D(p);
53
54
  if (v)
54
- return o = setTimeout(b, t), k(h);
55
+ return s = setTimeout(y, e), $(p);
55
56
  }
56
- return o === void 0 && (o = setTimeout(b, t)), l;
57
+ return s === void 0 && (s = setTimeout(y, e)), l;
57
58
  }
58
- return _.cancel = L, _.flush = M, _;
59
+ return j.cancel = M, j.flush = q, j;
59
60
  }
60
- function de(e, t, a) {
61
- var n = !0, r = !0;
62
- if (typeof e != "function")
61
+ function ce(t, e, a) {
62
+ var r = !0, i = !0;
63
+ if (typeof t != "function")
63
64
  throw new TypeError(R);
64
- return y(a) && (n = "leading" in a ? !!a.leading : n, r = "trailing" in a ? !!a.trailing : r), le(e, t, {
65
- leading: n,
66
- maxWait: t,
67
- trailing: r
65
+ return k(a) && (r = "leading" in a ? !!a.leading : r, i = "trailing" in a ? !!a.trailing : i), de(t, e, {
66
+ leading: r,
67
+ maxWait: e,
68
+ trailing: i
68
69
  });
69
70
  }
70
- function y(e) {
71
- var t = typeof e;
72
- return !!e && (t == "object" || t == "function");
71
+ function k(t) {
72
+ var e = typeof t;
73
+ return !!t && (e == "object" || e == "function");
73
74
  }
74
- function ce(e) {
75
- return !!e && typeof e == "object";
75
+ function he(t) {
76
+ return !!t && typeof t == "object";
76
77
  }
77
- function ue(e) {
78
- return typeof e == "symbol" || ce(e) && ne.call(e) == X;
78
+ function ue(t) {
79
+ return typeof t == "symbol" || he(t) && oe.call(t) == J;
79
80
  }
80
- function C(e) {
81
- if (typeof e == "number")
82
- return e;
83
- if (ue(e))
84
- return w;
85
- if (y(e)) {
86
- var t = typeof e.valueOf == "function" ? e.valueOf() : e;
87
- e = y(t) ? t + "" : t;
88
- }
89
- if (typeof e != "string")
90
- return e === 0 ? e : +e;
91
- e = e.replace(J, "");
92
- var a = Y.test(e);
93
- return a || Z.test(e) ? ee(e.slice(2), a ? 2 : 8) : Q.test(e) ? w : +e;
81
+ function W(t) {
82
+ if (typeof t == "number")
83
+ return t;
84
+ if (ue(t))
85
+ return C;
86
+ if (k(t)) {
87
+ var e = typeof t.valueOf == "function" ? t.valueOf() : t;
88
+ t = k(e) ? e + "" : e;
89
+ }
90
+ if (typeof t != "string")
91
+ return t === 0 ? t : +t;
92
+ t = t.replace(Q, "");
93
+ var a = Z.test(t);
94
+ return a || ee.test(t) ? te(t.slice(2), a ? 2 : 8) : Y.test(t) ? C : +t;
94
95
  }
95
- var he = de;
96
- const pe = /* @__PURE__ */ U(he), fe = `*,*:after,*:before{box-sizing:inherit}.c-textareaWrapper{--textarea-line-height: calc(var(--dt-font-body-l-line-height) * 1px);--textarea-border-thickness: 1px;--textarea-resize: none;--textarea-padding-inline: var(--dt-spacing-d);--textarea-padding-block: var(--dt-spacing-c);--textarea-background-color: var(--dt-color-container-default);--textarea-border-color: var(--dt-color-interactive-form);--textarea-content-color: var(--dt-color-content-default);--textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));line-height:0}.c-textareaWrapper textarea{font-size:calc(var(--dt-font-body-l-size) * 1px);line-height:var(--textarea-line-height);font-family:var(--dt-font-body-l-family);resize:var(--textarea-resize);border:var(--textarea-border-thickness) solid var(--textarea-border-color);background-color:var(--textarea-background-color);color:var(--textarea-content-color);border-radius:var(--dt-radius-rounded-c);block-size:var(--textarea-height);max-block-size:var(--textarea-max-height);min-block-size:var(--textarea-min-height);padding-block-start:var(--textarea-padding-block);padding-block-end:var(--textarea-padding-block);padding-inline-start:var(--textarea-padding-inline);padding-inline-end:var(--textarea-padding-inline)}.c-textareaWrapper textarea[disabled]{--textarea-background-color: var(--dt-color-disabled-01);--textarea-border-color: var(--dt-color-disabled-01);--textarea-content-color: var(--dt-color-content-disabled)}@media (hover: hover){.c-textareaWrapper textarea:hover:not([disabled]){--textarea-background-color: hsl(var(--dt-color-container-default-h), var(--dt-color-container-default-s), calc(var(--dt-color-container-default-l) + calc(-1 * var(--dt-color-hover-01))))}}.c-textareaWrapper textarea:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-textareaWrapper[data-pie-size=large]{--textarea-padding-block: var(--dt-spacing-d)}.c-textareaWrapper[data-pie-size=small]{--textarea-padding-block: var(--dt-spacing-b)}.c-textareaWrapper[data-pie-resize=manual]{--textarea-resize: vertical;--textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2))}@media (pointer: coarse){.c-textareaWrapper[data-pie-resize=manual]{--textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-resize: none}}.c-textareaWrapper[data-pie-resize=auto]{--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-min-height: var(--textarea-height)}
97
- `, xe = ["small", "medium", "large"], ve = ["auto", "manual"], c = {
96
+ var pe = ce;
97
+ const fe = /* @__PURE__ */ X(pe), ge = `*,*:after,*:before{box-sizing:inherit}.c-textareaWrapper{--textarea-line-height: calc(var(--dt-font-body-l-line-height) * 1px);--textarea-border-thickness: 1px;--textarea-resize: none;--textarea-padding-inline: var(--dt-spacing-d);--textarea-padding-block: var(--dt-spacing-c);--textarea-background-color: var(--dt-color-container-default);--textarea-border-color: var(--dt-color-interactive-form);--textarea-content-color: var(--dt-color-content-default);--textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));line-height:0}.c-textareaWrapper textarea{font-size:calc(var(--dt-font-body-l-size) * 1px);line-height:var(--textarea-line-height);font-family:var(--dt-font-body-l-family);resize:var(--textarea-resize);border:var(--textarea-border-thickness) solid var(--textarea-border-color);background-color:var(--textarea-background-color);color:var(--textarea-content-color);border-radius:var(--dt-radius-rounded-c);block-size:var(--textarea-height);max-block-size:var(--textarea-max-height);min-block-size:var(--textarea-min-height);padding-block-start:var(--textarea-padding-block);padding-block-end:var(--textarea-padding-block);padding-inline-start:var(--textarea-padding-inline);padding-inline-end:var(--textarea-padding-inline)}.c-textareaWrapper textarea[disabled]{--textarea-background-color: var(--dt-color-disabled-01);--textarea-border-color: var(--dt-color-disabled-01);--textarea-content-color: var(--dt-color-content-disabled)}@media (hover: hover){.c-textareaWrapper textarea:hover:not([disabled]){--textarea-background-color: hsl(var(--dt-color-container-default-h), var(--dt-color-container-default-s), calc(var(--dt-color-container-default-l) + calc(-1 * var(--dt-color-hover-01))))}}.c-textareaWrapper textarea:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-textareaWrapper[data-pie-size=large]{--textarea-padding-block: var(--dt-spacing-d)}.c-textareaWrapper[data-pie-size=small]{--textarea-padding-block: var(--dt-spacing-b)}.c-textareaWrapper[data-pie-resize=manual]{--textarea-resize: vertical;--textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2))}@media (pointer: coarse){.c-textareaWrapper[data-pie-resize=manual]{--textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-resize: none}}.c-textareaWrapper[data-pie-resize=auto]{--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-min-height: var(--textarea-height)}
98
+ `, ve = ["small", "medium", "large"], xe = ["auto", "manual"], c = {
98
99
  disabled: !1,
99
100
  size: "medium",
100
101
  resize: "auto",
102
+ label: "",
101
103
  value: "",
102
104
  autoFocus: !1,
103
105
  readonly: !1,
104
106
  required: !1
105
107
  };
106
- var ge = Object.defineProperty, be = Object.getOwnPropertyDescriptor, u = (e, t, a, n) => {
107
- for (var r = n > 1 ? void 0 : n ? be(t, a) : t, d = e.length - 1, l; d >= 0; d--)
108
- (l = e[d]) && (r = (n ? l(t, a, r) : l(r)) || r);
109
- return n && r && ge(t, a, r), r;
108
+ var be = Object.defineProperty, me = Object.getOwnPropertyDescriptor, d = (t, e, a, r) => {
109
+ for (var i = r > 1 ? void 0 : r ? me(e, a) : e, u = t.length - 1, l; u >= 0; u--)
110
+ (l = t[u]) && (i = (r ? l(e, a, i) : l(i)) || i);
111
+ return r && i && be(e, a, i), i;
110
112
  };
111
- const O = "pie-textarea";
112
- class s extends A(K(F)) {
113
+ const x = "pie-textarea";
114
+ class o extends K(G(w)) {
113
115
  constructor() {
114
- super(...arguments), this.value = c.value, this.disabled = c.disabled, this.size = c.size, this.resize = c.resize, this.readonly = c.readonly, this.autoFocus = c.autoFocus, this.required = c.required, this._throttledResize = pe(() => {
116
+ super(...arguments), this.value = c.value, this.disabled = c.disabled, this.size = c.size, this.resize = c.resize, this.label = c.label, this.readonly = c.readonly, this.autoFocus = c.autoFocus, this.required = c.required, this._throttledResize = fe(() => {
115
117
  this.resize === "auto" && (this._textarea.style.height = "auto", this._textarea.style.height = `${this._textarea.scrollHeight + 2}px`);
116
- }, 100), this.handleInput = (t) => {
117
- this.value = t.target.value, this._internals.setFormValue(this.value), this.handleResize();
118
- }, this.handleChange = (t) => {
119
- const a = G(t);
118
+ }, 100), this.handleInput = (e) => {
119
+ this.value = e.target.value, this.restrictInputLength(), this._internals.setFormValue(this.value), this.handleResize();
120
+ }, this.handleChange = (e) => {
121
+ const a = H(e);
120
122
  this.dispatchEvent(a);
121
- }, this.handleKeyDown = (t) => {
122
- t.key === "Enter" && t.stopPropagation();
123
+ }, this.handleKeyDown = (e) => {
124
+ e.key === "Enter" && e.stopPropagation();
123
125
  };
124
126
  }
125
127
  /**
@@ -135,8 +137,8 @@ class s extends A(K(F)) {
135
137
  * or because the disabled state changed on a <fieldset> that's an ancestor of this element.
136
138
  * @param disabled - The latest disabled state of the input.
137
139
  */
138
- formDisabledCallback(t) {
139
- this.disabled = t;
140
+ formDisabledCallback(e) {
141
+ this.disabled = e;
140
142
  }
141
143
  /**
142
144
  * Called when the form that owns this component is reset.
@@ -146,88 +148,108 @@ class s extends A(K(F)) {
146
148
  this.value = c.value, this._internals.setFormValue(this.value);
147
149
  }
148
150
  firstUpdated() {
149
- this._internals.setFormValue(this.value), this._textarea.addEventListener("keydown", this.handleKeyDown);
151
+ this.restrictInputLength(), this._internals.setFormValue(this.value), this._textarea.addEventListener("keydown", this.handleKeyDown);
150
152
  }
151
153
  handleResize() {
152
154
  this._throttledResize();
153
155
  }
154
- updated(t) {
155
- this.resize === "auto" && (t.has("resize") || t.has("size")) && this.handleResize(), t.has("value") && this._internals.setFormValue(this.value);
156
+ restrictInputLength() {
157
+ if (this.label.length && this.maxLength && this.value.length > this.maxLength) {
158
+ const e = this.value.slice(0, this.maxLength);
159
+ this._textarea.value = e, this.value = e;
160
+ }
161
+ }
162
+ updated(e) {
163
+ e.has("value") && (this.restrictInputLength(), this._internals.setFormValue(this.value)), this.resize === "auto" && (e.has("resize") || e.has("size")) && this.handleResize();
156
164
  }
157
165
  disconnectedCallback() {
158
166
  this._textarea.removeEventListener("keydown", this.handleKeyDown);
159
167
  }
168
+ renderLabel(e, a) {
169
+ const r = a ? `${this.value.length}/${a}` : void 0;
170
+ return e != null && e.length ? I`<pie-form-label for="${x}" trailing=${O(r)}>${e}</pie-form-label>` : B;
171
+ }
160
172
  render() {
161
173
  const {
162
- disabled: t,
174
+ disabled: e,
163
175
  resize: a,
164
- size: n,
165
- autocomplete: r,
166
- autoFocus: d,
176
+ size: r,
177
+ autocomplete: i,
178
+ autoFocus: u,
167
179
  name: l,
168
- readonly: o,
169
- value: h,
170
- required: x
180
+ readonly: s,
181
+ value: p,
182
+ required: g,
183
+ label: m,
184
+ maxLength: v
171
185
  } = this;
172
- return q`
186
+ return I`
173
187
  <div
174
188
  class="c-textareaWrapper"
175
189
  data-test-id="pie-textarea-wrapper"
176
- data-pie-size="${n}"
190
+ data-pie-size="${r}"
177
191
  data-pie-resize="${a}">
192
+ ${this.renderLabel(m, v)}
178
193
  <textarea
179
- name=${W(l)}
180
- autocomplete=${W(r)}
181
- .value=${V(h)}
182
- ?autofocus=${d}
183
- ?readonly=${o}
184
- ?required=${x}
185
- ?disabled=${t}
194
+ id="${x}"
195
+ data-test-id="${x}"
196
+ name=${O(l)}
197
+ autocomplete=${O(i)}
198
+ .value=${A(p)}
199
+ ?autofocus=${u}
200
+ ?readonly=${s}
201
+ ?required=${g}
202
+ ?disabled=${e}
186
203
  @input=${this.handleInput}
187
204
  @change=${this.handleChange}
188
- data-test-id="pie-textarea"
189
205
  ></textarea>
190
206
  </div>`;
191
207
  }
192
208
  }
193
- s.shadowRootOptions = { ...F.shadowRootOptions, delegatesFocus: !0 };
194
- s.styles = B(fe);
195
- u([
196
- p({ type: String })
197
- ], s.prototype, "value", 2);
198
- u([
199
- p({ type: Boolean, reflect: !0 })
200
- ], s.prototype, "disabled", 2);
201
- u([
202
- p({ type: String }),
203
- I(O, xe, c.size)
204
- ], s.prototype, "size", 2);
205
- u([
206
- p({ type: String }),
207
- I(O, ve, c.resize)
208
- ], s.prototype, "resize", 2);
209
- u([
210
- p({ type: Boolean })
211
- ], s.prototype, "readonly", 2);
212
- u([
213
- p({ type: Boolean })
214
- ], s.prototype, "autoFocus", 2);
215
- u([
216
- p({ type: Boolean })
217
- ], s.prototype, "required", 2);
218
- u([
219
- p({ type: String })
220
- ], s.prototype, "name", 2);
221
- u([
222
- p({ type: String })
223
- ], s.prototype, "autocomplete", 2);
224
- u([
225
- N("textarea")
226
- ], s.prototype, "_textarea", 2);
227
- H(O, s);
209
+ o.shadowRootOptions = { ...w.shadowRootOptions, delegatesFocus: !0 };
210
+ o.styles = N(ge);
211
+ d([
212
+ h({ type: String })
213
+ ], o.prototype, "value", 2);
214
+ d([
215
+ h({ type: Boolean, reflect: !0 })
216
+ ], o.prototype, "disabled", 2);
217
+ d([
218
+ h({ type: String }),
219
+ F(x, ve, c.size)
220
+ ], o.prototype, "size", 2);
221
+ d([
222
+ h({ type: String }),
223
+ F(x, xe, c.resize)
224
+ ], o.prototype, "resize", 2);
225
+ d([
226
+ h({ type: String })
227
+ ], o.prototype, "label", 2);
228
+ d([
229
+ h({ type: Number })
230
+ ], o.prototype, "maxLength", 2);
231
+ d([
232
+ h({ type: Boolean })
233
+ ], o.prototype, "readonly", 2);
234
+ d([
235
+ h({ type: Boolean })
236
+ ], o.prototype, "autoFocus", 2);
237
+ d([
238
+ h({ type: Boolean })
239
+ ], o.prototype, "required", 2);
240
+ d([
241
+ h({ type: String })
242
+ ], o.prototype, "name", 2);
243
+ d([
244
+ h({ type: String })
245
+ ], o.prototype, "autocomplete", 2);
246
+ d([
247
+ V("textarea")
248
+ ], o.prototype, "_textarea", 2);
249
+ U(x, o);
228
250
  export {
229
- s as PieTextarea,
251
+ o as PieTextarea,
230
252
  c as defaultProps,
231
- ve as resizeModes,
232
- xe as sizes
253
+ xe as resizeModes,
254
+ ve as sizes
233
255
  };
package/dist/react.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { CSSResult } from 'lit';
3
3
  import type { FormControlInterface } from '@justeattakeaway/pie-webc-core';
4
4
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
5
5
  import type { LitElement } from 'lit';
6
+ import type { nothing } from 'lit';
6
7
  import type { PropertyValues } from 'lit';
7
8
  import * as React_2 from 'react';
8
9
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
@@ -11,7 +12,7 @@ import type { TemplateResult } from 'lit-html';
11
12
  /**
12
13
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
13
14
  */
14
- declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete'>>;
15
+ declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength'>>;
15
16
 
16
17
  /**
17
18
  * Default values for optional properties that have default fallback values in the component.
@@ -35,6 +36,8 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
35
36
  disabled: boolean;
36
37
  size: "small" | "medium" | "large";
37
38
  resize: "auto" | "manual";
39
+ label: string;
40
+ maxLength: TextareaProps['maxLength'];
38
41
  readonly: boolean;
39
42
  autoFocus: boolean;
40
43
  required: boolean;
@@ -61,6 +64,7 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
61
64
  formResetCallback(): void;
62
65
  protected firstUpdated(): void;
63
66
  private handleResize;
67
+ private restrictInputLength;
64
68
  protected updated(changedProperties: PropertyValues<this>): void;
65
69
  /**
66
70
  * Handles data processing in response to the input event. The native input event is left to bubble up.
@@ -70,6 +74,7 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
70
74
  private handleChange;
71
75
  private handleKeyDown;
72
76
  disconnectedCallback(): void;
77
+ renderLabel(label: string, maxLength?: number): TemplateResult<1> | typeof nothing;
73
78
  render(): TemplateResult<1>;
74
79
  static styles: CSSResult;
75
80
  }
@@ -130,6 +135,15 @@ export declare interface TextareaProps {
130
135
  * If true, the textarea is required to have a value before submitting the form. If there is no value, then the component validity state will be invalid.
131
136
  */
132
137
  required?: boolean;
138
+ /**
139
+ * The label text for the textarea field.
140
+ */
141
+ label?: string;
142
+ /**
143
+ * The maximum number of characters allowed in the textarea field.
144
+ * If the `label` property is not set, this property will have no effect.
145
+ */
146
+ maxLength?: number;
133
147
  }
134
148
 
135
149
  export { }
package/dist/react.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import * as e from "react";
2
2
  import { createComponent as t } from "@lit/react";
3
3
  import { PieTextarea as a } from "./index.js";
4
- import { defaultProps as T, resizeModes as l, sizes as d } from "./index.js";
4
+ import { defaultProps as l, resizeModes as d, sizes as g } from "./index.js";
5
5
  import "lit";
6
6
  import "lit/decorators.js";
7
- import "lit/directives/if-defined.js";
8
7
  import "lit/directives/live.js";
9
8
  import "@justeattakeaway/pie-webc-core";
9
+ import "lit/directives/if-defined.js";
10
+ import "@justeattakeaway/pie-form-label";
10
11
  const r = t({
11
12
  displayName: "PieTextarea",
12
13
  elementClass: a,
@@ -18,10 +19,10 @@ const r = t({
18
19
  onChange: "change"
19
20
  // when the textarea value is changed.
20
21
  }
21
- }), c = r;
22
+ }), P = r;
22
23
  export {
23
- c as PieTextarea,
24
- T as defaultProps,
25
- l as resizeModes,
26
- d as sizes
24
+ P as PieTextarea,
25
+ l as defaultProps,
26
+ d as resizeModes,
27
+ g as sizes
27
28
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-textarea",
3
3
  "description": "PIE Design System Textarea built using Web Components",
4
- "version": "0.5.0",
4
+ "version": "0.6.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -41,6 +41,7 @@
41
41
  "cem-plugin-module-file-extensions": "0.0.5"
42
42
  },
43
43
  "dependencies": {
44
+ "@justeattakeaway/pie-form-label": "0.14.0",
44
45
  "@justeattakeaway/pie-webc-core": "0.24.0",
45
46
  "lodash.throttle": "4.1.1"
46
47
  },
package/src/defs.ts CHANGED
@@ -54,12 +54,23 @@ export interface TextareaProps {
54
54
  * If true, the textarea is required to have a value before submitting the form. If there is no value, then the component validity state will be invalid.
55
55
  */
56
56
  required?: boolean;
57
+
58
+ /**
59
+ * The label text for the textarea field.
60
+ */
61
+ label?: string;
62
+
63
+ /**
64
+ * The maximum number of characters allowed in the textarea field.
65
+ * If the `label` property is not set, this property will have no effect.
66
+ */
67
+ maxLength?: number;
57
68
  }
58
69
 
59
70
  /**
60
71
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
61
72
  */
62
- type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name'| 'autocomplete'>>;
73
+ type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength'>>;
63
74
 
64
75
  /**
65
76
  * Default values for optional properties that have default fallback values in the component.
@@ -68,6 +79,7 @@ export const defaultProps: DefaultProps = {
68
79
  disabled: false,
69
80
  size: 'medium',
70
81
  resize: 'auto',
82
+ label: '',
71
83
  value: '',
72
84
  autoFocus: false,
73
85
  readonly: false,
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
- LitElement, html, unsafeCSS, PropertyValues,
2
+ LitElement, html, unsafeCSS, PropertyValues, nothing,
3
3
  } from 'lit';
4
+
4
5
  import { property, query } from 'lit/decorators.js';
5
- import { ifDefined } from 'lit/directives/if-defined.js';
6
6
  import { live } from 'lit/directives/live.js';
7
7
  import throttle from 'lodash.throttle';
8
8
 
@@ -10,11 +10,14 @@ import {
10
10
  validPropertyValues, RtlMixin, defineCustomElement, FormControlMixin, wrapNativeEvent,
11
11
  } from '@justeattakeaway/pie-webc-core';
12
12
 
13
+ import { ifDefined } from 'lit/directives/if-defined.js';
13
14
  import styles from './textarea.scss?inline';
14
15
  import {
15
16
  TextareaProps, defaultProps, sizes, resizeModes,
16
17
  } from './defs';
17
18
 
19
+ import '@justeattakeaway/pie-form-label';
20
+
18
21
  // Valid values available to consumers
19
22
  export * from './defs';
20
23
 
@@ -42,6 +45,12 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
42
45
  @validPropertyValues(componentSelector, resizeModes, defaultProps.resize)
43
46
  public resize = defaultProps.resize;
44
47
 
48
+ @property({ type: String })
49
+ public label = defaultProps.label;
50
+
51
+ @property({ type: Number })
52
+ public maxLength: TextareaProps['maxLength'];
53
+
45
54
  @property({ type: Boolean })
46
55
  public readonly = defaultProps.readonly;
47
56
 
@@ -96,6 +105,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
96
105
  }
97
106
 
98
107
  protected firstUpdated (): void {
108
+ this.restrictInputLength();
99
109
  this._internals.setFormValue(this.value);
100
110
 
101
111
  this._textarea.addEventListener('keydown', this.handleKeyDown);
@@ -105,14 +115,25 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
105
115
  this._throttledResize();
106
116
  }
107
117
 
108
- protected updated (changedProperties: PropertyValues<this>) {
109
- if (this.resize === 'auto' && (changedProperties.has('resize') || changedProperties.has('size'))) {
110
- this.handleResize();
118
+ private restrictInputLength () {
119
+ if (this.label.length && this.maxLength && this.value.length > this.maxLength) {
120
+ const trimmedValue = this.value.slice(0, this.maxLength);
121
+ // Ensures that the internal text area is correctly trimmed and synced with our value.
122
+ // The live() directive does not solve this for us.
123
+ this._textarea.value = trimmedValue;
124
+ this.value = trimmedValue;
111
125
  }
126
+ }
112
127
 
128
+ protected updated (changedProperties: PropertyValues<this>) {
113
129
  if (changedProperties.has('value')) {
130
+ this.restrictInputLength();
114
131
  this._internals.setFormValue(this.value);
115
132
  }
133
+
134
+ if (this.resize === 'auto' && (changedProperties.has('resize') || changedProperties.has('size'))) {
135
+ this.handleResize();
136
+ }
116
137
  }
117
138
 
118
139
  /**
@@ -121,6 +142,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
121
142
  */
122
143
  private handleInput = (event: InputEvent) => {
123
144
  this.value = (event.target as HTMLTextAreaElement).value;
145
+ this.restrictInputLength();
124
146
  this._internals.setFormValue(this.value);
125
147
 
126
148
  this.handleResize();
@@ -146,6 +168,14 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
146
168
  this._textarea.removeEventListener('keydown', this.handleKeyDown);
147
169
  }
148
170
 
171
+ renderLabel (label: string, maxLength?: number) {
172
+ const characterCount = maxLength ? `${this.value.length}/${maxLength}` : undefined;
173
+
174
+ return label?.length
175
+ ? html`<pie-form-label for="${componentSelector}" trailing=${ifDefined(characterCount)}>${label}</pie-form-label>`
176
+ : nothing;
177
+ }
178
+
149
179
  render () {
150
180
  const {
151
181
  disabled,
@@ -157,6 +187,8 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
157
187
  readonly,
158
188
  value,
159
189
  required,
190
+ label,
191
+ maxLength,
160
192
  } = this;
161
193
 
162
194
  return html`
@@ -165,7 +197,10 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
165
197
  data-test-id="pie-textarea-wrapper"
166
198
  data-pie-size="${size}"
167
199
  data-pie-resize="${resize}">
200
+ ${this.renderLabel(label, maxLength)}
168
201
  <textarea
202
+ id="${componentSelector}"
203
+ data-test-id="${componentSelector}"
169
204
  name=${ifDefined(name)}
170
205
  autocomplete=${ifDefined(autocomplete)}
171
206
  .value=${live(value)}
@@ -175,7 +210,6 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
175
210
  ?disabled=${disabled}
176
211
  @input=${this.handleInput}
177
212
  @change=${this.handleChange}
178
- data-test-id="pie-textarea"
179
213
  ></textarea>
180
214
  </div>`;
181
215
  }