Skip to content

Commit 402f6bd

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

File tree

2 files changed

+118
-3
lines changed

2 files changed

+118
-3
lines changed

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

Lines changed: 62 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,20 @@ 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+
* It disables the resize property
23+
*/
24+
autoGrow?: boolean;
1525
};
1626
type RefType = HTMLTextAreaElement | null;
1727

1828
const TextAreaContainer = styled.textarea<{
1929
variant: TextAreaVariant;
2030
width?: CSSProperties['width'];
2131
height?: CSSProperties['height'];
32+
autoGrow?: boolean;
2233
}>`
2334
padding: ${spacing.r12} ${spacing.r8};
2435
border-radius: 4px;
@@ -46,6 +57,13 @@ const TextAreaContainer = styled.textarea<{
4657
height: ${props.height};
4758
`}
4859
60+
${(props) =>
61+
props.autoGrow &&
62+
css`
63+
resize: none;
64+
overflow: hidden;
65+
`}
66+
4967
&:placeholder-shown {
5068
font-style: italic;
5169
}
@@ -77,18 +95,55 @@ const TextAreaContainer = styled.textarea<{
7795
`;
7896

7997
function TextAreaElement(
80-
{ rows = 3, cols = 20, width, height, variant = 'code', ...rest }: Props,
98+
{
99+
rows = 3,
100+
cols = 20,
101+
width,
102+
height,
103+
variant = 'code',
104+
autoGrow = false,
105+
value,
106+
defaultValue,
107+
onChange,
108+
...rest
109+
}: Props,
81110
ref: ForwardedRef<RefType>,
82111
) {
112+
const internalRef = useRef<HTMLTextAreaElement>(null);
113+
114+
// Expose the textarea element to parent components via forwarded ref
115+
useImperativeHandle(ref, () => internalRef.current as HTMLTextAreaElement);
116+
117+
const adjustHeight = useCallback(() => {
118+
const textarea = internalRef.current;
119+
if (!textarea || !autoGrow) return;
120+
121+
// Reset height to auto to get the correct scrollHeight
122+
textarea.style.height = 'auto';
123+
124+
// Set the height to match the content
125+
const newHeight = textarea.scrollHeight;
126+
textarea.style.height = `${newHeight}px`;
127+
}, [autoGrow]);
128+
129+
// Adjust height on mount to fit initial content
130+
useEffect(() => {
131+
adjustHeight();
132+
}, []);
133+
83134
if (width || height) {
84135
return (
85136
<TextAreaContainer
86137
className="sc-textarea"
87138
width={width}
88139
height={height}
89140
variant={variant}
141+
autoGrow={autoGrow}
142+
value={value}
143+
defaultValue={defaultValue}
144+
onChange={onChange}
90145
{...rest}
91-
ref={ref}
146+
ref={internalRef}
92147
/>
93148
);
94149
}
@@ -99,8 +154,12 @@ function TextAreaElement(
99154
rows={rows}
100155
cols={cols}
101156
variant={variant}
157+
autoGrow={autoGrow}
158+
value={value}
159+
defaultValue={defaultValue}
160+
onChange={onChange}
102161
{...rest}
103-
ref={ref}
162+
ref={internalRef}
104163
/>
105164
);
106165
}

stories/textarea.stories.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,59 @@ 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+
readOnly: true,
89+
},
90+
};
91+
92+
/**
93+
* Auto-growing textarea with long command example
94+
* The entire command is visible without scrolling
95+
*/
96+
export const AutoGrowWithLongCommand = {
97+
args: {
98+
autoGrow: true,
99+
variant: 'code',
100+
value: `kubectl apply -f - <<EOF
101+
apiVersion: v1
102+
kind: Pod
103+
metadata:
104+
name: my-pod
105+
labels:
106+
app: myapp
107+
spec:
108+
containers:
109+
- name: nginx
110+
image: nginx:1.14.2
111+
ports:
112+
- containerPort: 80
113+
env:
114+
- name: DATABASE_URL
115+
value: "postgresql://user:password@localhost:5432/db"
116+
- name: API_KEY
117+
valueFrom:
118+
secretKeyRef:
119+
name: api-secret
120+
key: api-key
121+
EOF`,
122+
width: '600px',
123+
readOnly: true,
124+
},
125+
};

0 commit comments

Comments
 (0)