@agilant/toga-blox 1.0.47 → 1.0.49
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/dist/components/Badge/Badge.js +1 -1
- package/dist/components/Dropdown/Dropdown.js +1 -1
- package/dist/components/HeaderFilterIcon/HeaderFilterIcon.d.ts +4 -0
- package/dist/components/HeaderFilterIcon/HeaderFilterIcon.js +60 -0
- package/dist/components/HeaderFilterIcon/HeaderFilterIcon.stories.d.ts +9 -0
- package/dist/components/HeaderFilterIcon/HeaderFilterIcon.stories.js +76 -0
- package/dist/components/HeaderFilterIcon/HeaderFilterIcon.test.d.ts +1 -0
- package/dist/components/HeaderFilterIcon/HeaderFilterIcon.test.js +104 -0
- package/dist/components/HeaderFilterIcon/index.d.ts +2 -0
- package/dist/components/HeaderFilterIcon/index.js +2 -0
- package/dist/components/HeaderFilterIcon/types.d.ts +22 -0
- package/dist/components/HeaderFilterIcon/types.js +1 -0
- package/dist/components/Input/Input.js +3 -3
- package/dist/components/InputAndCheck/InputAndCheck.js +0 -9
- package/dist/components/MagnifyingIcon/MagnifyingIcon.js +1 -1
- package/dist/components/MagnifyingIcon/MagnifyingIcon.stories.js +1 -0
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.js +2 -2
- package/dist/components/SearchInput/SearchDropdownInput.d.ts +1 -0
- package/dist/components/SearchInput/SearchDropdownInput.js +2 -3
- package/dist/components/SearchInput/SearchInput.d.ts +1 -1
- package/dist/components/SearchInput/SearchInput.js +9 -51
- package/dist/components/SearchInput/SearchInput.stories.d.ts +1 -1
- package/dist/components/SearchInput/SearchInput.stories.js +32 -53
- package/dist/components/SearchInput/SearchInput.test.d.ts +1 -0
- package/dist/components/SearchInput/SearchInput.test.js +519 -0
- package/dist/components/SearchInput/SearchInput.types.d.ts +8 -12
- package/dist/components/SearchInput/SearchInputDatePicker.d.ts +23 -0
- package/dist/components/SearchInput/SearchInputDatePicker.js +72 -0
- package/dist/components/SearchInput/SearchNumberInput.d.ts +2 -14
- package/dist/components/SearchInput/SearchNumberInput.js +16 -35
- package/dist/components/SearchInput/SearchTextInput.d.ts +2 -11
- package/dist/components/SearchInput/SearchTextInput.js +8 -27
- package/package.json +2 -1
|
@@ -4,6 +4,6 @@ const Badge = ({ borderColor, borderWidth, borderRadius, backgroundColor, hasMob
|
|
|
4
4
|
const badgeBackgroundClasses = `${backgroundColor}`;
|
|
5
5
|
const badgeBorderClasses = `${borderWidth} ${borderColor} ${borderRadius}`;
|
|
6
6
|
const badgeClasses = `inline-flex items-center ${badgeBorderClasses} ${badgeBackgroundClasses} ${badgeContainerClasses}`;
|
|
7
|
-
return (_jsxs("span", { onClick: onClick, "data-testid": "badge-test-id", className: badgeClasses, style: styles, children: [image && _jsx("span", { className: "mr-2", children: image }), icon && hasLeftIcon && (_jsx("span", { className: `${iconClasses} max-md:hidden mr-2`, children: icon })), mobileIcon && hasMobileStyle && (_jsx("span", { className: `${iconClasses} hidden max-md:flex max-md:p-0 mr-2`, "aria-hidden": "false", "aria-label": mobileIconLabel, children: mobileIcon })), _jsx("div", { className: `${hasMobileStyle ? "max-md:hidden" : ""}`, children: text }), icon && hasRightIcon && (_jsx("span", { className: `${iconClasses} max-md:hidden ml-2`, children: icon }))] }));
|
|
7
|
+
return (_jsxs("span", { onClick: onClick, "data-testid": "badge-test-id", className: badgeClasses, style: styles, children: [image && _jsx("span", { className: "mr-2", children: image }), icon && hasLeftIcon && (_jsx("span", { className: `${iconClasses} max-md:hidden mr-2`, children: icon })), mobileIcon && hasMobileStyle && (_jsx("span", { className: `${iconClasses} hidden max-md:flex max-md:p-0 mr-2`, "aria-hidden": "false", "aria-label": mobileIconLabel, children: mobileIcon })), _jsx("div", { className: `${hasMobileStyle ? "max-md:hidden" : ""}`, children: text }), icon && hasRightIcon && (_jsx("span", { className: `${iconClasses} ${hasMobileStyle ? "max-md:hidden" : ""} ml-2`, children: icon }))] }));
|
|
8
8
|
};
|
|
9
9
|
export default Badge;
|
|
@@ -9,7 +9,7 @@ const Dropdown = ({ options, selectedOption, onOptionSelect, optionClasses = "fl
|
|
|
9
9
|
}, selectedOptionBgColor = "bg-gray-50", optionHoverBgColor = "hover:bg-gray-50", }) => {
|
|
10
10
|
const [showMenu, setShowMenu] = useState(false);
|
|
11
11
|
const toggleMenu = () => setShowMenu(!showMenu);
|
|
12
|
-
return (_jsxs("div", { className: `flex items-center justify-between relative min-w-32
|
|
12
|
+
return (_jsxs("div", { className: `flex items-center justify-between relative min-w-32 ${dropdownClasses}`, children: [_jsxs("div", { onClick: toggleMenu, className: `flex cursor-pointer items-center group h-full `, children: [_jsx("div", { className: `font-bold ${optionClasses}`, children: selectedOption }), _jsx("div", { className: `transform transition-transform duration-200 mx-1 px-1 rounded-full relative ${icon.iconClasses} ${showMenu ? "rotate-180" : "rotate-0"}`, "data-testid": "dropdown-icon", children: getFontAwesomeIcon(icon.name) })] }), showMenu && (_jsx(AnimatePresence, { children: showMenu && (_jsx(motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, className: `absolute top-0 z-10 right-0 left-0 ${menuClasses}`, children: _jsx("ul", { children: options.map((action) => (_jsxs("li", { className: `text-left px-4 py-2 cursor-pointer border-b ${action === selectedOption
|
|
13
13
|
? `{${selectedOptionBgColor} font-semibold}`
|
|
14
14
|
: `${optionHoverBgColor} text-black`}`, onClick: () => {
|
|
15
15
|
onOptionSelect(action);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
3
|
+
import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
|
|
4
|
+
const HeaderFilterIcon = forwardRef(({ columnIndex, editingHeader, setEditingHeader, setHeaderValue, column, setActiveColumn, iconColor = "text-blue-500", iconActiveColor = "text-green-500", iconActiveBackgroundColor = "bg-red-500", isActive, additionalIconClasses, setSearchText, searchText, isSearchOpen, icon = {
|
|
5
|
+
icon: "magnifyingGlass",
|
|
6
|
+
weight: "regular",
|
|
7
|
+
}, }, ref) => {
|
|
8
|
+
const [showActiveStyles, setShowActiveStyles] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (isActive !== undefined) {
|
|
11
|
+
setShowActiveStyles(isActive);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
const checkSearchCriteria = () => {
|
|
15
|
+
const searchCriteriaRaw = localStorage.getItem("searchCriteria");
|
|
16
|
+
let searchCriteria = [];
|
|
17
|
+
try {
|
|
18
|
+
if (searchCriteriaRaw) {
|
|
19
|
+
searchCriteria = JSON.parse(searchCriteriaRaw);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
console.error("Failed to parse searchCriteria from localStorage:", e);
|
|
24
|
+
searchCriteria = [];
|
|
25
|
+
}
|
|
26
|
+
const hasSearchValue = searchCriteria.some((item) => String(item.searchColumn.id) ===
|
|
27
|
+
String(column.id) &&
|
|
28
|
+
item.submittedSearchText &&
|
|
29
|
+
item.submittedSearchText.trim() !== "");
|
|
30
|
+
setShowActiveStyles(hasSearchValue || editingHeader === columnIndex);
|
|
31
|
+
};
|
|
32
|
+
checkSearchCriteria();
|
|
33
|
+
const handleStorageEvent = () => {
|
|
34
|
+
checkSearchCriteria();
|
|
35
|
+
};
|
|
36
|
+
window.addEventListener("localStorageUpdate", handleStorageEvent);
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener("localStorageUpdate", handleStorageEvent);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}, [isActive, editingHeader, column.id, columnIndex]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (isSearchOpen) {
|
|
44
|
+
console.log("Search is open with text:", searchText);
|
|
45
|
+
}
|
|
46
|
+
}, [isSearchOpen, searchText]);
|
|
47
|
+
const handleClick = (event) => {
|
|
48
|
+
event.stopPropagation();
|
|
49
|
+
if (!isSearchOpen) {
|
|
50
|
+
setSearchText?.("");
|
|
51
|
+
}
|
|
52
|
+
setEditingHeader(columnIndex);
|
|
53
|
+
setHeaderValue(column.Header);
|
|
54
|
+
setActiveColumn(columnIndex);
|
|
55
|
+
};
|
|
56
|
+
return (_jsx("div", { ref: ref, "data-testid": "magnifying-icon-test-id", className: `flex items-center cursor-pointer size-[18px] rounded relative ml-1 ${iconColor} ${showActiveStyles
|
|
57
|
+
? `${iconActiveBackgroundColor} hover:bg-blue-500 still-active`
|
|
58
|
+
: "hover:bg-white"}`, onClick: handleClick, children: _jsx("div", { className: `text-xs w-full ${additionalIconClasses} `, children: getFontAwesomeIcon(icon.icon, icon.weight) }) }));
|
|
59
|
+
});
|
|
60
|
+
export default HeaderFilterIcon;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import HeaderFilterIcon from "./HeaderFilterIcon";
|
|
3
|
+
declare const meta: Meta<typeof HeaderFilterIcon>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof HeaderFilterIcon>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const WithActiveClass: Story;
|
|
8
|
+
export declare const WithClickAlert: Story;
|
|
9
|
+
export declare const WithMockLocalStorage: Story;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import HeaderFilterIcon from "./HeaderFilterIcon";
|
|
3
|
+
const meta = {
|
|
4
|
+
title: "Table/HeaderFilterIcon",
|
|
5
|
+
component: HeaderFilterIcon,
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
argTypes: {
|
|
8
|
+
columnIndex: { control: "number" },
|
|
9
|
+
editingHeader: { control: "number" },
|
|
10
|
+
isActive: { control: "boolean" },
|
|
11
|
+
additionalIconClasses: { control: "text" },
|
|
12
|
+
icon: { icon: "x", weight: "regular" },
|
|
13
|
+
},
|
|
14
|
+
// Add a decorator to wrap all stories in a div
|
|
15
|
+
decorators: [
|
|
16
|
+
(Story) => (_jsx("div", { style: {
|
|
17
|
+
padding: "20px",
|
|
18
|
+
borderRadius: "8px",
|
|
19
|
+
width: "fit-content",
|
|
20
|
+
}, children: _jsx(Story, {}) })),
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
export default meta;
|
|
24
|
+
const mockColumnInstance = {
|
|
25
|
+
id: "exampleColumn",
|
|
26
|
+
Header: "Example Column",
|
|
27
|
+
isVisible: true,
|
|
28
|
+
render: () => _jsx("div", { children: "Mock Column" }),
|
|
29
|
+
};
|
|
30
|
+
// Default story (without active class)
|
|
31
|
+
export const Default = {
|
|
32
|
+
args: {
|
|
33
|
+
columnIndex: 0,
|
|
34
|
+
editingHeader: null,
|
|
35
|
+
setEditingHeader: () => { },
|
|
36
|
+
setHeaderValue: () => { },
|
|
37
|
+
additionalIconClasses: "p-4",
|
|
38
|
+
column: mockColumnInstance,
|
|
39
|
+
setActiveColumn: () => { },
|
|
40
|
+
isActive: false,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
// Story with active class
|
|
44
|
+
export const WithActiveClass = {
|
|
45
|
+
args: {
|
|
46
|
+
...Default.args,
|
|
47
|
+
isActive: true,
|
|
48
|
+
additionalIconClasses: "text-green-500",
|
|
49
|
+
icon: { icon: "x", weight: "regular" },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
export const WithClickAlert = {
|
|
53
|
+
args: {
|
|
54
|
+
...Default.args,
|
|
55
|
+
setEditingHeader: (columnIndex) => {
|
|
56
|
+
alert(`the column index and data have been updated! Column Index =` +
|
|
57
|
+
columnIndex);
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export const WithMockLocalStorage = {
|
|
62
|
+
args: {
|
|
63
|
+
...Default.args,
|
|
64
|
+
},
|
|
65
|
+
decorators: [
|
|
66
|
+
(Story) => {
|
|
67
|
+
localStorage.setItem("searchCriteria", JSON.stringify([
|
|
68
|
+
{
|
|
69
|
+
searchColumn: { id: "exampleColumn" },
|
|
70
|
+
submittedSearchText: "test",
|
|
71
|
+
},
|
|
72
|
+
]));
|
|
73
|
+
return _jsx(Story, {});
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
3
|
+
import { render, fireEvent, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import HeaderFilterIcon from "./HeaderFilterIcon";
|
|
5
|
+
describe("HeaderFilterIcon", () => {
|
|
6
|
+
const mockSetEditingHeader = vi.fn();
|
|
7
|
+
const mockSetHeaderValue = vi.fn();
|
|
8
|
+
const mockSetSearchColumn = vi.fn();
|
|
9
|
+
const mockSetActiveColumnFromSearch = vi.fn();
|
|
10
|
+
const mockSetActiveColumn = vi.fn();
|
|
11
|
+
const mockColumn = {
|
|
12
|
+
id: "exampleColumn",
|
|
13
|
+
Header: "Example Column",
|
|
14
|
+
isVisible: true,
|
|
15
|
+
render: vi.fn(),
|
|
16
|
+
};
|
|
17
|
+
const defaultProps = {
|
|
18
|
+
columnIndex: 2,
|
|
19
|
+
editingHeader: null,
|
|
20
|
+
setEditingHeader: mockSetEditingHeader,
|
|
21
|
+
setHeaderValue: mockSetHeaderValue,
|
|
22
|
+
column: mockColumn,
|
|
23
|
+
setSearchColumn: mockSetSearchColumn,
|
|
24
|
+
setActiveColumnFromSearch: mockSetActiveColumnFromSearch,
|
|
25
|
+
setActiveColumn: mockSetActiveColumn,
|
|
26
|
+
};
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.clearAllMocks();
|
|
29
|
+
localStorage.clear();
|
|
30
|
+
});
|
|
31
|
+
it("renders without crashing", () => {
|
|
32
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps }));
|
|
33
|
+
const iconDiv = screen.getByTestId("magnifying-icon-test-id");
|
|
34
|
+
expect(iconDiv).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
it("calls the appropriate props on click", () => {
|
|
37
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps }));
|
|
38
|
+
const iconDiv = screen.getByTestId("magnifying-icon-test-id");
|
|
39
|
+
fireEvent.click(iconDiv);
|
|
40
|
+
// Because setEditingHeader, setHeaderValue, and setActiveColumn
|
|
41
|
+
// are all called synchronously, we can just do normal expects:
|
|
42
|
+
expect(mockSetEditingHeader).toHaveBeenCalledWith(2);
|
|
43
|
+
expect(mockSetHeaderValue).toHaveBeenCalledWith("Example Column");
|
|
44
|
+
expect(mockSetActiveColumn).toHaveBeenCalledWith(2);
|
|
45
|
+
});
|
|
46
|
+
it("applies active styles if localStorage has matching search criteria", () => {
|
|
47
|
+
const fakeCriteria = [
|
|
48
|
+
{
|
|
49
|
+
searchColumn: { id: "exampleColumn" },
|
|
50
|
+
submittedSearchText: "Hello World",
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
localStorage.setItem("searchCriteria", JSON.stringify(fakeCriteria));
|
|
54
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps }));
|
|
55
|
+
const iconDiv = screen.getByTestId("magnifying-icon-test-id");
|
|
56
|
+
// Now "active" means we expect "bg-red-500" & "still-active"
|
|
57
|
+
expect(iconDiv.className).toContain("bg-red-500");
|
|
58
|
+
expect(iconDiv.className).toContain("still-active");
|
|
59
|
+
// Optionally also check the hover state:
|
|
60
|
+
expect(iconDiv.className).toContain("hover:bg-blue-500");
|
|
61
|
+
});
|
|
62
|
+
it("doesn't apply active styles if localStorage is empty", () => {
|
|
63
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps }));
|
|
64
|
+
const iconDiv = screen.getByTestId("magnifying-icon-test-id");
|
|
65
|
+
// Not active => no "bg-red-500"
|
|
66
|
+
expect(iconDiv.className).not.toContain("bg-red-500");
|
|
67
|
+
// By default, we see "hover:bg-white"
|
|
68
|
+
expect(iconDiv.className).toContain("hover:bg-white");
|
|
69
|
+
});
|
|
70
|
+
it("applies active styles if the column is being edited (editingHeader === columnIndex)", () => {
|
|
71
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps, editingHeader: 2 }));
|
|
72
|
+
const iconDiv = screen.getByTestId("magnifying-icon-test-id");
|
|
73
|
+
// Being edited => active => "bg-red-500"
|
|
74
|
+
expect(iconDiv.className).toContain("bg-red-500");
|
|
75
|
+
expect(iconDiv.className).toContain("still-active");
|
|
76
|
+
expect(iconDiv.className).toContain("hover:bg-blue-500");
|
|
77
|
+
});
|
|
78
|
+
it("handles invalid JSON in localStorage gracefully", () => {
|
|
79
|
+
localStorage.setItem("searchCriteria", "{ invalid json }");
|
|
80
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
81
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps }));
|
|
82
|
+
// We expect the parse error to have been caught:
|
|
83
|
+
expect(spy).toHaveBeenCalled();
|
|
84
|
+
spy.mockRestore();
|
|
85
|
+
});
|
|
86
|
+
it("updates active state when receiving a 'localStorageUpdate' event", async () => {
|
|
87
|
+
render(_jsx(HeaderFilterIcon, { ...defaultProps }));
|
|
88
|
+
const iconDiv = screen.getByTestId("magnifying-icon-test-id");
|
|
89
|
+
expect(iconDiv.className).not.toContain("bg-red-500");
|
|
90
|
+
// Set localStorage and dispatch the event
|
|
91
|
+
localStorage.setItem("searchCriteria", JSON.stringify([
|
|
92
|
+
{
|
|
93
|
+
searchColumn: { id: "exampleColumn" },
|
|
94
|
+
submittedSearchText: "some text",
|
|
95
|
+
},
|
|
96
|
+
]));
|
|
97
|
+
window.dispatchEvent(new Event("localStorageUpdate"));
|
|
98
|
+
// now wait for the re-render
|
|
99
|
+
await waitFor(() => {
|
|
100
|
+
expect(iconDiv.className).toContain("bg-red-500");
|
|
101
|
+
expect(iconDiv.className).toContain("still-active");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ColumnInstance } from "react-table";
|
|
2
|
+
export interface HeaderFilterIconProps<T extends object> {
|
|
3
|
+
columnIndex: number;
|
|
4
|
+
editingHeader: number | null;
|
|
5
|
+
setEditingHeader: (index: number | null) => void;
|
|
6
|
+
setHeaderValue: (value: string) => void;
|
|
7
|
+
column: ColumnInstance<T>;
|
|
8
|
+
setActiveColumn: (index: number) => void;
|
|
9
|
+
iconColor?: string;
|
|
10
|
+
iconBackgroundColor?: string;
|
|
11
|
+
iconActiveColor?: string;
|
|
12
|
+
iconActiveBackgroundColor?: string;
|
|
13
|
+
isActive?: boolean;
|
|
14
|
+
additionalIconClasses?: string;
|
|
15
|
+
setSearchText?: (text: string) => void;
|
|
16
|
+
searchText?: string;
|
|
17
|
+
isSearchOpen?: boolean;
|
|
18
|
+
icon?: {
|
|
19
|
+
icon: string;
|
|
20
|
+
weight: "regular" | "solid" | "light";
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -53,7 +53,7 @@ const InputField = forwardRef(({ label, placeholder, required = false, checked,
|
|
|
53
53
|
${firstIcon ? "pl-10" : ""}
|
|
54
54
|
${secondIcon ? "pr-10" : ""}
|
|
55
55
|
${additionalClasses}
|
|
56
|
-
`, children: readOnlyInfo }), iconPosition === "after" && renderSecondIcon()] })) : (_jsxs(_Fragment, { children: [label && (_jsxs("label", { htmlFor: id, className: ` block font-light text-left mb-1 ${labelClasses}`, children: [label, hasToolTip && (_jsxs("span", { className: "pl-4 group", children: [_jsx(FontAwesomeIcon, { icon: faCircleInfo, className: "text-primary group-hover:text-blue-600" }), _jsx("span", { className: "opacity-0 group-hover:opacity-100 bg-gray-800 text-white text-sm rounded-md px-2 py-1 absolute top-[-14px]", children: toolTipText })] }))] })), _jsxs("div", { className: " input-wrapper relative", children: [renderIcons(), _jsx("input", { autoComplete: "", ...registerProps, className: `
|
|
56
|
+
`, children: readOnlyInfo }), iconPosition === "after" && renderSecondIcon()] })) : (_jsxs(_Fragment, { children: [label && (_jsxs("label", { htmlFor: id, className: ` block font-light text-left mb-1 ${labelClasses}`, children: [label, hasToolTip && (_jsxs("span", { className: "pl-4 group", children: [_jsx(FontAwesomeIcon, { icon: faCircleInfo, className: "text-primary group-hover:text-blue-600" }), _jsx("span", { className: "opacity-0 group-hover:opacity-100 bg-gray-800 text-white text-sm rounded-md px-2 py-1 absolute top-[-14px]", children: toolTipText })] }))] })), _jsxs("div", { className: " input-wrapper relative", children: [renderIcons(), _jsx("input", { autoComplete: "", ...registerProps, className: `focused:ring-0 ${focusRingColor} focus:ring-1
|
|
57
57
|
focus:outline-none
|
|
58
58
|
${disabled
|
|
59
59
|
? "border-gray-500 focus:ring-gray-500"
|
|
@@ -62,8 +62,8 @@ const InputField = forwardRef(({ label, placeholder, required = false, checked,
|
|
|
62
62
|
: "border-redText focus:ring-red-500"} block w-full py-2 px-2
|
|
63
63
|
${firstIcon ? "pl-10" : ""}
|
|
64
64
|
${secondIcon ? "pr-10" : ""}
|
|
65
|
-
|
|
65
|
+
outline outline-0 outline-barelyPrimary focus:outline-4 md:w-full lg:w-full ${isFocused ? "hover:border-transparent" : ""} ${isFocused && !disabled
|
|
66
66
|
? ` ${isValid ? "" : "shadow-redText"}`
|
|
67
|
-
: disabled}`, autoFocus: hasAutoFocus, placeholder: placeholder, type: type, id: id, value: formattedValue, name: name, onChange: onChange, checked: type === "checkbox" ? checked : undefined, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), disabled: disabled, required: required && !hasValue, onKeyDown: onKeyDown }), iconPosition === "after" && renderSecondIcon()] })] }));
|
|
67
|
+
: disabled} ${additionalClasses}`, autoFocus: hasAutoFocus, placeholder: placeholder, type: type, id: id, value: formattedValue, name: name, onChange: onChange, checked: type === "checkbox" ? checked : undefined, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), disabled: disabled, required: required && !hasValue, onKeyDown: onKeyDown }), iconPosition === "after" && renderSecondIcon()] })] }));
|
|
68
68
|
});
|
|
69
69
|
export default InputField;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// In your NPM library @agilant/toga-blox/dist/components/InputAndCheck/InputAndCheck
|
|
3
|
-
// EXACTLY as you showed (with Badge, TableHeaderInput, etc.):
|
|
4
2
|
import { useEffect, useRef, useState } from "react";
|
|
5
3
|
import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
|
|
6
4
|
import Badge from "../Badge";
|
|
@@ -8,7 +6,6 @@ import TableHeaderInput from "../TableHeaderInput";
|
|
|
8
6
|
function InputAndCheck({ closeOutSearch, setResetSearch, setEditingHeader, column, searchCriteria, setSearchCriteria, badgeColor = "bg-blue-400", badgeIcon = { icon: "x", weight: "regular" }, cancelIcon = { icon: "xmark", weight: "regular" }, iconBadgeContainerClasses = "mr-2 rounded-full p-2 text-white cursor-pointer my-2 text-sm", searchIcon = { icon: "search", weight: "regular" }, searchIconClasses = " text-md absolute right-3 top-2 text-gray-400 hover:cursor-pointer hover:text-blue-500 ", additionalInputClasses = "min-w-[300px] max-w-[250px] border-b border-t-0 border-r-0 rounded-tl-none rounded-tr-lg bg-gray-50 text-gray-800 flex", secondIconClasses = "flex absolute right-6 top-0.5 items-center text-gray-400 hover:cursor-pointer hover:text-blue-400", initialIcon = { icon: "search", weight: "regular" }, initialIconClasses = "text-gray-400", }) {
|
|
9
7
|
const containerRef = useRef(null);
|
|
10
8
|
const [localSearchText, setLocalSearchText] = useState("");
|
|
11
|
-
// Load initial text from searchCriteria:
|
|
12
9
|
useEffect(() => {
|
|
13
10
|
const existing = searchCriteria.find((criterion) => criterion.searchColumn.id === column.id);
|
|
14
11
|
if (existing) {
|
|
@@ -18,19 +15,15 @@ function InputAndCheck({ closeOutSearch, setResetSearch, setEditingHeader, colum
|
|
|
18
15
|
setLocalSearchText("");
|
|
19
16
|
}
|
|
20
17
|
}, [searchCriteria, column.id]);
|
|
21
|
-
// Handle changes
|
|
22
18
|
const handleInputChange = (e) => {
|
|
23
19
|
setLocalSearchText(e.target.value);
|
|
24
20
|
};
|
|
25
|
-
// Submit
|
|
26
21
|
const handleSubmit = () => {
|
|
27
22
|
const trimmed = localSearchText.trim();
|
|
28
23
|
if (!trimmed) {
|
|
29
|
-
// remove criterion
|
|
30
24
|
setSearchCriteria((prev) => prev.filter((c) => c.searchColumn.id !== column.id));
|
|
31
25
|
}
|
|
32
26
|
else {
|
|
33
|
-
// add or update
|
|
34
27
|
setSearchCriteria((prev) => {
|
|
35
28
|
const existingIndex = prev.findIndex((c) => c.searchColumn.id === column.id);
|
|
36
29
|
if (existingIndex >= 0) {
|
|
@@ -51,14 +44,12 @@ function InputAndCheck({ closeOutSearch, setResetSearch, setEditingHeader, colum
|
|
|
51
44
|
}
|
|
52
45
|
setEditingHeader(null);
|
|
53
46
|
};
|
|
54
|
-
// Clear
|
|
55
47
|
const handleClearSearch = () => {
|
|
56
48
|
setSearchCriteria((prev) => prev.filter((c) => c.searchColumn.id !== column.id));
|
|
57
49
|
setLocalSearchText("");
|
|
58
50
|
setResetSearch((prev) => !prev);
|
|
59
51
|
closeOutSearch(null);
|
|
60
52
|
};
|
|
61
|
-
// Let user press Enter
|
|
62
53
|
const handleKeyDown = (e) => {
|
|
63
54
|
if (e.key === "Enter") {
|
|
64
55
|
e.preventDefault();
|
|
@@ -53,6 +53,6 @@ const MagnifyingIcon = forwardRef(({ columnIndex, editingHeader, setEditingHeade
|
|
|
53
53
|
};
|
|
54
54
|
return (_jsx("div", { ref: ref, "data-testid": "magnifying-icon-test-id", className: `flex items-center cursor-pointer size-[18px] rounded relative ml-1 ${iconColor} ${showActiveStyles
|
|
55
55
|
? `${iconActiveBackgroundColor} hover:bg-blue-500 still-active`
|
|
56
|
-
: "hover:bg-white"}`, onClick: handleClick, children: _jsx("div", { className: `text-xs w-full ${additionalIconClasses}
|
|
56
|
+
: "hover:bg-white"}`, onClick: handleClick, children: _jsx("div", { className: `text-xs w-full ${additionalIconClasses} `, children: getFontAwesomeIcon("magnifyingGlass", "solid") }) }));
|
|
57
57
|
});
|
|
58
58
|
export default MagnifyingIcon;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { createElement as _createElement } from "react";
|
|
3
3
|
import { useRef, useState, useMemo } from "react";
|
|
4
|
-
import
|
|
4
|
+
import HeaderFilterIcon from "../HeaderFilterIcon";
|
|
5
5
|
import TableHeaderContent from "../TableHeaderContent/TableHeaderContent";
|
|
6
6
|
import InputAndCheck from "../InputAndCheck/InputAndCheck";
|
|
7
7
|
import SortArrows from "../SortArrows";
|
|
@@ -66,7 +66,7 @@ const PrimaryTableHeader = ({ headerGroups, editingHeader, searchText, setSearch
|
|
|
66
66
|
})()] }) }), PortalComponent] })) : (_jsx(TableHeaderContent, { column: updatedColumn })), _jsx("div", { className: "ml-1", children: _jsx(SortArrows, { slug: mappingField || "", parentIndex: parentIndex, isNested: isNested, column: {
|
|
67
67
|
...updatedColumn,
|
|
68
68
|
accessor: updatedColumn.id,
|
|
69
|
-
}, setSortColumn: setSortColumn, onSortChange: handleSortChange }) }), shouldRenderMagnifyingIcon && (_jsx(
|
|
69
|
+
}, setSortColumn: setSortColumn, onSortChange: handleSortChange }) }), shouldRenderMagnifyingIcon && (_jsx(HeaderFilterIcon, { ref: magnifyingIconRef, column: updatedColumn, editingHeader: editingHeader, columnIndex: columnIndex, setEditingHeader: setEditingHeader, setHeaderValue: setHeaderValue, isActive: activeColumn === columnIndex, setActiveColumn: setActiveColumn, setSearchText: setSearchText, searchText: searchText, isSearchOpen: editingHeader !== null }))] })));
|
|
70
70
|
})))) }));
|
|
71
71
|
};
|
|
72
72
|
export default PrimaryTableHeader;
|
|
@@ -5,7 +5,7 @@ import "./SearchDrowpdown.scss";
|
|
|
5
5
|
* A wrapper around MultiSelectInput that appends a special "__footer__" option
|
|
6
6
|
* and uses a custom item renderer to display "Clear" and "Filter" buttons.
|
|
7
7
|
*/
|
|
8
|
-
const SearchDropdownInput = ({ options = [], selectedValue = [], onChange, placeholder = "Select
|
|
8
|
+
const SearchDropdownInput = ({ options = [], selectedValue = [], onChange, placeholder = "Select", disabled = false, hasSelectAll = true, bgColor, textHighlight, handleFilter, ...rest }) => {
|
|
9
9
|
const footerOption = [{ name: "", value: "__footer__" }];
|
|
10
10
|
const extendedOptions = [...options, ...footerOption];
|
|
11
11
|
const itemRenderer = ({ option, checked, disabled, onClick }) => {
|
|
@@ -16,8 +16,7 @@ const SearchDropdownInput = ({ options = [], selectedValue = [], onChange, place
|
|
|
16
16
|
onChange([]); // Clear all selections
|
|
17
17
|
}, children: "Clear" }), _jsx("button", { type: "button", className: `${bgColor} text-white px-3 py-1 rounded`, onClick: (e) => {
|
|
18
18
|
e.stopPropagation();
|
|
19
|
-
|
|
20
|
-
// ...your custom filter logic
|
|
19
|
+
handleFilter();
|
|
21
20
|
}, children: "Filter" })] }));
|
|
22
21
|
}
|
|
23
22
|
else if (option.label === "Select All") {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SearchInputProps } from "./SearchInput.types";
|
|
2
|
-
declare const SearchInput: <T extends object>({
|
|
2
|
+
declare const SearchInput: <T extends object>({ bgColor, textHighlight, inputType, dropdownIconProp, dropdownOptions, selectedDropdownOption, onDropdownOptionSelect, searchItems, setSearchItems, toggleStatus, setToggleStatus, minValue, setMinValue, maxValue, setMaxValue, onChange, selectedValue, selectedDate, onDateSelect, selectedStartDate, onStartDateSelect, selectedEndDate, onEndDateSelect, handleFilter, }: SearchInputProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export default SearchInput;
|
|
@@ -1,71 +1,29 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { useEffect, useRef
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
import SearchTextInput from "./SearchTextInput";
|
|
4
4
|
import SearchNumberInput from "./SearchNumberInput";
|
|
5
|
-
import { Controller } from "react-hook-form";
|
|
6
5
|
import SearchDropdownInput from "./SearchDropdownInput";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (typeof dynamicDefaultValue !== "undefined") {
|
|
10
|
-
return dynamicDefaultValue;
|
|
11
|
-
}
|
|
12
|
-
return "";
|
|
13
|
-
}
|
|
14
|
-
const SearchInput = ({ closeOutSearch, setResetSearch, setEditingHeader, bgColor = "bg-sky-500", textHighlight = "text-sky-500", column, inputType = "text", dropdownIconProp = {
|
|
6
|
+
import SearchDatePickerInput from "./SearchInputDatePicker";
|
|
7
|
+
const SearchInput = ({ bgColor = "bg-sky-500", textHighlight = "text-sky-500", inputType = "text", dropdownIconProp = {
|
|
15
8
|
name: "chevronDown",
|
|
16
9
|
weight: "bold",
|
|
17
10
|
iconClasses: "text-black",
|
|
18
|
-
}, dropdownOptions = [], selectedDropdownOption = "", onDropdownOptionSelect, searchItems = [], setSearchItems, toggleStatus = false, setToggleStatus, minValue, setMinValue, maxValue, setMaxValue,
|
|
11
|
+
}, dropdownOptions = [], selectedDropdownOption = "", onDropdownOptionSelect, searchItems = [], setSearchItems, toggleStatus = false, setToggleStatus, minValue, setMinValue, maxValue, setMaxValue, onChange, selectedValue, selectedDate, onDateSelect, selectedStartDate, onStartDateSelect, selectedEndDate, onEndDateSelect, handleFilter, }) => {
|
|
19
12
|
const containerRef = useRef(null);
|
|
20
|
-
const [localSearchText, setLocalSearchText] = useState("");
|
|
21
13
|
const inputRef = useRef(null);
|
|
22
14
|
useEffect(() => {
|
|
23
15
|
inputRef.current?.focus();
|
|
24
16
|
}, []);
|
|
25
|
-
const handleInputChange = (event) => {
|
|
26
|
-
setLocalSearchText(event.target.value);
|
|
27
|
-
};
|
|
28
|
-
const handleSubmitClick = () => {
|
|
29
|
-
const trimmedSearchText = localSearchText.trim();
|
|
30
|
-
let safeHeader;
|
|
31
|
-
if (typeof column.Header === "string") {
|
|
32
|
-
safeHeader = column.Header;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
safeHeader = column.accessor || "Unnamed Column";
|
|
36
|
-
}
|
|
37
|
-
if (trimmedSearchText === "") {
|
|
38
|
-
// Remove criterion if text is empty
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
// Update or add criterion
|
|
42
|
-
}
|
|
43
|
-
setEditingHeader(null);
|
|
44
|
-
};
|
|
45
17
|
return (_jsx("div", { ref: containerRef, className: "", children: (() => {
|
|
46
18
|
switch (inputType) {
|
|
47
19
|
case "text":
|
|
48
|
-
return (_jsx(SearchTextInput, {
|
|
20
|
+
return (_jsx(SearchTextInput, { dropdownIconProp: dropdownIconProp, dropdownOptions: dropdownOptions, selectedDropdownOption: selectedDropdownOption, onDropdownOptionSelect: onDropdownOptionSelect, searchItems: searchItems, setSearchItems: setSearchItems, handleFilter: handleFilter }));
|
|
49
21
|
case "number":
|
|
50
|
-
return (_jsx(SearchNumberInput, {
|
|
22
|
+
return (_jsx(SearchNumberInput, { dropdownIconProp: dropdownIconProp, dropdownOptions: dropdownOptions, selectedDropdownOption: selectedDropdownOption, onDropdownOptionSelect: onDropdownOptionSelect, toggleStatus: toggleStatus, setToggleStatus: setToggleStatus, minValue: minValue, maxValue: maxValue, setMinValue: setMinValue, setMaxValue: setMaxValue, handleFilter: handleFilter }));
|
|
51
23
|
case "multiSelect":
|
|
52
|
-
{
|
|
53
|
-
console.log(dropdownOptions, "dropdownOptions");
|
|
54
|
-
}
|
|
55
|
-
return (_jsx(SearchDropdownInput, { options: dropdownOptions, isSearchable: true, placeholder: "Search", isMulti: true, hideSelectedOptions: true, closeMenuOnSelect: false, inputWidth: "w-full", inputTextSize: "text-sm", additionalClasses: "", customClassNames: {}, onChange: onChange, value: [], selectedValue: selectedValue, bgColor: bgColor, textHighlight: textHighlight }));
|
|
56
|
-
case "boolean":
|
|
24
|
+
return (_jsx(SearchDropdownInput, { options: dropdownOptions, isSearchable: true, placeholder: "Search", isMulti: true, hideSelectedOptions: true, closeMenuOnSelect: false, inputWidth: "w-full", inputTextSize: "text-sm", additionalClasses: "", customClassNames: {}, onChange: onChange, value: [], selectedValue: selectedValue, bgColor: bgColor, textHighlight: textHighlight, handleFilter: handleFilter }));
|
|
57
25
|
case "date":
|
|
58
|
-
|
|
59
|
-
if (!control || !valueKey) {
|
|
60
|
-
return (_jsxs("div", { className: "text-red-500", children: ["Missing ", _jsx("code", { children: "control" }), " or", " ", _jsx("code", { children: "valueKey" }), " prop for React Hook Form"] }));
|
|
61
|
-
}
|
|
62
|
-
return (_jsx(Controller, { name: valueKey, control: control, defaultValue: getDefaultValue(dynamicDefaultValue, valueKey), render: ({ field }) => (_jsx("div", { className: "inline-flex items-center", children: _jsxs("label", { className: "flex items-center cursor-pointer relative", children: [_jsx("input", { type: "checkbox", className: "peer h-5 w-5 cursor-pointer transition-all appearance-none rounded border border-stroke", id: valueKey, checked: !!field.value, onChange: (e) => {
|
|
63
|
-
// You can log the value here:
|
|
64
|
-
console.log("Checkbox changed to:", e.target.checked);
|
|
65
|
-
field.onChange(e.target.checked);
|
|
66
|
-
} }), _jsx("span", { className: "absolute text-white opacity-0 peer-checked:opacity-100 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3.5 w-3.5", viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: _jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })] }) })) }));
|
|
67
|
-
default:
|
|
68
|
-
return null;
|
|
26
|
+
return (_jsx(SearchDatePickerInput, { textHighlight: textHighlight, dropdownOptions: dropdownOptions, selectedDropdownOption: selectedDropdownOption, onDropdownOptionSelect: onDropdownOptionSelect, toggleStatus: toggleStatus, setToggleStatus: setToggleStatus, selectedDate: selectedDate, onDateSelect: onDateSelect, selectedStartDate: selectedStartDate, onStartDateSelect: onStartDateSelect, selectedEndDate: selectedEndDate, onEndDateSelect: onEndDateSelect, handleFilter: handleFilter }));
|
|
69
27
|
}
|
|
70
28
|
})() }));
|
|
71
29
|
};
|