@telegraph/textarea 0.1.0 → 0.1.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @telegraph/textarea
2
2
 
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @telegraph/typography@0.1.24
9
+
10
+ ## 0.1.1
11
+
12
+ ### Patch Changes
13
+
14
+ - [#524](https://github.com/knocklabs/telegraph/pull/524) [`fd14d50`](https://github.com/knocklabs/telegraph/commit/fd14d509c3f3f76eafc07d08c73e30db79255a2e) Thanks [@kylemcd](https://github.com/kylemcd)! - bump packages to get tokens upgrades
15
+
16
+ - Updated dependencies []:
17
+ - @telegraph/typography@0.1.23
18
+
3
19
  ## 0.1.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -1,17 +1,471 @@
1
- ![Telegraph by Knock](https://github.com/knocklabs/telegraph/assets/29106675/9b5022e3-b02c-4582-ba57-3d6171e45e44)
1
+ # 📝 TextArea
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@telegraph/button.svg)](https://www.npmjs.com/package/@telegraph/textarea)
3
+ > Multi-line text input component with auto-resize, character counting, and form integration capabilities.
4
4
 
5
- # @telegraph/textarea
6
- > A multi-line user input.
5
+ ![Telegraph by Knock](https://github.com/knocklabs/telegraph/assets/29106675/9b5022e3-b02c-4582-ba57-3d6171e45e44)
7
6
 
8
- ## Installation Instructions
7
+ [![npm version](https://img.shields.io/npm/v/@telegraph/textarea.svg)](https://www.npmjs.com/package/@telegraph/textarea)
8
+ [![minzipped size](https://img.shields.io/bundlephobia/minzip/@telegraph/textarea)](https://bundlephobia.com/result?p=@telegraph/textarea)
9
+ [![license](https://img.shields.io/npm/l/@telegraph/textarea)](https://github.com/knocklabs/telegraph/blob/main/LICENSE)
9
10
 
10
- ```
11
+ ## Installation
12
+
13
+ ```bash
11
14
  npm install @telegraph/textarea
12
15
  ```
13
16
 
14
17
  ### Add stylesheet
18
+
19
+ Pick one:
20
+
21
+ Via CSS (preferred):
22
+
23
+ ```css
24
+ @import "@telegraph/textarea";
25
+ ```
26
+
27
+ Via Javascript:
28
+
29
+ ```tsx
30
+ import "@telegraph/textarea/default.css";
31
+ ```
32
+
33
+ > Then, include `className="tgph"` on the farthest parent element wrapping the telegraph components
34
+
35
+ ## Quick Start
36
+
37
+ ```tsx
38
+ import { TextArea } from "@telegraph/textarea";
39
+ import { useState } from "react";
40
+
41
+ export const CommentBox = () => {
42
+ const [comment, setComment] = useState("");
43
+
44
+ return (
45
+ <TextArea
46
+ value={comment}
47
+ onChange={(e) => setComment(e.target.value)}
48
+ placeholder="Share your thoughts..."
49
+ maxLength={500}
50
+ showCharacterCount
51
+ autoResize
52
+ />
53
+ );
54
+ };
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### `<TextArea>`
60
+
61
+ The main textarea component with enhanced features for multi-line text input.
62
+
63
+ | Prop | Type | Default | Description |
64
+ | -------------------- | ------------------------------------------------ | ------------ | ------------------------------------------------ |
65
+ | `size` | `"0" \| "1" \| "2" \| "3" \| "4"` | `"1"` | Size of the textarea |
66
+ | `variant` | `"ghost" \| "outline"` | `"outline"` | Visual style variant |
67
+ | `autoResize` | `boolean` | `false` | Automatically adjust height based on content |
68
+ | `minRows` | `number` | `3` | Minimum number of visible rows |
69
+ | `maxRows` | `number` | `undefined` | Maximum number of visible rows (for auto-resize) |
70
+ | `showCharacterCount` | `boolean` | `false` | Display character count indicator |
71
+ | `maxLength` | `number` | `undefined` | Maximum allowed characters |
72
+ | `resize` | `"none" \| "vertical" \| "horizontal" \| "both"` | `"vertical"` | CSS resize behavior |
73
+ | `state` | `"default" \| "error" \| "success"` | `"default"` | Validation state |
74
+ | `errorMessage` | `string` | `undefined` | Error message to display |
75
+ | `helperText` | `string` | `undefined` | Helper text to display |
76
+
77
+ All standard HTML textarea attributes are also supported (value, onChange, placeholder, disabled, etc.).
78
+
79
+ ## Usage Patterns
80
+
81
+ ### Basic Textarea
82
+
83
+ ```tsx
84
+ import { TextArea } from "@telegraph/textarea";
85
+
86
+ export const BasicExample = () => (
87
+ <TextArea placeholder="Enter your message..." rows={4} />
88
+ );
89
+ ```
90
+
91
+ ### Different Sizes
92
+
93
+ ```tsx
94
+ import { TextArea } from "@telegraph/textarea";
95
+
96
+ export const SizedTextAreas = () => (
97
+ <div>
98
+ <TextArea size="0" placeholder="Small textarea" />
99
+ <TextArea size="1" placeholder="Medium textarea" />
100
+ <TextArea size="2" placeholder="Large textarea" />
101
+ </div>
102
+ );
103
+ ```
104
+
105
+ ### With Character Limit
106
+
107
+ ```tsx
108
+ import { TextArea } from "@telegraph/textarea";
109
+ import { useState } from "react";
110
+
111
+ export const CharacterLimit = () => {
112
+ const [value, setValue] = useState("");
113
+
114
+ return (
115
+ <TextArea
116
+ value={value}
117
+ onChange={(e) => setValue(e.target.value)}
118
+ placeholder="Write a tweet..."
119
+ maxLength={280}
120
+ showCharacterCount
121
+ />
122
+ );
123
+ };
124
+ ```
125
+
126
+ ### Auto-resize
127
+
128
+ ```tsx
129
+ import { TextArea } from "@telegraph/textarea";
130
+
131
+ export const AutoResizeExample = () => (
132
+ <TextArea
133
+ autoResize
134
+ minRows={2}
135
+ maxRows={10}
136
+ placeholder="This textarea will grow as you type..."
137
+ />
138
+ );
139
+ ```
140
+
141
+ ### Validation States
142
+
143
+ ```tsx
144
+ import { TextArea } from "@telegraph/textarea";
145
+
146
+ export const ValidationStates = () => (
147
+ <div>
148
+ <TextArea
149
+ state="error"
150
+ errorMessage="This field is required"
151
+ placeholder="Error state"
152
+ />
153
+
154
+ <TextArea
155
+ state="success"
156
+ helperText="Looks good!"
157
+ placeholder="Success state"
158
+ />
159
+ </div>
160
+ );
161
+ ```
162
+
163
+ ### Different Variants
164
+
165
+ ```tsx
166
+ import { TextArea } from "@telegraph/textarea";
167
+
168
+ export const Variants = () => (
169
+ <div>
170
+ <TextArea variant="outline" placeholder="Outline variant" />
171
+ <TextArea variant="ghost" placeholder="Ghost variant" />
172
+ </div>
173
+ );
15
174
  ```
16
- @import "@telegraph/textarea"
175
+
176
+ ## Advanced Usage
177
+
178
+ ### Form Integration
179
+
180
+ ```tsx
181
+ import { TextArea } from "@telegraph/textarea";
182
+ import { Controller, useForm } from "react-hook-form";
183
+
184
+ type FormData = {
185
+ description: string;
186
+ notes: string;
187
+ };
188
+
189
+ export const FormExample = () => {
190
+ const {
191
+ control,
192
+ handleSubmit,
193
+ formState: { errors },
194
+ } = useForm<FormData>();
195
+
196
+ return (
197
+ <form onSubmit={handleSubmit(console.log)}>
198
+ <div>
199
+ <label htmlFor="description">Description</label>
200
+ <Controller
201
+ name="description"
202
+ control={control}
203
+ rules={{
204
+ required: "Description is required",
205
+ minLength: { value: 10, message: "Minimum 10 characters" },
206
+ }}
207
+ render={({ field, fieldState }) => (
208
+ <TextArea
209
+ {...field}
210
+ state={fieldState.error ? "error" : "default"}
211
+ errorMessage={fieldState.error?.message}
212
+ placeholder="Describe your project..."
213
+ maxLength={1000}
214
+ showCharacterCount
215
+ autoResize
216
+ />
217
+ )}
218
+ />
219
+ </div>
220
+
221
+ <div>
222
+ <label htmlFor="notes">Additional Notes</label>
223
+ <Controller
224
+ name="notes"
225
+ control={control}
226
+ render={({ field }) => (
227
+ <TextArea
228
+ {...field}
229
+ placeholder="Any additional notes (optional)"
230
+ variant="ghost"
231
+ maxLength={500}
232
+ showCharacterCount
233
+ />
234
+ )}
235
+ />
236
+ </div>
237
+
238
+ <button type="submit">Submit</button>
239
+ </form>
240
+ );
241
+ };
242
+ ```
243
+
244
+ ## Accessibility
245
+
246
+ - ✅ **Keyboard Navigation**: Full keyboard support for text input and navigation
247
+ - ✅ **Screen Reader Support**: Proper labeling and error announcement
248
+ - ✅ **Focus Management**: Clear focus indicators and logical tab order
249
+ - ✅ **High Contrast**: Compatible with high contrast modes
250
+ - ✅ **Error Handling**: Clear error states and validation messages
251
+
252
+ ### Keyboard Shortcuts
253
+
254
+ | Key | Action |
255
+ | -------------- | ------------------------------- |
256
+ | `Tab` | Move focus to/from the textarea |
257
+ | `Enter` | Insert new line |
258
+ | `Ctrl/Cmd + A` | Select all text |
259
+ | `Ctrl/Cmd + Z` | Undo last action |
260
+ | `Ctrl/Cmd + Y` | Redo last action |
261
+
262
+ ### ARIA Attributes
263
+
264
+ - `aria-label` or `aria-labelledby` - Associates labels with textarea
265
+ - `aria-describedby` - Links help text and error messages
266
+ - `aria-invalid` - Indicates validation state
267
+ - `role="textbox"` - Explicitly identifies as text input
268
+ - `aria-multiline="true"` - Indicates multi-line input
269
+
270
+ ### Best Practices
271
+
272
+ 1. **Provide Clear Labels**: Always include accessible labels for textareas
273
+ 2. **Error Messaging**: Use clear, specific error messages
274
+ 3. **Helper Text**: Provide helpful guidance about expected input
275
+ 4. **Character Limits**: Clearly communicate length restrictions
276
+ 5. **Auto-resize Limits**: Set reasonable min/max bounds for auto-resize
277
+
278
+ ## Examples
279
+
280
+ ### Basic Example
281
+
282
+ ```tsx
283
+ import { TextArea } from "@telegraph/textarea";
284
+
285
+ export const FeedbackForm = () => (
286
+ <div>
287
+ <label htmlFor="feedback">Your Feedback</label>
288
+ <TextArea
289
+ id="feedback"
290
+ placeholder="Tell us what you think..."
291
+ rows={4}
292
+ maxLength={1000}
293
+ showCharacterCount
294
+ />
295
+ </div>
296
+ );
297
+ ```
298
+
299
+ ### Advanced Example
300
+
301
+ ```tsx
302
+ import { TextArea } from "@telegraph/textarea";
303
+ import { useState } from "react";
304
+
305
+ export const BlogPostEditor = () => {
306
+ const [content, setContent] = useState("");
307
+ const [isDraft, setIsDraft] = useState(true);
308
+
309
+ const handleSave = () => {
310
+ // Save logic here
311
+ console.log("Saving:", content);
312
+ };
313
+
314
+ const handlePublish = () => {
315
+ setIsDraft(false);
316
+ // Publish logic here
317
+ console.log("Publishing:", content);
318
+ };
319
+
320
+ return (
321
+ <div className="blog-editor">
322
+ <div className="editor-header">
323
+ <h2>Write your blog post</h2>
324
+ <div className="status">Status: {isDraft ? "Draft" : "Published"}</div>
325
+ </div>
326
+
327
+ <TextArea
328
+ value={content}
329
+ onChange={(e) => setContent(e.target.value)}
330
+ placeholder="Start writing your blog post..."
331
+ autoResize
332
+ minRows={10}
333
+ maxRows={30}
334
+ showCharacterCount
335
+ helperText="Write in markdown format. Use **bold** and *italic* for formatting."
336
+ />
337
+
338
+ <div className="editor-actions">
339
+ <button onClick={handleSave} disabled={!content}>
340
+ Save Draft
341
+ </button>
342
+ <button
343
+ onClick={handlePublish}
344
+ disabled={!content || content.length < 100}
345
+ className="primary"
346
+ >
347
+ Publish Post
348
+ </button>
349
+ </div>
350
+ </div>
351
+ );
352
+ };
353
+ ```
354
+
355
+ ### Real-world Example
356
+
357
+ ```tsx
358
+ import { TextArea } from "@telegraph/textarea";
359
+ import { useEffect, useState } from "react";
360
+
361
+ export const CustomerSupportTicket = () => {
362
+ const [ticket, setTicket] = useState({
363
+ subject: "",
364
+ description: "",
365
+ priority: "medium",
366
+ });
367
+ const [attachments, setAttachments] = useState([]);
368
+ const [isSubmitting, setIsSubmitting] = useState(false);
369
+
370
+ const handleSubmit = async (e: React.FormEvent) => {
371
+ e.preventDefault();
372
+ setIsSubmitting(true);
373
+
374
+ try {
375
+ // Submit ticket logic
376
+ await submitTicket({ ...ticket, attachments });
377
+ alert("Ticket submitted successfully!");
378
+ } catch (error) {
379
+ alert("Failed to submit ticket. Please try again.");
380
+ } finally {
381
+ setIsSubmitting(false);
382
+ }
383
+ };
384
+
385
+ return (
386
+ <form onSubmit={handleSubmit} className="support-ticket">
387
+ <h2>Submit a Support Ticket</h2>
388
+
389
+ <div className="form-group">
390
+ <label htmlFor="subject">Subject</label>
391
+ <input
392
+ id="subject"
393
+ type="text"
394
+ value={ticket.subject}
395
+ onChange={(e) => setTicket({ ...ticket, subject: e.target.value })}
396
+ required
397
+ />
398
+ </div>
399
+
400
+ <div className="form-group">
401
+ <label htmlFor="priority">Priority</label>
402
+ <select
403
+ id="priority"
404
+ value={ticket.priority}
405
+ onChange={(e) => setTicket({ ...ticket, priority: e.target.value })}
406
+ >
407
+ <option value="low">Low</option>
408
+ <option value="medium">Medium</option>
409
+ <option value="high">High</option>
410
+ <option value="urgent">Urgent</option>
411
+ </select>
412
+ </div>
413
+
414
+ <div className="form-group">
415
+ <label htmlFor="description">Description</label>
416
+ <TextArea
417
+ id="description"
418
+ value={ticket.description}
419
+ onChange={(e) =>
420
+ setTicket({ ...ticket, description: e.target.value })
421
+ }
422
+ placeholder="Please describe your issue in detail. Include any error messages, steps to reproduce, and screenshots if applicable."
423
+ autoResize
424
+ minRows={6}
425
+ maxRows={15}
426
+ maxLength={5000}
427
+ showCharacterCount
428
+ state={ticket.description.length < 50 ? "default" : "success"}
429
+ helperText="Please provide at least 50 characters for a detailed description."
430
+ required
431
+ />
432
+ </div>
433
+
434
+ <div className="form-group">
435
+ <label>Attachments</label>
436
+ <FileUpload
437
+ onFilesChange={setAttachments}
438
+ maxFiles={5}
439
+ acceptedTypes={[".png", ".jpg", ".pdf", ".txt"]}
440
+ />
441
+ </div>
442
+
443
+ <div className="form-actions">
444
+ <button
445
+ type="submit"
446
+ disabled={
447
+ isSubmitting || !ticket.subject || ticket.description.length < 50
448
+ }
449
+ className="primary"
450
+ >
451
+ {isSubmitting ? "Submitting..." : "Submit Ticket"}
452
+ </button>
453
+ </div>
454
+ </form>
455
+ );
456
+ };
17
457
  ```
458
+
459
+ ## References
460
+
461
+ - [Storybook Demo](https://storybook.telegraph.dev/?path=/docs/textarea)
462
+ - [Input Component](../input/README.md) - Related single-line input component
463
+ - [Typography Component](../typography/README.md) - For text styling
464
+
465
+ ## Contributing
466
+
467
+ See our [Contributing Guide](../../CONTRIBUTING.md) for more details.
468
+
469
+ ## License
470
+
471
+ MIT License - see [LICENSE](../../LICENSE) for details.
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/TextArea/TextArea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,KAAK,IAAI,EAET,KAAK,OAAO,EAIb,MAAM,sBAAsB,CAAC;AAQ9B,KAAK,iBAAiB,GAAG;IACvB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,CAAC;IACrD,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;CAC3D,CAAC;AAEF,KAAK,aAAa,GAAG,iBAAiB,GACpC,kBAAkB,CAAC,OAAO,IAAI,CAAC,GAC/B,KAAK,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC;AAE7C,QAAA,MAAM,QAAQ,+EASX,aAAa,4CAmBf,CAAC;AAEF,KAAK,qBAAqB,GAAG,kBAAkB,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,KAAK,qBAAqB,IAAI,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/TextArea/TextArea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,KAAK,IAAI,EAET,KAAK,OAAO,EAIb,MAAM,sBAAsB,CAAC;AAQ9B,KAAK,iBAAiB,GAAG;IACvB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,CAAC;IACrD,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;CAC3D,CAAC;AAEF,KAAK,aAAa,GAAG,iBAAiB,GACpC,kBAAkB,CAAC,OAAO,IAAI,CAAC,GAC/B,KAAK,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC;AAE7C,QAAA,MAAM,QAAQ,GAAI,4EASf,aAAa,4CAmBf,CAAC;AAEF,KAAK,qBAAqB,GAAG,kBAAkB,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,KAAK,qBAAqB,IAAI,aAAa,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telegraph/textarea",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A multi-line user input.",
5
5
  "repository": "https://github.com/knocklabs/telegraph/tree/main/packages/textarea",
6
6
  "author": "@knocklabs",
@@ -33,19 +33,19 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@telegraph/helpers": "^0.0.13",
36
- "@telegraph/typography": "^0.1.22"
36
+ "@telegraph/typography": "^0.1.24"
37
37
  },
38
38
  "devDependencies": {
39
- "@knocklabs/eslint-config": "^0.0.4",
39
+ "@knocklabs/eslint-config": "^0.0.5",
40
40
  "@knocklabs/typescript-config": "^0.0.2",
41
- "@telegraph/postcss-config": "^0.0.28",
41
+ "@telegraph/postcss-config": "^0.0.30",
42
42
  "@telegraph/prettier-config": "^0.0.7",
43
43
  "@telegraph/vite-config": "^0.0.15",
44
- "@types/react": "^18.3.18",
44
+ "@types/react": "^19.1.11",
45
45
  "eslint": "^8.56.0",
46
- "react": "^18.3.1",
47
- "react-dom": "^18.3.1",
48
- "typescript": "^5.7.3",
46
+ "react": "^19.1.1",
47
+ "react-dom": "^19.1.1",
48
+ "typescript": "^5.9.2",
49
49
  "vite": "^6.0.11"
50
50
  },
51
51
  "peerDependencies": {