@justeattakeaway/pie-textarea 0.6.1 → 0.8.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.
@@ -28,13 +28,21 @@
28
28
  },
29
29
  "default": "['auto', 'manual']"
30
30
  },
31
+ {
32
+ "kind": "variable",
33
+ "name": "statusTypes",
34
+ "type": {
35
+ "text": "['default', 'success', 'error']"
36
+ },
37
+ "default": "['default', 'success', 'error']"
38
+ },
31
39
  {
32
40
  "kind": "variable",
33
41
  "name": "defaultProps",
34
42
  "type": {
35
43
  "text": "DefaultProps"
36
44
  },
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}",
45
+ "default": "{\n disabled: false,\n size: 'medium',\n resize: 'auto',\n label: '',\n value: '',\n status: 'default',\n autoFocus: false,\n readonly: false,\n required: false,\n}",
38
46
  "description": "Default values for optional properties that have default fallback values in the component."
39
47
  }
40
48
  ],
@@ -55,6 +63,14 @@
55
63
  "module": "src/defs.js"
56
64
  }
57
65
  },
66
+ {
67
+ "kind": "js",
68
+ "name": "statusTypes",
69
+ "declaration": {
70
+ "name": "statusTypes",
71
+ "module": "src/defs.js"
72
+ }
73
+ },
58
74
  {
59
75
  "kind": "js",
60
76
  "name": "defaultProps",
@@ -89,6 +105,15 @@
89
105
  "privacy": "public",
90
106
  "attribute": "value"
91
107
  },
108
+ {
109
+ "kind": "field",
110
+ "name": "defaultValue",
111
+ "type": {
112
+ "text": "TextareaProps['defaultValue']"
113
+ },
114
+ "privacy": "public",
115
+ "attribute": "defaultValue"
116
+ },
92
117
  {
93
118
  "kind": "field",
94
119
  "name": "disabled",
@@ -141,11 +166,26 @@
141
166
  "privacy": "public",
142
167
  "attribute": "required"
143
168
  },
169
+ {
170
+ "kind": "field",
171
+ "name": "status",
172
+ "privacy": "public",
173
+ "attribute": "status"
174
+ },
175
+ {
176
+ "kind": "field",
177
+ "name": "assistiveText",
178
+ "type": {
179
+ "text": "TextareaProps['assistiveText']"
180
+ },
181
+ "privacy": "public",
182
+ "attribute": "assistiveText"
183
+ },
144
184
  {
145
185
  "kind": "field",
146
186
  "name": "name",
147
187
  "type": {
148
- "text": "TextareaProps['name'] | undefined"
188
+ "text": "TextareaProps['name']"
149
189
  },
150
190
  "privacy": "public",
151
191
  "attribute": "name"
@@ -154,7 +194,7 @@
154
194
  "kind": "field",
155
195
  "name": "autocomplete",
156
196
  "type": {
157
- "text": "TextareaProps['autocomplete'] | undefined"
197
+ "text": "TextareaProps['autocomplete']"
158
198
  },
159
199
  "privacy": "public",
160
200
  "attribute": "autocomplete"
@@ -167,6 +207,14 @@
167
207
  },
168
208
  "privacy": "private"
169
209
  },
210
+ {
211
+ "kind": "field",
212
+ "name": "focusTarget",
213
+ "type": {
214
+ "text": "HTMLElement"
215
+ },
216
+ "privacy": "public"
217
+ },
170
218
  {
171
219
  "kind": "field",
172
220
  "name": "_throttledResize",
@@ -263,6 +311,10 @@
263
311
  }
264
312
  }
265
313
  ]
314
+ },
315
+ {
316
+ "kind": "method",
317
+ "name": "renderAssistiveText"
266
318
  }
267
319
  ],
268
320
  "events": [
@@ -286,6 +338,13 @@
286
338
  "name": "value",
287
339
  "fieldName": "value"
288
340
  },
341
+ {
342
+ "name": "defaultValue",
343
+ "type": {
344
+ "text": "TextareaProps['defaultValue']"
345
+ },
346
+ "fieldName": "defaultValue"
347
+ },
289
348
  {
290
349
  "name": "disabled",
291
350
  "fieldName": "disabled"
@@ -321,17 +380,28 @@
321
380
  "name": "required",
322
381
  "fieldName": "required"
323
382
  },
383
+ {
384
+ "name": "status",
385
+ "fieldName": "status"
386
+ },
387
+ {
388
+ "name": "assistiveText",
389
+ "type": {
390
+ "text": "TextareaProps['assistiveText']"
391
+ },
392
+ "fieldName": "assistiveText"
393
+ },
324
394
  {
325
395
  "name": "name",
326
396
  "type": {
327
- "text": "TextareaProps['name'] | undefined"
397
+ "text": "TextareaProps['name']"
328
398
  },
329
399
  "fieldName": "name"
330
400
  },
331
401
  {
332
402
  "name": "autocomplete",
333
403
  "type": {
334
- "text": "TextareaProps['autocomplete'] | undefined"
404
+ "text": "TextareaProps['autocomplete']"
335
405
  },
336
406
  "fieldName": "autocomplete"
337
407
  }
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ 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
6
  import type { nothing } from 'lit';
7
+ import type { PIEInputElement } from '@justeattakeaway/pie-webc-core';
7
8
  import type { PropertyValues } from 'lit';
8
9
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
9
10
  import type { TemplateResult } from 'lit-html';
@@ -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' | 'maxLength'>>;
15
+ declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'assistiveText' | 'defaultValue'>>;
15
16
 
16
17
  /**
17
18
  * Default values for optional properties that have default fallback values in the component.
@@ -23,13 +24,14 @@ export declare const defaultProps: DefaultProps;
23
24
  * @event {InputEvent} input - when the textarea value is changed.
24
25
  * @event {CustomEvent} change - when the textarea value is changed.
25
26
  */
26
- export declare class PieTextarea extends PieTextarea_base implements TextareaProps {
27
+ export declare class PieTextarea extends PieTextarea_base implements TextareaProps, PIEInputElement {
27
28
  static shadowRootOptions: {
28
29
  delegatesFocus: boolean;
29
30
  mode: ShadowRootMode;
30
31
  slotAssignment?: SlotAssignmentMode | undefined;
31
32
  };
32
33
  value: string;
34
+ defaultValue: TextareaProps['defaultValue'];
33
35
  disabled: boolean;
34
36
  size: "small" | "medium" | "large";
35
37
  resize: "auto" | "manual";
@@ -38,9 +40,12 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
38
40
  readonly: boolean;
39
41
  autoFocus: boolean;
40
42
  required: boolean;
41
- name?: TextareaProps['name'];
42
- autocomplete?: TextareaProps['autocomplete'];
43
+ status: "default" | "error" | "success";
44
+ assistiveText: TextareaProps['assistiveText'];
45
+ name: TextareaProps['name'];
46
+ autocomplete: TextareaProps['autocomplete'];
43
47
  private _textarea;
48
+ focusTarget: HTMLElement;
44
49
  private _throttledResize;
45
50
  /**
46
51
  * (Read-only) returns a ValidityState with the validity states that this element is in.
@@ -72,6 +77,7 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
72
77
  private handleKeyDown;
73
78
  disconnectedCallback(): void;
74
79
  renderLabel(label: string, maxLength?: number): TemplateResult<1> | typeof nothing;
80
+ renderAssistiveText(): TemplateResult<1> | typeof nothing;
75
81
  render(): TemplateResult<1>;
76
82
  static styles: CSSResult;
77
83
  }
@@ -82,6 +88,8 @@ export declare const resizeModes: readonly ["auto", "manual"];
82
88
 
83
89
  export declare const sizes: readonly ["small", "medium", "large"];
84
90
 
91
+ export declare const statusTypes: readonly ["default", "success", "error"];
92
+
85
93
  export declare interface TextareaProps {
86
94
  /**
87
95
  * Same as the HTML disabled attribute - indicates whether the textarea is disabled.
@@ -101,6 +109,18 @@ export declare interface TextareaProps {
101
109
  * The value of the textarea (used as a key/value pair in HTML forms with `name`).
102
110
  */
103
111
  value: string;
112
+ /**
113
+ * An optional default value to use when the textarea is reset.
114
+ */
115
+ defaultValue?: string;
116
+ /**
117
+ * An optional assistive text to display below the textarea element. Must be provided when the status is success or error.
118
+ */
119
+ assistiveText?: string;
120
+ /**
121
+ * The status of the textarea component / assistive text. Can be default, success or error.
122
+ */
123
+ status?: typeof statusTypes[number];
104
124
  /**
105
125
  * The name of the textarea (used as a key/value pair with `value`). This is required in order to work properly with forms.
106
126
  */
package/dist/index.js CHANGED
@@ -1,124 +1,127 @@
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";
1
+ import { LitElement as M, html as L, nothing as F, unsafeCSS as A } from "lit";
2
+ import { property as l, query as P } from "lit/decorators.js";
3
+ import { classMap as K } from "lit/directives/class-map.js";
4
+ import { ifDefined as b } from "lit/directives/if-defined.js";
5
+ import { live as G } from "lit/directives/live.js";
6
+ import "@justeattakeaway/pie-assistive-text";
6
7
  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) {
8
+ import { FormControlMixin as H, RtlMixin as U, wrapNativeEvent as X, validPropertyValues as O, defineCustomElement as J } from "@justeattakeaway/pie-webc-core";
9
+ var T = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
10
+ function Q(t) {
9
11
  return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
10
12
  }
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();
13
+ var V = "Expected a function", R = 0 / 0, Y = "[object Symbol]", Z = /^\s+|\s+$/g, ee = /^[-+]0x[0-9a-f]+$/i, te = /^0b[01]+$/i, ae = /^0o[0-7]+$/i, re = parseInt, ie = typeof T == "object" && T && T.Object === Object && T, ne = typeof self == "object" && self && self.Object === Object && self, oe = ie || ne || Function("return this")(), se = Object.prototype, le = se.toString, de = Math.max, ce = Math.min, j = function() {
14
+ return oe.Date.now();
13
15
  };
14
- function de(t, e, a) {
15
- var r, i, u, l, s, p, g = 0, m = !1, v = !1, _ = !0;
16
+ function ue(t, e, a) {
17
+ var r, i, h, u, d, p, x = 0, k = !1, v = !1, g = !0;
16
18
  if (typeof t != "function")
17
- throw new TypeError(R);
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;
19
+ throw new TypeError(V);
20
+ e = D(e) || 0, _(a) && (k = !!a.leading, v = "maxWait" in a, h = v ? de(D(a.maxWait) || 0, e) : h, g = "trailing" in a ? !!a.trailing : g);
21
+ function y(n) {
22
+ var f = r, z = i;
23
+ return r = i = void 0, x = n, u = t.apply(z, f), u;
22
24
  }
23
- function D(n) {
24
- return g = n, s = setTimeout(y, e), m ? $(n) : l;
25
+ function w(n) {
26
+ return x = n, d = setTimeout($, e), k ? y(n) : u;
25
27
  }
26
- function P(n) {
27
- var f = n - p, b = n - g, E = e - f;
28
- return v ? le(E, u - b) : E;
28
+ function q(n) {
29
+ var f = n - p, z = n - x, C = e - f;
30
+ return v ? ce(C, h - z) : C;
29
31
  }
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
+ function I(n) {
33
+ var f = n - p, z = n - x;
34
+ return p === void 0 || f >= e || f < 0 || v && z >= h;
33
35
  }
34
- function y() {
35
- var n = L();
36
- if (S(n))
37
- return T(n);
38
- s = setTimeout(y, P(n));
36
+ function $() {
37
+ var n = j();
38
+ if (I(n))
39
+ return W(n);
40
+ d = setTimeout($, q(n));
39
41
  }
40
- function T(n) {
41
- return s = void 0, _ && r ? $(n) : (r = i = void 0, l);
42
+ function W(n) {
43
+ return d = void 0, g && r ? y(n) : (r = i = void 0, u);
42
44
  }
43
- function M() {
44
- s !== void 0 && clearTimeout(s), g = 0, r = p = i = s = void 0;
45
+ function B() {
46
+ d !== void 0 && clearTimeout(d), x = 0, r = p = i = d = void 0;
45
47
  }
46
- function q() {
47
- return s === void 0 ? l : T(L());
48
+ function N() {
49
+ return d === void 0 ? u : W(j());
48
50
  }
49
- function j() {
50
- var n = L(), f = S(n);
51
+ function S() {
52
+ var n = j(), f = I(n);
51
53
  if (r = arguments, i = this, p = n, f) {
52
- if (s === void 0)
53
- return D(p);
54
+ if (d === void 0)
55
+ return w(p);
54
56
  if (v)
55
- return s = setTimeout(y, e), $(p);
57
+ return d = setTimeout($, e), y(p);
56
58
  }
57
- return s === void 0 && (s = setTimeout(y, e)), l;
59
+ return d === void 0 && (d = setTimeout($, e)), u;
58
60
  }
59
- return j.cancel = M, j.flush = q, j;
61
+ return S.cancel = B, S.flush = N, S;
60
62
  }
61
- function ce(t, e, a) {
63
+ function he(t, e, a) {
62
64
  var r = !0, i = !0;
63
65
  if (typeof t != "function")
64
- throw new TypeError(R);
65
- return k(a) && (r = "leading" in a ? !!a.leading : r, i = "trailing" in a ? !!a.trailing : i), de(t, e, {
66
+ throw new TypeError(V);
67
+ return _(a) && (r = "leading" in a ? !!a.leading : r, i = "trailing" in a ? !!a.trailing : i), ue(t, e, {
66
68
  leading: r,
67
69
  maxWait: e,
68
70
  trailing: i
69
71
  });
70
72
  }
71
- function k(t) {
73
+ function _(t) {
72
74
  var e = typeof t;
73
75
  return !!t && (e == "object" || e == "function");
74
76
  }
75
- function he(t) {
77
+ function pe(t) {
76
78
  return !!t && typeof t == "object";
77
79
  }
78
- function ue(t) {
79
- return typeof t == "symbol" || he(t) && oe.call(t) == J;
80
+ function fe(t) {
81
+ return typeof t == "symbol" || pe(t) && le.call(t) == Y;
80
82
  }
81
- function W(t) {
83
+ function D(t) {
82
84
  if (typeof t == "number")
83
85
  return t;
84
- if (ue(t))
85
- return C;
86
- if (k(t)) {
86
+ if (fe(t))
87
+ return R;
88
+ if (_(t)) {
87
89
  var e = typeof t.valueOf == "function" ? t.valueOf() : t;
88
- t = k(e) ? e + "" : e;
90
+ t = _(e) ? e + "" : e;
89
91
  }
90
92
  if (typeof t != "string")
91
93
  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
+ t = t.replace(Z, "");
95
+ var a = te.test(t);
96
+ return a || ae.test(t) ? re(t.slice(2), a ? 2 : 8) : ee.test(t) ? R : +t;
95
97
  }
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
+ var xe = he;
99
+ const ve = /* @__PURE__ */ Q(xe), 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-c);--textarea-padding-block: var(--dt-spacing-b);--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));line-height:0;padding:var(--dt-spacing-a);border:var(--textarea-border-thickness) solid var(--textarea-border-color);background-color:var(--textarea-background-color);border-radius:var(--dt-radius-rounded-c);inline-size:100%}.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:none;background-color:var(--textarea-background-color);color:var(--textarea-content-color);block-size:var(--textarea-height);max-block-size:var(--textarea-max-height);min-block-size:var(--textarea-min-height);inline-size:100%;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:focus{box-shadow:none;outline:none}.c-textareaWrapper.c-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:hover:not(.c-textarea--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:focus-within{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-textareaWrapper.c-textarea--large{--textarea-padding-block: var(--dt-spacing-c)}.c-textareaWrapper.c-textarea--small{--textarea-padding-block: var(--dt-spacing-a)}.c-textareaWrapper.c-textarea--resize-manual{--textarea-resize: vertical;--textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2))}@media (pointer: coarse){.c-textareaWrapper.c-textarea--resize-manual{--textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-resize: none}}.c-textareaWrapper.c-textarea--resize-auto{--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-min-height: var(--textarea-height)}.c-textareaWrapper.c-textarea--error{--textarea-border-color: var(--dt-color-support-error)}
100
+ `, me = ["small", "medium", "large"], be = ["auto", "manual"], ye = ["default", "success", "error"], c = {
99
101
  disabled: !1,
100
102
  size: "medium",
101
103
  resize: "auto",
102
104
  label: "",
103
105
  value: "",
106
+ status: "default",
104
107
  autoFocus: !1,
105
108
  readonly: !1,
106
109
  required: !1
107
110
  };
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;
111
+ var ze = Object.defineProperty, ke = Object.getOwnPropertyDescriptor, s = (t, e, a, r) => {
112
+ for (var i = r > 1 ? void 0 : r ? ke(e, a) : e, h = t.length - 1, u; h >= 0; h--)
113
+ (u = t[h]) && (i = (r ? u(e, a, i) : u(i)) || i);
114
+ return r && i && ze(e, a, i), i;
112
115
  };
113
- const x = "pie-textarea";
114
- class o extends K(G(w)) {
116
+ const m = "pie-textarea", E = "assistive-text";
117
+ class o extends H(U(M)) {
115
118
  constructor() {
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(() => {
117
- this.resize === "auto" && (this._textarea.style.height = "auto", this._textarea.style.height = `${this._textarea.scrollHeight + 2}px`);
119
+ 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.status = c.status, this._throttledResize = ve(() => {
120
+ this.resize === "auto" && (this._textarea.style.height = "auto", this._textarea.style.height = `${this._textarea.scrollHeight}px`);
118
121
  }, 100), this.handleInput = (e) => {
119
122
  this.value = e.target.value, this.restrictInputLength(), this._internals.setFormValue(this.value), this.handleResize();
120
123
  }, this.handleChange = (e) => {
121
- const a = H(e);
124
+ const a = X(e);
122
125
  this.dispatchEvent(a);
123
126
  }, this.handleKeyDown = (e) => {
124
127
  e.key === "Enter" && e.stopPropagation();
@@ -145,10 +148,10 @@ class o extends K(G(w)) {
145
148
  * Resets the value to the default value.
146
149
  */
147
150
  formResetCallback() {
148
- this.value = c.value, this._internals.setFormValue(this.value);
151
+ this.value = this.defaultValue ?? c.value, this._internals.setFormValue(this.value);
149
152
  }
150
153
  firstUpdated() {
151
- this.restrictInputLength(), this._internals.setFormValue(this.value), this._textarea.addEventListener("keydown", this.handleKeyDown);
154
+ this.restrictInputLength(), this._internals.setFormValue(this.value), window.addEventListener("resize", () => this.handleResize()), this._textarea.addEventListener("keydown", this.handleKeyDown);
152
155
  }
153
156
  handleResize() {
154
157
  this._throttledResize();
@@ -163,11 +166,21 @@ class o extends K(G(w)) {
163
166
  e.has("value") && (this.restrictInputLength(), this._internals.setFormValue(this.value)), this.resize === "auto" && (e.has("resize") || e.has("size")) && this.handleResize();
164
167
  }
165
168
  disconnectedCallback() {
166
- this._textarea.removeEventListener("keydown", this.handleKeyDown);
169
+ this._textarea.removeEventListener("keydown", this.handleKeyDown), window.removeEventListener("resize", () => this.handleResize());
167
170
  }
168
171
  renderLabel(e, a) {
169
172
  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;
173
+ return e != null && e.length ? L`<pie-form-label for="${m}" trailing=${b(r)}>${e}</pie-form-label>` : F;
174
+ }
175
+ renderAssistiveText() {
176
+ return this.assistiveText ? L`
177
+ <pie-assistive-text
178
+ id="${E}"
179
+ variant=${b(this.status)}
180
+ data-test-id="pie-textarea-assistive-text">
181
+ ${this.assistiveText}
182
+ </pie-assistive-text>
183
+ ` : F;
171
184
  }
172
185
  render() {
173
186
  const {
@@ -175,81 +188,106 @@ class o extends K(G(w)) {
175
188
  resize: a,
176
189
  size: r,
177
190
  autocomplete: i,
178
- autoFocus: u,
179
- name: l,
180
- readonly: s,
191
+ autoFocus: h,
192
+ name: u,
193
+ readonly: d,
181
194
  value: p,
182
- required: g,
183
- label: m,
184
- maxLength: v
185
- } = this;
186
- return I`
195
+ required: x,
196
+ label: k,
197
+ maxLength: v,
198
+ status: g,
199
+ assistiveText: y
200
+ } = this, w = {
201
+ "c-textareaWrapper": !0,
202
+ "c-textarea--disabled": e,
203
+ "c-textarea--error": g === "error",
204
+ [`c-textarea--resize-${a}`]: !0,
205
+ [`c-textarea--${r}`]: !0
206
+ };
207
+ return L`<div>
208
+ ${this.renderLabel(k, v)}
187
209
  <div
188
- class="c-textareaWrapper"
189
- data-test-id="pie-textarea-wrapper"
190
- data-pie-size="${r}"
191
- data-pie-resize="${a}">
192
- ${this.renderLabel(m, v)}
210
+ class="${K(w)}"
211
+ data-test-id="pie-textarea-wrapper">
193
212
  <textarea
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}
213
+ id="${m}"
214
+ data-test-id="${m}"
215
+ name=${b(u)}
216
+ autocomplete=${b(i)}
217
+ .value=${G(p)}
218
+ ?autofocus=${h}
219
+ ?readonly=${d}
220
+ ?required=${x}
202
221
  ?disabled=${e}
222
+ aria-describedby=${b(y ? E : void 0)}
223
+ aria-invalid=${g === "error" ? "true" : "false"}
224
+ aria-errormessage="${b(g === "error" ? E : void 0)}"
203
225
  @input=${this.handleInput}
204
226
  @change=${this.handleChange}
205
227
  ></textarea>
206
- </div>`;
228
+ </div>
229
+ ${this.renderAssistiveText()}
230
+ </div>`;
207
231
  }
208
232
  }
209
- o.shadowRootOptions = { ...w.shadowRootOptions, delegatesFocus: !0 };
210
- o.styles = N(ge);
211
- d([
212
- h({ type: String })
233
+ o.shadowRootOptions = { ...M.shadowRootOptions, delegatesFocus: !0 };
234
+ o.styles = A(ge);
235
+ s([
236
+ l({ type: String })
213
237
  ], o.prototype, "value", 2);
214
- d([
215
- h({ type: Boolean, reflect: !0 })
238
+ s([
239
+ l({ type: String })
240
+ ], o.prototype, "defaultValue", 2);
241
+ s([
242
+ l({ type: Boolean, reflect: !0 })
216
243
  ], o.prototype, "disabled", 2);
217
- d([
218
- h({ type: String }),
219
- F(x, ve, c.size)
244
+ s([
245
+ l({ type: String }),
246
+ O(m, me, c.size)
220
247
  ], o.prototype, "size", 2);
221
- d([
222
- h({ type: String }),
223
- F(x, xe, c.resize)
248
+ s([
249
+ l({ type: String }),
250
+ O(m, be, c.resize)
224
251
  ], o.prototype, "resize", 2);
225
- d([
226
- h({ type: String })
252
+ s([
253
+ l({ type: String })
227
254
  ], o.prototype, "label", 2);
228
- d([
229
- h({ type: Number })
255
+ s([
256
+ l({ type: Number })
230
257
  ], o.prototype, "maxLength", 2);
231
- d([
232
- h({ type: Boolean })
258
+ s([
259
+ l({ type: Boolean })
233
260
  ], o.prototype, "readonly", 2);
234
- d([
235
- h({ type: Boolean })
261
+ s([
262
+ l({ type: Boolean })
236
263
  ], o.prototype, "autoFocus", 2);
237
- d([
238
- h({ type: Boolean })
264
+ s([
265
+ l({ type: Boolean })
239
266
  ], o.prototype, "required", 2);
240
- d([
241
- h({ type: String })
267
+ s([
268
+ l({ type: String }),
269
+ O(m, ye, c.status)
270
+ ], o.prototype, "status", 2);
271
+ s([
272
+ l({ type: String })
273
+ ], o.prototype, "assistiveText", 2);
274
+ s([
275
+ l({ type: String })
242
276
  ], o.prototype, "name", 2);
243
- d([
244
- h({ type: String })
277
+ s([
278
+ l({ type: String })
245
279
  ], o.prototype, "autocomplete", 2);
246
- d([
247
- V("textarea")
280
+ s([
281
+ P("textarea")
248
282
  ], o.prototype, "_textarea", 2);
249
- U(x, o);
283
+ s([
284
+ P("textarea")
285
+ ], o.prototype, "focusTarget", 2);
286
+ J(m, o);
250
287
  export {
251
288
  o as PieTextarea,
252
289
  c as defaultProps,
253
- xe as resizeModes,
254
- ve as sizes
290
+ be as resizeModes,
291
+ me as sizes,
292
+ ye as statusTypes
255
293
  };
package/dist/react.d.ts CHANGED
@@ -4,6 +4,7 @@ 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
6
  import type { nothing } from 'lit';
7
+ import type { PIEInputElement } from '@justeattakeaway/pie-webc-core';
7
8
  import type { PropertyValues } from 'lit';
8
9
  import * as React_2 from 'react';
9
10
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
@@ -12,7 +13,7 @@ import type { TemplateResult } from 'lit-html';
12
13
  /**
13
14
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
14
15
  */
15
- declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength'>>;
16
+ declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'assistiveText' | 'defaultValue'>>;
16
17
 
17
18
  /**
18
19
  * Default values for optional properties that have default fallback values in the component.
@@ -26,13 +27,14 @@ export declare const PieTextarea: React_2.ForwardRefExoticComponent<TextareaProp
26
27
  * @event {InputEvent} input - when the textarea value is changed.
27
28
  * @event {CustomEvent} change - when the textarea value is changed.
28
29
  */
29
- declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
30
+ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps, PIEInputElement {
30
31
  static shadowRootOptions: {
31
32
  delegatesFocus: boolean;
32
33
  mode: ShadowRootMode;
33
34
  slotAssignment?: SlotAssignmentMode | undefined;
34
35
  };
35
36
  value: string;
37
+ defaultValue: TextareaProps['defaultValue'];
36
38
  disabled: boolean;
37
39
  size: "small" | "medium" | "large";
38
40
  resize: "auto" | "manual";
@@ -41,9 +43,12 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
41
43
  readonly: boolean;
42
44
  autoFocus: boolean;
43
45
  required: boolean;
44
- name?: TextareaProps['name'];
45
- autocomplete?: TextareaProps['autocomplete'];
46
+ status: "default" | "error" | "success";
47
+ assistiveText: TextareaProps['assistiveText'];
48
+ name: TextareaProps['name'];
49
+ autocomplete: TextareaProps['autocomplete'];
46
50
  private _textarea;
51
+ focusTarget: HTMLElement;
47
52
  private _throttledResize;
48
53
  /**
49
54
  * (Read-only) returns a ValidityState with the validity states that this element is in.
@@ -75,6 +80,7 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
75
80
  private handleKeyDown;
76
81
  disconnectedCallback(): void;
77
82
  renderLabel(label: string, maxLength?: number): TemplateResult<1> | typeof nothing;
83
+ renderAssistiveText(): TemplateResult<1> | typeof nothing;
78
84
  render(): TemplateResult<1>;
79
85
  static styles: CSSResult;
80
86
  }
@@ -92,6 +98,8 @@ export declare const resizeModes: readonly ["auto", "manual"];
92
98
 
93
99
  export declare const sizes: readonly ["small", "medium", "large"];
94
100
 
101
+ export declare const statusTypes: readonly ["default", "success", "error"];
102
+
95
103
  export declare interface TextareaProps {
96
104
  /**
97
105
  * Same as the HTML disabled attribute - indicates whether the textarea is disabled.
@@ -111,6 +119,18 @@ export declare interface TextareaProps {
111
119
  * The value of the textarea (used as a key/value pair in HTML forms with `name`).
112
120
  */
113
121
  value: string;
122
+ /**
123
+ * An optional default value to use when the textarea is reset.
124
+ */
125
+ defaultValue?: string;
126
+ /**
127
+ * An optional assistive text to display below the textarea element. Must be provided when the status is success or error.
128
+ */
129
+ assistiveText?: string;
130
+ /**
131
+ * The status of the textarea component / assistive text. Can be default, success or error.
132
+ */
133
+ status?: typeof statusTypes[number];
114
134
  /**
115
135
  * The name of the textarea (used as a key/value pair with `value`). This is required in order to work properly with forms.
116
136
  */
package/dist/react.js CHANGED
@@ -1,13 +1,15 @@
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 l, resizeModes as d, sizes as g } from "./index.js";
4
+ import { defaultProps as d, resizeModes as g, sizes as C, statusTypes as h } from "./index.js";
5
5
  import "lit";
6
6
  import "lit/decorators.js";
7
- import "lit/directives/live.js";
8
- import "@justeattakeaway/pie-webc-core";
7
+ import "lit/directives/class-map.js";
9
8
  import "lit/directives/if-defined.js";
9
+ import "lit/directives/live.js";
10
+ import "@justeattakeaway/pie-assistive-text";
10
11
  import "@justeattakeaway/pie-form-label";
12
+ import "@justeattakeaway/pie-webc-core";
11
13
  const r = t({
12
14
  displayName: "PieTextarea",
13
15
  elementClass: a,
@@ -19,10 +21,11 @@ const r = t({
19
21
  onChange: "change"
20
22
  // when the textarea value is changed.
21
23
  }
22
- }), P = r;
24
+ }), f = r;
23
25
  export {
24
- P as PieTextarea,
25
- l as defaultProps,
26
- d as resizeModes,
27
- g as sizes
26
+ f as PieTextarea,
27
+ d as defaultProps,
28
+ g as resizeModes,
29
+ C as sizes,
30
+ h as statusTypes
28
31
  };
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.6.1",
4
+ "version": "0.8.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "license": "Apache-2.0",
37
37
  "devDependencies": {
38
38
  "@custom-elements-manifest/analyzer": "0.9.0",
39
- "@justeattakeaway/pie-components-config": "0.17.0",
39
+ "@justeattakeaway/pie-components-config": "0.18.0",
40
40
  "@justeattakeaway/pie-css": "0.12.1",
41
41
  "@types/lodash.throttle": "4.1.9",
42
42
  "cem-plugin-module-file-extensions": "0.0.5"
package/src/defs.ts CHANGED
@@ -2,6 +2,7 @@ import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
2
 
3
3
  export const sizes = ['small', 'medium', 'large'] as const;
4
4
  export const resizeModes = ['auto', 'manual'] as const;
5
+ export const statusTypes = ['default', 'success', 'error'] as const;
5
6
 
6
7
  export interface TextareaProps {
7
8
  /**
@@ -26,6 +27,21 @@ export interface TextareaProps {
26
27
  */
27
28
  value: string;
28
29
 
30
+ /**
31
+ * An optional default value to use when the textarea is reset.
32
+ */
33
+ defaultValue?: string;
34
+
35
+ /**
36
+ * An optional assistive text to display below the textarea element. Must be provided when the status is success or error.
37
+ */
38
+ assistiveText?: string;
39
+
40
+ /**
41
+ * The status of the textarea component / assistive text. Can be default, success or error.
42
+ */
43
+ status?: typeof statusTypes[number];
44
+
29
45
  /**
30
46
  * The name of the textarea (used as a key/value pair with `value`). This is required in order to work properly with forms.
31
47
  */
@@ -70,7 +86,7 @@ export interface TextareaProps {
70
86
  /**
71
87
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
72
88
  */
73
- type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength'>>;
89
+ type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'assistiveText' | 'defaultValue'>>;
74
90
 
75
91
  /**
76
92
  * Default values for optional properties that have default fallback values in the component.
@@ -81,6 +97,7 @@ export const defaultProps: DefaultProps = {
81
97
  resize: 'auto',
82
98
  label: '',
83
99
  value: '',
100
+ status: 'default',
84
101
  autoFocus: false,
85
102
  readonly: false,
86
103
  required: false,
package/src/index.ts CHANGED
@@ -1,39 +1,43 @@
1
1
  import {
2
- LitElement, html, unsafeCSS, PropertyValues, nothing,
2
+ LitElement, html, unsafeCSS, type PropertyValues, nothing,
3
3
  } from 'lit';
4
-
5
4
  import { property, query } from 'lit/decorators.js';
5
+ import { classMap } from 'lit/directives/class-map.js';
6
+ import { ifDefined } from 'lit/directives/if-defined.js';
6
7
  import { live } from 'lit/directives/live.js';
7
8
  import throttle from 'lodash.throttle';
8
9
 
10
+ import '@justeattakeaway/pie-assistive-text';
11
+ import '@justeattakeaway/pie-form-label';
9
12
  import {
10
- validPropertyValues, RtlMixin, defineCustomElement, FormControlMixin, wrapNativeEvent,
13
+ validPropertyValues, RtlMixin, defineCustomElement, FormControlMixin, wrapNativeEvent, type PIEInputElement,
11
14
  } from '@justeattakeaway/pie-webc-core';
12
15
 
13
- import { ifDefined } from 'lit/directives/if-defined.js';
14
16
  import styles from './textarea.scss?inline';
15
17
  import {
16
- TextareaProps, defaultProps, sizes, resizeModes,
18
+ type TextareaProps, defaultProps, sizes, resizeModes, statusTypes,
17
19
  } from './defs';
18
20
 
19
- import '@justeattakeaway/pie-form-label';
20
-
21
21
  // Valid values available to consumers
22
22
  export * from './defs';
23
23
 
24
24
  const componentSelector = 'pie-textarea';
25
+ const assistiveTextIdValue = 'assistive-text';
25
26
 
26
27
  /**
27
28
  * @tagname pie-textarea
28
29
  * @event {InputEvent} input - when the textarea value is changed.
29
30
  * @event {CustomEvent} change - when the textarea value is changed.
30
31
  */
31
- export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implements TextareaProps {
32
+ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implements TextareaProps, PIEInputElement {
32
33
  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
33
34
 
34
35
  @property({ type: String })
35
36
  public value = defaultProps.value;
36
37
 
38
+ @property({ type: String })
39
+ public defaultValue: TextareaProps['defaultValue'];
40
+
37
41
  @property({ type: Boolean, reflect: true })
38
42
  public disabled = defaultProps.disabled;
39
43
 
@@ -61,18 +65,28 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
61
65
  public required = defaultProps.required;
62
66
 
63
67
  @property({ type: String })
64
- public name?: TextareaProps['name'];
68
+ @validPropertyValues(componentSelector, statusTypes, defaultProps.status)
69
+ public status = defaultProps.status;
65
70
 
66
71
  @property({ type: String })
67
- public autocomplete?: TextareaProps['autocomplete'];
72
+ public assistiveText: TextareaProps['assistiveText'];
73
+
74
+ @property({ type: String })
75
+ public name: TextareaProps['name'];
76
+
77
+ @property({ type: String })
78
+ public autocomplete: TextareaProps['autocomplete'];
68
79
 
69
80
  @query('textarea')
70
81
  private _textarea!: HTMLTextAreaElement;
71
82
 
83
+ @query('textarea')
84
+ public focusTarget!: HTMLElement;
85
+
72
86
  private _throttledResize = throttle(() => {
73
87
  if (this.resize === 'auto') {
74
88
  this._textarea.style.height = 'auto';
75
- this._textarea.style.height = `${this._textarea.scrollHeight + 2}px`; // +2 for border thicknesses
89
+ this._textarea.style.height = `${this._textarea.scrollHeight}px`;
76
90
  }
77
91
  }, 100);
78
92
 
@@ -99,7 +113,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
99
113
  * Resets the value to the default value.
100
114
  */
101
115
  public formResetCallback (): void {
102
- this.value = defaultProps.value;
116
+ this.value = this.defaultValue ?? defaultProps.value;
103
117
 
104
118
  this._internals.setFormValue(this.value);
105
119
  }
@@ -108,6 +122,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
108
122
  this.restrictInputLength();
109
123
  this._internals.setFormValue(this.value);
110
124
 
125
+ window.addEventListener('resize', () => this.handleResize());
111
126
  this._textarea.addEventListener('keydown', this.handleKeyDown);
112
127
  }
113
128
 
@@ -166,6 +181,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
166
181
 
167
182
  public disconnectedCallback (): void {
168
183
  this._textarea.removeEventListener('keydown', this.handleKeyDown);
184
+ window.removeEventListener('resize', () => this.handleResize());
169
185
  }
170
186
 
171
187
  renderLabel (label: string, maxLength?: number) {
@@ -176,6 +192,21 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
176
192
  : nothing;
177
193
  }
178
194
 
195
+ renderAssistiveText () {
196
+ if (!this.assistiveText) {
197
+ return nothing;
198
+ }
199
+
200
+ return html`
201
+ <pie-assistive-text
202
+ id="${assistiveTextIdValue}"
203
+ variant=${ifDefined(this.status)}
204
+ data-test-id="pie-textarea-assistive-text">
205
+ ${this.assistiveText}
206
+ </pie-assistive-text>
207
+ `;
208
+ }
209
+
179
210
  render () {
180
211
  const {
181
212
  disabled,
@@ -189,15 +220,23 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
189
220
  required,
190
221
  label,
191
222
  maxLength,
223
+ status,
224
+ assistiveText,
192
225
  } = this;
193
226
 
194
- return html`
227
+ const classes = {
228
+ 'c-textareaWrapper': true,
229
+ 'c-textarea--disabled': disabled,
230
+ 'c-textarea--error': status === 'error',
231
+ [`c-textarea--resize-${resize}`]: true,
232
+ [`c-textarea--${size}`]: true,
233
+ };
234
+
235
+ return html`<div>
236
+ ${this.renderLabel(label, maxLength)}
195
237
  <div
196
- class="c-textareaWrapper"
197
- data-test-id="pie-textarea-wrapper"
198
- data-pie-size="${size}"
199
- data-pie-resize="${resize}">
200
- ${this.renderLabel(label, maxLength)}
238
+ class="${classMap(classes)}"
239
+ data-test-id="pie-textarea-wrapper">
201
240
  <textarea
202
241
  id="${componentSelector}"
203
242
  data-test-id="${componentSelector}"
@@ -208,10 +247,15 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
208
247
  ?readonly=${readonly}
209
248
  ?required=${required}
210
249
  ?disabled=${disabled}
250
+ aria-describedby=${ifDefined(assistiveText ? assistiveTextIdValue : undefined)}
251
+ aria-invalid=${status === 'error' ? 'true' : 'false'}
252
+ aria-errormessage="${ifDefined(status === 'error' ? assistiveTextIdValue : undefined)}"
211
253
  @input=${this.handleInput}
212
254
  @change=${this.handleChange}
213
255
  ></textarea>
214
- </div>`;
256
+ </div>
257
+ ${this.renderAssistiveText()}
258
+ </div>`;
215
259
  }
216
260
 
217
261
  // Renders a `CSSResult` generated from SCSS by Vite
package/src/textarea.scss CHANGED
@@ -1,6 +1,7 @@
1
1
  @use '@justeattakeaway/pie-css/scss' as p;
2
2
 
3
3
  // Heights are being defined based on the line height of the text and the padding.
4
+ // Some of the padding is in the wrapper element to properly position the textarea resize handle.
4
5
  // Changing the `size` property affects the padding and therefore the height of the textarea.
5
6
  // Default height is two lines of text.
6
7
  // Minimum height in manual resize mode is one line of text.
@@ -9,81 +10,95 @@
9
10
  --textarea-line-height: #{p.line-height(--dt-font-body-l-line-height)};
10
11
  --textarea-border-thickness: 1px;
11
12
  --textarea-resize: none;
12
- --textarea-padding-inline: var(--dt-spacing-d);
13
- --textarea-padding-block: var(--dt-spacing-c);
13
+ --textarea-padding-inline: var(--dt-spacing-c);
14
+ --textarea-padding-block: var(--dt-spacing-b);
14
15
  --textarea-background-color: var(--dt-color-container-default);
15
16
  --textarea-border-color: var(--dt-color-interactive-form);
16
17
  --textarea-content-color: var(--dt-color-content-default);
17
18
 
18
19
  // Default height is two lines of text
19
- --textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
20
+ --textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2));
20
21
 
21
- line-height: 0; // Remove once there is text outside the textarea
22
+ line-height: 0;
23
+ padding: var(--dt-spacing-a);
24
+ border: var(--textarea-border-thickness) solid var(--textarea-border-color);
25
+ background-color: var(--textarea-background-color);
26
+ border-radius: var(--dt-radius-rounded-c);
27
+ inline-size: 100%;
22
28
 
23
29
  textarea {
24
30
  @include p.font-size(--dt-font-body-l-size);
25
31
  line-height: var(--textarea-line-height);
26
32
  font-family: var(--dt-font-body-l-family);
27
33
  resize: var(--textarea-resize);
28
- border: var(--textarea-border-thickness) solid var(--textarea-border-color);
34
+ border: none;
29
35
  background-color: var(--textarea-background-color);
30
36
  color: var(--textarea-content-color);
31
37
 
32
- border-radius: var(--dt-radius-rounded-c);
33
38
  block-size: var(--textarea-height);
34
39
  max-block-size: var(--textarea-max-height);
35
40
  min-block-size: var(--textarea-min-height);
41
+ inline-size: 100%;
36
42
 
37
43
  padding-block-start: var(--textarea-padding-block);
38
44
  padding-block-end: var(--textarea-padding-block);
39
45
  padding-inline-start: var(--textarea-padding-inline);
40
46
  padding-inline-end: var(--textarea-padding-inline);
41
47
 
42
- &[disabled] {
43
- --textarea-background-color: var(--dt-color-disabled-01);
44
- --textarea-border-color: var(--dt-color-disabled-01);
45
- --textarea-content-color: var(--dt-color-content-disabled);
48
+ &:focus {
49
+ box-shadow: none;
50
+ outline: none;
46
51
  }
52
+ }
47
53
 
48
- @media (hover: hover) {
49
- &:hover:not([disabled]) {
50
- --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))));
51
- }
52
- }
54
+ &.c-textarea--disabled {
55
+ --textarea-background-color: var(--dt-color-disabled-01);
56
+ --textarea-border-color: var(--dt-color-disabled-01);
57
+ --textarea-content-color: var(--dt-color-content-disabled);
58
+ }
53
59
 
54
- &:focus-visible {
55
- @include p.focus;
60
+ @media (hover: hover) {
61
+ &:hover:not(.c-textarea--disabled) {
62
+ --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))));
56
63
  }
57
64
  }
58
65
 
59
- &[data-pie-size="large"] {
60
- --textarea-padding-block: var(--dt-spacing-d);
66
+ &:focus-within {
67
+ @include p.focus;
61
68
  }
62
69
 
63
- &[data-pie-size="small"] {
64
- --textarea-padding-block: var(--dt-spacing-b);
70
+ &.c-textarea--large {
71
+ --textarea-padding-block: var(--dt-spacing-c);
65
72
  }
66
73
 
67
- &[data-pie-resize="manual"] {
74
+ &.c-textarea--small {
75
+ --textarea-padding-block: var(--dt-spacing-a);
76
+ }
77
+
78
+ &.c-textarea--resize-manual {
68
79
  --textarea-resize: vertical;
69
80
 
70
81
  // Minimum is one line of text
71
- --textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2)); // One line of text
82
+ --textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2));
72
83
 
73
84
  @media (pointer: coarse) {
74
85
  // Fixed size for touch devices
75
- --textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
76
- --textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
77
- --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
86
+ --textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
87
+ --textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
88
+ --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
78
89
  --textarea-resize: none;
79
90
  }
80
91
  }
81
92
 
82
- &[data-pie-resize="auto"] {
93
+ &.c-textarea--resize-auto {
83
94
  // Maximum is six lines of text
84
- --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
95
+ --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
85
96
 
86
97
  // Minimum is two lines of text
87
98
  --textarea-min-height: var(--textarea-height);
88
99
  }
100
+
101
+ &.c-textarea--error {
102
+ --textarea-border-color: var(--dt-color-support-error);
103
+ }
89
104
  }