@appcorp/shadcn 1.1.15 → 1.1.17

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.
@@ -4,6 +4,7 @@ interface EnhancedCheckboxProps extends React.ComponentPropsWithoutRef<typeof Ch
4
4
  error?: string;
5
5
  info?: string;
6
6
  label?: string;
7
+ testId?: string;
7
8
  }
8
9
  declare const EnhancedCheckbox: React.ForwardRefExoticComponent<EnhancedCheckboxProps & React.RefAttributes<HTMLButtonElement>>;
9
10
  export { EnhancedCheckbox };
@@ -62,10 +62,10 @@ var utils_1 = require("../lib/utils");
62
62
  var checkbox_1 = require("./ui/checkbox");
63
63
  var enhanced_label_1 = require("./enhanced-label");
64
64
  var EnhancedCheckbox = React.forwardRef(function (_a, ref) {
65
- var className = _a.className, error = _a.error, info = _a.info, label = _a.label, id = _a.id, props = __rest(_a, ["className", "error", "info", "label", "id"]);
65
+ var className = _a.className, error = _a.error, info = _a.info, label = _a.label, id = _a.id, testId = _a.testId, props = __rest(_a, ["className", "error", "info", "label", "id", "testId"]);
66
66
  // Determine if there's an error (for aria-invalid and styling)
67
67
  var hasError = Boolean(error);
68
- var checkboxElement = (React.createElement(checkbox_1.Checkbox, __assign({ ref: ref, id: id, "data-slot": "checkbox", "aria-invalid": hasError, className: (0, utils_1.cn)(
68
+ var checkboxElement = (React.createElement(checkbox_1.Checkbox, __assign({ ref: ref, id: id, "data-slot": "checkbox", "data-testid": testId, "aria-invalid": hasError, className: (0, utils_1.cn)(
69
69
  // Error state styling
70
70
  hasError &&
71
71
  "data-[state=checked]:bg-destructive data-[state=unchecked]:bg-destructive/20 border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20", className) }, props)));
@@ -4,27 +4,72 @@ export interface ComboboxOption {
4
4
  label: string;
5
5
  id?: string;
6
6
  }
7
+ /**
8
+ * Props for the `EnhancedCombobox` component.
9
+ *
10
+ * This component is a fully-featured combobox with optional virtualization,
11
+ * search, and scroll indicators. All `testId*` props are optional and intended
12
+ * to be used only for testing (e.g. `data-testid` attributes).
13
+ */
7
14
  interface EnhancedComboboxProps {
15
+ /** Array of options to show in the list. */
8
16
  options: ComboboxOption[];
17
+ /** Currently selected value. */
9
18
  value?: string;
19
+ /** Called when the selected value changes. */
10
20
  onValueChange: (value: string) => void;
21
+ /** Optional handler invoked when the search text changes. */
11
22
  onSearchChange?: (search: string) => void;
23
+ /** Placeholder text shown in the trigger when no option is selected. */
12
24
  placeholder?: string;
25
+ /** Placeholder shown inside the search input within the popover. */
13
26
  searchPlaceholder?: string;
27
+ /** Text to display when no options are available. */
14
28
  emptyText?: string;
29
+ /** Whether the component is in a loading/searching state. */
15
30
  loading?: boolean;
31
+ /** Whether the combobox is disabled. */
16
32
  disabled?: boolean;
33
+ /** Additional classes to apply to the trigger button. */
17
34
  className?: string;
35
+ /** Maximum height of the dropdown list in pixels. */
18
36
  maxHeight?: number;
37
+ /** Show gradient scroll indicators at the top/bottom of the list. */
19
38
  showScrollIndicators?: boolean;
39
+ /** Number of options above which virtualization is enabled. */
20
40
  virtualizeThreshold?: number;
41
+ /** Optional label displayed above the trigger. */
21
42
  label?: string;
43
+ /** Whether the field is required. */
22
44
  required?: boolean;
45
+ /** `id` applied to the trigger button (also used for label `htmlFor`). */
23
46
  id?: string;
47
+ /** Optional informational text displayed below the component. */
24
48
  info?: string;
49
+ /** Optional error text displayed below the component (error state). */
25
50
  error?: string;
51
+ /** `data-testid` for the trigger button (e.g. `my-combobox-trigger`). */
52
+ testIdTrigger?: string;
53
+ /** `data-testid` for the search input inside the popover. */
54
+ testIdSearchInput?: string;
55
+ /** `data-testid` for the popover content container. */
56
+ testIdPopoverContent?: string;
57
+ /** `data-testid` used on the empty state element (no results). */
58
+ testIdEmpty?: string;
59
+ /** `data-testid` for the list container (scrollable element). */
60
+ testIdList?: string;
61
+ /**
62
+ * Prefix used for option items. Each option will receive a `data-testid`
63
+ * equal to `${testIdOptionPrefix}-${option.value}`. Defaults to
64
+ * `combobox-option` when not provided.
65
+ */
66
+ testIdOptionPrefix?: string;
67
+ /** `data-testid` for the scroll-up button (when scroll indicators enabled). */
68
+ testIdScrollUp?: string;
69
+ /** `data-testid` for the scroll-down button (when scroll indicators enabled). */
70
+ testIdScrollDown?: string;
26
71
  }
27
- export declare function EnhancedCombobox({ className, disabled, emptyText, error, id, info, label, loading, maxHeight, onSearchChange, onValueChange, options, placeholder, required, searchPlaceholder, showScrollIndicators, value, virtualizeThreshold, }: EnhancedComboboxProps): React.JSX.Element;
72
+ export declare function EnhancedCombobox({ className, disabled, emptyText, error, id, info, label, loading, maxHeight, onSearchChange, onValueChange, options, placeholder, required, searchPlaceholder, showScrollIndicators, value, virtualizeThreshold, testIdTrigger, testIdSearchInput, testIdPopoverContent, testIdEmpty, testIdList, testIdOptionPrefix, testIdScrollUp, testIdScrollDown, }: EnhancedComboboxProps): React.JSX.Element;
28
73
  interface CompanyComboboxProps {
29
74
  companies: Array<{
30
75
  id?: string;
@@ -45,7 +45,7 @@ var enhanced_label_1 = require("./enhanced-label");
45
45
  var command_1 = require("./ui/command");
46
46
  var popover_1 = require("./ui/popover");
47
47
  function VirtualizedList(_a) {
48
- var options = _a.options, selectedValue = _a.selectedValue, onSelect = _a.onSelect, scrollContainer = _a.scrollContainer;
48
+ var options = _a.options, selectedValue = _a.selectedValue, onSelect = _a.onSelect, scrollContainer = _a.scrollContainer, testIdOptionPrefix = _a.testIdOptionPrefix;
49
49
  var _b = (0, react_1.useState)({
50
50
  start: 0,
51
51
  end: Math.min(options.length, 25), // Increased initial visible items
@@ -123,13 +123,13 @@ function VirtualizedList(_a) {
123
123
  var bottomSpacer = Math.max(0, (options.length - safeEnd) * 32);
124
124
  return (react_1.default.createElement(react_1.default.Fragment, null,
125
125
  topSpacer > 0 && react_1.default.createElement("div", { style: { height: topSpacer } }),
126
- visibleOptions.map(function (option, index) { return (react_1.default.createElement(command_1.CommandItem, { key: "".concat(option.value, "-").concat(safeStart + index), value: option.label, onSelect: function () { return onSelect(option.value); }, className: "cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors h-8 flex items-center" },
126
+ visibleOptions.map(function (option, index) { return (react_1.default.createElement(command_1.CommandItem, { key: "".concat(option.value, "-").concat(safeStart + index), value: option.label, onSelect: function () { return onSelect(option.value); }, "data-testid": "".concat(testIdOptionPrefix !== null && testIdOptionPrefix !== void 0 ? testIdOptionPrefix : "combobox-option", "-").concat(option.value), className: "cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors h-8 flex items-center" },
127
127
  react_1.default.createElement(lucide_react_1.Check, { className: (0, utils_1.cn)("mr-2 h-4 w-4", selectedValue === option.value ? "opacity-100" : "opacity-0") }),
128
128
  react_1.default.createElement("span", { className: "truncate" }, option.label))); }),
129
129
  bottomSpacer > 0 && react_1.default.createElement("div", { style: { height: bottomSpacer } })));
130
130
  }
131
131
  function EnhancedCombobox(_a) {
132
- var className = _a.className, _b = _a.disabled, disabled = _b === void 0 ? false : _b, _c = _a.emptyText, emptyText = _c === void 0 ? "No options found." : _c, error = _a.error, id = _a.id, info = _a.info, label = _a.label, _d = _a.loading, loading = _d === void 0 ? false : _d, _e = _a.maxHeight, maxHeight = _e === void 0 ? 256 : _e, onSearchChange = _a.onSearchChange, onValueChange = _a.onValueChange, _f = _a.options, options = _f === void 0 ? [] : _f, _g = _a.placeholder, placeholder = _g === void 0 ? "Select option..." : _g, _h = _a.required, required = _h === void 0 ? false : _h, _j = _a.searchPlaceholder, searchPlaceholder = _j === void 0 ? "Search..." : _j, _k = _a.showScrollIndicators, showScrollIndicators = _k === void 0 ? true : _k, value = _a.value, _l = _a.virtualizeThreshold, virtualizeThreshold = _l === void 0 ? 50 : _l;
132
+ var className = _a.className, _b = _a.disabled, disabled = _b === void 0 ? false : _b, _c = _a.emptyText, emptyText = _c === void 0 ? "No options found." : _c, error = _a.error, id = _a.id, info = _a.info, label = _a.label, _d = _a.loading, loading = _d === void 0 ? false : _d, _e = _a.maxHeight, maxHeight = _e === void 0 ? 256 : _e, onSearchChange = _a.onSearchChange, onValueChange = _a.onValueChange, _f = _a.options, options = _f === void 0 ? [] : _f, _g = _a.placeholder, placeholder = _g === void 0 ? "Select option..." : _g, _h = _a.required, required = _h === void 0 ? false : _h, _j = _a.searchPlaceholder, searchPlaceholder = _j === void 0 ? "Search..." : _j, _k = _a.showScrollIndicators, showScrollIndicators = _k === void 0 ? true : _k, value = _a.value, _l = _a.virtualizeThreshold, virtualizeThreshold = _l === void 0 ? 50 : _l, testIdTrigger = _a.testIdTrigger, testIdSearchInput = _a.testIdSearchInput, testIdPopoverContent = _a.testIdPopoverContent, testIdEmpty = _a.testIdEmpty, testIdList = _a.testIdList, testIdOptionPrefix = _a.testIdOptionPrefix, testIdScrollUp = _a.testIdScrollUp, testIdScrollDown = _a.testIdScrollDown;
133
133
  var _m = (0, react_1.useState)(false), open = _m[0], setOpen = _m[1];
134
134
  var _o = (0, react_1.useState)(""), searchValue = _o[0], setSearchValue = _o[1];
135
135
  var _p = (0, react_1.useState)(false), canScrollUp = _p[0], setCanScrollUp = _p[1];
@@ -201,30 +201,30 @@ function EnhancedCombobox(_a) {
201
201
  };
202
202
  var comboboxElement = (react_1.default.createElement(popover_1.Popover, { open: open, onOpenChange: setOpen, modal: true },
203
203
  react_1.default.createElement(popover_1.PopoverTrigger, { asChild: true },
204
- react_1.default.createElement(button_1.Button, { id: id, variant: "outline", role: "combobox", "aria-expanded": open, disabled: disabled, className: (0, utils_1.cn)("w-full justify-between", !selectedOption && "text-muted-foreground", className) },
204
+ react_1.default.createElement(button_1.Button, { id: id, "data-testid": testIdTrigger, variant: "outline", role: "combobox", "aria-expanded": open, disabled: disabled, className: (0, utils_1.cn)("w-full justify-between", !selectedOption && "text-muted-foreground", className) },
205
205
  (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.label) || placeholder,
206
206
  react_1.default.createElement(lucide_react_1.ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" }))),
207
- react_1.default.createElement(popover_1.PopoverContent, { className: "w-[var(--radix-popover-trigger-width)] p-0", side: "bottom", align: "start", avoidCollisions: true, collisionPadding: 8, style: { zIndex: 1000 }, onOpenAutoFocus: function (e) { return e.preventDefault(); } },
207
+ react_1.default.createElement(popover_1.PopoverContent, { "data-testid": testIdPopoverContent, className: "w-[var(--radix-popover-trigger-width)] p-0", side: "bottom", align: "start", avoidCollisions: true, collisionPadding: 8, style: { zIndex: 1000 }, onOpenAutoFocus: function (e) { return e.preventDefault(); } },
208
208
  react_1.default.createElement(command_1.Command, { shouldFilter: false },
209
- react_1.default.createElement(command_1.CommandInput, { "data-slot": "command-input", placeholder: searchPlaceholder, value: searchValue, onValueChange: handleSearchChange }),
210
- react_1.default.createElement(command_1.CommandEmpty, { "data-slot": "command-empty" }, loading ? (react_1.default.createElement("div", { className: "flex items-center justify-center py-6" },
209
+ react_1.default.createElement(command_1.CommandInput, { "data-slot": "command-input", "data-testid": testIdSearchInput, placeholder: searchPlaceholder, value: searchValue, onValueChange: handleSearchChange }),
210
+ react_1.default.createElement(command_1.CommandEmpty, { "data-slot": "command-empty", "data-testid": testIdEmpty }, loading ? (react_1.default.createElement("div", { className: "flex items-center justify-center py-6" },
211
211
  react_1.default.createElement("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-primary" }),
212
212
  react_1.default.createElement("span", { className: "ml-2 text-sm" }, "Searching..."))) : (emptyText)),
213
213
  react_1.default.createElement("div", { className: "relative" },
214
214
  showScrollIndicators && canScrollUp && (react_1.default.createElement("div", { className: "absolute top-0 left-0 right-0 z-10 h-8 bg-gradient-to-b from-background to-transparent pointer-events-none" },
215
- react_1.default.createElement("button", { onClick: scrollToTop, className: "absolute top-1 left-1/2 transform -translate-x-1/2 p-1 rounded-full bg-background shadow-sm border pointer-events-auto hover:bg-muted transition-colors", "aria-label": "Scroll to top" },
215
+ react_1.default.createElement("button", { "data-testid": testIdScrollUp, onClick: scrollToTop, className: "absolute top-1 left-1/2 transform -translate-x-1/2 p-1 rounded-full bg-background shadow-sm border pointer-events-auto hover:bg-muted transition-colors", "aria-label": "Scroll to top" },
216
216
  react_1.default.createElement(lucide_react_1.ChevronUp, { className: "h-3 w-3" })))),
217
- react_1.default.createElement(command_1.CommandGroup, { ref: scrollContainerRef, className: (0, utils_1.cn)("overflow-y-auto smooth-scroll", "scrollbar-thin scrollbar-thumb-muted-foreground/20 scrollbar-track-transparent hover:scrollbar-thumb-muted-foreground/40", "scroll-smooth", filteredOptions.length > 10 && "pb-2" // Extra padding for many options
217
+ react_1.default.createElement(command_1.CommandGroup, { "data-testid": testIdList, ref: scrollContainerRef, className: (0, utils_1.cn)("overflow-y-auto smooth-scroll", "scrollbar-thin scrollbar-thumb-muted-foreground/20 scrollbar-track-transparent hover:scrollbar-thumb-muted-foreground/40", "scroll-smooth", filteredOptions.length > 10 && "pb-2" // Extra padding for many options
218
218
  ), style: { maxHeight: "".concat(maxHeight, "px") }, onScroll: updateScrollIndicators }, shouldVirtualize &&
219
219
  filteredOptions.length > virtualizeThreshold ? (
220
220
  // Virtualized rendering for large lists
221
- react_1.default.createElement(VirtualizedList, { options: filteredOptions, selectedValue: value, onSelect: handleSelect, scrollContainer: scrollContainerRef })) : (
221
+ react_1.default.createElement(VirtualizedList, { options: filteredOptions, selectedValue: value, onSelect: handleSelect, scrollContainer: scrollContainerRef, testIdOptionPrefix: testIdOptionPrefix })) : (
222
222
  // Regular rendering for smaller lists or filtered results
223
- filteredOptions.map(function (option) { return (react_1.default.createElement(command_1.CommandItem, { key: option.value, value: option.label, onSelect: function () { return handleSelect(option.value); }, className: "cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors" },
223
+ filteredOptions.map(function (option) { return (react_1.default.createElement(command_1.CommandItem, { key: option.value, value: option.label, onSelect: function () { return handleSelect(option.value); }, "data-testid": "".concat(testIdOptionPrefix !== null && testIdOptionPrefix !== void 0 ? testIdOptionPrefix : "combobox-option", "-").concat(option.value), className: "cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors" },
224
224
  react_1.default.createElement(lucide_react_1.Check, { className: (0, utils_1.cn)("mr-2 h-4 w-4", value === option.value ? "opacity-100" : "opacity-0") }),
225
225
  react_1.default.createElement("span", { className: "truncate" }, option.label))); }))),
226
226
  showScrollIndicators && canScrollDown && (react_1.default.createElement("div", { className: "absolute bottom-0 left-0 right-0 z-10 h-8 bg-gradient-to-t from-background to-transparent pointer-events-none" },
227
- react_1.default.createElement("button", { onClick: scrollToBottom, className: "absolute bottom-1 left-1/2 transform -translate-x-1/2 p-1 rounded-full bg-background shadow-sm border pointer-events-auto hover:bg-muted transition-colors", "aria-label": "Scroll to bottom" },
227
+ react_1.default.createElement("button", { "data-testid": testIdScrollDown, onClick: scrollToBottom, className: "absolute bottom-1 left-1/2 transform -translate-x-1/2 p-1 rounded-full bg-background shadow-sm border pointer-events-auto hover:bg-muted transition-colors", "aria-label": "Scroll to bottom" },
228
228
  react_1.default.createElement(lucide_react_1.ChevronDown, { className: "h-3 w-3" })))))))));
229
229
  // If label is provided, render the complete combobox with label structure
230
230
  if (label) {
@@ -1,21 +1,63 @@
1
1
  import React from "react";
2
2
  import type { DropzoneOptions } from "react-dropzone";
3
+ /**
4
+ * Props for `EnhancedDropzone` component.
5
+ */
3
6
  export type EnhancedDropzoneProps = {
7
+ /** HTML id attribute applied to the hidden file input and used for labeling. */
4
8
  id?: string;
9
+ /** Additional className(s) applied to the root container. */
5
10
  className?: string;
11
+ /** Optional label text displayed above the dropzone. */
6
12
  label?: string;
13
+ /** Informational helper text shown below the component (non-error). */
7
14
  info?: string;
15
+ /** Error message text shown below the component; puts component into error state. */
8
16
  error?: string;
17
+ /**
18
+ * Accepted file types. Matches `react-dropzone` `accept` option or a list of
19
+ * mime types / extensions (e.g. `['image/*']` or `{ 'image/*': [] }`).
20
+ */
9
21
  accept?: DropzoneOptions["accept"] | string[];
22
+ /** Maximum number of files allowed to be selected. Defaults to `10`. */
10
23
  maxFiles?: number;
24
+ /** Maximum file size in bytes. */
11
25
  maxSize?: number;
26
+ /** Minimum file size in bytes. */
12
27
  minSize?: number;
28
+ /** Whether the dropzone is disabled (prevents interactions). */
13
29
  disabled?: boolean;
14
- /** Remote image URLs to display as previews */
30
+ /** Remote image URLs to display as previews (read-only remote values). */
15
31
  value?: string[];
16
- /** Called when files are selected or removed */
32
+ /** Called when the selected files change (local additions/removals). */
17
33
  onChange?: (files: File[]) => void;
18
- /** Called when a remote URL is removed */
34
+ /** Called when a remote URL preview is removed by the user. */
19
35
  onRemoveRemote?: (url: string) => void;
36
+ /** `data-testid` for the root dropzone container. */
37
+ testIdDropzone?: string;
38
+ /** `data-testid` for the hidden file input element. */
39
+ testIdInput?: string;
40
+ /** `data-testid` for the carousel previous button. */
41
+ testIdPrev?: string;
42
+ /** `data-testid` for the carousel next button. */
43
+ testIdNext?: string;
44
+ /** Prefix used for preview items: `${prefix}-${index}`. */
45
+ testIdPreviewPrefix?: string;
46
+ /** Prefix for preview image elements: `${prefix}-${index}`. */
47
+ testIdImagePrefix?: string;
48
+ /** Prefix for remove buttons on previews: `${prefix}-${index}`. */
49
+ testIdRemovePrefix?: string;
50
+ /** `data-testid` for the preview count text element. */
51
+ testIdCount?: string;
52
+ /** `data-testid` for the empty-state icon wrapper. */
53
+ testIdEmptyIcon?: string;
54
+ /** `data-testid` for the empty-state title. */
55
+ testIdEmptyTitle?: string;
56
+ /** `data-testid` for the empty-state subtitle. */
57
+ testIdEmptySubtitle?: string;
58
+ /** `data-testid` for the empty-state note (max files info). */
59
+ testIdEmptyNote?: string;
60
+ /** `data-testid` for the error/info message container. */
61
+ testIdMessage?: string;
20
62
  };
21
63
  export declare const EnhancedDropzone: React.FC<EnhancedDropzoneProps>;
@@ -62,7 +62,7 @@ var carousel_1 = require("./ui/carousel");
62
62
  var button_1 = require("./ui/button");
63
63
  var lucide_react_1 = require("lucide-react");
64
64
  var EnhancedDropzone = function (_a) {
65
- var id = _a.id, label = _a.label, info = _a.info, error = _a.error, accept = _a.accept, _b = _a.maxFiles, maxFiles = _b === void 0 ? 10 : _b, maxSize = _a.maxSize, minSize = _a.minSize, disabled = _a.disabled, _c = _a.value, value = _c === void 0 ? [] : _c, onChange = _a.onChange, onRemoveRemote = _a.onRemoveRemote, className = _a.className;
65
+ var id = _a.id, label = _a.label, info = _a.info, error = _a.error, accept = _a.accept, _b = _a.maxFiles, maxFiles = _b === void 0 ? 10 : _b, maxSize = _a.maxSize, minSize = _a.minSize, disabled = _a.disabled, _c = _a.value, value = _c === void 0 ? [] : _c, onChange = _a.onChange, onRemoveRemote = _a.onRemoveRemote, className = _a.className, testIdDropzone = _a.testIdDropzone, testIdInput = _a.testIdInput, testIdPrev = _a.testIdPrev, testIdNext = _a.testIdNext, testIdPreviewPrefix = _a.testIdPreviewPrefix, testIdImagePrefix = _a.testIdImagePrefix, testIdRemovePrefix = _a.testIdRemovePrefix, testIdCount = _a.testIdCount, testIdEmptyIcon = _a.testIdEmptyIcon, testIdEmptyTitle = _a.testIdEmptyTitle, testIdEmptySubtitle = _a.testIdEmptySubtitle, testIdEmptyNote = _a.testIdEmptyNote, testIdMessage = _a.testIdMessage;
66
66
  // Local files selected by user
67
67
  var _d = (0, react_1.useState)([]), localFiles = _d[0], setLocalFiles = _d[1];
68
68
  // Track object URLs created for local files
@@ -139,18 +139,18 @@ var EnhancedDropzone = function (_a) {
139
139
  url: localPreviewsRef.current.get(file) || "",
140
140
  index: index,
141
141
  }); }), true);
142
- return (react_1.default.createElement("div", { className: (0, utils_1.cn)("w-full", className) },
142
+ return (react_1.default.createElement("div", { className: (0, utils_1.cn)("w-full", className), "data-testid": testIdDropzone },
143
143
  label && (react_1.default.createElement("label", { className: "mb-2 block text-sm font-medium" }, label)),
144
144
  react_1.default.createElement("div", __assign({}, getRootProps(), { className: (0, utils_1.cn)("relative w-full rounded-md border border-dashed p-6 text-center min-h-[280px] flex items-center justify-center", isDragActive && "ring-2 ring-ring ring-offset-2", disabled && "opacity-60 pointer-events-none") }),
145
- react_1.default.createElement("input", __assign({}, getInputProps(), { id: id })),
145
+ react_1.default.createElement("input", __assign({}, getInputProps(), { id: id, "data-testid": testIdInput })),
146
146
  allPreviews.length > 0 ? (react_1.default.createElement("div", { className: "flex flex-col items-center w-full" },
147
147
  react_1.default.createElement("div", { className: "relative w-full max-w-md" },
148
148
  react_1.default.createElement(carousel_1.Carousel, { className: "w-full" },
149
- react_1.default.createElement(carousel_1.CarouselPrevious, { type: "button", onClick: function (e) { return e.stopPropagation(); }, onPointerDown: function (e) { return e.stopPropagation(); }, onMouseDown: function (e) { return e.stopPropagation(); } }),
150
- react_1.default.createElement(carousel_1.CarouselNext, { type: "button", onClick: function (e) { return e.stopPropagation(); }, onPointerDown: function (e) { return e.stopPropagation(); }, onMouseDown: function (e) { return e.stopPropagation(); } }),
151
- react_1.default.createElement(carousel_1.CarouselContent, { className: "ml-0" }, allPreviews.map(function (preview, idx) { return (react_1.default.createElement(carousel_1.CarouselItem, { key: "".concat(preview.type, "-").concat(preview.url, "-").concat(idx), className: "pl-4" },
149
+ react_1.default.createElement(carousel_1.CarouselPrevious, { type: "button", onClick: function (e) { return e.stopPropagation(); }, onPointerDown: function (e) { return e.stopPropagation(); }, onMouseDown: function (e) { return e.stopPropagation(); }, "data-testid": testIdPrev }),
150
+ react_1.default.createElement(carousel_1.CarouselNext, { type: "button", onClick: function (e) { return e.stopPropagation(); }, onPointerDown: function (e) { return e.stopPropagation(); }, onMouseDown: function (e) { return e.stopPropagation(); }, "data-testid": testIdNext }),
151
+ react_1.default.createElement(carousel_1.CarouselContent, { className: "ml-0" }, allPreviews.map(function (preview, idx) { return (react_1.default.createElement(carousel_1.CarouselItem, { key: "".concat(preview.type, "-").concat(preview.url, "-").concat(idx), className: "pl-4", "data-testid": "".concat(testIdPreviewPrefix !== null && testIdPreviewPrefix !== void 0 ? testIdPreviewPrefix : "dropzone-preview", "-").concat(idx) },
152
152
  react_1.default.createElement("div", { className: "relative aspect-square w-full max-w-xs mx-auto" }, preview.url ? (react_1.default.createElement(react_1.default.Fragment, null,
153
- react_1.default.createElement("img", { src: preview.url, alt: "Preview ".concat(idx + 1), className: "h-full w-full rounded-lg object-cover", onClick: function (e) { return e.stopPropagation(); } }),
153
+ react_1.default.createElement("img", { src: preview.url, alt: "Preview ".concat(idx + 1), className: "h-full w-full rounded-lg object-cover", onClick: function (e) { return e.stopPropagation(); }, "data-testid": "".concat(testIdImagePrefix !== null && testIdImagePrefix !== void 0 ? testIdImagePrefix : "dropzone-image", "-").concat(idx) }),
154
154
  react_1.default.createElement(button_1.Button, { type: "button", size: "icon", variant: "destructive", onClick: function (e) {
155
155
  e.stopPropagation();
156
156
  if (preview.type === "remote") {
@@ -159,29 +159,29 @@ var EnhancedDropzone = function (_a) {
159
159
  else {
160
160
  handleRemoveLocal(preview.index);
161
161
  }
162
- }, className: "absolute right-2 top-2 h-8 w-8 rounded-full", "aria-label": "Remove image" },
162
+ }, className: "absolute right-2 top-2 h-8 w-8 rounded-full", "aria-label": "Remove image", "data-testid": "".concat(testIdRemovePrefix !== null && testIdRemovePrefix !== void 0 ? testIdRemovePrefix : "dropzone-remove", "-").concat(idx) },
163
163
  react_1.default.createElement(lucide_react_1.XIcon, { className: "h-4 w-4" })))) : (react_1.default.createElement("div", { className: "flex h-full w-full items-center justify-center rounded-lg bg-muted text-muted-foreground" },
164
164
  react_1.default.createElement("span", { className: "text-sm" }, "Loading...")))))); })))),
165
- react_1.default.createElement("p", { className: "mt-4 text-sm font-medium text-muted-foreground" },
165
+ react_1.default.createElement("p", { className: "mt-4 text-sm font-medium text-muted-foreground", "data-testid": testIdCount },
166
166
  allPreviews.length,
167
167
  " image",
168
168
  allPreviews.length !== 1 ? "s" : "",
169
169
  " ",
170
170
  "selected"))) : (react_1.default.createElement("div", { className: "flex flex-col items-center justify-center gap-2" },
171
- react_1.default.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-lg bg-muted text-muted-foreground" },
171
+ react_1.default.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-lg bg-muted text-muted-foreground", "data-testid": testIdEmptyIcon },
172
172
  react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-image" },
173
173
  react_1.default.createElement("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
174
174
  react_1.default.createElement("circle", { cx: "9", cy: "9", r: "2" }),
175
175
  react_1.default.createElement("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" }))),
176
176
  react_1.default.createElement("div", { className: "space-y-1 text-center" },
177
- react_1.default.createElement("p", { className: "font-medium text-sm" },
177
+ react_1.default.createElement("p", { className: "font-medium text-sm", "data-testid": testIdEmptyTitle },
178
178
  "Upload ",
179
179
  maxFiles === 1 ? "an image" : "images"),
180
- react_1.default.createElement("p", { className: "text-muted-foreground text-xs" }, "Drag and drop or click to browse"),
181
- maxFiles > 1 && (react_1.default.createElement("p", { className: "text-muted-foreground text-xs" },
180
+ react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptySubtitle }, "Drag and drop or click to browse"),
181
+ maxFiles > 1 && (react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptyNote },
182
182
  "Up to ",
183
183
  maxFiles,
184
184
  " files")))))),
185
- (error || info) && (react_1.default.createElement("div", { className: "mt-2" }, error ? (react_1.default.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (react_1.default.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
185
+ (error || info) && (react_1.default.createElement("div", { className: "mt-2", "data-testid": testIdMessage }, error ? (react_1.default.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (react_1.default.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
186
186
  };
187
187
  exports.EnhancedDropzone = EnhancedDropzone;
@@ -1,8 +1,24 @@
1
1
  import * as React from "react";
2
+ /** Props for `EnhancedInput` component. */
2
3
  interface EnhancedInputProps extends React.ComponentProps<"input"> {
4
+ /** Error message text shown below the input; sets error styling when present. */
3
5
  error?: string;
6
+ /** Informational helper text shown below the input (when no error). */
4
7
  info?: string;
8
+ /** Optional label text displayed above the input. */
5
9
  label?: string;
10
+ /** `data-testid` for the root wrapper (the outermost `div`). */
11
+ testIdWrapper?: string;
12
+ /** `data-testid` for the native `input` element. */
13
+ testIdInput?: string;
14
+ /** `data-testid` for the label element (passes to `EnhancedLabel`). */
15
+ testIdLabel?: string;
16
+ /** `data-testid` for the message container (holds error/info). */
17
+ testIdMessage?: string;
18
+ /** `data-testid` for the error text element. */
19
+ testIdError?: string;
20
+ /** `data-testid` for the info text element. */
21
+ testIdInfo?: string;
6
22
  }
7
23
  declare const EnhancedInput: React.ForwardRefExoticComponent<Omit<EnhancedInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
8
24
  export { EnhancedInput };
@@ -61,10 +61,10 @@ var utils_1 = require("../lib/utils");
61
61
  var enhanced_label_1 = require("./enhanced-label");
62
62
  var input_1 = require("./ui/input");
63
63
  var EnhancedInput = React.forwardRef(function (_a, ref) {
64
- var className = _a.className, type = _a.type, error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, props = __rest(_a, ["className", "type", "error", "info", "label", "required", "id"]);
64
+ var className = _a.className, type = _a.type, error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, testIdWrapper = _a.testIdWrapper, testIdInput = _a.testIdInput, testIdLabel = _a.testIdLabel, testIdMessage = _a.testIdMessage, testIdError = _a.testIdError, testIdInfo = _a.testIdInfo, props = __rest(_a, ["className", "type", "error", "info", "label", "required", "id", "testIdWrapper", "testIdInput", "testIdLabel", "testIdMessage", "testIdError", "testIdInfo"]);
65
65
  // Determine if there's an error (for aria-invalid and styling)
66
66
  var hasError = Boolean(error);
67
- var inputElement = (React.createElement(input_1.Input, __assign({ type: type, ref: ref, id: id, "aria-invalid": hasError, required: required, className: (0, utils_1.cn)(
67
+ var inputElement = (React.createElement(input_1.Input, __assign({ type: type, ref: ref, id: id, "aria-invalid": hasError, required: required, "data-testid": testIdInput, className: (0, utils_1.cn)(
68
68
  // Error state styling
69
69
  hasError &&
70
70
  "border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20",
@@ -73,15 +73,15 @@ var EnhancedInput = React.forwardRef(function (_a, ref) {
73
73
  "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className) }, props)));
74
74
  // If label is provided, render the complete input with label structure
75
75
  if (label) {
76
- return (React.createElement("div", { className: "w-full space-y-2" },
77
- React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required }, label),
76
+ return (React.createElement("div", { className: "w-full space-y-2", "data-testid": testIdWrapper },
77
+ React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required, "data-testid": testIdLabel }, label),
78
78
  inputElement,
79
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
79
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
80
80
  }
81
81
  // If no label, render just the input with messages (backward compatibility)
82
- return (React.createElement("div", { className: "w-full" },
82
+ return (React.createElement("div", { className: "w-full", "data-testid": testIdWrapper },
83
83
  inputElement,
84
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
84
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
85
85
  });
86
86
  exports.EnhancedInput = EnhancedInput;
87
87
  EnhancedInput.displayName = "EnhancedInput";
@@ -1,7 +1,13 @@
1
1
  import * as React from "react";
2
2
  import * as LabelPrimitive from "@radix-ui/react-label";
3
+ /** Props for `EnhancedLabel` component. */
3
4
  interface EnhancedLabelProps extends React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> {
5
+ /** Show a required field indicator (asterisk). */
4
6
  required?: boolean;
7
+ /** `data-testid` for the root label element. */
8
+ testIdLabel?: string;
9
+ /** `data-testid` for the required asterisk element when `required` is true. */
10
+ testIdRequired?: string;
5
11
  }
6
- declare function EnhancedLabel({ className, required, children, ...props }: EnhancedLabelProps): React.JSX.Element;
12
+ declare function EnhancedLabel({ className, required, children, testIdLabel, testIdRequired, ...props }: EnhancedLabelProps): React.JSX.Element;
7
13
  export { EnhancedLabel };
@@ -61,8 +61,8 @@ var React = __importStar(require("react"));
61
61
  var utils_1 = require("../lib/utils");
62
62
  var label_1 = require("./ui/label");
63
63
  function EnhancedLabel(_a) {
64
- var className = _a.className, _b = _a.required, required = _b === void 0 ? false : _b, children = _a.children, props = __rest(_a, ["className", "required", "children"]);
65
- return (React.createElement(label_1.Label, __assign({ "data-slot": "label", className: (0, utils_1.cn)("flex items-center gap-1 select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 min-h-5", className) }, props),
64
+ var className = _a.className, _b = _a.required, required = _b === void 0 ? false : _b, children = _a.children, testIdLabel = _a.testIdLabel, testIdRequired = _a.testIdRequired, props = __rest(_a, ["className", "required", "children", "testIdLabel", "testIdRequired"]);
65
+ return (React.createElement(label_1.Label, __assign({ "data-slot": "label", "data-testid": testIdLabel, className: (0, utils_1.cn)("flex items-center gap-1 select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 min-h-5", className) }, props),
66
66
  children,
67
- required && (React.createElement("span", { className: "text-destructive text-sm", "aria-label": "required" }, "*"))));
67
+ required && (React.createElement("span", { className: "text-destructive text-sm", "aria-label": "required", "data-testid": testIdRequired }, "*"))));
68
68
  }
@@ -1,25 +1,54 @@
1
1
  import * as React from "react";
2
2
  import * as SelectPrimitive from "@radix-ui/react-select";
3
+ /** Props for `EnhancedSelect` wrapper component. */
3
4
  interface EnhancedSelectProps {
5
+ /** Error message text shown below the select; sets error styling when present. */
4
6
  error?: string;
7
+ /** Informational helper text shown below the select (when no error). */
5
8
  info?: string;
9
+ /** Optional label text displayed above the select. */
6
10
  label?: string;
11
+ /** Whether the field is required (adds required indicator to label). */
7
12
  required?: boolean;
13
+ /** HTML id used for labeling and accessibility. */
8
14
  id?: string;
15
+ /** Child nodes, typically `SelectTrigger` and `SelectContent`/items. */
9
16
  children?: React.ReactNode;
17
+ /** Controlled selected value. */
10
18
  value?: string;
19
+ /** Uncontrolled initial value. */
11
20
  defaultValue?: string;
21
+ /** Callback fired when the selected value changes. */
12
22
  onValueChange?: (value: string) => void;
23
+ /** Disable the select input and interactions. */
13
24
  disabled?: boolean;
25
+ /** Name attribute for form submissions. */
14
26
  name?: string;
27
+ /** Text direction for the select: left-to-right or right-to-left. */
15
28
  dir?: "ltr" | "rtl";
29
+ /** Control the open state of the select popup. */
16
30
  open?: boolean;
31
+ /** Uncontrolled initial open state. */
17
32
  defaultOpen?: boolean;
33
+ /** Called when the open state changes. */
18
34
  onOpenChange?: (open: boolean) => void;
35
+ /** `data-testid` for the root wrapper (outermost `div`). */
36
+ testIdWrapper?: string;
37
+ /** `data-testid` forwarded to the `EnhancedLabel` component. */
38
+ testIdLabel?: string;
39
+ /** `data-testid` for the message container (holds error/info). */
40
+ testIdMessage?: string;
41
+ /** `data-testid` for the error text element. */
42
+ testIdError?: string;
43
+ /** `data-testid` for the info text element. */
44
+ testIdInfo?: string;
19
45
  }
20
46
  interface EnhancedSelectTriggerProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
47
+ /** Error text used to style the trigger when present. */
21
48
  error?: string;
49
+ /** `data-testid` for the trigger (the interactive button). */
50
+ testIdTrigger?: string;
22
51
  }
23
- declare const EnhancedSelect: ({ error, info, label, required, id, children, ...selectProps }: EnhancedSelectProps) => React.JSX.Element;
52
+ declare const EnhancedSelect: ({ error, info, label, required, id, children, testIdWrapper, testIdLabel, testIdMessage, testIdError, testIdInfo, ...selectProps }: EnhancedSelectProps) => React.JSX.Element;
24
53
  declare const EnhancedSelectTrigger: React.ForwardRefExoticComponent<EnhancedSelectTriggerProps & React.RefAttributes<HTMLButtonElement>>;
25
54
  export { EnhancedSelect, EnhancedSelectTrigger };
@@ -62,11 +62,11 @@ var utils_1 = require("../lib/utils");
62
62
  var select_1 = require("./ui/select");
63
63
  var enhanced_label_1 = require("./enhanced-label");
64
64
  var EnhancedSelect = function (_a) {
65
- var error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, children = _a.children, selectProps = __rest(_a, ["error", "info", "label", "required", "id", "children"]);
66
- return (React.createElement("div", { className: "w-full space-y-2" },
67
- label && (React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required }, label)),
65
+ var error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, children = _a.children, testIdWrapper = _a.testIdWrapper, testIdLabel = _a.testIdLabel, testIdMessage = _a.testIdMessage, testIdError = _a.testIdError, testIdInfo = _a.testIdInfo, selectProps = __rest(_a, ["error", "info", "label", "required", "id", "children", "testIdWrapper", "testIdLabel", "testIdMessage", "testIdError", "testIdInfo"]);
66
+ return (React.createElement("div", { className: "w-full space-y-2", "data-testid": testIdWrapper },
67
+ label && (React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required, testIdLabel: testIdLabel }, label)),
68
68
  React.createElement(select_1.Select, __assign({}, selectProps), children),
69
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
69
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
70
70
  };
71
71
  exports.EnhancedSelect = EnhancedSelect;
72
72
  var EnhancedSelectTrigger = React.forwardRef(function (_a, ref) {
@@ -1,10 +1,31 @@
1
1
  import * as React from "react";
2
2
  import * as SwitchPrimitives from "@radix-ui/react-switch";
3
+ /** Props for `EnhancedSwitch` component. */
3
4
  interface EnhancedSwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {
5
+ /** Error message text shown below the switch; sets error styling when present. */
4
6
  error?: string;
7
+ /** Informational helper text shown below the switch (when no error). */
5
8
  info?: string;
9
+ /** Optional label text displayed beside the switch. */
6
10
  label?: string;
11
+ /** Optional description text shown under the label. */
7
12
  description?: string;
13
+ /** `data-testid` for the root wrapper (outermost `div`). */
14
+ testIdWrapper?: string;
15
+ /** `data-testid` forwarded to the `EnhancedLabel` component. */
16
+ testIdLabel?: string;
17
+ /** `data-testid` for the switch root element. */
18
+ testIdSwitch?: string;
19
+ /** `data-testid` for the switch thumb element. */
20
+ testIdThumb?: string;
21
+ /** `data-testid` for the description paragraph. */
22
+ testIdDescription?: string;
23
+ /** `data-testid` for the message container (holds error/info). */
24
+ testIdMessage?: string;
25
+ /** `data-testid` for the error text element. */
26
+ testIdError?: string;
27
+ /** `data-testid` for the info text element. */
28
+ testIdInfo?: string;
8
29
  }
9
30
  declare const EnhancedSwitch: React.ForwardRefExoticComponent<EnhancedSwitchProps & React.RefAttributes<HTMLButtonElement>>;
10
31
  export { EnhancedSwitch };
@@ -62,28 +62,28 @@ var SwitchPrimitives = __importStar(require("@radix-ui/react-switch"));
62
62
  var utils_1 = require("../lib/utils");
63
63
  var enhanced_label_1 = require("./enhanced-label");
64
64
  var EnhancedSwitch = React.forwardRef(function (_a, ref) {
65
- var className = _a.className, error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, description = _a.description, props = __rest(_a, ["className", "error", "info", "label", "required", "id", "description"]);
65
+ var className = _a.className, error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, description = _a.description, testIdWrapper = _a.testIdWrapper, testIdLabel = _a.testIdLabel, testIdSwitch = _a.testIdSwitch, testIdThumb = _a.testIdThumb, testIdDescription = _a.testIdDescription, testIdMessage = _a.testIdMessage, testIdError = _a.testIdError, testIdInfo = _a.testIdInfo, props = __rest(_a, ["className", "error", "info", "label", "required", "id", "description", "testIdWrapper", "testIdLabel", "testIdSwitch", "testIdThumb", "testIdDescription", "testIdMessage", "testIdError", "testIdInfo"]);
66
66
  // Determine if there's an error (for aria-invalid and styling)
67
67
  var hasError = Boolean(error);
68
- var switchElement = (React.createElement(SwitchPrimitives.Root, __assign({ ref: ref, id: id, "data-slot": "switch", "aria-invalid": hasError, required: required, className: (0, utils_1.cn)("peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
68
+ var switchElement = (React.createElement(SwitchPrimitives.Root, __assign({ ref: ref, id: id, "data-slot": "switch", "data-testid": testIdSwitch, "aria-invalid": hasError, required: required, className: (0, utils_1.cn)("peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
69
69
  // Error state styling
70
70
  hasError &&
71
71
  "data-[state=checked]:bg-destructive data-[state=unchecked]:bg-destructive/20 border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20", className) }, props),
72
- React.createElement(SwitchPrimitives.Thumb, { "data-slot": "switch-thumb", className: (0, utils_1.cn)("pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0") })));
72
+ React.createElement(SwitchPrimitives.Thumb, { "data-slot": "switch-thumb", "data-testid": testIdThumb, className: (0, utils_1.cn)("pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0") })));
73
73
  // If label is provided, render the complete switch with label structure
74
74
  if (label) {
75
- return (React.createElement("div", { className: "w-full space-y-2" },
75
+ return (React.createElement("div", { className: "w-full space-y-2", "data-testid": testIdWrapper },
76
76
  React.createElement("div", { className: "flex items-center justify-between space-x-2" },
77
77
  React.createElement("div", null,
78
- React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required }, label),
79
- description && (React.createElement("p", { className: "text-sm text-gray-400" }, description))),
78
+ React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required, testIdLabel: testIdLabel }, label),
79
+ description && (React.createElement("p", { className: "text-sm text-gray-400", "data-testid": testIdDescription }, description))),
80
80
  switchElement),
81
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
81
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
82
82
  }
83
83
  // If no label, render just the switch with messages (backward compatibility)
84
- return (React.createElement("div", { className: "w-full" },
84
+ return (React.createElement("div", { className: "w-full", "data-testid": testIdWrapper },
85
85
  switchElement,
86
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
86
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
87
87
  });
88
88
  exports.EnhancedSwitch = EnhancedSwitch;
89
89
  EnhancedSwitch.displayName = "EnhancedSwitch";
@@ -30,6 +30,16 @@ interface EnhancedTableFooterActionProps {
30
30
  previousPageLabel?: string;
31
31
  /** Label for next page button */
32
32
  nextPageLabel?: string;
33
+ /** `data-testid` for the root wrapper containing the action buttons. */
34
+ testIdWrapper?: string;
35
+ /** `data-testid` for the previous button. */
36
+ testIdPrevious?: string;
37
+ /** `data-testid` for the next button. */
38
+ testIdNext?: string;
39
+ /** `data-testid` for the previous icon element. */
40
+ testIdPreviousIcon?: string;
41
+ /** `data-testid` for the next icon element. */
42
+ testIdNextIcon?: string;
33
43
  }
34
44
  export declare const EnhancedTableFooterAction: FC<EnhancedTableFooterActionProps>;
35
45
  export {};
@@ -22,17 +22,17 @@ var react_1 = __importDefault(require("react"));
22
22
  var lucide_react_1 = require("lucide-react");
23
23
  var button_1 = require("./ui/button");
24
24
  var EnhancedTableFooterAction = function (_a) {
25
- var handleNextOnClick = _a.handleNextOnClick, handlePreviousOnClick = _a.handlePreviousOnClick, isNextDisabled = _a.isNextDisabled, isPreviousDisabled = _a.isPreviousDisabled, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, loading = _a.loading, _c = _a.nextPageLabel, nextPageLabel = _c === void 0 ? "Next page" : _c, _d = _a.previousPageLabel, previousPageLabel = _d === void 0 ? "Previous page" : _d;
25
+ var handleNextOnClick = _a.handleNextOnClick, handlePreviousOnClick = _a.handlePreviousOnClick, isNextDisabled = _a.isNextDisabled, isPreviousDisabled = _a.isPreviousDisabled, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, loading = _a.loading, _c = _a.nextPageLabel, nextPageLabel = _c === void 0 ? "Next page" : _c, _d = _a.previousPageLabel, previousPageLabel = _d === void 0 ? "Previous page" : _d, testIdWrapper = _a.testIdWrapper, testIdPrevious = _a.testIdPrevious, testIdNext = _a.testIdNext, testIdPreviousIcon = _a.testIdPreviousIcon, testIdNextIcon = _a.testIdNextIcon;
26
26
  // In RTL, arrows should be swapped:
27
27
  // Previous should show → (ChevronRight) as it goes to the right in RTL reading order
28
28
  // Next should show ← (ChevronLeft) as it goes to the left in RTL reading order
29
29
  var PreviousIcon = isRTL ? lucide_react_1.ChevronRight : lucide_react_1.ChevronLeft;
30
30
  var NextIcon = isRTL ? lucide_react_1.ChevronLeft : lucide_react_1.ChevronRight;
31
- return (react_1.default.createElement("div", { className: "flex gap-1 ".concat(isRTL ? "flex-row-reverse" : "") },
32
- react_1.default.createElement(button_1.Button, { variant: "outline", size: "icon", onClick: handlePreviousOnClick, disabled: isPreviousDisabled || loading, "aria-label": previousPageLabel, title: previousPageLabel, className: "".concat(isRTL ? "rotate-180" : "") },
33
- react_1.default.createElement(PreviousIcon, { className: "h-4 w-4" })),
34
- react_1.default.createElement(button_1.Button, { variant: "outline", size: "icon", onClick: handleNextOnClick, disabled: isNextDisabled || loading, "aria-label": nextPageLabel, title: nextPageLabel, className: "".concat(isRTL ? "rotate-180" : "") },
35
- react_1.default.createElement(NextIcon, { className: "h-4 w-4" }))));
31
+ return (react_1.default.createElement("div", { className: "flex gap-1 ".concat(isRTL ? "flex-row-reverse" : ""), "data-testid": testIdWrapper },
32
+ react_1.default.createElement(button_1.Button, { variant: "outline", size: "icon", "data-testid": testIdPrevious, onClick: handlePreviousOnClick, disabled: isPreviousDisabled || loading, "aria-label": previousPageLabel, title: previousPageLabel, className: "".concat(isRTL ? "rotate-180" : "") },
33
+ react_1.default.createElement(PreviousIcon, { className: "h-4 w-4", "data-testid": testIdPreviousIcon })),
34
+ react_1.default.createElement(button_1.Button, { variant: "outline", size: "icon", "data-testid": testIdNext, onClick: handleNextOnClick, disabled: isNextDisabled || loading, "aria-label": nextPageLabel, title: nextPageLabel, className: "".concat(isRTL ? "rotate-180" : "") },
35
+ react_1.default.createElement(NextIcon, { className: "h-4 w-4", "data-testid": testIdNextIcon }))));
36
36
  };
37
37
  exports.EnhancedTableFooterAction = EnhancedTableFooterAction;
38
38
  // ============================================================================
@@ -29,6 +29,20 @@ interface EnhancedTableFooterPageProps {
29
29
  loadingLabel?: string;
30
30
  /** Optional CSS classes for custom styling */
31
31
  className?: string;
32
+ /** `data-testid` for the root wrapper element. */
33
+ testIdWrapper?: string;
34
+ /** `data-testid` for the current page number element. */
35
+ testIdCurrent?: string;
36
+ /** `data-testid` for the page label element (the word "Page"). */
37
+ testIdPageLabel?: string;
38
+ /** `data-testid` for the "of" label element. */
39
+ testIdOfLabel?: string;
40
+ /** `data-testid` for the total pages element. */
41
+ testIdTotal?: string;
42
+ /** `data-testid` for the loading wrapper when `loading` is true. */
43
+ testIdLoadingWrapper?: string;
44
+ /** `data-testid` for the loading spinner (progressbar). */
45
+ testIdLoadingSpinner?: string;
32
46
  }
33
47
  export declare const EnhancedTableFooterPage: FC<EnhancedTableFooterPageProps>;
34
48
  export {};
@@ -19,23 +19,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.EnhancedTableFooterPage = void 0;
20
20
  var react_1 = __importDefault(require("react"));
21
21
  var EnhancedTableFooterPage = function (_a) {
22
- var currentPage = _a.currentPage, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, loading = _a.loading, _c = _a.loadingLabel, loadingLabel = _c === void 0 ? "Loading" : _c, _d = _a.ofLabel, ofLabel = _d === void 0 ? "of" : _d, _e = _a.pageLabel, pageLabel = _e === void 0 ? "Page" : _e, totalPages = _a.totalPages, _f = _a.className, className = _f === void 0 ? "" : _f;
23
- return (react_1.default.createElement("div", { className: "flex items-center gap-2 ".concat(isRTL ? "rtl" : "ltr", " ").concat(className), dir: isRTL ? "rtl" : "ltr", role: "status", "aria-live": "polite", "aria-label": "".concat(pageLabel, " ").concat(currentPage, " ").concat(ofLabel, " ").concat(totalPages) },
22
+ var currentPage = _a.currentPage, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, loading = _a.loading, _c = _a.loadingLabel, loadingLabel = _c === void 0 ? "Loading" : _c, _d = _a.ofLabel, ofLabel = _d === void 0 ? "of" : _d, _e = _a.pageLabel, pageLabel = _e === void 0 ? "Page" : _e, totalPages = _a.totalPages, _f = _a.className, className = _f === void 0 ? "" : _f, testIdWrapper = _a.testIdWrapper, testIdCurrent = _a.testIdCurrent, testIdPageLabel = _a.testIdPageLabel, testIdOfLabel = _a.testIdOfLabel, testIdTotal = _a.testIdTotal, testIdLoadingWrapper = _a.testIdLoadingWrapper, testIdLoadingSpinner = _a.testIdLoadingSpinner;
23
+ return (react_1.default.createElement("div", { className: "flex items-center gap-2 ".concat(isRTL ? "rtl" : "ltr", " ").concat(className), dir: isRTL ? "rtl" : "ltr", role: "status", "aria-live": "polite", "aria-label": "".concat(pageLabel, " ").concat(currentPage, " ").concat(ofLabel, " ").concat(totalPages), "data-testid": testIdWrapper },
24
24
  react_1.default.createElement("span", { className: "text-sm text-muted-foreground" }, isRTL ? (
25
25
  // RTL layout: Numbers read right-to-left but maintain logical order
26
26
  react_1.default.createElement("span", { className: "inline-flex items-center gap-1" },
27
- react_1.default.createElement("span", null, totalPages),
28
- react_1.default.createElement("span", null, ofLabel),
29
- react_1.default.createElement("span", null, currentPage),
30
- react_1.default.createElement("span", null, pageLabel))) : (
27
+ react_1.default.createElement("span", { "data-testid": testIdTotal }, totalPages),
28
+ react_1.default.createElement("span", { "data-testid": testIdOfLabel }, ofLabel),
29
+ react_1.default.createElement("span", { "data-testid": testIdCurrent }, currentPage),
30
+ react_1.default.createElement("span", { "data-testid": testIdPageLabel }, pageLabel))) : (
31
31
  // LTR layout: "Page currentPage of totalPages"
32
32
  react_1.default.createElement("span", { className: "inline-flex items-center gap-1" },
33
- react_1.default.createElement("span", null, pageLabel),
34
- react_1.default.createElement("span", null, currentPage),
35
- react_1.default.createElement("span", null, ofLabel),
36
- react_1.default.createElement("span", null, totalPages)))),
37
- loading && (react_1.default.createElement("div", { className: "flex items-center gap-2 ".concat(isRTL ? "order-first" : "order-last"), "aria-label": loadingLabel },
38
- react_1.default.createElement("div", { className: "w-4 h-4 border-2 border-primary border-t-transparent rounded-full animate-spin", role: "progressbar", "aria-label": loadingLabel })))));
33
+ react_1.default.createElement("span", { "data-testid": testIdPageLabel }, pageLabel),
34
+ react_1.default.createElement("span", { "data-testid": testIdCurrent }, currentPage),
35
+ react_1.default.createElement("span", { "data-testid": testIdOfLabel }, ofLabel),
36
+ react_1.default.createElement("span", { "data-testid": testIdTotal }, totalPages)))),
37
+ loading && (react_1.default.createElement("div", { className: "flex items-center gap-2 ".concat(isRTL ? "order-first" : "order-last"), "aria-label": loadingLabel, "data-testid": testIdLoadingWrapper },
38
+ react_1.default.createElement("div", { className: "w-4 h-4 border-2 border-primary border-t-transparent rounded-full animate-spin", role: "progressbar", "aria-label": loadingLabel, "data-testid": testIdLoadingSpinner })))));
39
39
  };
40
40
  exports.EnhancedTableFooterPage = EnhancedTableFooterPage;
41
41
  // ============================================================================
@@ -33,6 +33,18 @@ interface EnhancedTableFooterPageProps {
33
33
  rowsPerPageLabel?: string;
34
34
  /** Optional CSS classes for custom styling */
35
35
  className?: string;
36
+ /** `data-testid` for the root pagination wrapper element. */
37
+ testIdWrapper?: string;
38
+ /** `data-testid` for the label element. */
39
+ testIdLabel?: string;
40
+ /** `data-testid` for the SelectTrigger element. */
41
+ testIdSelectTrigger?: string;
42
+ /** `data-testid` for the SelectValue element (shows current value). */
43
+ testIdSelectValue?: string;
44
+ /** `data-testid` for the SelectContent container (popup). */
45
+ testIdSelectContent?: string;
46
+ /** Prefix used for option test ids: `${prefix}-${option}`. */
47
+ testIdOptionPrefix?: string;
36
48
  }
37
49
  export declare const EnhancedTableFooterPagination: FC<EnhancedTableFooterPageProps>;
38
50
  export {};
@@ -21,15 +21,17 @@ exports.EnhancedTableFooterPagination = void 0;
21
21
  var select_1 = require("./ui/select");
22
22
  var react_1 = __importDefault(require("react"));
23
23
  var EnhancedTableFooterPagination = function (_a) {
24
- var handleOnSelect = _a.handleOnSelect, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, listOptions = _a.listOptions, loading = _a.loading, nodeSelectKey = _a.nodeSelectKey, pageLimit = _a.pageLimit, _c = _a.rowsPerPageLabel, rowsPerPageLabel = _c === void 0 ? "Rows per page:" : _c, _d = _a.className, className = _d === void 0 ? "" : _d;
25
- return (react_1.default.createElement("div", { className: "flex items-center gap-2 ".concat(className), dir: isRTL ? "rtl" : "ltr" },
26
- react_1.default.createElement("label", { htmlFor: "page-limit-select-".concat(nodeSelectKey), className: "text-sm text-muted-foreground whitespace-nowrap" }, rowsPerPageLabel),
24
+ var handleOnSelect = _a.handleOnSelect, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, listOptions = _a.listOptions, loading = _a.loading, nodeSelectKey = _a.nodeSelectKey, pageLimit = _a.pageLimit, _c = _a.rowsPerPageLabel, rowsPerPageLabel = _c === void 0 ? "Rows per page:" : _c, _d = _a.className, className = _d === void 0 ? "" : _d, testIdWrapper = _a.testIdWrapper, testIdLabel = _a.testIdLabel, testIdSelectTrigger = _a.testIdSelectTrigger, testIdSelectValue = _a.testIdSelectValue, testIdSelectContent = _a.testIdSelectContent, testIdOptionPrefix = _a.testIdOptionPrefix;
25
+ return (react_1.default.createElement("div", { className: "flex items-center gap-2 ".concat(className), dir: isRTL ? "rtl" : "ltr", "data-testid": testIdWrapper },
26
+ react_1.default.createElement("label", { htmlFor: "page-limit-select-".concat(nodeSelectKey), className: "text-sm text-muted-foreground whitespace-nowrap", "data-testid": testIdLabel }, rowsPerPageLabel),
27
27
  react_1.default.createElement(select_1.Select, { value: String(pageLimit), onValueChange: function (value) {
28
28
  return handleOnSelect(nodeSelectKey, { option: value });
29
29
  }, disabled: loading },
30
- react_1.default.createElement(select_1.SelectTrigger, { id: "page-limit-select-".concat(nodeSelectKey), className: "w-20", "aria-label": "".concat(rowsPerPageLabel, " ").concat(pageLimit) },
31
- react_1.default.createElement(select_1.SelectValue, null)),
32
- react_1.default.createElement(select_1.SelectContent, null, listOptions.map(function (option) { return (react_1.default.createElement(select_1.SelectItem, { key: option.option, value: option.option, "aria-label": "".concat(option.option, " rows per page") }, option.option)); })))));
30
+ react_1.default.createElement(select_1.SelectTrigger, { id: "page-limit-select-".concat(nodeSelectKey), className: "w-20", "aria-label": "".concat(rowsPerPageLabel, " ").concat(pageLimit), "data-testid": testIdSelectTrigger },
31
+ react_1.default.createElement(select_1.SelectValue, { "data-testid": testIdSelectValue })),
32
+ react_1.default.createElement(select_1.SelectContent, { "data-testid": testIdSelectContent }, listOptions.map(function (option) { return (react_1.default.createElement(select_1.SelectItem, { key: option.option, value: option.option, "aria-label": "".concat(option.option, " rows per page"), "data-testid": testIdOptionPrefix
33
+ ? "".concat(testIdOptionPrefix, "-").concat(option.option)
34
+ : undefined }, option.option)); })))));
33
35
  };
34
36
  exports.EnhancedTableFooterPagination = EnhancedTableFooterPagination;
35
37
  // ============================================================================
@@ -1,9 +1,20 @@
1
1
  import { FC } from "react";
2
2
  import { HeaderAction } from "./enhanced-table";
3
3
  interface EnhancedTableHeaderActionProps {
4
+ /** Header-level actions to show as buttons in the table header. */
4
5
  headerActions: HeaderAction[];
6
+ /** Number of active filters (used to show a badge on filter buttons). */
5
7
  numberOfFilters?: number;
8
+ /** Callback invoked when the user clears all filters (clear icon). */
6
9
  onClearFilters?: () => void;
10
+ /** `data-testid` for the wrapper containing all header action buttons. */
11
+ testIdWrapper?: string;
12
+ /** `data-testid` prefix for individual action buttons: `${prefix}-${index}`. */
13
+ testIdActionPrefix?: string;
14
+ /** `data-testid` for the badge that shows number of filters. */
15
+ testIdFilterBadge?: string;
16
+ /** `data-testid` for the clear-filters button/icon. */
17
+ testIdClearFilters?: string;
7
18
  }
8
19
  export declare const EnhancedTableHeaderAction: FC<EnhancedTableHeaderActionProps>;
9
20
  export {};
@@ -9,8 +9,8 @@ var lucide_react_1 = require("lucide-react");
9
9
  var button_1 = require("./ui/button");
10
10
  var badge_1 = require("./ui/badge");
11
11
  var EnhancedTableHeaderAction = function (_a) {
12
- var headerActions = _a.headerActions, numberOfFilters = _a.numberOfFilters, onClearFilters = _a.onClearFilters;
13
- return (react_1.default.createElement(react_1.default.Fragment, null, headerActions.length > 0 && (react_1.default.createElement("div", { className: "flex gap-2" }, headerActions
12
+ var headerActions = _a.headerActions, numberOfFilters = _a.numberOfFilters, onClearFilters = _a.onClearFilters, testIdWrapper = _a.testIdWrapper, testIdActionPrefix = _a.testIdActionPrefix, testIdFilterBadge = _a.testIdFilterBadge, testIdClearFilters = _a.testIdClearFilters;
13
+ return (react_1.default.createElement(react_1.default.Fragment, null, headerActions.length > 0 && (react_1.default.createElement("div", { className: "flex gap-2", "data-testid": testIdWrapper }, headerActions
14
14
  .filter(function (action) { return action.enabled !== false; })
15
15
  .sort(function (a, b) { return (a.order || 0) - (b.order || 0); })
16
16
  .map(function (action, index) {
@@ -21,15 +21,17 @@ var EnhancedTableHeaderAction = function (_a) {
21
21
  typeof numberOfFilters === "number" &&
22
22
  numberOfFilters > 0;
23
23
  return (react_1.default.createElement("div", { key: action.id || action.key || index, className: "relative" },
24
- react_1.default.createElement(button_1.Button, { variant: action.variant || "default", onClick: function () {
24
+ react_1.default.createElement(button_1.Button, { variant: action.variant || "default", "data-testid": testIdActionPrefix
25
+ ? "".concat(testIdActionPrefix, "-").concat(index)
26
+ : undefined, onClick: function () {
25
27
  if (action.onClick)
26
28
  action.onClick();
27
29
  if (action.handleOnClick)
28
30
  action.handleOnClick();
29
31
  } }, action.label),
30
32
  showFilterIndicators && (react_1.default.createElement(react_1.default.Fragment, null,
31
- react_1.default.createElement(badge_1.Badge, { variant: "secondary", className: "absolute -top-2 -right-2 h-5 w-5 flex items-center justify-center p-0 text-xs rounded-full" }, numberOfFilters),
32
- onClearFilters && (react_1.default.createElement("button", { onClick: function (e) {
33
+ react_1.default.createElement(badge_1.Badge, { variant: "secondary", className: "absolute -top-2 -right-2 h-5 w-5 flex items-center justify-center p-0 text-xs rounded-full", "data-testid": testIdFilterBadge }, numberOfFilters),
34
+ onClearFilters && (react_1.default.createElement("button", { "data-testid": testIdClearFilters, onClick: function (e) {
33
35
  e.stopPropagation();
34
36
  onClearFilters();
35
37
  }, className: "absolute -top-2 -left-2 h-5 w-5 rounded-full bg-muted hover:bg-muted/80 flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Clear all filters" },
@@ -1,12 +1,27 @@
1
1
  import { FC } from "react";
2
2
  interface EnhancedTableHeaderSearchProps {
3
+ /** Called when the search input changes. */
3
4
  handleSearchInput: (key: string, value: string) => void;
5
+ /** Whether the search area is currently loading. */
4
6
  loading: boolean;
7
+ /** Disable the search input. */
5
8
  searchDisabled?: boolean;
9
+ /** Enable or disable rendering of the search field. */
6
10
  searchEnabled?: boolean;
11
+ /** `id` applied to the native search input. */
7
12
  searchId?: string;
13
+ /** Placeholder text for the search input. */
8
14
  searchPlaceholder?: string;
15
+ /** Controlled search input value. */
9
16
  searchValue: string;
17
+ /** `data-testid` for the wrapper containing the search input. */
18
+ testIdWrapper?: string;
19
+ /** `data-testid` for the native input element. */
20
+ testIdInput?: string;
21
+ /** `data-testid` for the search icon element. */
22
+ testIdIcon?: string;
23
+ /** `data-testid` for a potential loading indicator. */
24
+ testIdLoading?: string;
10
25
  }
11
26
  export declare const EnhancedTableHeaderSearch: FC<EnhancedTableHeaderSearchProps>;
12
27
  export {};
@@ -8,10 +8,11 @@ var react_1 = __importDefault(require("react"));
8
8
  var lucide_react_1 = require("lucide-react");
9
9
  var input_1 = require("./ui/input");
10
10
  var EnhancedTableHeaderSearch = function (_a) {
11
- var handleSearchInput = _a.handleSearchInput, loading = _a.loading, _b = _a.searchDisabled, searchDisabled = _b === void 0 ? false : _b, _c = _a.searchEnabled, searchEnabled = _c === void 0 ? true : _c, _d = _a.searchId, searchId = _d === void 0 ? "table-search" : _d, _e = _a.searchPlaceholder, searchPlaceholder = _e === void 0 ? "Search..." : _e, searchValue = _a.searchValue;
12
- return (react_1.default.createElement(react_1.default.Fragment, null, searchEnabled && (react_1.default.createElement("div", { className: "flex items-center gap-2 max-w-xl" },
11
+ var handleSearchInput = _a.handleSearchInput, loading = _a.loading, _b = _a.searchDisabled, searchDisabled = _b === void 0 ? false : _b, _c = _a.searchEnabled, searchEnabled = _c === void 0 ? true : _c, _d = _a.searchId, searchId = _d === void 0 ? "table-search" : _d, _e = _a.searchPlaceholder, searchPlaceholder = _e === void 0 ? "Search..." : _e, searchValue = _a.searchValue, testIdWrapper = _a.testIdWrapper, testIdInput = _a.testIdInput, testIdIcon = _a.testIdIcon, testIdLoading = _a.testIdLoading;
12
+ return (react_1.default.createElement(react_1.default.Fragment, null, searchEnabled && (react_1.default.createElement("div", { className: "flex items-center gap-2 max-w-xl", "data-testid": testIdWrapper },
13
13
  react_1.default.createElement("div", { className: "relative flex-1" },
14
- react_1.default.createElement(lucide_react_1.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
15
- react_1.default.createElement(input_1.Input, { id: searchId, placeholder: searchPlaceholder, value: searchValue, onChange: function (e) { return handleSearchInput("search", e.target.value); }, disabled: searchDisabled || loading, className: "pl-9" }))))));
14
+ react_1.default.createElement(lucide_react_1.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground", "data-testid": testIdIcon }),
15
+ react_1.default.createElement(input_1.Input, { id: searchId, placeholder: searchPlaceholder, value: searchValue, onChange: function (e) { return handleSearchInput("search", e.target.value); }, disabled: searchDisabled || loading, className: "pl-9", "data-testid": testIdInput }),
16
+ loading && testIdLoading && (react_1.default.createElement("span", { className: "sr-only", "data-testid": testIdLoading })))))));
16
17
  };
17
18
  exports.EnhancedTableHeaderSearch = EnhancedTableHeaderSearch;
@@ -44,47 +44,143 @@ export interface PageLimitOption {
44
44
  option: string;
45
45
  }
46
46
  export interface EnhancedTableProps {
47
+ /** Columns configuration for the table header. */
47
48
  tableHeadItems: TableColumn[];
49
+ /** Row data to render in the table body. */
48
50
  tableBodyRows: TableRow[];
51
+ /** Column definitions describing how to render each cell. */
49
52
  tableBodyCols: Array<{
53
+ /** Key path(s) used to resolve the value from a row. Supports colon-separated nested paths. */
50
54
  key?: string | string[];
55
+ /** Component type (from `COMPONENT_TYPE`) or a compatible external string. */
51
56
  componentType: COMPONENT_TYPE | string;
57
+ /** Optional custom formatter invoked with `(value, row)` to render the cell. */
52
58
  textFormatter?: (value: unknown, row: unknown) => React.ReactNode;
53
59
  }>;
60
+ /** Visible table heading shown above the table. */
54
61
  tableHeading: string;
62
+ /** Optional description text shown beneath the heading. */
55
63
  tableDescription?: string;
64
+ /** Current page number (1-based). */
56
65
  currentPage: number;
66
+ /** Total number of pages available. */
57
67
  totalPages: number;
68
+ /** Items per page (page size). */
58
69
  pageLimit: number;
70
+ /** Options for rows-per-page selector. */
59
71
  listOptions: PageLimitOption[];
72
+ /** Called when the user navigates to the next page. */
60
73
  handleNextOnClick: () => void;
74
+ /** Called when the user navigates to the previous page. */
61
75
  handlePreviousOnClick: () => void;
76
+ /** Handler for select changes in pagination controls. */
62
77
  handleOnSelect: (node: string, value: object) => void;
78
+ /** Whether the "next" control is disabled. */
63
79
  isNextDisabled: boolean;
80
+ /** Whether the "previous" control is disabled. */
64
81
  isPreviousDisabled: boolean;
82
+ /** Enable/disable the search input in the table header. */
65
83
  searchEnabled?: boolean;
84
+ /** Current search input value. */
66
85
  searchValue: string;
86
+ /** Placeholder text for the search input. */
67
87
  searchPlaceholder?: string;
88
+ /** Optional id attribute for the search input element. */
68
89
  searchId?: string;
90
+ /** Disable the search input. */
69
91
  searchDisabled?: boolean;
92
+ /** Called when the search input value changes. */
70
93
  handleSearchInput: (key: string, value: string) => void;
94
+ /** Header-level actions to show in the table header. */
71
95
  headerActions: HeaderAction[];
96
+ /** Row-level actions for each row. */
72
97
  rowActions: RowAction[];
98
+ /** Number of active filters (shown in header actions). */
73
99
  numberOfFilters?: number;
100
+ /** Called to clear applied filters. */
74
101
  onClearFilters?: () => void;
102
+ /** Whether the table is currently loading data. */
75
103
  loading: boolean;
104
+ /** Render layout in right-to-left mode when true. */
76
105
  isRTL?: boolean;
106
+ /** Optional override labels used for UI text in the table. */
77
107
  translationLabels?: {
108
+ /** Label to use for boolean `true`. */
78
109
  booleanYes?: string;
110
+ /** Label to use for boolean `false`. */
79
111
  booleanNo?: string;
112
+ /** Empty-state text when no rows exist. */
80
113
  noResultsFound?: string;
114
+ /** Label for the previous page control. */
81
115
  previousPage?: string;
116
+ /** Label for the next page control. */
82
117
  nextPage?: string;
118
+ /** Label for the word "page" used in pagination. */
83
119
  page?: string;
120
+ /** Label for the word "of" used in pagination. */
84
121
  of?: string;
122
+ /** Label used while loading. */
85
123
  loading?: string;
124
+ /** Label for the rows-per-page control. */
86
125
  rowsPerPage?: string;
87
126
  };
127
+ /** Optional key name used by the pagination select control (defaults to "pageLimit"). */
88
128
  nodeSelectKey?: string;
129
+ /** `data-testid` for the root container wrapping the whole component. */
130
+ testIdWrapper?: string;
131
+ /** `data-testid` for the table heading (`h2`). */
132
+ testIdHeading?: string;
133
+ /** `data-testid` for the optional table description paragraph. */
134
+ testIdTableDescription?: string;
135
+ /** `data-testid` for the search wrapper (contains the header search component). */
136
+ testIdSearch?: string;
137
+ /** `data-testid` for the header actions wrapper. */
138
+ testIdHeaderActions?: string;
139
+ /** `data-testid` for the table wrapper (the bordered `div` that contains the table). */
140
+ testIdTableWrapper?: string;
141
+ /** Prefix used for row test ids: `${prefix}-${rowIndex}`. */
142
+ testIdRowPrefix?: string;
143
+ /** Prefix used for cell test ids: `${prefix}-${rowIndex}-${colIndex}`. */
144
+ testIdCellPrefix?: string;
145
+ /** `data-testid` for the no-results empty state cell. */
146
+ testIdNoResults?: string;
147
+ /** Prefix used for loading row test ids: `${prefix}-${index}`. */
148
+ testIdLoadingRowPrefix?: string;
149
+ /** `data-testid` for the pagination wrapper area. */
150
+ testIdPaginationWrapper?: string;
151
+ /** `data-testid` forwarded to the footer pagination component. */
152
+ testIdFooterPagination?: string;
153
+ /** `data-testid` forwarded to the footer page component. */
154
+ testIdFooterPage?: string;
155
+ /** `data-testid` forwarded to the footer action component. */
156
+ testIdFooterAction?: string;
157
+ /** RTL `data-testid` for the root container. */
158
+ testIdWrapperRtl?: string;
159
+ /** RTL `data-testid` for the table heading. */
160
+ testIdHeadingRtl?: string;
161
+ /** RTL `data-testid` for the table description. */
162
+ testIdTableDescriptionRtl?: string;
163
+ /** RTL `data-testid` for the search wrapper. */
164
+ testIdSearchRtl?: string;
165
+ /** RTL `data-testid` for the header actions wrapper. */
166
+ testIdHeaderActionsRtl?: string;
167
+ /** RTL `data-testid` for the table wrapper. */
168
+ testIdTableWrapperRtl?: string;
169
+ /** Prefix used for row test ids in RTL: `${prefix}-${rowIndex}`. */
170
+ testIdRowPrefixRtl?: string;
171
+ /** Prefix used for cell test ids in RTL: `${prefix}-${rowIndex}-${colIndex}`. */
172
+ testIdCellPrefixRtl?: string;
173
+ /** RTL `data-testid` for the no-results empty state cell. */
174
+ testIdNoResultsRtl?: string;
175
+ /** Prefix used for loading row test ids in RTL: `${prefix}-${index}`. */
176
+ testIdLoadingRowPrefixRtl?: string;
177
+ /** RTL `data-testid` for the pagination wrapper area. */
178
+ testIdPaginationWrapperRtl?: string;
179
+ /** RTL `data-testid` forwarded to the footer pagination component. */
180
+ testIdFooterPaginationRtl?: string;
181
+ /** RTL `data-testid` forwarded to the footer page component. */
182
+ testIdFooterPageRtl?: string;
183
+ /** RTL `data-testid` forwarded to the footer action component. */
184
+ testIdFooterActionRtl?: string;
89
185
  }
90
186
  export declare const EnhancedTable: React.FC<EnhancedTableProps>;
@@ -41,7 +41,7 @@ var COMPONENT_TYPE;
41
41
  COMPONENT_TYPE["BOOLEAN"] = "BOOLEAN";
42
42
  })(COMPONENT_TYPE || (exports.COMPONENT_TYPE = COMPONENT_TYPE = {}));
43
43
  var EnhancedTable = function (_a) {
44
- var currentPage = _a.currentPage, handleNextOnClick = _a.handleNextOnClick, handleOnSelect = _a.handleOnSelect, handlePreviousOnClick = _a.handlePreviousOnClick, handleSearchInput = _a.handleSearchInput, headerActions = _a.headerActions, isNextDisabled = _a.isNextDisabled, isPreviousDisabled = _a.isPreviousDisabled, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, listOptions = _a.listOptions, loading = _a.loading, _c = _a.nodeSelectKey, nodeSelectKey = _c === void 0 ? "pageLimit" : _c, numberOfFilters = _a.numberOfFilters, onClearFilters = _a.onClearFilters, pageLimit = _a.pageLimit, rowActions = _a.rowActions, _d = _a.searchDisabled, searchDisabled = _d === void 0 ? false : _d, _e = _a.searchEnabled, searchEnabled = _e === void 0 ? true : _e, _f = _a.searchId, searchId = _f === void 0 ? "table-search" : _f, _g = _a.searchPlaceholder, searchPlaceholder = _g === void 0 ? "Search..." : _g, searchValue = _a.searchValue, tableBodyCols = _a.tableBodyCols, tableBodyRows = _a.tableBodyRows, tableDescription = _a.tableDescription, tableHeading = _a.tableHeading, tableHeadItems = _a.tableHeadItems, totalPages = _a.totalPages, _h = _a.translationLabels, translationLabels = _h === void 0 ? {} : _h;
44
+ var currentPage = _a.currentPage, handleNextOnClick = _a.handleNextOnClick, handleOnSelect = _a.handleOnSelect, handlePreviousOnClick = _a.handlePreviousOnClick, handleSearchInput = _a.handleSearchInput, headerActions = _a.headerActions, isNextDisabled = _a.isNextDisabled, isPreviousDisabled = _a.isPreviousDisabled, _b = _a.isRTL, isRTL = _b === void 0 ? false : _b, listOptions = _a.listOptions, loading = _a.loading, _c = _a.nodeSelectKey, nodeSelectKey = _c === void 0 ? "pageLimit" : _c, numberOfFilters = _a.numberOfFilters, onClearFilters = _a.onClearFilters, pageLimit = _a.pageLimit, rowActions = _a.rowActions, _d = _a.searchDisabled, searchDisabled = _d === void 0 ? false : _d, _e = _a.searchEnabled, searchEnabled = _e === void 0 ? true : _e, _f = _a.searchId, searchId = _f === void 0 ? "table-search" : _f, _g = _a.searchPlaceholder, searchPlaceholder = _g === void 0 ? "Search..." : _g, searchValue = _a.searchValue, tableBodyCols = _a.tableBodyCols, tableBodyRows = _a.tableBodyRows, tableDescription = _a.tableDescription, tableHeading = _a.tableHeading, tableHeadItems = _a.tableHeadItems, totalPages = _a.totalPages, _h = _a.translationLabels, translationLabels = _h === void 0 ? {} : _h, testIdWrapper = _a.testIdWrapper, testIdHeading = _a.testIdHeading, testIdTableDescription = _a.testIdTableDescription, testIdSearch = _a.testIdSearch, testIdHeaderActions = _a.testIdHeaderActions, testIdTableWrapper = _a.testIdTableWrapper, testIdRowPrefix = _a.testIdRowPrefix, testIdCellPrefix = _a.testIdCellPrefix, testIdNoResults = _a.testIdNoResults, testIdLoadingRowPrefix = _a.testIdLoadingRowPrefix, testIdPaginationWrapper = _a.testIdPaginationWrapper, testIdFooterPagination = _a.testIdFooterPagination, testIdFooterPage = _a.testIdFooterPage, testIdFooterAction = _a.testIdFooterAction, testIdWrapperRtl = _a.testIdWrapperRtl, testIdHeadingRtl = _a.testIdHeadingRtl, testIdTableDescriptionRtl = _a.testIdTableDescriptionRtl, testIdSearchRtl = _a.testIdSearchRtl, testIdHeaderActionsRtl = _a.testIdHeaderActionsRtl, testIdTableWrapperRtl = _a.testIdTableWrapperRtl, testIdRowPrefixRtl = _a.testIdRowPrefixRtl, testIdCellPrefixRtl = _a.testIdCellPrefixRtl, testIdNoResultsRtl = _a.testIdNoResultsRtl, testIdLoadingRowPrefixRtl = _a.testIdLoadingRowPrefixRtl, testIdPaginationWrapperRtl = _a.testIdPaginationWrapperRtl, testIdFooterPaginationRtl = _a.testIdFooterPaginationRtl, testIdFooterPageRtl = _a.testIdFooterPageRtl, testIdFooterActionRtl = _a.testIdFooterActionRtl;
45
45
  // Default translation values
46
46
  var defaultLabels = {
47
47
  booleanYes: "Yes",
@@ -56,6 +56,49 @@ var EnhancedTable = function (_a) {
56
56
  };
57
57
  // Merge provided labels with defaults
58
58
  var labels = __assign(__assign({}, defaultLabels), translationLabels);
59
+ // Resolve which test ids to use depending on `isRTL` mode.
60
+ var _testIdWrapper = isRTL
61
+ ? (testIdWrapperRtl !== null && testIdWrapperRtl !== void 0 ? testIdWrapperRtl : testIdWrapper)
62
+ : testIdWrapper;
63
+ var _testIdHeading = isRTL
64
+ ? (testIdHeadingRtl !== null && testIdHeadingRtl !== void 0 ? testIdHeadingRtl : testIdHeading)
65
+ : testIdHeading;
66
+ var _testIdTableDescription = isRTL
67
+ ? (testIdTableDescriptionRtl !== null && testIdTableDescriptionRtl !== void 0 ? testIdTableDescriptionRtl : testIdTableDescription)
68
+ : testIdTableDescription;
69
+ var _testIdSearch = isRTL
70
+ ? (testIdSearchRtl !== null && testIdSearchRtl !== void 0 ? testIdSearchRtl : testIdSearch)
71
+ : testIdSearch;
72
+ var _testIdHeaderActions = isRTL
73
+ ? (testIdHeaderActionsRtl !== null && testIdHeaderActionsRtl !== void 0 ? testIdHeaderActionsRtl : testIdHeaderActions)
74
+ : testIdHeaderActions;
75
+ var _testIdTableWrapper = isRTL
76
+ ? (testIdTableWrapperRtl !== null && testIdTableWrapperRtl !== void 0 ? testIdTableWrapperRtl : testIdTableWrapper)
77
+ : testIdTableWrapper;
78
+ var _testIdRowPrefix = isRTL
79
+ ? (testIdRowPrefixRtl !== null && testIdRowPrefixRtl !== void 0 ? testIdRowPrefixRtl : testIdRowPrefix)
80
+ : testIdRowPrefix;
81
+ var _testIdCellPrefix = isRTL
82
+ ? (testIdCellPrefixRtl !== null && testIdCellPrefixRtl !== void 0 ? testIdCellPrefixRtl : testIdCellPrefix)
83
+ : testIdCellPrefix;
84
+ var _testIdNoResults = isRTL
85
+ ? (testIdNoResultsRtl !== null && testIdNoResultsRtl !== void 0 ? testIdNoResultsRtl : testIdNoResults)
86
+ : testIdNoResults;
87
+ var _testIdLoadingRowPrefix = isRTL
88
+ ? (testIdLoadingRowPrefixRtl !== null && testIdLoadingRowPrefixRtl !== void 0 ? testIdLoadingRowPrefixRtl : testIdLoadingRowPrefix)
89
+ : testIdLoadingRowPrefix;
90
+ var _testIdPaginationWrapper = isRTL
91
+ ? (testIdPaginationWrapperRtl !== null && testIdPaginationWrapperRtl !== void 0 ? testIdPaginationWrapperRtl : testIdPaginationWrapper)
92
+ : testIdPaginationWrapper;
93
+ var _testIdFooterPagination = isRTL
94
+ ? (testIdFooterPaginationRtl !== null && testIdFooterPaginationRtl !== void 0 ? testIdFooterPaginationRtl : testIdFooterPagination)
95
+ : testIdFooterPagination;
96
+ var _testIdFooterPage = isRTL
97
+ ? (testIdFooterPageRtl !== null && testIdFooterPageRtl !== void 0 ? testIdFooterPageRtl : testIdFooterPage)
98
+ : testIdFooterPage;
99
+ var _testIdFooterAction = isRTL
100
+ ? (testIdFooterActionRtl !== null && testIdFooterActionRtl !== void 0 ? testIdFooterActionRtl : testIdFooterAction)
101
+ : testIdFooterAction;
59
102
  var renderCellContent = function (row, col) {
60
103
  var _a, _b;
61
104
  // Helper to safely resolve nested keys (max depth 3)
@@ -236,27 +279,38 @@ var EnhancedTable = function (_a) {
236
279
  };
237
280
  // Create loading skeleton rows
238
281
  var renderLoadingRows = function () {
239
- return Array.from({ length: pageLimit || 5 }).map(function (_, index) { return (react_1.default.createElement(table_1.TableRow, { key: "loading-".concat(index), className: "hover:bg-muted/50" }, tableHeadItems.map(function (_, colIndex) { return (react_1.default.createElement(table_1.TableCell, { key: colIndex, className: "py-4" },
282
+ return Array.from({ length: pageLimit || 5 }).map(function (_, index) { return (react_1.default.createElement(table_1.TableRow, { key: "loading-".concat(index), className: "hover:bg-muted/50", "data-testid": _testIdLoadingRowPrefix
283
+ ? "".concat(_testIdLoadingRowPrefix, "-").concat(index)
284
+ : undefined }, tableHeadItems.map(function (_, colIndex) { return (react_1.default.createElement(table_1.TableCell, { key: colIndex, className: "py-4" },
240
285
  react_1.default.createElement("div", { className: "h-4 bg-muted animate-pulse rounded w-full" }))); }))); });
241
286
  };
242
- return (react_1.default.createElement("div", { className: "space-y-4" },
287
+ return (react_1.default.createElement("div", { className: "space-y-4", "data-testid": _testIdWrapper },
243
288
  react_1.default.createElement("div", { className: "flex items-center justify-between" },
244
289
  react_1.default.createElement("div", { className: "space-y-1" },
245
- react_1.default.createElement("h2", { className: "text-2xl font-bold tracking-tight" }, tableHeading),
246
- tableDescription && (react_1.default.createElement("p", { className: "text-muted-foreground" }, tableDescription))),
290
+ react_1.default.createElement("h2", { className: "text-2xl font-bold tracking-tight", "data-testid": _testIdHeading }, tableHeading),
291
+ tableDescription && (react_1.default.createElement("p", { className: "text-muted-foreground", "data-testid": _testIdTableDescription }, tableDescription))),
247
292
  react_1.default.createElement("div", { className: "flex flex-row items-center justify-end gap-2" },
248
- react_1.default.createElement(enhanced_table_header_search_1.EnhancedTableHeaderSearch, { handleSearchInput: handleSearchInput, loading: loading, searchDisabled: searchDisabled, searchEnabled: searchEnabled, searchId: searchId, searchPlaceholder: searchPlaceholder, searchValue: searchValue }),
249
- react_1.default.createElement(enhanced_table_header_action_1.EnhancedTableHeaderAction, { headerActions: headerActions, numberOfFilters: numberOfFilters, onClearFilters: onClearFilters }))),
250
- react_1.default.createElement("div", { className: "border rounded-md" },
293
+ react_1.default.createElement("div", { "data-testid": _testIdSearch },
294
+ react_1.default.createElement(enhanced_table_header_search_1.EnhancedTableHeaderSearch, { handleSearchInput: handleSearchInput, loading: loading, searchDisabled: searchDisabled, searchEnabled: searchEnabled, searchId: searchId, searchPlaceholder: searchPlaceholder, searchValue: searchValue })),
295
+ react_1.default.createElement("div", { "data-testid": _testIdHeaderActions },
296
+ react_1.default.createElement(enhanced_table_header_action_1.EnhancedTableHeaderAction, { headerActions: headerActions, numberOfFilters: numberOfFilters, onClearFilters: onClearFilters })))),
297
+ react_1.default.createElement("div", { className: "border rounded-md", "data-testid": _testIdTableWrapper },
251
298
  react_1.default.createElement(table_1.Table, null,
252
299
  react_1.default.createElement(table_1.TableHeader, null,
253
300
  react_1.default.createElement(table_1.TableRow, null, tableHeadItems.map(function (header, index) { return (react_1.default.createElement(table_1.TableHead, { key: index, style: { width: header.width }, className: (0, utils_1.cn)("font-medium", index === 0 && "rounded-tl-md", index === tableHeadItems.length - 1 && "rounded-tr-md") }, header.label)); }))),
254
301
  react_1.default.createElement(table_1.TableBody, null, loading ? (renderLoadingRows()) : tableBodyRows.length === 0 ? (react_1.default.createElement(table_1.TableRow, null,
255
- react_1.default.createElement(table_1.TableCell, { colSpan: tableHeadItems.length, className: "h-24 text-center text-muted-foreground" }, labels.noResultsFound))) : (tableBodyRows.map(function (row, rowIndex) { return (react_1.default.createElement(table_1.TableRow, { key: rowIndex, className: "hover:bg-muted/50" }, tableBodyCols.map(function (col, colIndex) { return (react_1.default.createElement(table_1.TableCell, { key: colIndex, className: "py-4" }, renderCellContent(row, col))); }))); }))))),
256
- react_1.default.createElement("div", { className: "flex items-center justify-between" },
257
- react_1.default.createElement(enhanced_table_footer_pagination_1.EnhancedTableFooterPagination, { handleOnSelect: handleOnSelect, isRTL: isRTL, listOptions: listOptions, loading: loading, nodeSelectKey: nodeSelectKey, pageLimit: pageLimit, rowsPerPageLabel: labels.rowsPerPage, totalPages: totalPages }),
302
+ react_1.default.createElement(table_1.TableCell, { colSpan: tableHeadItems.length, className: "h-24 text-center text-muted-foreground", "data-testid": _testIdNoResults }, labels.noResultsFound))) : (tableBodyRows.map(function (row, rowIndex) { return (react_1.default.createElement(table_1.TableRow, { key: rowIndex, className: "hover:bg-muted/50", "data-testid": _testIdRowPrefix
303
+ ? "".concat(_testIdRowPrefix, "-").concat(rowIndex)
304
+ : undefined }, tableBodyCols.map(function (col, colIndex) { return (react_1.default.createElement(table_1.TableCell, { key: colIndex, className: "py-4", "data-testid": _testIdCellPrefix
305
+ ? "".concat(_testIdCellPrefix, "-").concat(rowIndex, "-").concat(colIndex)
306
+ : undefined }, renderCellContent(row, col))); }))); }))))),
307
+ react_1.default.createElement("div", { className: "flex items-center justify-between", "data-testid": _testIdPaginationWrapper },
308
+ react_1.default.createElement("div", { "data-testid": _testIdFooterPagination },
309
+ react_1.default.createElement(enhanced_table_footer_pagination_1.EnhancedTableFooterPagination, { handleOnSelect: handleOnSelect, isRTL: isRTL, listOptions: listOptions, loading: loading, nodeSelectKey: nodeSelectKey, pageLimit: pageLimit, rowsPerPageLabel: labels.rowsPerPage, totalPages: totalPages })),
258
310
  react_1.default.createElement("div", { className: "flex items-center gap-4" },
259
- react_1.default.createElement(enhanced_table_footer_page_1.EnhancedTableFooterPage, { currentPage: currentPage, isRTL: isRTL, loading: loading, loadingLabel: labels.loading, ofLabel: labels.of, pageLabel: labels.page, totalPages: totalPages }),
260
- react_1.default.createElement(enhanced_table_footer_action_1.EnhancedTableFooterAction, { handleNextOnClick: handleNextOnClick, handlePreviousOnClick: handlePreviousOnClick, isNextDisabled: isNextDisabled, isPreviousDisabled: isPreviousDisabled, isRTL: isRTL, loading: loading, nextPageLabel: labels.nextPage, previousPageLabel: labels.previousPage })))));
311
+ react_1.default.createElement("div", { "data-testid": _testIdFooterPage },
312
+ react_1.default.createElement(enhanced_table_footer_page_1.EnhancedTableFooterPage, { currentPage: currentPage, isRTL: isRTL, loading: loading, loadingLabel: labels.loading, ofLabel: labels.of, pageLabel: labels.page, totalPages: totalPages })),
313
+ react_1.default.createElement("div", { "data-testid": _testIdFooterAction },
314
+ react_1.default.createElement(enhanced_table_footer_action_1.EnhancedTableFooterAction, { handleNextOnClick: handleNextOnClick, handlePreviousOnClick: handlePreviousOnClick, isNextDisabled: isNextDisabled, isPreviousDisabled: isPreviousDisabled, isRTL: isRTL, loading: loading, nextPageLabel: labels.nextPage, previousPageLabel: labels.previousPage }))))));
261
315
  };
262
316
  exports.EnhancedTable = EnhancedTable;
@@ -1,8 +1,24 @@
1
1
  import * as React from "react";
2
+ /** Props for `EnhancedTextarea` component. */
2
3
  interface EnhancedTextareaProps extends React.ComponentProps<"textarea"> {
4
+ /** Error message text shown below the textarea; sets error styling when present. */
3
5
  error?: string;
6
+ /** Informational helper text shown below the textarea (when no error). */
4
7
  info?: string;
8
+ /** Optional label text displayed above the textarea. */
5
9
  label?: string;
10
+ /** `data-testid` for the root wrapper (outermost `div`). */
11
+ testIdWrapper?: string;
12
+ /** `data-testid` for the label element (forwards to `EnhancedLabel`). */
13
+ testIdLabel?: string;
14
+ /** `data-testid` for the native textarea element. */
15
+ testIdTextarea?: string;
16
+ /** `data-testid` for the message container (holds error/info). */
17
+ testIdMessage?: string;
18
+ /** `data-testid` for the error text element. */
19
+ testIdError?: string;
20
+ /** `data-testid` for the info text element. */
21
+ testIdInfo?: string;
6
22
  }
7
23
  declare const EnhancedTextarea: React.ForwardRefExoticComponent<Omit<EnhancedTextareaProps, "ref"> & React.RefAttributes<HTMLTextAreaElement>>;
8
24
  export { EnhancedTextarea };
@@ -61,10 +61,10 @@ var utils_1 = require("../lib/utils");
61
61
  var textarea_1 = require("./ui/textarea");
62
62
  var enhanced_label_1 = require("./enhanced-label");
63
63
  var EnhancedTextarea = React.forwardRef(function (_a, ref) {
64
- var className = _a.className, error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, props = __rest(_a, ["className", "error", "info", "label", "required", "id"]);
64
+ var className = _a.className, error = _a.error, info = _a.info, label = _a.label, required = _a.required, id = _a.id, testIdWrapper = _a.testIdWrapper, testIdLabel = _a.testIdLabel, testIdTextarea = _a.testIdTextarea, testIdMessage = _a.testIdMessage, testIdError = _a.testIdError, testIdInfo = _a.testIdInfo, props = __rest(_a, ["className", "error", "info", "label", "required", "id", "testIdWrapper", "testIdLabel", "testIdTextarea", "testIdMessage", "testIdError", "testIdInfo"]);
65
65
  // Determine if there's an error (for aria-invalid and styling)
66
66
  var hasError = Boolean(error);
67
- var textareaElement = (React.createElement(textarea_1.Textarea, __assign({ ref: ref, id: id, "aria-invalid": hasError, required: required, className: (0, utils_1.cn)(
67
+ var textareaElement = (React.createElement(textarea_1.Textarea, __assign({ ref: ref, id: id, "data-testid": testIdTextarea, "aria-invalid": hasError, required: required, className: (0, utils_1.cn)(
68
68
  // Error state styling
69
69
  hasError &&
70
70
  "border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20",
@@ -73,15 +73,15 @@ var EnhancedTextarea = React.forwardRef(function (_a, ref) {
73
73
  "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className) }, props)));
74
74
  // If label is provided, render the complete textarea with label structure
75
75
  if (label) {
76
- return (React.createElement("div", { className: "w-full space-y-2" },
77
- React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required }, label),
76
+ return (React.createElement("div", { className: "w-full space-y-2", "data-testid": testIdWrapper },
77
+ React.createElement(enhanced_label_1.EnhancedLabel, { htmlFor: id, required: required, testIdLabel: testIdLabel }, label),
78
78
  textareaElement,
79
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
79
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
80
80
  }
81
81
  // If no label, render just the textarea with messages (backward compatibility)
82
- return (React.createElement("div", { className: "w-full" },
82
+ return (React.createElement("div", { className: "w-full", "data-testid": testIdWrapper },
83
83
  textareaElement,
84
- (error || info) && (React.createElement("div", null, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))));
84
+ (error || info) && (React.createElement("div", { "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive", "data-testid": testIdError }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400", "data-testid": testIdInfo }, info)) : null))));
85
85
  });
86
86
  exports.EnhancedTextarea = EnhancedTextarea;
87
87
  EnhancedTextarea.displayName = "EnhancedTextarea";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/shadcn",
3
- "version": "1.1.15",
3
+ "version": "1.1.17",
4
4
  "scripts": {
5
5
  "build:next": "next build",
6
6
  "build:storybook": "storybook build -c .storybook -o .out",