Skip to content

Commit 288c01b

Browse files
committed
Add autoGrow to TextArea so the size fits the content
1 parent 0b730ab commit 288c01b

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

src/lib/components/textarea/TextArea.component.tsx

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import {
33
forwardRef,
44
TextareaHTMLAttributes,
55
ForwardedRef,
6+
useEffect,
7+
useRef,
8+
useImperativeHandle,
9+
useCallback,
610
} from 'react';
711
import styled, { css } from 'styled-components';
812
import { spacing } from '../../spacing';
@@ -12,13 +16,19 @@ type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
1216
variant?: TextAreaVariant;
1317
width?: CSSProperties['width'];
1418
height?: CSSProperties['height'];
19+
/**
20+
* Automatically adjust height to fit content
21+
* When enabled, the textarea will grow/shrink to show all content
22+
*/
23+
autoGrow?: boolean;
1524
};
1625
type RefType = HTMLTextAreaElement | null;
1726

1827
const TextAreaContainer = styled.textarea<{
1928
variant: TextAreaVariant;
2029
width?: CSSProperties['width'];
2130
height?: CSSProperties['height'];
31+
autoGrow?: boolean;
2232
}>`
2333
padding: ${spacing.r12} ${spacing.r8};
2434
border-radius: 4px;
@@ -46,6 +56,13 @@ const TextAreaContainer = styled.textarea<{
4656
height: ${props.height};
4757
`}
4858
59+
${(props) =>
60+
props.autoGrow &&
61+
css`
62+
resize: none;
63+
overflow: hidden;
64+
`}
65+
4966
&:placeholder-shown {
5067
font-style: italic;
5168
}
@@ -77,18 +94,67 @@ const TextAreaContainer = styled.textarea<{
7794
`;
7895

7996
function TextAreaElement(
80-
{ rows = 3, cols = 20, width, height, variant = 'code', ...rest }: Props,
97+
{
98+
rows = 3,
99+
cols = 20,
100+
width,
101+
height,
102+
variant = 'code',
103+
autoGrow = false,
104+
value,
105+
defaultValue,
106+
onChange,
107+
...rest
108+
}: Props,
81109
ref: ForwardedRef<RefType>,
82110
) {
111+
const internalRef = useRef<HTMLTextAreaElement>(null);
112+
113+
// Expose the textarea element to parent components via forwarded ref
114+
useImperativeHandle(ref, () => internalRef.current as HTMLTextAreaElement);
115+
116+
const adjustHeight = useCallback(() => {
117+
const textarea = internalRef.current;
118+
if (!textarea || !autoGrow) return;
119+
120+
// Reset height to auto to get the correct scrollHeight
121+
textarea.style.height = 'auto';
122+
123+
// Set the height to match the content
124+
const newHeight = textarea.scrollHeight;
125+
textarea.style.height = `${newHeight}px`;
126+
}, [autoGrow]);
127+
128+
// Adjust height when content changes
129+
useEffect(() => {
130+
adjustHeight();
131+
}, [value, defaultValue, autoGrow, adjustHeight]);
132+
133+
// Adjust height on mount
134+
useEffect(() => {
135+
adjustHeight();
136+
}, [adjustHeight]);
137+
138+
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
139+
if (autoGrow) {
140+
adjustHeight();
141+
}
142+
onChange?.(e);
143+
};
144+
83145
if (width || height) {
84146
return (
85147
<TextAreaContainer
86148
className="sc-textarea"
87149
width={width}
88150
height={height}
89151
variant={variant}
152+
autoGrow={autoGrow}
153+
value={value}
154+
defaultValue={defaultValue}
155+
onChange={handleChange}
90156
{...rest}
91-
ref={ref}
157+
ref={internalRef}
92158
/>
93159
);
94160
}
@@ -99,8 +165,12 @@ function TextAreaElement(
99165
rows={rows}
100166
cols={cols}
101167
variant={variant}
168+
autoGrow={autoGrow}
169+
value={value}
170+
defaultValue={defaultValue}
171+
onChange={handleChange}
102172
{...rest}
103-
ref={ref}
173+
ref={internalRef}
104174
/>
105175
);
106176
}

stories/textarea.stories.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,57 @@ export const RowsAndColsSet = {
6767
placeholder: 'With rows = 20 and cols = 40',
6868
},
6969
};
70+
71+
/**
72+
* Auto-growing textarea adjusts its height based on content
73+
* Perfect for displaying commands or long text where you want the entire content visible
74+
* Simply set autoGrow={true} and the textarea will grow to show all content
75+
*/
76+
export const AutoGrowTextArea = {
77+
args: {
78+
autoGrow: true,
79+
placeholder:
80+
'Type or paste content here...\nThe textarea will automatically grow to fit all the content.',
81+
value: `docker run -d \\
82+
--name my-container \\
83+
-p 8080:80 \\
84+
-v /host/path:/container/path \\
85+
-e ENV_VAR=value \\
86+
my-image:latest`,
87+
width: '500px',
88+
},
89+
};
90+
91+
/**
92+
* Auto-growing textarea with long command example
93+
* The entire command is visible without scrolling
94+
*/
95+
export const AutoGrowWithLongCommand = {
96+
args: {
97+
autoGrow: true,
98+
variant: 'code',
99+
value: `kubectl apply -f - <<EOF
100+
apiVersion: v1
101+
kind: Pod
102+
metadata:
103+
name: my-pod
104+
labels:
105+
app: myapp
106+
spec:
107+
containers:
108+
- name: nginx
109+
image: nginx:1.14.2
110+
ports:
111+
- containerPort: 80
112+
env:
113+
- name: DATABASE_URL
114+
value: "postgresql://user:password@localhost:5432/db"
115+
- name: API_KEY
116+
valueFrom:
117+
secretKeyRef:
118+
name: api-secret
119+
key: api-key
120+
EOF`,
121+
width: '600px',
122+
},
123+
};

0 commit comments

Comments
 (0)