@trackunit/react-form-components 1.11.17 → 1.11.19
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/index.cjs.js +832 -45
- package/index.esm.js +834 -47
- package/package.json +7 -7
- package/src/components/Checkbox/Checkbox.d.ts +46 -2
- package/src/components/ColorField/ColorField.d.ts +34 -2
- package/src/components/DateField/DateField.d.ts +44 -3
- package/src/components/DropZone/DropZone.d.ts +56 -1
- package/src/components/EmailField/EmailField.d.ts +46 -2
- package/src/components/MultiSelectField/MultiSelectField.d.ts +56 -2
- package/src/components/NumberField/NumberField.d.ts +46 -2
- package/src/components/PasswordField/PasswordField.d.ts +52 -3
- package/src/components/PhoneField/PhoneField.d.ts +45 -12
- package/src/components/RadioGroup/RadioGroup.d.ts +49 -2
- package/src/components/SelectField/SelectField.d.ts +62 -1
- package/src/components/TextAreaField/TextAreaField.d.ts +45 -3
- package/src/components/TextField/TextField.d.ts +55 -0
- package/src/components/ToggleSwitch/ToggleSwitch.d.ts +47 -3
- package/src/components/ToggleSwitchOption/ToggleSwitchOption.d.ts +56 -4
- package/src/components/UploadField/UploadField.d.ts +47 -1
- package/src/components/UrlField/UrlField.d.ts +46 -2
package/index.esm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { useNamespaceTranslation, registerTranslations, NamespaceTrans } from '@trackunit/i18n-library-translation';
|
|
3
3
|
import { Temporal } from '@js-temporal/polyfill';
|
|
4
|
-
import { IconButton, Icon, Tooltip, cvaMenu, cvaMenuList, Tag, useIsTextTruncated, ZStack, MenuItem, useMeasure, useDebounce,
|
|
4
|
+
import { IconButton, Icon, Tooltip, cvaMenu, cvaMenuList, Tag, useIsTextTruncated, ZStack, MenuItem, useMeasure, useDebounce, useMergeRefs, Spinner, useScrollBlock, Text, Heading, useIsFirstRender } from '@trackunit/react-components';
|
|
5
5
|
import { themeSpacing } from '@trackunit/ui-design-tokens';
|
|
6
|
-
import { forwardRef, useRef, useEffect, useImperativeHandle, useCallback, useState,
|
|
6
|
+
import { forwardRef, useRef, useEffect, useImperativeHandle, useCallback, useState, cloneElement, isValidElement, useLayoutEffect, useReducer, useMemo, createContext, useContext } from 'react';
|
|
7
7
|
import { cvaMerge } from '@trackunit/css-class-variance-utilities';
|
|
8
8
|
import { titleCase } from 'string-ts';
|
|
9
9
|
import { useCopyToClipboard } from 'usehooks-ts';
|
|
@@ -1035,10 +1035,54 @@ const IndeterminateIcon = ({ className }) => (jsx("svg", { className: className,
|
|
|
1035
1035
|
*
|
|
1036
1036
|
* Checkboxes are used for multiple choices, not for mutually exclusive choices. Each checkbox works independently from other checkboxes in the list, therefore checking an additional box does not affect any other selections.
|
|
1037
1037
|
*
|
|
1038
|
-
*
|
|
1038
|
+
* ### When to use
|
|
1039
|
+
* Use checkboxes to allow selection in a form, for filtering, terms and conditions to indicate agreement, and bulk actions in lists or tables.
|
|
1039
1040
|
*
|
|
1040
|
-
*
|
|
1041
|
+
* ### When not to use
|
|
1042
|
+
* Do not use checkboxes if the user can only select one option from a list. Use RadioGroup instead.
|
|
1041
1043
|
*
|
|
1044
|
+
* @example Basic checkbox
|
|
1045
|
+
* ```tsx
|
|
1046
|
+
* import { Checkbox } from "@trackunit/react-form-components";
|
|
1047
|
+
* import { useState } from "react";
|
|
1048
|
+
*
|
|
1049
|
+
* const MyForm = () => {
|
|
1050
|
+
* const [accepted, setAccepted] = useState(false);
|
|
1051
|
+
*
|
|
1052
|
+
* return (
|
|
1053
|
+
* <Checkbox
|
|
1054
|
+
* label="I accept the terms and conditions"
|
|
1055
|
+
* checked={accepted}
|
|
1056
|
+
* onChange={(e) => setAccepted(e.target.checked)}
|
|
1057
|
+
* />
|
|
1058
|
+
* );
|
|
1059
|
+
* };
|
|
1060
|
+
* ```
|
|
1061
|
+
* @example Checkbox group for multiple selections
|
|
1062
|
+
* ```tsx
|
|
1063
|
+
* import { Checkbox } from "@trackunit/react-form-components";
|
|
1064
|
+
* import { useState } from "react";
|
|
1065
|
+
*
|
|
1066
|
+
* const NotificationSettings = () => {
|
|
1067
|
+
* const [notifications, setNotifications] = useState({
|
|
1068
|
+
* email: true,
|
|
1069
|
+
* sms: false,
|
|
1070
|
+
* push: true,
|
|
1071
|
+
* });
|
|
1072
|
+
*
|
|
1073
|
+
* const handleChange = (key: keyof typeof notifications) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
1074
|
+
* setNotifications((prev) => ({ ...prev, [key]: e.target.checked }));
|
|
1075
|
+
* };
|
|
1076
|
+
*
|
|
1077
|
+
* return (
|
|
1078
|
+
* <div className="flex flex-col gap-2">
|
|
1079
|
+
* <Checkbox label="Email notifications" checked={notifications.email} onChange={handleChange("email")} />
|
|
1080
|
+
* <Checkbox label="SMS notifications" checked={notifications.sms} onChange={handleChange("sms")} />
|
|
1081
|
+
* <Checkbox label="Push notifications" checked={notifications.push} onChange={handleChange("push")} />
|
|
1082
|
+
* </div>
|
|
1083
|
+
* );
|
|
1084
|
+
* };
|
|
1085
|
+
* ```
|
|
1042
1086
|
* @description A reference to the input element is provided as the `ref` prop.
|
|
1043
1087
|
* @augments props from [React.InputHTMLAttributes](https://reactjs.org/docs/dom-elements.html#input)
|
|
1044
1088
|
* @param {CheckboxProps} props - The props for the Checkbox component
|
|
@@ -2095,9 +2139,41 @@ const isValidHEXColor = (value) => {
|
|
|
2095
2139
|
return hexRegex.test(value);
|
|
2096
2140
|
};
|
|
2097
2141
|
/**
|
|
2098
|
-
* The ColorField component is used to enter
|
|
2099
|
-
*
|
|
2142
|
+
* The `<ColorField>` component is used to select and enter colors.
|
|
2143
|
+
* It provides both a color picker and a text input for hex color codes,
|
|
2144
|
+
* with validation ensuring valid hex format (#RRGGBB).
|
|
2145
|
+
*
|
|
2146
|
+
* ### When to use
|
|
2147
|
+
* - Color selection in theming or customization forms
|
|
2148
|
+
* - Brand color configuration
|
|
2149
|
+
* - Any input requiring a valid hex color value
|
|
2150
|
+
*
|
|
2151
|
+
* ### When not to use
|
|
2152
|
+
* - When you need a full color palette with named colors
|
|
2153
|
+
* - When opacity/alpha channel is required (only supports 6-digit hex)
|
|
2154
|
+
*
|
|
2155
|
+
* @example Basic usage
|
|
2156
|
+
* ```tsx
|
|
2157
|
+
* import { ColorField } from "@trackunit/react-form-components";
|
|
2158
|
+
*
|
|
2159
|
+
* const [color, setColor] = useState("#3B82F6");
|
|
2160
|
+
*
|
|
2161
|
+
* <ColorField
|
|
2162
|
+
* label="Brand Color"
|
|
2163
|
+
* value={color}
|
|
2164
|
+
* onChange={(e) => setColor(e.target.value)}
|
|
2165
|
+
* />
|
|
2166
|
+
* ```
|
|
2167
|
+
* @example With validation
|
|
2168
|
+
* ```tsx
|
|
2169
|
+
* import { ColorField } from "@trackunit/react-form-components";
|
|
2100
2170
|
*
|
|
2171
|
+
* <ColorField
|
|
2172
|
+
* label="Primary Color"
|
|
2173
|
+
* helpText="Enter a valid hex color (e.g., #FF5500)"
|
|
2174
|
+
* required
|
|
2175
|
+
* />
|
|
2176
|
+
* ```
|
|
2101
2177
|
*/
|
|
2102
2178
|
const ColorField = forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, "data-testid": dataTestId, value: propValue, onChange, isInvalid, onBlur, fieldSize = "medium", style, disabled, readOnly, isWarning, inputClassName, ...inputProps }, ref) => {
|
|
2103
2179
|
const renderAsDisabled = Boolean(disabled);
|
|
@@ -2156,11 +2232,52 @@ const ColorField = forwardRef(({ label, id, tip, helpText, errorMessage, helpAdd
|
|
|
2156
2232
|
ColorField.displayName = "ColorField";
|
|
2157
2233
|
|
|
2158
2234
|
/**
|
|
2159
|
-
* The date field component is used for entering date values.
|
|
2235
|
+
* The date field component is used for entering date values with a native date picker.
|
|
2160
2236
|
*
|
|
2161
|
-
*
|
|
2237
|
+
* ### When to use
|
|
2238
|
+
* Use DateField for selecting calendar dates such as birthdates, deadlines, or scheduling.
|
|
2162
2239
|
*
|
|
2163
|
-
*
|
|
2240
|
+
* ### When not to use
|
|
2241
|
+
* Do not use DateField for non-serialized dates or free-form date text input. Use TextField instead.
|
|
2242
|
+
*
|
|
2243
|
+
* @example Basic date field
|
|
2244
|
+
* ```tsx
|
|
2245
|
+
* import { DateField } from "@trackunit/react-form-components";
|
|
2246
|
+
* import { useState } from "react";
|
|
2247
|
+
*
|
|
2248
|
+
* const MyForm = () => {
|
|
2249
|
+
* const [date, setDate] = useState("");
|
|
2250
|
+
*
|
|
2251
|
+
* return (
|
|
2252
|
+
* <DateField
|
|
2253
|
+
* label="Start Date"
|
|
2254
|
+
* value={date}
|
|
2255
|
+
* onChange={(e) => setDate(e.target.value)}
|
|
2256
|
+
* />
|
|
2257
|
+
* );
|
|
2258
|
+
* };
|
|
2259
|
+
* ```
|
|
2260
|
+
* @example Date field with constraints
|
|
2261
|
+
* ```tsx
|
|
2262
|
+
* import { DateField } from "@trackunit/react-form-components";
|
|
2263
|
+
* import { useState } from "react";
|
|
2264
|
+
*
|
|
2265
|
+
* const BookingForm = () => {
|
|
2266
|
+
* const [checkIn, setCheckIn] = useState("");
|
|
2267
|
+
* const today = new Date().toISOString().split("T")[0];
|
|
2268
|
+
*
|
|
2269
|
+
* return (
|
|
2270
|
+
* <DateField
|
|
2271
|
+
* label="Check-in Date"
|
|
2272
|
+
* value={checkIn}
|
|
2273
|
+
* onChange={(e) => setCheckIn(e.target.value)}
|
|
2274
|
+
* min={today}
|
|
2275
|
+
* helpText="Select a date from today onwards"
|
|
2276
|
+
* required
|
|
2277
|
+
* />
|
|
2278
|
+
* );
|
|
2279
|
+
* };
|
|
2280
|
+
* ```
|
|
2164
2281
|
*/
|
|
2165
2282
|
const DateField = ({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, className, defaultValue, "data-testid": dataTestId, ref, ...rest }) => {
|
|
2166
2283
|
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
@@ -2228,8 +2345,63 @@ const DropZoneDefaultLabel = () => (jsx(Trans, { components: {
|
|
|
2228
2345
|
}, i18nKey: "dropzone.label.default", values: {} }));
|
|
2229
2346
|
|
|
2230
2347
|
/**
|
|
2231
|
-
* The
|
|
2348
|
+
* The `<DropZone>` component provides a drag-and-drop area for file uploads.
|
|
2349
|
+
* Users can either drag files onto the zone or click to browse the file system.
|
|
2350
|
+
*
|
|
2351
|
+
* ### When to use
|
|
2352
|
+
* - Drag-and-drop file upload experience
|
|
2353
|
+
* - Batch file uploads
|
|
2354
|
+
* - When visual feedback during drag is important
|
|
2355
|
+
*
|
|
2356
|
+
* ### When not to use
|
|
2357
|
+
* - Simple single file uploads - use `UploadField` instead
|
|
2358
|
+
* - When form field styling (label, help text) is needed - use `UploadField`
|
|
2359
|
+
*
|
|
2360
|
+
* @example Basic usage
|
|
2361
|
+
* ```tsx
|
|
2362
|
+
* import { DropZone } from "@trackunit/react-form-components";
|
|
2363
|
+
*
|
|
2364
|
+
* <DropZone
|
|
2365
|
+
* filesSelected={(files) => handleFiles(files)}
|
|
2366
|
+
* accept="image/*"
|
|
2367
|
+
* />
|
|
2368
|
+
* ```
|
|
2369
|
+
* @example With custom label
|
|
2370
|
+
* ```tsx
|
|
2371
|
+
* import { DropZone } from "@trackunit/react-form-components";
|
|
2232
2372
|
*
|
|
2373
|
+
* <DropZone
|
|
2374
|
+
* filesSelected={(files) => handleFiles(files)}
|
|
2375
|
+
* label={
|
|
2376
|
+
* <span>
|
|
2377
|
+
* <strong>Drop files here</strong> or click to browse
|
|
2378
|
+
* </span>
|
|
2379
|
+
* }
|
|
2380
|
+
* accept=".pdf,.doc,.docx"
|
|
2381
|
+
* />
|
|
2382
|
+
* ```
|
|
2383
|
+
* @example Multiple files with size options
|
|
2384
|
+
* ```tsx
|
|
2385
|
+
* import { DropZone } from "@trackunit/react-form-components";
|
|
2386
|
+
*
|
|
2387
|
+
* <DropZone
|
|
2388
|
+
* filesSelected={(files) => {
|
|
2389
|
+
* Array.from(files).forEach(file => uploadFile(file));
|
|
2390
|
+
* }}
|
|
2391
|
+
* multiple
|
|
2392
|
+
* size="medium"
|
|
2393
|
+
* accept="image/png,image/jpeg"
|
|
2394
|
+
* />
|
|
2395
|
+
* ```
|
|
2396
|
+
* @example Disabled state
|
|
2397
|
+
* ```tsx
|
|
2398
|
+
* import { DropZone } from "@trackunit/react-form-components";
|
|
2399
|
+
*
|
|
2400
|
+
* <DropZone
|
|
2401
|
+
* filesSelected={(files) => handleFiles(files)}
|
|
2402
|
+
* disabled={isUploading}
|
|
2403
|
+
* />
|
|
2404
|
+
* ```
|
|
2233
2405
|
* @param {DropZoneProps} props - The props for the DropZone component
|
|
2234
2406
|
* @returns {ReactElement} DropZone component
|
|
2235
2407
|
*/
|
|
@@ -2341,9 +2513,53 @@ const EmailBaseInput = ({ fieldSize = "medium", disabled = false, "data-testid":
|
|
|
2341
2513
|
};
|
|
2342
2514
|
|
|
2343
2515
|
/**
|
|
2344
|
-
* The EmailField component is used to enter email.
|
|
2345
|
-
*
|
|
2516
|
+
* The `<EmailField>` component is used to enter and validate email addresses.
|
|
2517
|
+
* It automatically validates the format on blur and displays appropriate error messages.
|
|
2518
|
+
*
|
|
2519
|
+
* ### When to use
|
|
2520
|
+
* - Collecting user email addresses in forms
|
|
2521
|
+
* - Contact forms that require email validation
|
|
2522
|
+
* - Any input that must be a valid email format
|
|
2523
|
+
*
|
|
2524
|
+
* ### When not to use
|
|
2525
|
+
* - For general text input - use `TextField` instead
|
|
2526
|
+
* - For URLs - use `UrlField` instead
|
|
2527
|
+
* - For phone numbers - use `PhoneField` instead
|
|
2528
|
+
*
|
|
2529
|
+
* @example Basic usage
|
|
2530
|
+
* ```tsx
|
|
2531
|
+
* import { EmailField } from "@trackunit/react-form-components";
|
|
2532
|
+
*
|
|
2533
|
+
* const [email, setEmail] = useState("");
|
|
2534
|
+
*
|
|
2535
|
+
* <EmailField
|
|
2536
|
+
* label="Email Address"
|
|
2537
|
+
* value={email}
|
|
2538
|
+
* onChange={(e) => setEmail(e.target.value)}
|
|
2539
|
+
* required
|
|
2540
|
+
* />
|
|
2541
|
+
* ```
|
|
2542
|
+
* @example With help text
|
|
2543
|
+
* ```tsx
|
|
2544
|
+
* import { EmailField } from "@trackunit/react-form-components";
|
|
2545
|
+
*
|
|
2546
|
+
* <EmailField
|
|
2547
|
+
* label="Work Email"
|
|
2548
|
+
* helpText="We'll use this to send notifications"
|
|
2549
|
+
* placeholder="name@company.com"
|
|
2550
|
+
* />
|
|
2551
|
+
* ```
|
|
2552
|
+
* @example With custom error message
|
|
2553
|
+
* ```tsx
|
|
2554
|
+
* import { EmailField } from "@trackunit/react-form-components";
|
|
2346
2555
|
*
|
|
2556
|
+
* <EmailField
|
|
2557
|
+
* label="Email"
|
|
2558
|
+
* value={email}
|
|
2559
|
+
* onChange={(e) => setEmail(e.target.value)}
|
|
2560
|
+
* errorMessage={isEmailTaken ? "This email is already registered" : undefined}
|
|
2561
|
+
* />
|
|
2562
|
+
* ```
|
|
2347
2563
|
*/
|
|
2348
2564
|
const EmailField = ({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, "data-testid": dataTestId, value, onChange, onBlur, isInvalid = false, ref, ...rest }) => {
|
|
2349
2565
|
const htmlForId = id ? id : "emailField-" + uuidv4();
|
|
@@ -2452,9 +2668,63 @@ const FormFieldSelectAdapterMulti = (props) => {
|
|
|
2452
2668
|
};
|
|
2453
2669
|
|
|
2454
2670
|
/**
|
|
2455
|
-
* MultiSelectField
|
|
2456
|
-
*
|
|
2671
|
+
* The `<MultiSelectField>` component allows selecting multiple options from a dropdown list.
|
|
2672
|
+
* It wraps the select input with FormGroup for consistent form styling including label,
|
|
2673
|
+
* help text, and validation.
|
|
2674
|
+
*
|
|
2675
|
+
* ### When to use
|
|
2676
|
+
* - Selecting multiple items from a predefined list (tags, categories, permissions)
|
|
2677
|
+
* - When users need to see and manage all selected items
|
|
2678
|
+
* - Filtering by multiple criteria
|
|
2679
|
+
*
|
|
2680
|
+
* ### When not to use
|
|
2681
|
+
* - Single selection - use `SelectField` instead
|
|
2682
|
+
* - Very long lists - consider a searchable/async variant
|
|
2683
|
+
* - Binary choices - use `Checkbox` or `ToggleSwitchOption` instead
|
|
2684
|
+
*
|
|
2685
|
+
* @example Basic usage
|
|
2686
|
+
* ```tsx
|
|
2687
|
+
* import { MultiSelectField } from "@trackunit/react-form-components";
|
|
2688
|
+
*
|
|
2689
|
+
* const options = [
|
|
2690
|
+
* { value: "read", label: "Read" },
|
|
2691
|
+
* { value: "write", label: "Write" },
|
|
2692
|
+
* { value: "delete", label: "Delete" },
|
|
2693
|
+
* ];
|
|
2694
|
+
*
|
|
2695
|
+
* <MultiSelectField
|
|
2696
|
+
* label="Permissions"
|
|
2697
|
+
* options={options}
|
|
2698
|
+
* value={selectedPermissions}
|
|
2699
|
+
* onChange={(selected) => setSelectedPermissions(selected)}
|
|
2700
|
+
* />
|
|
2701
|
+
* ```
|
|
2702
|
+
* @example With placeholder and help text
|
|
2703
|
+
* ```tsx
|
|
2704
|
+
* import { MultiSelectField } from "@trackunit/react-form-components";
|
|
2705
|
+
*
|
|
2706
|
+
* <MultiSelectField
|
|
2707
|
+
* label="Categories"
|
|
2708
|
+
* options={categoryOptions}
|
|
2709
|
+
* value={selectedCategories}
|
|
2710
|
+
* onChange={(selected) => setSelectedCategories(selected)}
|
|
2711
|
+
* placeholder="Select categories..."
|
|
2712
|
+
* helpText="Select all that apply"
|
|
2713
|
+
* />
|
|
2714
|
+
* ```
|
|
2715
|
+
* @example With validation
|
|
2716
|
+
* ```tsx
|
|
2717
|
+
* import { MultiSelectField } from "@trackunit/react-form-components";
|
|
2457
2718
|
*
|
|
2719
|
+
* <MultiSelectField
|
|
2720
|
+
* label="Required Tags"
|
|
2721
|
+
* options={tagOptions}
|
|
2722
|
+
* value={selectedTags}
|
|
2723
|
+
* onChange={(selected) => setSelectedTags(selected)}
|
|
2724
|
+
* required
|
|
2725
|
+
* errorMessage={selectedTags.length === 0 ? "Select at least one tag" : undefined}
|
|
2726
|
+
* />
|
|
2727
|
+
* ```
|
|
2458
2728
|
* @param {MultiSelectFieldProps} props - The props for the MultiSelectField component
|
|
2459
2729
|
* @returns {ReactElement} MultiSelectField component
|
|
2460
2730
|
*/
|
|
@@ -2516,9 +2786,53 @@ const validateNumber = (number, required = false, min, max) => {
|
|
|
2516
2786
|
/**
|
|
2517
2787
|
* The number field component is used for entering numeric values and includes controls for incrementally increasing or decreasing the value.
|
|
2518
2788
|
*
|
|
2519
|
-
*
|
|
2789
|
+
* ### When to use
|
|
2790
|
+
* Use NumberField when the controls to incrementally increase or decrease makes the task easier for the user, such as quantity selectors or numeric settings.
|
|
2520
2791
|
*
|
|
2521
|
-
*
|
|
2792
|
+
* ### When not to use
|
|
2793
|
+
* Do not use NumberField for non-serialized numbers or IDs. Use TextField instead.
|
|
2794
|
+
*
|
|
2795
|
+
* @example Basic number field
|
|
2796
|
+
* ```tsx
|
|
2797
|
+
* import { NumberField } from "@trackunit/react-form-components";
|
|
2798
|
+
* import { useState } from "react";
|
|
2799
|
+
*
|
|
2800
|
+
* const MyForm = () => {
|
|
2801
|
+
* const [quantity, setQuantity] = useState(1);
|
|
2802
|
+
*
|
|
2803
|
+
* return (
|
|
2804
|
+
* <NumberField
|
|
2805
|
+
* label="Quantity"
|
|
2806
|
+
* value={quantity}
|
|
2807
|
+
* onChange={(e) => setQuantity(Number(e.target.value))}
|
|
2808
|
+
* min={1}
|
|
2809
|
+
* max={100}
|
|
2810
|
+
* />
|
|
2811
|
+
* );
|
|
2812
|
+
* };
|
|
2813
|
+
* ```
|
|
2814
|
+
* @example Number field with validation
|
|
2815
|
+
* ```tsx
|
|
2816
|
+
* import { NumberField } from "@trackunit/react-form-components";
|
|
2817
|
+
* import { useState } from "react";
|
|
2818
|
+
*
|
|
2819
|
+
* const TemperatureInput = () => {
|
|
2820
|
+
* const [temp, setTemp] = useState(20);
|
|
2821
|
+
*
|
|
2822
|
+
* return (
|
|
2823
|
+
* <NumberField
|
|
2824
|
+
* label="Temperature (°C)"
|
|
2825
|
+
* value={temp}
|
|
2826
|
+
* onChange={(e) => setTemp(Number(e.target.value))}
|
|
2827
|
+
* min={-40}
|
|
2828
|
+
* max={60}
|
|
2829
|
+
* step={0.5}
|
|
2830
|
+
* helpText="Enter a value between -40 and 60"
|
|
2831
|
+
* required
|
|
2832
|
+
* />
|
|
2833
|
+
* );
|
|
2834
|
+
* };
|
|
2835
|
+
* ```
|
|
2522
2836
|
*/
|
|
2523
2837
|
const NumberField = ({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, maxLength, className, value, "data-testid": dataTestId, defaultValue, onBlur, onChange, ref, ...rest }) => {
|
|
2524
2838
|
const htmlForId = id ? id : "numberField-" + uuidv4();
|
|
@@ -2665,11 +2979,60 @@ const PasswordBaseInput = ({ ref, fieldSize, ...rest }) => {
|
|
|
2665
2979
|
};
|
|
2666
2980
|
|
|
2667
2981
|
/**
|
|
2668
|
-
*
|
|
2982
|
+
* The `<PasswordField>` component is used to enter passwords or other confidential information.
|
|
2983
|
+
* Characters are masked as they are typed, with an option to toggle visibility.
|
|
2984
|
+
*
|
|
2985
|
+
* ### When to use
|
|
2986
|
+
* - Password input for login or registration forms
|
|
2987
|
+
* - Entering sensitive data that should be obfuscated (API keys, secrets)
|
|
2988
|
+
* - Any confidential text input
|
|
2989
|
+
*
|
|
2990
|
+
* ### When not to use
|
|
2991
|
+
* - Confirming user actions like deletion - use a Checkbox instead
|
|
2992
|
+
* - Non-sensitive text input - use `TextField` instead
|
|
2993
|
+
* - PIN codes - consider a specialized PIN input
|
|
2994
|
+
*
|
|
2995
|
+
* @example Basic usage
|
|
2996
|
+
* ```tsx
|
|
2997
|
+
* import { PasswordField } from "@trackunit/react-form-components";
|
|
2998
|
+
*
|
|
2999
|
+
* const [password, setPassword] = useState("");
|
|
2669
3000
|
*
|
|
2670
|
-
*
|
|
3001
|
+
* <PasswordField
|
|
3002
|
+
* label="Password"
|
|
3003
|
+
* value={password}
|
|
3004
|
+
* onChange={(e) => setPassword(e.target.value)}
|
|
3005
|
+
* required
|
|
3006
|
+
* />
|
|
3007
|
+
* ```
|
|
3008
|
+
* @example With validation
|
|
3009
|
+
* ```tsx
|
|
3010
|
+
* import { PasswordField } from "@trackunit/react-form-components";
|
|
2671
3011
|
*
|
|
2672
|
-
*
|
|
3012
|
+
* <PasswordField
|
|
3013
|
+
* label="New Password"
|
|
3014
|
+
* value={password}
|
|
3015
|
+
* onChange={(e) => setPassword(e.target.value)}
|
|
3016
|
+
* helpText="Must be at least 8 characters"
|
|
3017
|
+
* errorMessage={password.length < 8 ? "Password too short" : undefined}
|
|
3018
|
+
* />
|
|
3019
|
+
* ```
|
|
3020
|
+
* @example Confirm password pattern
|
|
3021
|
+
* ```tsx
|
|
3022
|
+
* import { PasswordField } from "@trackunit/react-form-components";
|
|
3023
|
+
*
|
|
3024
|
+
* <PasswordField
|
|
3025
|
+
* label="Password"
|
|
3026
|
+
* value={password}
|
|
3027
|
+
* onChange={(e) => setPassword(e.target.value)}
|
|
3028
|
+
* />
|
|
3029
|
+
* <PasswordField
|
|
3030
|
+
* label="Confirm Password"
|
|
3031
|
+
* value={confirmPassword}
|
|
3032
|
+
* onChange={(e) => setConfirmPassword(e.target.value)}
|
|
3033
|
+
* errorMessage={confirmPassword !== password ? "Passwords do not match" : undefined}
|
|
3034
|
+
* />
|
|
3035
|
+
* ```
|
|
2673
3036
|
*/
|
|
2674
3037
|
const PasswordField = ({ id, label, tip, helpText, helpAddon, errorMessage, isInvalid, maxLength, onChange, className, value, "data-testid": dataTestId, ref, ...rest }) => {
|
|
2675
3038
|
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
@@ -2732,19 +3095,52 @@ const phoneErrorMessage = (phoneNumber, required) => {
|
|
|
2732
3095
|
};
|
|
2733
3096
|
|
|
2734
3097
|
/**
|
|
2735
|
-
* The PhoneField component is used to enter phone
|
|
2736
|
-
* It
|
|
2737
|
-
* It is used to render a phone number field with a label, a tip, a help text, a help addon and an error message.
|
|
3098
|
+
* The `<PhoneField>` component is used to enter and validate phone numbers.
|
|
3099
|
+
* It includes built-in validation for phone number format and displays appropriate error messages.
|
|
2738
3100
|
*
|
|
2739
|
-
*
|
|
2740
|
-
*
|
|
2741
|
-
*
|
|
2742
|
-
*
|
|
2743
|
-
*
|
|
2744
|
-
*
|
|
2745
|
-
*
|
|
2746
|
-
*
|
|
2747
|
-
*
|
|
3101
|
+
* ### When to use
|
|
3102
|
+
* - Collecting phone numbers in contact forms
|
|
3103
|
+
* - User profile phone number fields
|
|
3104
|
+
* - Any input requiring phone number validation
|
|
3105
|
+
*
|
|
3106
|
+
* ### When not to use
|
|
3107
|
+
* - General text input - use `TextField` instead
|
|
3108
|
+
* - International phone with country selector - consider specialized international phone component
|
|
3109
|
+
*
|
|
3110
|
+
* @example Basic usage
|
|
3111
|
+
* ```tsx
|
|
3112
|
+
* import { PhoneField } from "@trackunit/react-form-components";
|
|
3113
|
+
*
|
|
3114
|
+
* const [phone, setPhone] = useState("");
|
|
3115
|
+
*
|
|
3116
|
+
* <PhoneField
|
|
3117
|
+
* label="Phone Number"
|
|
3118
|
+
* value={phone}
|
|
3119
|
+
* onChange={(e) => setPhone(e.target.value)}
|
|
3120
|
+
* required
|
|
3121
|
+
* />
|
|
3122
|
+
* ```
|
|
3123
|
+
* @example With help text
|
|
3124
|
+
* ```tsx
|
|
3125
|
+
* import { PhoneField } from "@trackunit/react-form-components";
|
|
3126
|
+
*
|
|
3127
|
+
* <PhoneField
|
|
3128
|
+
* label="Mobile Phone"
|
|
3129
|
+
* helpText="Include country code for international numbers"
|
|
3130
|
+
* placeholder="+1 555 123 4567"
|
|
3131
|
+
* />
|
|
3132
|
+
* ```
|
|
3133
|
+
* @example With custom error handling
|
|
3134
|
+
* ```tsx
|
|
3135
|
+
* import { PhoneField } from "@trackunit/react-form-components";
|
|
3136
|
+
*
|
|
3137
|
+
* <PhoneField
|
|
3138
|
+
* label="Contact Phone"
|
|
3139
|
+
* value={phone}
|
|
3140
|
+
* onChange={(e) => setPhone(e.target.value)}
|
|
3141
|
+
* errorMessage={isPhoneDuplicate ? "This phone is already registered" : undefined}
|
|
3142
|
+
* />
|
|
3143
|
+
* ```
|
|
2748
3144
|
*/
|
|
2749
3145
|
const PhoneField = ({ label, id, tip, helpText, isInvalid, errorMessage, value, helpAddon, className, defaultValue, "data-testid": dataTestId, name, onBlur, ref, ...rest }) => {
|
|
2750
3146
|
const htmlForId = id ? id : "phoneField-" + uuidv4();
|
|
@@ -2862,10 +3258,57 @@ const RadioGroupContext = createContext(null);
|
|
|
2862
3258
|
*
|
|
2863
3259
|
* Radio buttons are used for mutually exclusive choices, not for multiple choices. Only one radio button can be selected at a time. When a user chooses a new item, the previous choice is automatically deselected.
|
|
2864
3260
|
*
|
|
2865
|
-
*
|
|
3261
|
+
* ### When to use
|
|
3262
|
+
* Use RadioGroup in forms, settings, or selections in a list where only one option can be selected.
|
|
3263
|
+
*
|
|
3264
|
+
* ### When not to use
|
|
3265
|
+
* Do not use RadioGroup if a user can select many options from a list. Use Checkbox instead.
|
|
3266
|
+
*
|
|
3267
|
+
* @example Basic radio group
|
|
3268
|
+
* ```tsx
|
|
3269
|
+
* import { RadioGroup, RadioItem } from "@trackunit/react-form-components";
|
|
3270
|
+
* import { useState, ChangeEvent } from "react";
|
|
3271
|
+
*
|
|
3272
|
+
* const MyForm = () => {
|
|
3273
|
+
* const [size, setSize] = useState("medium");
|
|
3274
|
+
*
|
|
3275
|
+
* return (
|
|
3276
|
+
* <RadioGroup
|
|
3277
|
+
* id="size-selection"
|
|
3278
|
+
* label="Select size"
|
|
3279
|
+
* value={size}
|
|
3280
|
+
* onChange={(e: ChangeEvent<HTMLInputElement>) => setSize(e.target.value)}
|
|
3281
|
+
* >
|
|
3282
|
+
* <RadioItem label="Small" value="small" />
|
|
3283
|
+
* <RadioItem label="Medium" value="medium" />
|
|
3284
|
+
* <RadioItem label="Large" value="large" />
|
|
3285
|
+
* </RadioGroup>
|
|
3286
|
+
* );
|
|
3287
|
+
* };
|
|
3288
|
+
* ```
|
|
3289
|
+
* @example Inline radio group with descriptions
|
|
3290
|
+
* ```tsx
|
|
3291
|
+
* import { RadioGroup, RadioItem } from "@trackunit/react-form-components";
|
|
3292
|
+
* import { useState, ChangeEvent } from "react";
|
|
2866
3293
|
*
|
|
2867
|
-
*
|
|
3294
|
+
* const PlanSelector = () => {
|
|
3295
|
+
* const [plan, setPlan] = useState("basic");
|
|
2868
3296
|
*
|
|
3297
|
+
* return (
|
|
3298
|
+
* <RadioGroup
|
|
3299
|
+
* id="plan-selection"
|
|
3300
|
+
* label="Choose your plan"
|
|
3301
|
+
* value={plan}
|
|
3302
|
+
* onChange={(e: ChangeEvent<HTMLInputElement>) => setPlan(e.target.value)}
|
|
3303
|
+
* inline
|
|
3304
|
+
* >
|
|
3305
|
+
* <RadioItem label="Basic" value="basic" description="$9/month" />
|
|
3306
|
+
* <RadioItem label="Pro" value="pro" description="$29/month" />
|
|
3307
|
+
* <RadioItem label="Enterprise" value="enterprise" description="Contact us" />
|
|
3308
|
+
* </RadioGroup>
|
|
3309
|
+
* );
|
|
3310
|
+
* };
|
|
3311
|
+
* ```
|
|
2869
3312
|
* @param {RadioGroupProps} props - The props for the RadioGroup component
|
|
2870
3313
|
* @returns {ReactElement} RadioGroup component
|
|
2871
3314
|
*/
|
|
@@ -3252,8 +3695,69 @@ const CreatableSelectField = ({ allowCreateWhileLoading = false, onCreateOption,
|
|
|
3252
3695
|
CreatableSelectField.displayName = "CreatableSelectField";
|
|
3253
3696
|
|
|
3254
3697
|
/**
|
|
3255
|
-
*
|
|
3698
|
+
* SelectField is a dropdown select component wrapped in the FormGroup component for selecting a single option from a list.
|
|
3699
|
+
*
|
|
3700
|
+
* ### When to use
|
|
3701
|
+
* Use SelectField when users need to choose one option from a predefined list of 4 or more items.
|
|
3702
|
+
*
|
|
3703
|
+
* ### When not to use
|
|
3704
|
+
* Do not use SelectField for fewer than 4 options. Use RadioGroup instead for better usability with small option sets.
|
|
3705
|
+
*
|
|
3706
|
+
* @example Basic select field
|
|
3707
|
+
* ```tsx
|
|
3708
|
+
* import { SelectField } from "@trackunit/react-form-components";
|
|
3709
|
+
* import { useState } from "react";
|
|
3710
|
+
*
|
|
3711
|
+
* const options = [
|
|
3712
|
+
* { value: "us", label: "United States" },
|
|
3713
|
+
* { value: "uk", label: "United Kingdom" },
|
|
3714
|
+
* { value: "de", label: "Germany" },
|
|
3715
|
+
* { value: "fr", label: "France" },
|
|
3716
|
+
* ];
|
|
3717
|
+
*
|
|
3718
|
+
* const MyForm = () => {
|
|
3719
|
+
* const [country, setCountry] = useState<{ value: string; label: string } | null>(null);
|
|
3720
|
+
*
|
|
3721
|
+
* return (
|
|
3722
|
+
* <SelectField
|
|
3723
|
+
* label="Country"
|
|
3724
|
+
* options={options}
|
|
3725
|
+
* value={country}
|
|
3726
|
+
* onChange={(option) => setCountry(option)}
|
|
3727
|
+
* placeholder="Select a country"
|
|
3728
|
+
* />
|
|
3729
|
+
* );
|
|
3730
|
+
* };
|
|
3731
|
+
* ```
|
|
3732
|
+
* @example Async select field with search
|
|
3733
|
+
* ```tsx
|
|
3734
|
+
* import { SelectField } from "@trackunit/react-form-components";
|
|
3735
|
+
* import { useState } from "react";
|
|
3736
|
+
*
|
|
3737
|
+
* const MyAsyncForm = () => {
|
|
3738
|
+
* const [selected, setSelected] = useState(null);
|
|
3256
3739
|
*
|
|
3740
|
+
* const loadOptions = async (inputValue: string) => {
|
|
3741
|
+
* const response = await fetch(`/api/users?search=${inputValue}`);
|
|
3742
|
+
* const users = await response.json();
|
|
3743
|
+
* return users.map((user: { id: string; name: string }) => ({
|
|
3744
|
+
* value: user.id,
|
|
3745
|
+
* label: user.name,
|
|
3746
|
+
* }));
|
|
3747
|
+
* };
|
|
3748
|
+
*
|
|
3749
|
+
* return (
|
|
3750
|
+
* <SelectField
|
|
3751
|
+
* label="Assign to user"
|
|
3752
|
+
* isAsync
|
|
3753
|
+
* loadOptions={loadOptions}
|
|
3754
|
+
* value={selected}
|
|
3755
|
+
* onChange={setSelected}
|
|
3756
|
+
* isClearable
|
|
3757
|
+
* />
|
|
3758
|
+
* );
|
|
3759
|
+
* };
|
|
3760
|
+
* ```
|
|
3257
3761
|
* @param {SelectFieldProps} props - The props for the SelectField component
|
|
3258
3762
|
*/
|
|
3259
3763
|
function SelectField({ ref, ...props }) {
|
|
@@ -3273,12 +3777,54 @@ const TextLengthIndicator = ({ length, maxLength }) => {
|
|
|
3273
3777
|
};
|
|
3274
3778
|
|
|
3275
3779
|
/**
|
|
3276
|
-
*
|
|
3780
|
+
* The `<TextAreaField>` component is used for multi-line text input.
|
|
3781
|
+
* Use when you need to allow users to enter a large amount of text, such as comments or descriptions.
|
|
3782
|
+
*
|
|
3783
|
+
* ### When to use
|
|
3784
|
+
* - Longer text inputs like comments, descriptions, or notes
|
|
3785
|
+
* - When the expected input is more than one line
|
|
3786
|
+
* - For free-form text that benefits from a larger input area
|
|
3787
|
+
*
|
|
3788
|
+
* ### When not to use
|
|
3789
|
+
* - Single-line inputs - use `TextField` instead
|
|
3790
|
+
* - Structured data like email or phone - use specialized field components
|
|
3791
|
+
* - Rich text editing - consider a rich text editor component
|
|
3792
|
+
*
|
|
3793
|
+
* @example Basic usage
|
|
3794
|
+
* ```tsx
|
|
3795
|
+
* import { TextAreaField } from "@trackunit/react-form-components";
|
|
3277
3796
|
*
|
|
3278
|
-
*
|
|
3797
|
+
* const [description, setDescription] = useState("");
|
|
3279
3798
|
*
|
|
3280
|
-
*
|
|
3799
|
+
* <TextAreaField
|
|
3800
|
+
* label="Description"
|
|
3801
|
+
* value={description}
|
|
3802
|
+
* onChange={(e) => setDescription(e.target.value)}
|
|
3803
|
+
* placeholder="Enter a description..."
|
|
3804
|
+
* />
|
|
3805
|
+
* ```
|
|
3806
|
+
* @example With character limit
|
|
3807
|
+
* ```tsx
|
|
3808
|
+
* import { TextAreaField } from "@trackunit/react-form-components";
|
|
3809
|
+
*
|
|
3810
|
+
* <TextAreaField
|
|
3811
|
+
* label="Comment"
|
|
3812
|
+
* maxLength={500}
|
|
3813
|
+
* helpText="Add any additional notes"
|
|
3814
|
+
* required
|
|
3815
|
+
* />
|
|
3816
|
+
* ```
|
|
3817
|
+
* @example With validation error
|
|
3818
|
+
* ```tsx
|
|
3819
|
+
* import { TextAreaField } from "@trackunit/react-form-components";
|
|
3281
3820
|
*
|
|
3821
|
+
* <TextAreaField
|
|
3822
|
+
* label="Notes"
|
|
3823
|
+
* value={notes}
|
|
3824
|
+
* onChange={(e) => setNotes(e.target.value)}
|
|
3825
|
+
* errorMessage={notes.length < 10 ? "Notes must be at least 10 characters" : undefined}
|
|
3826
|
+
* />
|
|
3827
|
+
* ```
|
|
3282
3828
|
* @param {TextAreaFieldProps} props - The props for the TextAreaField component
|
|
3283
3829
|
* @returns {ReactElement} TextAreaField component
|
|
3284
3830
|
*/
|
|
@@ -3299,6 +3845,61 @@ TextAreaField.displayName = "TextAreaField";
|
|
|
3299
3845
|
|
|
3300
3846
|
/**
|
|
3301
3847
|
* Text fields enable the user to interact with and input content and data. This component can be used for long and short form entries. Allow the size of the text input box to reflect the length of the content you expect the user to enter.
|
|
3848
|
+
*
|
|
3849
|
+
* ### When to use
|
|
3850
|
+
* Use text fields for short-form text input such as names, emails, or search queries.
|
|
3851
|
+
*
|
|
3852
|
+
* ### When not to use
|
|
3853
|
+
* Do not use text fields for multi-line text input. Use TextAreaField instead.
|
|
3854
|
+
*
|
|
3855
|
+
* @example Basic text field
|
|
3856
|
+
* ```tsx
|
|
3857
|
+
* import { TextField } from "@trackunit/react-form-components";
|
|
3858
|
+
* import { useState } from "react";
|
|
3859
|
+
*
|
|
3860
|
+
* const MyForm = () => {
|
|
3861
|
+
* const [name, setName] = useState("");
|
|
3862
|
+
*
|
|
3863
|
+
* return (
|
|
3864
|
+
* <TextField
|
|
3865
|
+
* label="Name"
|
|
3866
|
+
* value={name}
|
|
3867
|
+
* onChange={(e) => setName(e.target.value)}
|
|
3868
|
+
* placeholder="Enter your name"
|
|
3869
|
+
* />
|
|
3870
|
+
* );
|
|
3871
|
+
* };
|
|
3872
|
+
* ```
|
|
3873
|
+
* @example Text field with validation and help text
|
|
3874
|
+
* ```tsx
|
|
3875
|
+
* import { TextField } from "@trackunit/react-form-components";
|
|
3876
|
+
* import { useState } from "react";
|
|
3877
|
+
*
|
|
3878
|
+
* const MyForm = () => {
|
|
3879
|
+
* const [email, setEmail] = useState("");
|
|
3880
|
+
* const [error, setError] = useState<string | undefined>();
|
|
3881
|
+
*
|
|
3882
|
+
* const handleBlur = () => {
|
|
3883
|
+
* if (!email.includes("@")) {
|
|
3884
|
+
* setError("Please enter a valid email address");
|
|
3885
|
+
* } else {
|
|
3886
|
+
* setError(undefined);
|
|
3887
|
+
* }
|
|
3888
|
+
* };
|
|
3889
|
+
*
|
|
3890
|
+
* return (
|
|
3891
|
+
* <TextField
|
|
3892
|
+
* label="Email"
|
|
3893
|
+
* value={email}
|
|
3894
|
+
* onChange={(e) => setEmail(e.target.value)}
|
|
3895
|
+
* onBlur={handleBlur}
|
|
3896
|
+
* errorMessage={error}
|
|
3897
|
+
* helpText="We'll never share your email"
|
|
3898
|
+
* required
|
|
3899
|
+
* />
|
|
3900
|
+
* );
|
|
3901
|
+
* };
|
|
3902
|
+
* ```
|
|
3302
3903
|
*/
|
|
3303
3904
|
const TextField = ({ id, label, tip, helpText, helpAddon, errorMessage, isInvalid, maxLength, onChange, className, value, "data-testid": dataTestId, isWarning, ref, ...rest }) => {
|
|
3304
3905
|
const [valueLength, setValueLength] = useState(value ? `${value}`.length : 0);
|
|
@@ -3401,11 +4002,55 @@ const cvaToggleSwitchThumb = cvaMerge(["block", "rounded-full", "bg-white", "asp
|
|
|
3401
4002
|
});
|
|
3402
4003
|
|
|
3403
4004
|
/**
|
|
3404
|
-
*
|
|
3405
|
-
* or
|
|
4005
|
+
* The `<ToggleSwitch>` is a low-level checkbox input wrapper with `role="switch"`.
|
|
4006
|
+
* It renders just the switch control without label or description.
|
|
4007
|
+
*
|
|
4008
|
+
* **Not intended for standalone use** - use `ToggleSwitchOption` instead for forms.
|
|
4009
|
+
* This component is for building custom toggle implementations or wrapping in other components.
|
|
4010
|
+
*
|
|
4011
|
+
* ### When to use
|
|
4012
|
+
* - Building custom toggle components with different layouts
|
|
4013
|
+
* - Integrating a toggle into a component that provides its own label (e.g., menu items)
|
|
4014
|
+
* - Creating specialized switch controls
|
|
4015
|
+
*
|
|
4016
|
+
* ### When not to use
|
|
4017
|
+
* - Standard forms - use `ToggleSwitchOption` instead (includes label/description)
|
|
4018
|
+
* - Multiple selections - use `Checkbox` components instead
|
|
4019
|
+
*
|
|
4020
|
+
* @example Basic usage (in custom component)
|
|
4021
|
+
* ```tsx
|
|
4022
|
+
* import { ToggleSwitch } from "@trackunit/react-form-components";
|
|
3406
4023
|
*
|
|
3407
|
-
*
|
|
4024
|
+
* const [isEnabled, setIsEnabled] = useState(false);
|
|
4025
|
+
*
|
|
4026
|
+
* <ToggleSwitch
|
|
4027
|
+
* toggled={isEnabled}
|
|
4028
|
+
* onChange={(toggled) => setIsEnabled(toggled)}
|
|
4029
|
+
* />
|
|
4030
|
+
* ```
|
|
4031
|
+
* @example Inside a custom wrapper
|
|
4032
|
+
* ```tsx
|
|
4033
|
+
* import { ToggleSwitch } from "@trackunit/react-form-components";
|
|
4034
|
+
*
|
|
4035
|
+
* <div className="flex items-center gap-2">
|
|
4036
|
+
* <span>Dark Mode</span>
|
|
4037
|
+
* <ToggleSwitch
|
|
4038
|
+
* toggled={darkMode}
|
|
4039
|
+
* onChange={(toggled) => setDarkMode(toggled)}
|
|
4040
|
+
* size="small"
|
|
4041
|
+
* />
|
|
4042
|
+
* </div>
|
|
4043
|
+
* ```
|
|
4044
|
+
* @example Disabled state
|
|
4045
|
+
* ```tsx
|
|
4046
|
+
* import { ToggleSwitch } from "@trackunit/react-form-components";
|
|
3408
4047
|
*
|
|
4048
|
+
* <ToggleSwitch
|
|
4049
|
+
* toggled={true}
|
|
4050
|
+
* onChange={() => {}}
|
|
4051
|
+
* disabled
|
|
4052
|
+
* />
|
|
4053
|
+
* ```
|
|
3409
4054
|
* @param {ToggleSwitchProps} props - The props for the ToggleSwitch component
|
|
3410
4055
|
* @returns {ReactElement} ToggleSwitch component
|
|
3411
4056
|
*/
|
|
@@ -3452,13 +4097,65 @@ const ToggleSwitch = forwardRef(({ onChange, onClick, preventDefaultOnClick, cla
|
|
|
3452
4097
|
ToggleSwitch.displayName = "ToggleSwitch";
|
|
3453
4098
|
|
|
3454
4099
|
/**
|
|
3455
|
-
*
|
|
3456
|
-
*
|
|
4100
|
+
* The `<ToggleSwitchOption>` component is used for binary on/off settings in forms.
|
|
4101
|
+
* It combines a toggle switch with a label and optional description for complete form integration.
|
|
4102
|
+
*
|
|
4103
|
+
* ### When to use
|
|
4104
|
+
* - Enable/disable feature toggles in settings
|
|
4105
|
+
* - Binary choices where the action takes effect immediately
|
|
4106
|
+
* - Preferences that represent on/off states
|
|
4107
|
+
*
|
|
4108
|
+
* ### When not to use
|
|
4109
|
+
* - Multiple selections from a list - use `Checkbox` components instead
|
|
4110
|
+
* - Mutually exclusive options - use `RadioGroup` instead
|
|
4111
|
+
* - Actions requiring confirmation - use buttons with confirmation dialog
|
|
4112
|
+
*
|
|
4113
|
+
* @example Basic usage
|
|
4114
|
+
* ```tsx
|
|
4115
|
+
* import { ToggleSwitchOption } from "@trackunit/react-form-components";
|
|
4116
|
+
*
|
|
4117
|
+
* const [notifications, setNotifications] = useState(true);
|
|
3457
4118
|
*
|
|
3458
|
-
*
|
|
4119
|
+
* <ToggleSwitchOption
|
|
4120
|
+
* id="notifications"
|
|
4121
|
+
* label="Enable Notifications"
|
|
4122
|
+
* toggled={notifications}
|
|
4123
|
+
* onChange={(toggled) => setNotifications(toggled)}
|
|
4124
|
+
* />
|
|
4125
|
+
* ```
|
|
4126
|
+
* @example With description
|
|
4127
|
+
* ```tsx
|
|
4128
|
+
* import { ToggleSwitchOption } from "@trackunit/react-form-components";
|
|
3459
4129
|
*
|
|
3460
|
-
*
|
|
4130
|
+
* <ToggleSwitchOption
|
|
4131
|
+
* id="email-alerts"
|
|
4132
|
+
* label="Email Alerts"
|
|
4133
|
+
* description="Receive email notifications for important updates"
|
|
4134
|
+
* toggled={emailAlerts}
|
|
4135
|
+
* onChange={(toggled) => setEmailAlerts(toggled)}
|
|
4136
|
+
* />
|
|
4137
|
+
* ```
|
|
4138
|
+
* @example In a settings list
|
|
4139
|
+
* ```tsx
|
|
4140
|
+
* import { ToggleSwitchOption } from "@trackunit/react-form-components";
|
|
3461
4141
|
*
|
|
4142
|
+
* <div className="space-y-4">
|
|
4143
|
+
* <ToggleSwitchOption
|
|
4144
|
+
* id="dark-mode"
|
|
4145
|
+
* label="Dark Mode"
|
|
4146
|
+
* description="Use dark theme throughout the application"
|
|
4147
|
+
* toggled={darkMode}
|
|
4148
|
+
* onChange={(toggled) => setDarkMode(toggled)}
|
|
4149
|
+
* />
|
|
4150
|
+
* <ToggleSwitchOption
|
|
4151
|
+
* id="auto-save"
|
|
4152
|
+
* label="Auto-save"
|
|
4153
|
+
* description="Automatically save changes as you type"
|
|
4154
|
+
* toggled={autoSave}
|
|
4155
|
+
* onChange={(toggled) => setAutoSave(toggled)}
|
|
4156
|
+
* />
|
|
4157
|
+
* </div>
|
|
4158
|
+
* ```
|
|
3462
4159
|
* @param {ToggleSwitchOptionProps} props - The props for the ToggleSwitchOption component
|
|
3463
4160
|
* @returns {ReactElement} ToggleSwitchOption component
|
|
3464
4161
|
*/
|
|
@@ -3498,7 +4195,53 @@ const UploadInput = ({ disabled, acceptedTypes, nonInteractive, uploadLabel, mul
|
|
|
3498
4195
|
UploadInput.displayName = "UploadInput";
|
|
3499
4196
|
|
|
3500
4197
|
/**
|
|
3501
|
-
*
|
|
4198
|
+
* The `<UploadField>` component enables users to upload files through a form field.
|
|
4199
|
+
* It wraps the UploadInput with FormGroup for consistent form styling including label,
|
|
4200
|
+
* help text, and error handling.
|
|
4201
|
+
*
|
|
4202
|
+
* ### When to use
|
|
4203
|
+
* - Single file upload in forms (documents, images)
|
|
4204
|
+
* - When you need form field styling (label, validation messages)
|
|
4205
|
+
* - File attachments in form submissions
|
|
4206
|
+
*
|
|
4207
|
+
* ### When not to use
|
|
4208
|
+
* - Drag and drop file uploads - use `DropZone` instead
|
|
4209
|
+
* - Multiple file uploads with preview - consider specialized upload component
|
|
4210
|
+
* - Large file uploads requiring progress indication
|
|
4211
|
+
*
|
|
4212
|
+
* @example Basic usage
|
|
4213
|
+
* ```tsx
|
|
4214
|
+
* import { UploadField } from "@trackunit/react-form-components";
|
|
4215
|
+
*
|
|
4216
|
+
* <UploadField
|
|
4217
|
+
* label="Upload Document"
|
|
4218
|
+
* accept=".pdf,.doc,.docx"
|
|
4219
|
+
* onChange={(file) => handleFileUpload(file)}
|
|
4220
|
+
* />
|
|
4221
|
+
* ```
|
|
4222
|
+
* @example With validation
|
|
4223
|
+
* ```tsx
|
|
4224
|
+
* import { UploadField } from "@trackunit/react-form-components";
|
|
4225
|
+
*
|
|
4226
|
+
* <UploadField
|
|
4227
|
+
* label="Profile Picture"
|
|
4228
|
+
* accept="image/*"
|
|
4229
|
+
* helpText="Max file size: 5MB"
|
|
4230
|
+
* errorMessage={fileTooLarge ? "File exceeds 5MB limit" : undefined}
|
|
4231
|
+
* required
|
|
4232
|
+
* />
|
|
4233
|
+
* ```
|
|
4234
|
+
* @example In a form context
|
|
4235
|
+
* ```tsx
|
|
4236
|
+
* import { UploadField } from "@trackunit/react-form-components";
|
|
4237
|
+
*
|
|
4238
|
+
* <UploadField
|
|
4239
|
+
* label="Attachment"
|
|
4240
|
+
* tip="Optional supporting document"
|
|
4241
|
+
* accept=".pdf"
|
|
4242
|
+
* onChange={(file) => setAttachment(file)}
|
|
4243
|
+
* />
|
|
4244
|
+
* ```
|
|
3502
4245
|
*/
|
|
3503
4246
|
const UploadField = ({ label, id, tip, helpText, errorMessage, isInvalid, className, value, "data-testid": dataTestId, ref, ...rest }) => {
|
|
3504
4247
|
const renderAsInvalid = isInvalid || Boolean(errorMessage);
|
|
@@ -3550,9 +4293,53 @@ const UrlBaseInput = ({ isInvalid = false, "data-testid": dataTestId, disabled =
|
|
|
3550
4293
|
};
|
|
3551
4294
|
|
|
3552
4295
|
/**
|
|
3553
|
-
* The UrlField component is used to enter
|
|
3554
|
-
*
|
|
4296
|
+
* The `<UrlField>` component is used to enter and validate URLs/web addresses.
|
|
4297
|
+
* It automatically validates the format on blur and displays appropriate error messages.
|
|
4298
|
+
*
|
|
4299
|
+
* ### When to use
|
|
4300
|
+
* - Collecting website URLs in forms
|
|
4301
|
+
* - Social media profile links
|
|
4302
|
+
* - Any input requiring valid URL format
|
|
3555
4303
|
*
|
|
4304
|
+
* ### When not to use
|
|
4305
|
+
* - General text input - use `TextField` instead
|
|
4306
|
+
* - Email addresses - use `EmailField` instead
|
|
4307
|
+
* - Internal application links - use standard link/routing
|
|
4308
|
+
*
|
|
4309
|
+
* @example Basic usage
|
|
4310
|
+
* ```tsx
|
|
4311
|
+
* import { UrlField } from "@trackunit/react-form-components";
|
|
4312
|
+
*
|
|
4313
|
+
* const [website, setWebsite] = useState("");
|
|
4314
|
+
*
|
|
4315
|
+
* <UrlField
|
|
4316
|
+
* label="Website"
|
|
4317
|
+
* value={website}
|
|
4318
|
+
* onChange={(e) => setWebsite(e.target.value)}
|
|
4319
|
+
* placeholder="https://example.com"
|
|
4320
|
+
* />
|
|
4321
|
+
* ```
|
|
4322
|
+
* @example With help text
|
|
4323
|
+
* ```tsx
|
|
4324
|
+
* import { UrlField } from "@trackunit/react-form-components";
|
|
4325
|
+
*
|
|
4326
|
+
* <UrlField
|
|
4327
|
+
* label="Company Website"
|
|
4328
|
+
* helpText="Enter the full URL including https://"
|
|
4329
|
+
* required
|
|
4330
|
+
* />
|
|
4331
|
+
* ```
|
|
4332
|
+
* @example With custom validation
|
|
4333
|
+
* ```tsx
|
|
4334
|
+
* import { UrlField } from "@trackunit/react-form-components";
|
|
4335
|
+
*
|
|
4336
|
+
* <UrlField
|
|
4337
|
+
* label="Documentation URL"
|
|
4338
|
+
* value={docUrl}
|
|
4339
|
+
* onChange={(e) => setDocUrl(e.target.value)}
|
|
4340
|
+
* errorMessage={!docUrl.includes("docs") ? "URL must point to documentation" : undefined}
|
|
4341
|
+
* />
|
|
4342
|
+
* ```
|
|
3556
4343
|
*/
|
|
3557
4344
|
const UrlField = ({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, "data-testid": dataTestId, isInvalid = false, value, onBlur, ref, ...rest }) => {
|
|
3558
4345
|
const htmlForId = id ? id : "urlField-" + uuidv4();
|