@appcorp/shadcn 1.1.16 → 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.
- package/components/enhanced-checkbox.d.ts +1 -0
- package/components/enhanced-checkbox.js +2 -2
- package/components/enhanced-combobox.d.ts +46 -1
- package/components/enhanced-combobox.js +12 -12
- package/components/enhanced-dropzone.d.ts +45 -3
- package/components/enhanced-dropzone.js +14 -14
- package/components/enhanced-input.d.ts +16 -0
- package/components/enhanced-input.js +7 -7
- package/components/enhanced-label.d.ts +7 -1
- package/components/enhanced-label.js +3 -3
- package/components/enhanced-select.d.ts +30 -1
- package/components/enhanced-select.js +4 -4
- package/components/enhanced-switch.d.ts +21 -0
- package/components/enhanced-switch.js +9 -9
- package/components/enhanced-table-footer-action.d.ts +10 -0
- package/components/enhanced-table-footer-action.js +6 -6
- package/components/enhanced-table-footer-page.d.ts +14 -0
- package/components/enhanced-table-footer-page.js +12 -12
- package/components/enhanced-table-footer-pagination.d.ts +12 -0
- package/components/enhanced-table-footer-pagination.js +8 -6
- package/components/enhanced-table-header-action.d.ts +11 -0
- package/components/enhanced-table-header-action.js +7 -5
- package/components/enhanced-table-header-search.d.ts +15 -0
- package/components/enhanced-table-header-search.js +5 -4
- package/components/enhanced-table.d.ts +96 -0
- package/components/enhanced-table.js +67 -13
- package/components/enhanced-textarea.d.ts +16 -0
- package/components/enhanced-textarea.js +7 -7
- package/package.json +1 -1
|
@@ -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
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
28
|
-
react_1.default.createElement("span",
|
|
29
|
-
react_1.default.createElement("span",
|
|
30
|
-
react_1.default.createElement("span",
|
|
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",
|
|
34
|
-
react_1.default.createElement("span",
|
|
35
|
-
react_1.default.createElement("span",
|
|
36
|
-
react_1.default.createElement("span",
|
|
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,
|
|
32
|
-
react_1.default.createElement(select_1.SelectContent,
|
|
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",
|
|
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"
|
|
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(
|
|
249
|
-
|
|
250
|
-
|
|
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"
|
|
256
|
-
|
|
257
|
-
|
|
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(
|
|
260
|
-
|
|
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",
|
|
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",
|
|
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";
|