@hubspot/ui-extensions 0.0.0 → 0.0.1-beta.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.
package/README.md ADDED
@@ -0,0 +1,1715 @@
1
+ # UI Extensions
2
+
3
+ React components and utilities for extending HubSpot's UI.
4
+
5
+ ## TOC
6
+
7
+ - [Alert](#alert)
8
+ - [Button](#button)
9
+ - [ButtonRow](#buttonrow)
10
+ - [Card](#card)
11
+ - [DescriptionList](#descriptionlist)
12
+ - [DescriptionListItem](#descriptionlistitem)
13
+ - [Divider](#divider)
14
+ - [Form](#form)
15
+ - [EmptyState](#emptystate)
16
+ - [ErrorState](#errorstate)
17
+ - [Heading](#heading)
18
+ - [Image](#image)
19
+ - [Input](#input)
20
+ - [Link](#link)
21
+ - [LoadingSpinner](#loadingspinner)
22
+ - [ProgressBar](#progressbar)
23
+ - [Select](#select)
24
+ - [Stack](#stack)
25
+ - [Statistics](#statistics)
26
+ - [StatisticsItem](#statisticsitem)
27
+ - [StatisticsTrend](#statisticstrend)
28
+ - [Table](#table)
29
+ - [TableBody](#tablebody)
30
+ - [TableCell](#tablecell)
31
+ - [TableFooter](#tablefooter)
32
+ - [TableHead](#tablehead)
33
+ - [TableHeader](#tableheader)
34
+ - [TableRow](#tablerow)
35
+ - [Tag](#tag)
36
+ - [Text](#text)
37
+ - [Textarea](#textarea)
38
+ - [Tile](#tile)
39
+ - [ToggleGroup](#togglegroup)
40
+
41
+ ## Components
42
+
43
+ ### Alert
44
+
45
+ ##### Import
46
+
47
+ ```javascript
48
+ import { Alert } from '@hubspot/ui-extensions';
49
+ ```
50
+
51
+ ##### Props
52
+
53
+ The Alert component accepts the following props:
54
+
55
+ ```typescript
56
+ export interface AlertProps {
57
+ title: string;
58
+ body?: string;
59
+ children?: ReactNode;
60
+ variant?: 'info' | 'warning' | 'success' | 'error' | 'danger';
61
+ }
62
+ ```
63
+
64
+ | Prop | Type | Default | Description |
65
+ | --- | --- | --- | --- |
66
+ | `title` | `string` | `N/A` | The title text for the alert message. |
67
+ | `body` | `string(optional)` | `N/A` | The main content of the alert message. If not provided, the children prop is used. |
68
+ | `children` | `ReactNode(optional)` | `N/A` | The main content of the alert message when the body prop is not provided. |
69
+ | `variant` | `'info' \| 'warning' \| 'success' \|'error' \| 'danger'` `(optional)` | `'info'` | Sets the color variation of the alert |
70
+
71
+ ##### Usage
72
+
73
+ ```javascript
74
+ const Extension = () => {
75
+ return (
76
+ <>
77
+ <Alert title="Important Info" variant="info">
78
+ This is an informative message.
79
+ </Alert>
80
+ <Alert
81
+ title="Success"
82
+ body="Operation completed successfully."
83
+ variant="success"
84
+ />
85
+ <Alert title="Warning" body="Proceed with caution." variant="warning" />
86
+ <Alert
87
+ title="Error"
88
+ body="Something went wrong. Please try again."
89
+ variant="error"
90
+ />
91
+ <Alert
92
+ title="Danger"
93
+ body="This action cannot be undone. Be careful."
94
+ variant="danger"
95
+ />
96
+ </>
97
+ );
98
+ };
99
+ ```
100
+
101
+ ### Button
102
+
103
+ ##### Import
104
+
105
+ ```javascript
106
+ import { Button } from '@hubspot/ui-extensions';
107
+ ```
108
+
109
+ ##### Props
110
+
111
+ ```typescript
112
+ interface ButtonProps {
113
+ children: string;
114
+ onClick?: () => void;
115
+ href?: string;
116
+ disabled?: boolean;
117
+ variant?: 'primary' | 'secondary' | 'destructive';
118
+ type?: 'button' | 'reset' | 'submit';
119
+ }
120
+ ```
121
+
122
+ | Prop | Type | Default | Description |
123
+ | --- | --- | --- | --- |
124
+ | `children` | `string` | `N/A` | The displayable text for the Button |
125
+ | `onClick` | `() => void` `(optional)` | `N/A` | A function that will be invoked when the button is clicked. It receives no arguments and it's return value is ignored |
126
+ | `href` | `string(optional)` | `N/A` | A URL that will be opened when the button is clicked. If the value is a URL external to HubSpot it will be opened in a new tab. |
127
+ | `disabled` | `boolean(optional)` | `N/A` | Determines if the button should be disabled or not. |
128
+ | `variant` | `'primary' \| 'secondary' \| 'destructive'` `(optional)` | `'secondary'` | Sets the color variation of the button |
129
+ | `type` | `'button' \| 'reset' \| 'submit'` `(optional)` | `'button'` | Sets the HTML attribute "role" of the button |
130
+
131
+ ##### Usage
132
+
133
+ ```javascript
134
+ const Extension = () => {
135
+ return (
136
+ <Button
137
+ onClick={() => {
138
+ console.log('Someone clicked on the button!');
139
+ }}
140
+ href="https://hubspot.com"
141
+ variant="destructive"
142
+ type="button"
143
+ >
144
+ Click me!
145
+ </Button>
146
+ );
147
+ };
148
+ ```
149
+
150
+ ### ButtonRow
151
+
152
+ ##### Import
153
+
154
+ ```javascript
155
+ import { ButtonRow } from '@hubspot/ui-extensions';
156
+ ```
157
+
158
+ ##### Props
159
+
160
+ ```typescript
161
+ interface ButtonRowProps {
162
+ children: ReactNode;
163
+ disableDropdown?: boolean;
164
+ }
165
+ ```
166
+
167
+ | Prop | Type | Default | Description |
168
+ | --- | --- | --- | --- |
169
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
170
+ | `disableDropdown` | `boolean(optional)` | `false` | Disables the dropdown list of buttons that appears the the children expand beyond the horizontal space |
171
+
172
+ ##### Usage
173
+
174
+ ```javascript
175
+ const Extension = () => {
176
+ return (
177
+ <ButtonRow disableDropdown={false}>
178
+ <Button
179
+ onClick={() => {
180
+ console.log('Regular button clicked');
181
+ }}
182
+ >
183
+ Regular Button
184
+ </Button>
185
+ <Button
186
+ onClick={() => {
187
+ console.log('Reset button clicked');
188
+ }}
189
+ variant="destructive"
190
+ type="reset"
191
+ >
192
+ Reset
193
+ </Button>
194
+ <Button
195
+ onClick={() => {
196
+ console.log('Submit button clicked');
197
+ }}
198
+ variant="primary"
199
+ type="submit"
200
+ >
201
+ Submit
202
+ </Button>
203
+ </ButtonRow>
204
+ );
205
+ };
206
+ ```
207
+
208
+ ### Card
209
+
210
+ ##### Import
211
+
212
+ ```javascript
213
+ import { Card } from '@hubspot/ui-extensions';
214
+ ```
215
+
216
+ ##### Props
217
+
218
+ ```typescript
219
+ interface CardProps {
220
+ children: ReactNode;
221
+ }
222
+ ```
223
+
224
+ | Prop | Type | Default | Description |
225
+ | --- | --- | --- | --- |
226
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
227
+
228
+ ##### Usage
229
+
230
+ ```javascript
231
+ const Extension = () => {
232
+ return (
233
+ <Card>
234
+ <Button
235
+ onClick={() => {
236
+ console.log('Regular button clicked');
237
+ }}
238
+ >
239
+ Regular Button
240
+ </Button>
241
+ </Card>
242
+ );
243
+ };
244
+ ```
245
+
246
+ ### DescriptionList
247
+
248
+ ##### Import
249
+
250
+ ```javascript
251
+ import { DescriptionList } from '@hubspot/ui-extensions';
252
+ ```
253
+
254
+ ##### Props
255
+
256
+ ```typescript
257
+ interface DescriptionListProps {
258
+ children: ReactNode;
259
+ direction?: 'row' | 'column';
260
+ }
261
+ ```
262
+
263
+ | Prop | Type | Default | Description |
264
+ | --- | --- | --- | --- |
265
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. The children should be [DescriptionListItem](#descriptionlistitem) |
266
+ | `direction` | `'row' \| 'column'` `(optional)` | `'column'` | The direction the label/value pairs are placed in the description list container. |
267
+
268
+ ##### Usage
269
+
270
+ ```javascript
271
+ const Extension = () => {
272
+ return (
273
+ <DescriptionList direction="row">
274
+ <DescriptionListItem label={'First Name'}>
275
+ <Text>Alan</Text>
276
+ </DescriptionListItem>
277
+ <DescriptionListItem label={'Last Name'}>
278
+ <Text>Turing</Text>
279
+ </DescriptionListItem>
280
+ </DescriptionList>
281
+ );
282
+ };
283
+ ```
284
+
285
+ #### DescriptionListItem
286
+
287
+ ##### Import
288
+
289
+ ```javascript
290
+ import { DescriptionListItem } from '@hubspot/ui-extensions';
291
+ ```
292
+
293
+ ##### Props
294
+
295
+ ```typescript
296
+ interface DescriptionListItemProps {
297
+ children: ReactNode;
298
+ label: string;
299
+ }
300
+ ```
301
+
302
+ | Prop | Type | Default | Description |
303
+ | --- | --- | --- | --- |
304
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
305
+ | `label` | `string` | `N/A` | Text to be displayed as the label |
306
+
307
+ ##### Usage
308
+
309
+ ```javascript
310
+ const Extension = () => {
311
+ return (
312
+ <DescriptionList direction="row">
313
+ <DescriptionListItem label={'First Name'}>
314
+ <Text>Alan</Text>
315
+ </DescriptionListItem>
316
+ <DescriptionListItem label={'Last Name'}>
317
+ <Text>Turing</Text>
318
+ </DescriptionListItem>
319
+ </DescriptionList>
320
+ );
321
+ };
322
+ ```
323
+
324
+ ### Divider
325
+
326
+ ##### Import
327
+
328
+ ```javascript
329
+ import { Divider } from '@hubspot/ui-extensions';
330
+ ```
331
+
332
+ ##### Props
333
+
334
+ ```typescript
335
+ interface DividerProps {
336
+ distance?:
337
+ | 'flush'
338
+ | 'extra-small'
339
+ | 'small'
340
+ | 'medium'
341
+ | 'large'
342
+ | 'extra-large';
343
+ }
344
+ ```
345
+
346
+ | Prop | Type | Default | Description |
347
+ | --- | --- | --- | --- |
348
+ | `distance` | `'flush'\| 'extra-small' \| 'small' \| 'medium' \| 'large'\| 'extra-large'` `(optional)` | `'small'` | The size of the padding above and below the divider. |
349
+
350
+ ##### Usage
351
+
352
+ ```javascript
353
+ const Extension = () => {
354
+ return <Divider distance="extra-large" />;
355
+ };
356
+ ```
357
+
358
+ ### Form
359
+
360
+ ##### Import
361
+
362
+ ```javascript
363
+ import { Form } from '@hubspot/ui-extensions';
364
+ ```
365
+
366
+ ##### Props
367
+
368
+ ```typescript
369
+ interface FormProps {
370
+ children: ReactNode;
371
+ onSubmit?: (event: RemoteEvent<FormInputValues>) => void;
372
+ preventDefault?: boolean;
373
+ }
374
+ ```
375
+
376
+ | Prop | Type | Default | Description |
377
+ | --- | --- | --- | --- |
378
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
379
+ | `onSubmit` | `function(optional)` | `N/A` | A function that will be called when the form is submitted. It will receive a `RemoteEvent` as argument and it's return value will be ignored. |
380
+ | `preventDefault` | `boolean(optional)` | `false` | If set to `true` `event.preventDefault()` will be invoked before your `onSubmit` function is called, preventing the default html form behavior. |
381
+
382
+ ##### Usage
383
+
384
+ ```javascript
385
+ const Extension = () => {
386
+ return (
387
+ <Form onSubmit={() => { console.log('Form submitted!')}} preventDefault={true}>
388
+ <Input {...} />
389
+ <Select {...} />
390
+ <Button
391
+ onClick={() => {
392
+ console.log('Submit button clicked');
393
+ }}
394
+ variant="primary"
395
+ type="submit"
396
+ >
397
+ Submit
398
+ </Button>
399
+ </Form>
400
+ );
401
+ }
402
+ ```
403
+
404
+ ### EmptyState
405
+
406
+ ##### Import
407
+
408
+ ```javascript
409
+ import { EmptyState } from '@hubspot/ui-extensions';
410
+ ```
411
+
412
+ ##### Props
413
+
414
+ ```typescript
415
+ interface EmptyStateProps {
416
+ children: ReactNode;
417
+ flush?: boolean;
418
+ imageWidth?: number;
419
+ layout?: 'horizontal' | 'vertical';
420
+ reverseOrder?: boolean;
421
+ title?: string;
422
+ }
423
+ ```
424
+
425
+ | Prop | Type | Default | Description |
426
+ | --- | --- | --- | --- |
427
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
428
+ | `flush` | `boolean(optional)` | `false` | Removes the default vertical margins for the component. |
429
+ | `imageWidth` | `number(optional)` | `250` | The max-width for the image container. |
430
+ | `layout` | `'horizontal' \| 'vertical'` `(optional)` | `'horizontal'` | Sets the layout direction for the content. |
431
+ | `reverseOrder` | `boolean(optional)` | `false` | Swaps the visual order of the text (primary) and image (secondary) content. This ensures the primary content is still presented first to assistive technology. |
432
+ | `title` | `string(optional)` | `Intl('All is not lost.')` | The text for the title header rendered above the `children`. |
433
+
434
+ ##### Usage
435
+
436
+ ```javascript
437
+ const Extension = ({ data }) => {
438
+ if (!data || !data.length) {
439
+ return (
440
+ <EmptyState title="Nothing here yet" layout="vertical" reverseOrder={true}>
441
+ <Text>Go out there and get some leads!</Text>
442
+ </EmptyState>
443
+ )
444
+ }
445
+
446
+ return (
447
+ <Stack>
448
+ {data.map(...)}
449
+ </Stack>
450
+ );
451
+ }
452
+ ```
453
+
454
+ ### ErrorState
455
+
456
+ ##### Import
457
+
458
+ ```javascript
459
+ import { ErrorState } from '@hubspot/ui-extensions';
460
+ ```
461
+
462
+ ##### Props
463
+
464
+ ```typescript
465
+ interface ErrorStateProps {
466
+ children: ReactNode;
467
+ title?: string;
468
+ type?: 'error' | 'support' | 'lock';
469
+ }
470
+ ```
471
+
472
+ | Prop | Type | Default | Description |
473
+ | --- | --- | --- | --- |
474
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
475
+ | `title` | `string(optional)` | `Intl('All is not lost.')` | The text for the title header rendered above the `children`. |
476
+ | `type` | `'error' \| 'support' \| 'lock'` `(optional)` | `'error'` | Sets the type of error image that will be shown. |
477
+
478
+ ##### Usage
479
+
480
+ ```javascript
481
+ const Extension = ({ data, error, fetchData }) => {
482
+ if (error) {
483
+ return (
484
+ <ErrorState title="Trouble fetching properties." layout="vertical" reverseOrder={true}>
485
+ <Stack>
486
+ <Text>
487
+ Please try again in a few moments.
488
+ </Text>
489
+ <Button onClick={fetchData}>
490
+ Try again
491
+ </Button>
492
+ </Stack>
493
+ </ErrorState>
494
+ )
495
+ }
496
+
497
+ return (
498
+ <Stack>
499
+ {data.map(...)}
500
+ </Stack>
501
+ );
502
+ }
503
+ ```
504
+
505
+ ### Heading
506
+
507
+ ##### Import
508
+
509
+ ```javascript
510
+ import { Heading } from '@hubspot/ui-extensions';
511
+ ```
512
+
513
+ ##### Props
514
+
515
+ ```typescript
516
+ interface HeadingProps {
517
+ children: ReactNode;
518
+ inline?: boolean;
519
+ }
520
+ ```
521
+
522
+ | Prop | Type | Default | Description |
523
+ | --- | --- | --- | --- |
524
+ | `children` | `string` | `N/A` | Text to be displayed as heading text. |
525
+ | `inline` | `boolean(optional)` | `false` | Determines if the text will break line or share the space. |
526
+
527
+ ##### Usage
528
+
529
+ ```javascript
530
+ const Extension = () => {
531
+ return <Heading>Plain text, nothing special here</Heading>;
532
+ };
533
+ ```
534
+
535
+ ### Image
536
+
537
+ ##### Import
538
+
539
+ ```javascript
540
+ import { Image } from '@hubspot/ui-extensions';
541
+ ```
542
+
543
+ ##### Props
544
+
545
+ ```typescript
546
+ interface ImageProps {
547
+ alt?: string;
548
+ href?: string;
549
+ onClick?: () => void;
550
+ src: string;
551
+ width?: number;
552
+ }
553
+ ```
554
+
555
+ | Prop | Type | Description |
556
+ | --- | --- | --- |
557
+ | `alt` | `string(optional)` | The alt text for the image, similar to the `alt` attribute for the html [img tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes) |
558
+ | `href` | `string(optional)` | If provided, it will be used as a href that will be opened in a new tag when the image is clicked |
559
+ | `onClick` | `function(optional)` | A function that will be called when the image is clicked on. This function will receive no arguments any returned values will be ignored. |
560
+ | `src` | `string` | The url to the image to display, similar to [img tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes) |
561
+ | `width` | `number(optional)` | The pixel width of the image |
562
+
563
+ ##### Usage
564
+
565
+ ```javascript
566
+ const Extension = () => {
567
+ return (
568
+ <Image
569
+ alt="A picture of an adorable black lab puppy, click on me to see in a new tab"
570
+ src="https://picsum.photos/id/237/200/300"
571
+ href="https://picsum.photos/id/237"
572
+ onClick={() => {
573
+ console.log('Someone clicked on the image!');
574
+ }}
575
+ width={200}
576
+ />
577
+ );
578
+ };
579
+ ```
580
+
581
+ ### Input
582
+
583
+ ##### Import
584
+
585
+ ```javascript
586
+ import { Input } from '@hubspot/ui-extensions';
587
+ ```
588
+
589
+ ##### Props
590
+
591
+ ```typescript
592
+ interface InputProps {
593
+ label: string;
594
+ name: string;
595
+ value?: string;
596
+ required?: boolean;
597
+ readOnly?: boolean;
598
+ description?: string;
599
+ tooltip?: string;
600
+ placeholder?: string;
601
+ error?: boolean;
602
+ validationMessage?: string;
603
+ onChange?: (value: string) => void;
604
+ onInput?: (value: string) => void;
605
+ onBlur?: (value: string) => void;
606
+ onFocus?: (value: string) => void;
607
+ }
608
+ ```
609
+
610
+ | Prop | Type | Default | Description |
611
+ | --- | --- | --- | --- |
612
+ | `label` | `string` | `N/A` | The label text to display for the form input element |
613
+ | `name` | `string` | `N/A` | The unique identifier for the input element, this could be thought of as the HTML5 [Input element's name attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name) |
614
+ | `value` | `string(optional)` | `''` | The value of the input |
615
+ | `required` | `boolean(optional)` | `false` | Determines if the required indicator should be displayed |
616
+ | `readOnly` | `boolean(optional)` | `false` | Determines if the field is editable or not. |
617
+ | `description` | `string(optional)` | `N/A` | Instructional message to display to the user to help understand the purpose of the input. |
618
+ | `tooltip` | `string(optional)` | `N/A` | Text that will appear in a tooltip next to the input label. |
619
+ | `placeholder` | `string(optional)` | `N/A` | Text that appears in the input when it has no value set. |
620
+ | `error` | `boolean(optional)` | `false` | If set to true, `validationMessage` is displayed as an error message, if it was provided. The input will also render it's error state to let the user know there is an error. If false, `validationMessage` is displayed as a success message. |
621
+ | `validationMessage` | `string(optional)` | `''` | The text to show if the input has an error. |
622
+ | `onChange` | `(value: string) => void(optional)` | `N/A` | A callback function that is invoked when the value is committed. Currently these times are `onBlur` of the input and when the user submits the form. |
623
+ | `onInput` | `(value: string) => void(optional)` | `N/A` | A function that is called and passed the value every time the field is edited by the user. It is recommended that you do not use this value to update state, that is what `onChange` should be used for. Instead this should be used for validation. |
624
+ | `onBlur` | `(value: string) => void(optional)` | `N/A` | A function that is called and passed the value every time the field loses focus. |
625
+ | `onFocus` | `(value: string) => void(optional)` | `N/A` | A function that is called and passed the value every time the field gets focused. |
626
+
627
+ ##### Usage
628
+
629
+ ```javascript
630
+ import { useState } from 'react';
631
+
632
+ const Extension = () => {
633
+ const [name, setName] = useState('');
634
+ const [validationMessage, setValidationMessage] = useState('');
635
+ const [isValid, setIsValid] = useState(true);
636
+
637
+ return (
638
+ <Form>
639
+ <Input
640
+ label="First Name"
641
+ name="first-name"
642
+ tooltip="Please enter your first name"
643
+ description="Please enter your first name"
644
+ placeholder="First name"
645
+ required={true}
646
+ error={!isValid}
647
+ validationMessage={validationMessage}
648
+ onChange={value => {
649
+ setName(value);
650
+ }}
651
+ onInput={value => {
652
+ if (value !== 'Bill') {
653
+ setValidationMessage('This form only works for people named Bill');
654
+ setIsValid(false);
655
+ } else if (value === '') {
656
+ setValidationMessage('First name is required');
657
+ setIsValid(false);
658
+ } else {
659
+ setValidationMessage('Valid first name!');
660
+ setIsValid(true);
661
+ }
662
+ }}
663
+ />
664
+ </Form>
665
+ );
666
+ };
667
+ ```
668
+
669
+ ### Link
670
+
671
+ ##### Import
672
+
673
+ ```javascript
674
+ import { Link } from '@hubspot/ui-extensions';
675
+ ```
676
+
677
+ ##### Props
678
+
679
+ ```typescript
680
+ export interface LinkProps {
681
+ href: string;
682
+ variant?: 'primary' | 'destructive' | 'light' | 'dark';
683
+ children: ReactNode;
684
+ }
685
+ ```
686
+
687
+ | Prop | Type | Default | Description |
688
+ | --- | --- | --- | --- |
689
+ | `href` | `string` | `N/A` | A URL that will be opened when the link is clicked. If the value is a URL external to HubSpot it will be opened in a new tab. |
690
+ | `variant` | `'primary' \| 'light' \| 'dark' \| 'destructive'` `(optional)` | `'primary'` | Sets the color variation of the link |
691
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. |
692
+
693
+ ##### Usage
694
+
695
+ ```javascript
696
+ const Extension = () => {
697
+ return <Link href="https://app.hubspot.com/">HubSpot</Link>;
698
+ };
699
+ ```
700
+
701
+ ### LoadingSpinner
702
+
703
+ ##### Import
704
+
705
+ ```javascript
706
+ import { LoadingSpinner } from '@hubspot/ui-extensions';
707
+ ```
708
+
709
+ ##### Props
710
+
711
+ ```typescript
712
+ export interface LoadingSpinnerProps {
713
+ label: string;
714
+ showLabel?: boolean;
715
+ size?: 'xs' | 'sm' | 'md';
716
+ layout?: 'inline' | 'centered';
717
+ }
718
+ ```
719
+
720
+ | Prop | Type | Default | Description |
721
+ | --- | --- | --- | --- |
722
+ | `label` | `string` | `N/A` | The companion text for the loading spinner. |
723
+ | `showLabel` | `boolean(optional)` | `false` | if `true`, the label will be visible alongside the loading spinner. |
724
+ | `size` | `'xs'\| 'sm' \| 'md'` `(optional)` | `'sm'` | The size of the loading spinner icon. |
725
+ | `layout` | `'inline'\| 'centered'` `(optional)` | `N/A` | Use the `centered` option for layout as a convenience for the common pattern of filling the space of its parent. |
726
+
727
+ ##### Usage
728
+
729
+ ```javascript
730
+ const Extension = () => {
731
+ return <LoadingSpinner label="Loading..." />;
732
+ };
733
+ ```
734
+
735
+ ### ProgressBar
736
+
737
+ ##### Import
738
+
739
+ ```javascript
740
+ import { ProgressBar } from '@hubspot/ui-extensions';
741
+ ```
742
+
743
+ ##### Props
744
+
745
+ ```typescript
746
+ export interface ProgressBarProps {
747
+ title?: string;
748
+ showPercentage?: boolean;
749
+ value?: number;
750
+ valueMax?: number;
751
+ valueDescription?: string;
752
+ variant?: 'success' | 'danger' | 'warning';
753
+ }
754
+ ```
755
+
756
+ | Prop | Type | Default | Description |
757
+ | --- | --- | --- | --- |
758
+ | `title` | `string(optional)` | `N/A` | Text to be displayed in the progressbar title. |
759
+ | `showPercentage` | `boolean(optional)` | `false` | Toggles the display of the completion percentage. |
760
+ | `value` | `number(optional)` | `0` | The value of the progress indicator. |
761
+ | `valueMax` | `number(optional)` | `100` | The maximum value of the progress. |
762
+ | `valueDescription` | `string(optional)` | `N/A` | Text that explains the current state of the `value` prop. Renders to the right of `title`. **Example: "10,000 of 7,500"** |
763
+ | `variant` | `'success' \| 'danger' \| 'warning'` | `'success'` | The type of progressbar to display. Defaults to success. |
764
+
765
+ ##### Usage
766
+
767
+ ```javascript
768
+ const Extension = () => {
769
+ return (
770
+ <ProgressBar
771
+ variant="warning"
772
+ value={50}
773
+ valueMax={200}
774
+ showPercentage={true}
775
+ />
776
+ );
777
+ };
778
+ ```
779
+
780
+ ### Select
781
+
782
+ ##### Import
783
+
784
+ ```javascript
785
+ import { Select } from '@hubspot/ui-extensions';
786
+ ```
787
+
788
+ ##### Props
789
+
790
+ ```typescript
791
+ interface SelectProps {
792
+ label: string;
793
+ name: string;
794
+ value?: string | number | boolean;
795
+ required?: boolean;
796
+ readOnly?: boolean;
797
+ description?: string;
798
+ tooltip?: string;
799
+ placeholder?: string;
800
+ error?: boolean;
801
+ errorMessage?: string;
802
+ onChange: (value: string) => void;
803
+ options: {
804
+ label: string;
805
+ value: string | number | boolean;
806
+ }[];
807
+ }
808
+ ```
809
+
810
+ | Prop | Type | Default | Description |
811
+ | --- | --- | --- | --- |
812
+ | `label` | `string` | `N/A` | The label text to display for the select element |
813
+ | `name` | `string` | `N/A` | The unique identifier for the select element. |
814
+ | `value` | `string \| number \| boolean` | `''` | The value of the select input. |
815
+ | `required` | `boolean` | `false` | Determines if the required indicator should be displayed |
816
+ | `readOnly` | `boolean` | `false` | Determines if the field is editable or not. |
817
+ | `description` | `string` | `N/A` | Instructional message to display to the user to help understand the purpose of the input. |
818
+ | `tooltip` | `string` | `N/A` | Text that will appear in a tooltip next to the input label. |
819
+ | `placeholder` | `string` | `N/A` | Text that appears in the input when it has no value set. |
820
+ | `error` | `boolean(optional)` | `false` | If set to true, `validationMessage` is displayed as an error message, if it was provided. The input will also render it's error state to let the user know there is an error. If false, `validationMessage` is displayed as a success message. |
821
+ | `validationMessage` | `string(optional)` | `''` | The text to show if the input has an error. |
822
+ | `onChange` | `(value: string) => void` | `N/A` | Function that is called with the new value when it is updated. |
823
+ | `options` | `Array<{label: string; value: string \| number \| boolean}>` | `N/A` | Array of options to be displayed in the select. `label` will be used as the display text in the dropdown list and `value` should be a **unique** identifier. `value` is the data that will be submitted with the form. |
824
+
825
+ ##### Usage
826
+
827
+ ```javascript
828
+ const Extension = () => {
829
+ const [name, setName] = useState(null);
830
+ const [validationMessage, setValidationMessage] = useState('');
831
+ const [isValid, setIsValid] = useState(true);
832
+
833
+ const options = [
834
+ { label: 'Bill', value: 42 },
835
+ { label: 'Ted', value: 43 },
836
+ ];
837
+
838
+ return (
839
+ <Form>
840
+ <Select
841
+ label="Best Bill & Ted Character?"
842
+ name="best-char"
843
+ tooltip="Please choose"
844
+ description="Please choose"
845
+ placeholder="Bill or Ted?"
846
+ required={true}
847
+ error={!isValid}
848
+ validationMessage={validationMessage}
849
+ onChange={value => {
850
+ setName(value);
851
+ if (!value) {
852
+ setValidationMessage('This is required');
853
+ setIsValid(false);
854
+ } else {
855
+ setValidationMessage('Excellent!');
856
+ setIsValid(true);
857
+ }
858
+ }}
859
+ options={options}
860
+ />
861
+ </Form>
862
+ );
863
+ };
864
+ ```
865
+
866
+ ### Stack
867
+
868
+ ##### Import
869
+
870
+ ```javascript
871
+ import { Stack } from '@hubspot/ui-extensions';
872
+ ```
873
+
874
+ ##### Props
875
+
876
+ ```typescript
877
+ interface StackProps {
878
+ distance?: 'flush' | 'small' | 'extra-small' | 'medium' | 'large';
879
+ direction?: 'row' | 'column';
880
+ children?: ReactNode;
881
+ }
882
+ ```
883
+
884
+ | Prop | Type | Default | Description |
885
+ | --- | --- | --- | --- |
886
+ | `distance` | `'flush' \| 'extra-small' \| 'small' \| 'medium' \| 'large'` | `'small'` | Amount of space between each child component passed as children |
887
+ | `direction` | `'row' \| 'column'` | `'column'` | Stacks elements in the vertical or horizontal direction. |
888
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
889
+
890
+ ##### Usage
891
+
892
+ ```javascript
893
+ const Extension = () => {
894
+ return (
895
+ <Stack>
896
+ <Image {...} />
897
+ <Text {...} />
898
+ </Stack>
899
+ );
900
+ }
901
+ ```
902
+
903
+ ### Statistics
904
+
905
+ ##### Import
906
+
907
+ ```javascript
908
+ import { Statistics } from '@hubspot/ui-extensions';
909
+ ```
910
+
911
+ ##### Props
912
+
913
+ ```typescript
914
+ interface StatisticsProps {
915
+ children: ReactNode;
916
+ }
917
+ ```
918
+
919
+ | Prop | Type | Default | Description |
920
+ | --- | --- | --- | --- |
921
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. These children should be [StatisticsItem](#statisticsitem) or [StatisticsTrend](#statisticstrend) |
922
+
923
+ ##### Usage
924
+
925
+ ```javascript
926
+ const Extension = () => {
927
+ return (
928
+ <Statistics>
929
+ <StatisticsItem {...} />
930
+ <StatisticsTrend {...} />
931
+ </Statistics>
932
+ );
933
+ }
934
+ ```
935
+
936
+ #### StatisticsItem
937
+
938
+ ##### Import
939
+
940
+ ```javascript
941
+ import { StatisticsItem } from '@hubspot/ui-extensions';
942
+ ```
943
+
944
+ ##### Props
945
+
946
+ ```typescript
947
+ interface StatisticsItemProps {
948
+ id?: string;
949
+ label: string;
950
+ number: string;
951
+ children: ReactNode;
952
+ }
953
+ ```
954
+
955
+ | Prop | Type | Default | Description |
956
+ | --- | --- | --- | --- |
957
+ | `id` | `string(optional)` | `N/A` | The unique identifier |
958
+ | `label` | `string` | `N/A` | The text to be used as a label |
959
+ | `number` | `string` | `N/A` | Fully formatted string to be displayed as the item's primary number |
960
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. These children should be [Text](#text) or [StatisticsTrend](#statisticstrend) |
961
+
962
+ ##### Usage
963
+
964
+ ```javascript
965
+ const Extension = () => {
966
+ return (
967
+ <Statistics>
968
+ <StatisticsItem label="Sales" number={'30000'}>
969
+ <Text>Big Numbers</Text>
970
+ </StatisticsItem>
971
+ </Statistics>
972
+ );
973
+ };
974
+ ```
975
+
976
+ #### StatisticsTrend
977
+
978
+ ##### Import
979
+
980
+ ```javascript
981
+ import { StatisticsTrend } from '@hubspot/ui-extensions';
982
+ ```
983
+
984
+ ##### Props
985
+
986
+ ```typescript
987
+ interface StatisticsTrendProps {
988
+ value: string;
989
+ direction: 'increase' | 'decrease';
990
+ }
991
+ ```
992
+
993
+ | Prop | Type | Default | Description |
994
+ | --- | --- | --- | --- |
995
+ | `value` | `string` | `N/A` | Formatted string to be displayed as trend value |
996
+ | `direction` | `'increase' \| 'decrease'` | `N/A` | Direction in which the trend arrow should be displayed |
997
+
998
+ ##### Usage
999
+
1000
+ ```javascript
1001
+ const Extension = () => {
1002
+ return (
1003
+ <Statistics>
1004
+ <StatisticsItem label="Item A Sales" number="10000">
1005
+ <StatisticsTrend direction="decrease" value="200%" />
1006
+ </StatisticsItem>
1007
+ <StatisticsItem label="Item B Sales" number="100000">
1008
+ <StatisticsTrend direction="increase" value="100%" />
1009
+ </StatisticsItem>
1010
+ </Statistics>
1011
+ );
1012
+ };
1013
+ ```
1014
+
1015
+ ### Table
1016
+
1017
+ ##### Import
1018
+
1019
+ ```javascript
1020
+ import { Table } from '@hubspot/ui-extensions';
1021
+ ```
1022
+
1023
+ ##### Props
1024
+
1025
+ ```typescript
1026
+ interface TableProps {
1027
+ children: ReactNode;
1028
+ flush?: boolean;
1029
+ bordered?: boolean;
1030
+ paginated?: boolean;
1031
+ // if paginated=true
1032
+ pageCount: number;
1033
+ onPageChange: (pageNumber: number) => void;
1034
+ showButtonLabels?: boolean;
1035
+ showFirstLastButtons?: boolean;
1036
+ maxVisiblePageButtons?: number;
1037
+ page?: number;
1038
+ }
1039
+ ```
1040
+
1041
+ | Prop | Type | Default | Description |
1042
+ | --- | --- | --- | --- |
1043
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. These children should be [TableHead](#tablehead), [TableFooter](#tablefooter), or [TableBody](#tablebody) |
1044
+ | `flush` | `boolean(optional)` | `false` | If true the table will not have bottom margin. |
1045
+ | `bordered` | `boolean(optional)` | `true` | If false the table will not haver borders around its content. |
1046
+ | `paginated` | `boolean(optional)` | `false` | If true, the table will display the paginator component and consumer will have to provide extra pagination props. |
1047
+
1048
+ **Props for paginated=true**
1049
+
1050
+ | Prop | Type | Default | Description |
1051
+ | --- | --- | --- | --- |
1052
+ | `pageCount` | `number` | `N/A` | The total number of pages available |
1053
+ | `onPageChange` | `onPageChange: (pageNumber: number) => void` | `N/A` | A function that will be invoked when the pagination button is clicked. It receives the new page number as argument. |
1054
+ | `showButtonLabels` | `boolean(optional)` | `true` | if `false`, it hides the text labels for the First/Prev/Next/Last buttons. The button labels will still be accessible for screen readers. |
1055
+ | `showFirstLastButtons` | `boolean(optional)` | `false` | if `true`, it displays the First page and Last page buttons. |
1056
+ | `maxVisiblePageButtons` | `number(optional)` | `5` | Changes how many page buttons are shown. |
1057
+ | `page` | `number(optional)` | `N/A` | Denotes the current page. |
1058
+
1059
+ ##### Usage
1060
+
1061
+ ```javascript
1062
+ const Extension = () => {
1063
+ return (
1064
+ <Table bordered={true}>
1065
+ <TableHead>
1066
+ <TableRow>
1067
+ <TableHeader>Name</TableHeader>
1068
+ <TableHeader>Phone</TableHeader>
1069
+ </TableRow>
1070
+ </TableHead>
1071
+ <TableBody>
1072
+ <TableRow>
1073
+ <TableCell>Roger Federer</TableCell>
1074
+ <TableCell>555-555-7866</TableCell>
1075
+ </TableRow>
1076
+ </TableBody>
1077
+ </Table>
1078
+ );
1079
+ };
1080
+
1081
+ // Paginated example
1082
+
1083
+ function generateData(count = 15) {
1084
+ const result = [];
1085
+
1086
+ for (let index = 0; index < count; index++) {
1087
+ result.push({
1088
+ name: `Jane Doe ${index}`,
1089
+ email: `janedoemail${index}@hubspot.com`,
1090
+ });
1091
+ }
1092
+
1093
+ return result;
1094
+ }
1095
+
1096
+ function PaginatedTable() {
1097
+ const ITEMS_PER_PAGE = 10;
1098
+ const data = generateData(30);
1099
+
1100
+ const [currentPage, setCurrentPage] = useState(1);
1101
+ const pageCount = data.length / ITEMS_PER_PAGE;
1102
+
1103
+ const dataToDisplay = data.slice(
1104
+ (currentPage - 1) * ITEMS_PER_PAGE,
1105
+ currentPage * ITEMS_PER_PAGE
1106
+ );
1107
+
1108
+ return (
1109
+ <Table
1110
+ paginated={true}
1111
+ pageCount={pageCount}
1112
+ page={currentPage}
1113
+ onPageChange={(nextPageNumber: number) => {
1114
+ setCurrentPage(nextPageNumber);
1115
+ }}
1116
+ >
1117
+ <TableHead>
1118
+ <TableRow>
1119
+ <TableHeader>Name</TableHeader>
1120
+ <TableHeader>Email</TableHeader>
1121
+ </TableRow>
1122
+ </TableHead>
1123
+ <TableBody>
1124
+ {dataToDisplay.map(({ name, email }) => {
1125
+ console.log(name, email);
1126
+ return (
1127
+ <TableRow key={email}>
1128
+ <TableCell>{name}</TableCell>
1129
+ <TableCell>{email}</TableCell>
1130
+ </TableRow>
1131
+ );
1132
+ })}
1133
+ </TableBody>
1134
+ </Table>
1135
+ );
1136
+ }
1137
+ ```
1138
+
1139
+ ### TableBody
1140
+
1141
+ ##### Import
1142
+
1143
+ ```javascript
1144
+ import { TableBody } from '@hubspot/ui-extensions';
1145
+ ```
1146
+
1147
+ ##### Props
1148
+
1149
+ ```typescript
1150
+ interface TableBodyProps {
1151
+ children: ReactNode;
1152
+ }
1153
+ ```
1154
+
1155
+ | Prop | Type | Default | Description |
1156
+ | --- | --- | --- | --- |
1157
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. These children should be [TableRow](#tablerow). |
1158
+
1159
+ ##### Usage
1160
+
1161
+ ```javascript
1162
+ const Extension = () => {
1163
+ return (
1164
+ <Table bordered={true}>
1165
+ <TableHead>
1166
+ <TableRow>
1167
+ <TableHeader>Name</TableHeader>
1168
+ <TableHeader>Phone</TableHeader>
1169
+ </TableRow>
1170
+ </TableHead>
1171
+ <TableBody>
1172
+ <TableRow>
1173
+ <TableCell>Roger Federer</TableCell>
1174
+ <TableCell>555-555-7866</TableCell>
1175
+ </TableRow>
1176
+ </TableBody>
1177
+ </Table>
1178
+ );
1179
+ };
1180
+ ```
1181
+
1182
+ ### TableCell
1183
+
1184
+ ##### Import
1185
+
1186
+ ```javascript
1187
+ import { TableCell } from '@hubspot/ui-extensions';
1188
+ ```
1189
+
1190
+ ##### Props
1191
+
1192
+ ```typescript
1193
+ interface TableCellProps {
1194
+ children: ReactNode;
1195
+ }
1196
+ ```
1197
+
1198
+ | Prop | Type | Default | Description |
1199
+ | --- | --- | --- | --- |
1200
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
1201
+
1202
+ ##### Usage
1203
+
1204
+ ```javascript
1205
+ const Extension = () => {
1206
+ return (
1207
+ <Table bordered={true}>
1208
+ <TableHead>
1209
+ <TableRow>
1210
+ <TableHeader>Name</TableHeader>
1211
+ <TableHeader>Phone</TableHeader>
1212
+ </TableRow>
1213
+ </TableHead>
1214
+ <TableBody>
1215
+ <TableRow>
1216
+ <TableCell>Roger Federer</TableCell>
1217
+ <TableCell>555-555-7866</TableCell>
1218
+ </TableRow>
1219
+ </TableBody>
1220
+ </Table>
1221
+ );
1222
+ };
1223
+ ```
1224
+
1225
+ ### TableFooter
1226
+
1227
+ ##### Import
1228
+
1229
+ ```javascript
1230
+ import { TableFooter } from '@hubspot/ui-extensions';
1231
+ ```
1232
+
1233
+ ##### Props
1234
+
1235
+ ```typescript
1236
+ interface TableFooterProps {
1237
+ children: ReactNode;
1238
+ }
1239
+ ```
1240
+
1241
+ | Prop | Type | Default | Description |
1242
+ | --- | --- | --- | --- |
1243
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. These children should be [TableRow](#tablerow). |
1244
+
1245
+ ##### Usage
1246
+
1247
+ ```javascript
1248
+ const Extension = () => {
1249
+ return (
1250
+ <Table>
1251
+ <TableHead>
1252
+ <TableRow>
1253
+ <TableHeader>Series</TableHeader>
1254
+ <TableHeader>Years on air</TableHeader>
1255
+ <TableHeader>Emmys</TableHeader>
1256
+ </TableRow>
1257
+ </TableHead>
1258
+ <TableFooter>
1259
+ <TableRow>
1260
+ <TableHeader>Totals</TableHeader>
1261
+ <TableHeader>43</TableHeader>
1262
+ <TableHeader>50</TableHeader>
1263
+ </TableRow>
1264
+ </TableFooter>
1265
+ <TableBody>
1266
+ <TableRow>
1267
+ <TableCell>The Simpsons</TableCell>
1268
+ <TableCell>28</TableCell>
1269
+ <TableCell>31</TableCell>
1270
+ </TableRow>
1271
+ <TableRow>
1272
+ <TableCell>M*A*S*H</TableCell>
1273
+ <TableCell>11</TableCell>
1274
+ <TableCell>14</TableCell>
1275
+ </TableRow>
1276
+ <TableRow>
1277
+ <TableCell>Arrested Development</TableCell>
1278
+ <TableCell>4</TableCell>
1279
+ <TableCell>5</TableCell>
1280
+ </TableRow>
1281
+ </TableBody>
1282
+ </Table>
1283
+ );
1284
+ };
1285
+ ```
1286
+
1287
+ ### TableHead
1288
+
1289
+ ##### Import
1290
+
1291
+ ```javascript
1292
+ import { TableHead } from '@hubspot/ui-extensions';
1293
+ ```
1294
+
1295
+ ##### Props
1296
+
1297
+ ```typescript
1298
+ interface TableHeadProps {
1299
+ children: ReactNode;
1300
+ }
1301
+ ```
1302
+
1303
+ | Prop | Type | Default | Description |
1304
+ | --- | --- | --- | --- |
1305
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. These children should be [TableHeader](#tableheader). |
1306
+
1307
+ ##### Usage
1308
+
1309
+ ```javascript
1310
+ const Extension = () => {
1311
+ return (
1312
+ <Table bordered={true}>
1313
+ <TableHead>
1314
+ <TableRow>
1315
+ <TableHeader>Name</TableHeader>
1316
+ <TableHeader>Phone</TableHeader>
1317
+ </TableRow>
1318
+ </TableHead>
1319
+ <TableBody>
1320
+ <TableRow>
1321
+ <TableCell>Roger Federer</TableCell>
1322
+ <TableCell>555-555-7866</TableCell>
1323
+ </TableRow>
1324
+ </TableBody>
1325
+ </Table>
1326
+ );
1327
+ };
1328
+ ```
1329
+
1330
+ ### TableHeader
1331
+
1332
+ ##### Import
1333
+
1334
+ ```javascript
1335
+ import { TableHeader } from '@hubspot/ui-extensions';
1336
+ ```
1337
+
1338
+ ##### Props
1339
+
1340
+ ```typescript
1341
+ interface TableHeaderProps {
1342
+ children: ReactNode;
1343
+ }
1344
+ ```
1345
+
1346
+ | Prop | Type | Default | Description |
1347
+ | --- | --- | --- | --- |
1348
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
1349
+
1350
+ ##### Usage
1351
+
1352
+ ```javascript
1353
+ const Extension = () => {
1354
+ return (
1355
+ <Table bordered={true}>
1356
+ <TableHead>
1357
+ <TableRow>
1358
+ <TableHeader>Name</TableHeader>
1359
+ <TableHeader>Phone</TableHeader>
1360
+ </TableRow>
1361
+ </TableHead>
1362
+ <TableBody>
1363
+ <TableRow>
1364
+ <TableCell>Roger Federer</TableCell>
1365
+ <TableCell>555-555-7866</TableCell>
1366
+ </TableRow>
1367
+ </TableBody>
1368
+ </Table>
1369
+ );
1370
+ };
1371
+ ```
1372
+
1373
+ ### TableRow
1374
+
1375
+ ##### Import
1376
+
1377
+ ```javascript
1378
+ import { TableRow } from '@hubspot/ui-extensions';
1379
+ ```
1380
+
1381
+ ##### Props
1382
+
1383
+ ```typescript
1384
+ interface TableRowProps {
1385
+ children: ReactNode;
1386
+ }
1387
+ ```
1388
+
1389
+ | Prop | Type | Default | Description |
1390
+ | --- | --- | --- | --- |
1391
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
1392
+
1393
+ ##### Usage
1394
+
1395
+ ```javascript
1396
+ const Extension = () => {
1397
+ return (
1398
+ <Table bordered={true}>
1399
+ <TableHead>
1400
+ <TableRow>
1401
+ <TableHeader>Name</TableHeader>
1402
+ <TableHeader>Phone</TableHeader>
1403
+ </TableRow>
1404
+ </TableHead>
1405
+ <TableBody>
1406
+ <TableRow>
1407
+ <TableCell>Roger Federer</TableCell>
1408
+ <TableCell>555-555-7866</TableCell>
1409
+ </TableRow>
1410
+ </TableBody>
1411
+ </Table>
1412
+ );
1413
+ };
1414
+ ```
1415
+
1416
+ ### Tag
1417
+
1418
+ ##### Import
1419
+
1420
+ ```javascript
1421
+ import { Tag } from '@hubspot/ui-extensions';
1422
+ ```
1423
+
1424
+ ##### Props
1425
+
1426
+ ```typescript
1427
+ interface TagProps {
1428
+ children: ReactNode;
1429
+ onClick?: () => void;
1430
+ variant?: 'default' | 'warning' | 'success' | 'error';
1431
+ }
1432
+ ```
1433
+
1434
+ | Prop | Type | Default | Description |
1435
+ | --- | --- | --- | --- |
1436
+ | `children` | `string` | `N/A` | Text to be displayed in the tag. |
1437
+ | `onClick` | `() => void` `(optional)` | `N/A` | A function that will be invoked when the `Tag` is clicked. It receives no arguments and it's return value is ignored. |
1438
+ | `variant` | `'default' \| 'warning' \| 'success' \| 'error'` `(optional)` | `'default'` | The color variation of the tag to display |
1439
+
1440
+ ##### Usage
1441
+
1442
+ ```javascript
1443
+ const Extension = () => {
1444
+ return (
1445
+ <Tag
1446
+ variant="success"
1447
+ onClick={() => {
1448
+ console.log('Tag clicked!');
1449
+ }}
1450
+ >
1451
+ Success
1452
+ </Tag>
1453
+ );
1454
+ };
1455
+ ```
1456
+
1457
+ ### Text
1458
+
1459
+ ##### Import
1460
+
1461
+ ```javascript
1462
+ import { Text } from '@hubspot/ui-extensions';
1463
+ ```
1464
+
1465
+ ##### Props
1466
+
1467
+ ```typescript
1468
+ interface TextProps {
1469
+ format?: 'plaintext' | 'markdown';
1470
+ variant?: 'bodytext' | 'microcopy';
1471
+ children: ReactNode;
1472
+ tagName?: 'p' | 'span' | 'small';
1473
+ }
1474
+ ```
1475
+
1476
+ | Prop | Type | Default | Description |
1477
+ | --- | --- | --- | --- |
1478
+ | `format` | `'plaintext' \| 'markdown'` `(optional)` | `'plaintext'` | Type of formatting for the display text. |
1479
+ | `children` | `string` | `N/A` | Text to be displayed as body text. |
1480
+ | `variant` | `'bodytext' \| 'microcopy'` | `'bodytext'` | Type of text to display |
1481
+ | `tagName` | `'p' \| 'small' \| 'span'` | `'bodytext'` | Type of text element(tag) to display. |
1482
+
1483
+ #### Markdown
1484
+
1485
+ Markdown syntax supported in the component:
1486
+
1487
+ - bold text: `**like this**` or `__like this__`
1488
+ - italicized text: `*like this*` or `_like this_`
1489
+ - inline code: `` `like this` ``
1490
+ - links: `[visible anchor text](https://www.hubspot.com/)`
1491
+
1492
+ ##### Usage
1493
+
1494
+ ```javascript
1495
+ const Extension = () => {
1496
+ return (
1497
+ <>
1498
+ <Text format="markdown">**Bold**</Text>
1499
+ <Text format="markdown">*Italics*</Text>
1500
+ <Text>**Not Bold because format isn't markdown**</Text>
1501
+ <Text variant="microcopy">This is going to be small</Text>
1502
+ </>
1503
+ );
1504
+ };
1505
+ ```
1506
+
1507
+ ### Textarea
1508
+
1509
+ ##### Import
1510
+
1511
+ ```javascript
1512
+ import { Textarea } from '@hubspot/ui-extensions';
1513
+ ```
1514
+
1515
+ ##### Props
1516
+
1517
+ ```typescript
1518
+ interface TextareaProps {
1519
+ label: string;
1520
+ name: string;
1521
+ value?: string;
1522
+ required?: boolean;
1523
+ readOnly?: boolean;
1524
+ description?: string;
1525
+ tooltip?: string;
1526
+ placeholder?: string;
1527
+ error?: boolean;
1528
+ validationMessage?: string;
1529
+ onChange?: (value: string) => void;
1530
+ onInput?: (value: string) => void;
1531
+ onBlur?: (value: string) => void;
1532
+ onFocus?: (value: string) => void;
1533
+ cols?: number;
1534
+ maxLength?: number;
1535
+ rows?: number;
1536
+ resize?: 'vertical' | 'horizontal' | 'both' | 'none';
1537
+ }
1538
+ ```
1539
+
1540
+ | Prop | Type | Default | Description |
1541
+ | --- | --- | --- | --- |
1542
+ | `label` | `string` | `N/A` | The label text to display for the textarea element |
1543
+ | `name` | `string` | `N/A` | The unique identifier for the textarea element, this could be thought of as the HTML5 [Textarea element's name attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#name) |
1544
+ | `value` | `string(optional)` | `''` | The value of the textarea |
1545
+ | `required` | `boolean(optional)` | `false` | Determines if the required indicator should be displayed |
1546
+ | `readOnly` | `boolean(optional)` | `false` | Determines if the field is editable or not. |
1547
+ | `description` | `string(optional)` | `N/A` | Instructional message to display to the user to help understand the purpose of the textarea. |
1548
+ | `tooltip` | `string(optional)` | `N/A` | Text that will appear in a tooltip next to the textarea label. |
1549
+ | `placeholder` | `string(optional)` | `N/A` | Text that appears in the textarea when it has no value set. |
1550
+ | `error` | `boolean(optional)` | `false` | If set to true, `validationMessage` is displayed as an error message, if it was provided. The textarea will also render it's error state to let the user know there is an error. If false, `validationMessage` is displayed as a success message. |
1551
+ | `validationMessage` | `string(optional)` | `''` | The text to show if the textarea has an error. |
1552
+ | `onChange` | `(value: string) => void(optional)` | `N/A` | A callback function that is invoked when the value is committed. Currently these times are `onBlur` of the textarea and when the user submits the form. |
1553
+ | `onInput` | `(value: string) => void(optional)` | `N/A` | A function that is called and passed the value every time the field is edited by the user. It is recommended that you do not use this value to update state, that is what `onChange` should be used for. Instead this should be used for validation. |
1554
+ | `onBlur` | `(value: string) => void(optional)` | `N/A` | A function that is called and passed the value every time the field loses focus. |
1555
+ | `onFocus` | `(value: string) => void(optional)` | `N/A` | A function that is called and passed the value every time the field gets focused. |
1556
+ | `cols` | `number(optional)` | `N/A` | The visible width of the text control, in average character widths. |
1557
+ | `rows` | `number(optional)` | `N/A` | The number of visible text lines for the control. |
1558
+ | `maxLength` | `number(optional)` | `N/A` | The maximum number of characters (UTF-16 code units) that the user can enter. If this value isn't specified, the user can enter an unlimited number of characters. |
1559
+ | `resize` | `'vertical' \| 'horizontal' \| 'both' \| 'none'` `(optional)` | `'vertical'` | Sets whether an element is resizable, and if so, in which directions. |
1560
+
1561
+ ##### Usage
1562
+
1563
+ ```javascript
1564
+ import { useState } from 'react';
1565
+
1566
+ const Extension = () => {
1567
+ const [desciptiption, setDescription] = useState('');
1568
+ const [validationMessage, setValidationMessage] = useState('');
1569
+ const [isValid, setIsValid] = useState(true);
1570
+
1571
+ return (
1572
+ <Form>
1573
+ <Textarea
1574
+ label="Description"
1575
+ name="description"
1576
+ tooltip="Provide as much detail as possible"
1577
+ description="Please include a link"
1578
+ placeholder="My desription"
1579
+ required={true}
1580
+ error={!isValid}
1581
+ validationMessage={validationMessage}
1582
+ onChange={value => {
1583
+ setDescription(value);
1584
+ }}
1585
+ onInput={value => {
1586
+ if (!value.includes('http')) {
1587
+ setValidationMessage('A link must be included.');
1588
+ setIsValid(false);
1589
+ } else {
1590
+ setValidationMessage('Valid description!');
1591
+ setIsValid(true);
1592
+ }
1593
+ }}
1594
+ />
1595
+ </Form>
1596
+ );
1597
+ };
1598
+ ```
1599
+
1600
+ ### Tile
1601
+
1602
+ ##### Import
1603
+
1604
+ ```javascript
1605
+ import { Tile } from '@hubspot/ui-extensions';
1606
+ ```
1607
+
1608
+ ##### Props
1609
+
1610
+ ```typescript
1611
+ interface TileProps {
1612
+ children: ReactNode;
1613
+ flush?: boolean;
1614
+ }
1615
+ ```
1616
+
1617
+ | Prop | Type | Default | Description |
1618
+ | --- | --- | --- | --- |
1619
+ | `children` | `ReactNode` | `N/A` | Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. |
1620
+ | `flush` | `boolean(optional)` | `false` | If true the content of the Tile will have no left or right padding. |
1621
+
1622
+ ##### Usage
1623
+
1624
+ ```javascript
1625
+ const Extension = () => {
1626
+ return (
1627
+ <>
1628
+ <Tile flush={true}>
1629
+ <Text>No left padding</Text>
1630
+ </Tile>
1631
+ <Tile>
1632
+ <Text>Small amount of left padding</Text>
1633
+ </Tile>
1634
+ </>
1635
+ );
1636
+ };
1637
+ ```
1638
+
1639
+ ### ToggleGroup
1640
+
1641
+ ##### Import
1642
+
1643
+ ```javascript
1644
+ import { ToggleGroup } from '@hubspot/ui-extensions';
1645
+ ```
1646
+
1647
+ ##### Props
1648
+
1649
+ ```typescript
1650
+ type ToggleGroupProps = {
1651
+ toggleType: 'checkboxList' | 'radioButtonList';
1652
+ name: string;
1653
+ label: string;
1654
+ value?: string | string[];
1655
+ onChange?: (value: string) => void | (value: string[]) => void;
1656
+ validationMessage?: string;
1657
+ required?: boolean;
1658
+ tooltip?: string;
1659
+ error?: boolean;
1660
+ options: {
1661
+ label: string;
1662
+ value: string;
1663
+ initialIsChecked?: boolean;
1664
+ readonly?: boolean;
1665
+ description?: string;
1666
+ }[];
1667
+ inline?: boolean;
1668
+ variant?: 'default' | 'small';
1669
+ };
1670
+
1671
+ ```
1672
+
1673
+ | Prop | Type | Default | Description |
1674
+ | --- | --- | --- | --- | --- |
1675
+ | `toggleType` | `'radioButtonList' \| 'checkboxList'` | `N/A` | The type of toggles that will be shown. `'checkboxList'` is a multi-select and `'radioButtonList'` is a single select | ; |
1676
+ | `name` | `string` | `N/A` | The unique identifier for the toggle group element. |
1677
+ | `label` | `string` | `N/A` | The label text to display for the toggle group element. |
1678
+ | `options` | `Array<{ label: string; value: string; initialIsChecked?: boolean; readonly?: boolean; description?: string }>` | `N/A` | |
1679
+ | `value` | `string \| string[]` `(optional)` | `N/A` | The value of the toggle group. If `toggleType` is `'radioButtonList'`, this should be a `string`. If `toggleType` is `'checkboxList'` this should be an array of `string`s. |
1680
+ | `onChange` | `(value: string) => void \| (value: string[]) => void` `(optional)` | `N/A` | A function that is called with the new value or values when it is updated. If `toggleType` is `'radioButtonList'`, the function will be called with the `value` that is a `string`. If `toggleType` is `'checkboxList'` the function will be called with the `value` that is an array of `string`s. |
1681
+ | `required` | `boolean(optional)` | `false` | Determines if the required indicator should be displayed |
1682
+ | `tooltip` | `string` | `N/A` | Text that will appear in a tooltip next to the toggle group label. |
1683
+ | `error` | `boolean(optional)` | `false` | If set to true, `validationMessage` is displayed as an error message, if it was provided. The input will also render it's error state to let the user know there is an error. If false, `validationMessage` is displayed as a success message. |
1684
+ | `validationMessage` | `string(optional)` | `''` | The text to show if the input has an error. |
1685
+ | `inline` | `boolean(optional)` | `false` | Determines of the options are stacked vertically or share a line horizontally. |
1686
+ | `variant` | `'default' \| 'small'` `(optional)` | `default` | The size variation of the individual options. |
1687
+
1688
+ ##### Usage
1689
+
1690
+ ```javascript
1691
+ const options = [1, 2, 3, 4].map(n => ({
1692
+ label: `Option ${n}`,
1693
+ value: `${n}`,
1694
+ initialIsChecked: n === 2,
1695
+ readonly: false,
1696
+ description: `This is option ${n}`,
1697
+ }));
1698
+
1699
+ const Extension = () => {
1700
+ return (
1701
+ <ToggleGroup
1702
+ name="toggle-checkboxes"
1703
+ label="Toggle these things"
1704
+ error={false}
1705
+ options={options}
1706
+ tooltip="Here's a secret tip."
1707
+ validationMessage="Make sure you do the thing correctly."
1708
+ required={false}
1709
+ inline={false}
1710
+ toggleType="checkboxList"
1711
+ variant="default"
1712
+ />
1713
+ );
1714
+ };
1715
+ ```