@dr.pogodin/react-utils 1.43.22 → 1.43.24

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.
@@ -1,12 +1,14 @@
1
1
  import { type ChangeEventHandler, type FocusEventHandler, type KeyboardEventHandler } from 'react';
2
2
  import { type Theme } from '@dr.pogodin/react-themes';
3
- type ThemeKeyT = 'container' | 'hidden' | 'textarea';
3
+ type ThemeKeyT = 'container' | 'hidden' | 'label' | 'textarea';
4
4
  type Props = {
5
5
  disabled?: boolean;
6
+ label?: string;
6
7
  onBlur?: FocusEventHandler<HTMLTextAreaElement>;
7
8
  onChange?: ChangeEventHandler<HTMLTextAreaElement>;
8
9
  onKeyDown?: KeyboardEventHandler<HTMLTextAreaElement>;
9
10
  placeholder?: string;
11
+ testId?: string;
10
12
  theme: Theme<ThemeKeyT>;
11
13
  value?: string;
12
14
  };
@@ -3,4 +3,5 @@ export declare const container: string;
3
3
  export declare const context: string;
4
4
  export declare const hidden: string;
5
5
  export declare const hoc: string;
6
+ export declare const label: string;
6
7
  export declare const textarea: string;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.43.22",
2
+ "version": "1.43.24",
3
3
  "bin": {
4
4
  "react-utils-build": "bin/build.js",
5
5
  "react-utils-setup": "bin/setup.js"
@@ -4,6 +4,7 @@ import {
4
4
  type FunctionComponent,
5
5
  type KeyboardEventHandler,
6
6
  useEffect,
7
+ useLayoutEffect,
7
8
  useRef,
8
9
  useState,
9
10
  } from 'react';
@@ -15,24 +16,29 @@ import defaultTheme from './style.scss';
15
16
  type ThemeKeyT =
16
17
  | 'container'
17
18
  | 'hidden'
19
+ | 'label'
18
20
  | 'textarea';
19
21
 
20
22
  type Props = {
21
23
  disabled?: boolean;
24
+ label?: string;
22
25
  onBlur?: FocusEventHandler<HTMLTextAreaElement>;
23
26
  onChange?: ChangeEventHandler<HTMLTextAreaElement>;
24
27
  onKeyDown?: KeyboardEventHandler<HTMLTextAreaElement>;
25
28
  placeholder?: string;
29
+ testId?: string;
26
30
  theme: Theme<ThemeKeyT>;
27
31
  value?: string;
28
32
  };
29
33
 
30
34
  const TextArea: FunctionComponent<Props> = ({
31
35
  disabled,
36
+ label,
32
37
  onBlur,
33
38
  onChange,
34
39
  onKeyDown,
35
40
  placeholder,
41
+ testId,
36
42
  theme,
37
43
  value,
38
44
  }) => {
@@ -58,14 +64,21 @@ const TextArea: FunctionComponent<Props> = ({
58
64
  };
59
65
  }, []);
60
66
 
61
- // This resizes the text area when its content is modified.
62
- useEffect(() => {
67
+ // Resizes the text area when its content is modified.
68
+ //
69
+ // NOTE: useLayoutEffect() instead of useEffect() makes difference here,
70
+ // as it helps to avoid visible "content/height" jumps (i.e. with just
71
+ // useEffect() it becomes visible how the content is modified first,
72
+ // and then input height is incremented, if necessary).
73
+ // See: https://github.com/birdofpreyru/react-utils/issues/313
74
+ useLayoutEffect(() => {
63
75
  const el = hiddenAreaRef.current;
64
76
  if (el) setHeight(el.scrollHeight);
65
77
  }, [localValue]);
66
78
 
67
79
  return (
68
80
  <div className={theme.container}>
81
+ {label === undefined ? null : <div className={theme.label}>{label}</div>}
69
82
  <textarea
70
83
  className={`${theme.textarea} ${theme.hidden}`}
71
84
 
@@ -74,10 +87,20 @@ const TextArea: FunctionComponent<Props> = ({
74
87
  // of the primary textarea's height.
75
88
  readOnly
76
89
  ref={hiddenAreaRef}
77
- value={localValue}
90
+
91
+ // The "-1" value of "tabIndex" removes this hidden text area from
92
+ // the tab-focus-chain.
93
+ tabIndex={-1}
94
+
95
+ // NOTE: With empty string value ("") the scrolling height of this text
96
+ // area is zero, thus collapsing <TextArea> height below the single line
97
+ // input height. To avoid it we fallback to whitespace (" ") character
98
+ // here.
99
+ value={localValue || ' '}
78
100
  />
79
101
  <textarea
80
102
  className={theme.textarea}
103
+ data-testid={process.env.NODE_ENV === 'production' ? undefined : testId}
81
104
  disabled={disabled}
82
105
  onBlur={onBlur}
83
106
 
@@ -8,6 +8,10 @@
8
8
  position: relative;
9
9
  }
10
10
 
11
+ &.label {
12
+ margin: 0 0.3em;
13
+ }
14
+
11
15
  &.textarea {
12
16
  background: white;
13
17
  border: 1px solid gray;
@@ -19,6 +23,7 @@
19
23
  overflow: hidden;
20
24
  padding: 0.3em 0.3em calc(0.3em + 1px);
21
25
  resize: none;
26
+ width: 100%;
22
27
 
23
28
  &:focus {
24
29
  border-color: blue;
@@ -38,7 +43,9 @@
38
43
  }
39
44
 
40
45
  &.hidden {
46
+ // NOTE: We hide it this way, as setting "display: none" will interfere
47
+ // with measurements, making the hidden input height zero.
48
+ opacity: 0;
41
49
  position: absolute;
42
- z-index: -1;
43
50
  }
44
51
  }