@agilant/toga-blox 1.0.48 → 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/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/PrimaryTableHeader/PrimaryTableHeader.js +2 -2
- package/dist/components/SearchInput/SearchNumberInput.js +3 -3
- package/dist/components/SearchInput/SearchTextInput.js +4 -4
- package/package.json +1 -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;
|
|
@@ -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();
|
|
@@ -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;
|
|
@@ -30,11 +30,11 @@ const SearchNumberInput = ({ textHighlight = "text-sky-500", dropdownIconProp =
|
|
|
30
30
|
// };
|
|
31
31
|
return (_jsx("div", { ref: containerRef, className: "w-[425px]", children: _jsxs("div", { className: `flex flex-col p-4 h-[130px] border-2 border-navy-200 rounded-md`, children: [_jsx("div", { className: `flex flex-[1] ${toggleStatus ? "" : "border-2"} h-full max-h-11 items-center justify-around`, children: toggleStatus ? (_jsxs(_Fragment, { children: [_jsx(Input, { focusRingColor: "focus:ring-2", hasAutoFocus: true, value: minValue, iconColor: "text-navy-400",
|
|
32
32
|
// onKeyDown={handleKeyDown}
|
|
33
|
-
required: false, id: "", name: "", type: "number", onChange: handleInputChange, additionalClasses: "min-w-[180px] max-w-[180px] h-10 text-gray flex
|
|
33
|
+
required: false, id: "", name: "", type: "number", onChange: handleInputChange, additionalClasses: "min-w-[180px] max-w-[180px] h-10 text-gray flex focus:border-l-2 ", placeholder: "Min" }), _jsx(Text, { size: "text-md", tag: "span", text: "to", additionalClasses: "px-2" }), _jsx(Input, { focusRingColor: "focus:ring-2", value: maxValue, iconColor: "text-navy-400",
|
|
34
34
|
// onKeyDown={handleKeyDown}
|
|
35
|
-
required: false, id: "", name: "", type: "number", onChange: handleMaximumInputChange, additionalClasses: "min-w-[180px] max-w-[180px] h-10 text-gray flex border-2 focus:border-
|
|
35
|
+
required: false, id: "", name: "", type: "number", onChange: handleMaximumInputChange, additionalClasses: "min-w-[180px] max-w-[180px] h-10 text-gray flex border-2 focus:border-l-2 focus:border- ", placeholder: "Max", hasIcons: true, iconPosition: "both" })] })) : (_jsxs(_Fragment, { children: [_jsx(Dropdown, { options: dropdownOptions, selectedOption: selectedDropdownOption, onOptionSelect: onDropdownOptionSelect, optionClasses: "px-4 h-full flex items-center", menuClasses: "bg-white w-min-[150px] top-[-8px] left-[-2px]", dropdownClasses: "border-0 w-auto", icon: dropdownIconProp }), _jsx(Input, { focusRingColor: "focus:ring-transparent", hasAutoFocus: true, value: minValue, iconColor: "text-navy-400",
|
|
36
36
|
// onKeyDown={handleKeyDown}
|
|
37
|
-
required: false, id: "", name: "", type: "number", onChange: handleInputChange, additionalClasses: "min-w-[200px] h-10 text-gray flex border-l-2 ", placeholder: "Amount", hasIcons: true, iconPosition: "both" })] })) }), _jsxs("div", { className: "flex flex-[1] justify-between items-end bg-white px-2
|
|
37
|
+
required: false, id: "", name: "", type: "number", onChange: handleInputChange, additionalClasses: "min-w-[200px] h-10 text-gray flex border-l-2 ", placeholder: "Amount", hasIcons: true, iconPosition: "both" })] })) }), _jsxs("div", { className: "flex flex-[1] justify-between items-end bg-white px-2 ", children: [_jsx(ToggleButton, { initialStatus: toggleStatus, onClick: () => {
|
|
38
38
|
setToggleStatus(!toggleStatus);
|
|
39
39
|
}, activeColorBackground: "bg-sky-500", activeColorBorder: "border-sky-500", activeLabel: "Range", activeTextColor: "text-sky-500", additionalClasses: "flex items-center", inactiveColorBackground: "bg-gray-300", inactiveColorBorder: "border-gray-300", inactiveLabel: "Range", inactiveTextColor: "text-gray-500", pillHeight: "h-8", textPosition: "right", textSize: "text-sm", smallToggle: false, borderStyle: false }), _jsx(BaseButton, { text: "Filter", backgroundColor: "bg-sky-500", additionalClasses: "py-1.5 px-6 text-white", borderColor: "border-none", onClick: handleFilter, shape: "rounded-full" })] })] }) }));
|
|
40
40
|
};
|
|
@@ -27,20 +27,20 @@ const SearchTextInput = ({ pillColor = "bg-sky-500", textHighlight = "text-sky-5
|
|
|
27
27
|
setSearchItems(filteredItems);
|
|
28
28
|
};
|
|
29
29
|
const handleKeyDown = (e) => {
|
|
30
|
-
if (e.key === "Enter") {
|
|
30
|
+
if (e.key === "Enter" && localSearchText.length > 0) {
|
|
31
31
|
e.preventDefault();
|
|
32
32
|
setSearchItems([...searchItems, localSearchText]);
|
|
33
33
|
setLocalSearchText("");
|
|
34
34
|
handleFilter();
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
const pillClassnames = `${pillColor} p-1 max-w-fit min-w-20 rounded-full flex justify-between items-center text-white text-xs px-4 border-none mr-4`;
|
|
37
|
+
const pillClassnames = `${pillColor} p-1 max-w-fit min-w-20 rounded-full flex justify-between items-center text-white text-xs px-4 border-none mr-4 mb-1`;
|
|
38
38
|
return (_jsx("div", { ref: containerRef, className: "w-[425px]", children: _jsxs("div", { className: "flex flex-col border-2 border-navy-200 rounded-md", children: [_jsxs("div", { className: `flex ${searchItems.length ? "border-b-2" : ""} h-full`, children: [_jsx(Dropdown, { options: dropdownOptions, selectedOption: selectedDropdownOption, onOptionSelect: onDropdownOptionSelect, optionClasses: "px-4 py-1 h-full flex items-center", menuClasses: "bg-white min-w-32", icon: dropdownIconProp, dropdownClasses: "border-0 border-r-2 w-auto" }), _jsx(Input, { focusRingColor: "focus:ring-transparent", hasAutoFocus: true, value: localSearchText, iconColor: "text-navy-400", onKeyDown: handleKeyDown, required: false, id: "", name: "", type: "text", onChange: handleInputChange, additionalClasses: " min-w-[250px] min-h-full text-gray flex", placeholder: "Search", hasIcons: true, firstIcon: localSearchText === "" ? (_jsx(AnimatePresence, { children: _jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", className: "text-navy-400", children: getFontAwesomeIcon("search", "regular") }) })) : undefined, iconPosition: "both", secondIcon: _jsx("div", { className: "border-transparent text-white min-w-9", children: _jsx(AnimatePresence, { children: localSearchText !== "" && (_jsxs(motion.div, { className: "flex justify-between items-center min-w-4 text-navy-400 hover:cursor-pointer hover:text-primary", initial: "initial", animate: "animate", exit: "exit", children: [_jsx("div", { className: "bg-navy-50 pr-2 pl-1 text-gray-500", onClick: () => {
|
|
39
39
|
setLocalSearchText("");
|
|
40
40
|
}, "data-testid": "clear-icon", children: getFontAwesomeIcon("xmark", "regular") }), _jsx("div", { className: `${textHighlight} text-sm hover:text-primary`, children: getFontAwesomeIcon("search", "solid") })] })) }) }), onIconClick: () => {
|
|
41
41
|
setLocalSearchText("");
|
|
42
|
-
} })] }), searchItems?.length ? (_jsx("div", { className: " flex bg-white py-2 px-2 rounded-md ", children: searchItems?.map((item, index) => {
|
|
43
|
-
return (_jsx(Badge, { backgroundColor: pillColor, borderRadius: "rounded-full",
|
|
42
|
+
} })] }), searchItems?.length ? (_jsx("div", { className: " flex flex-wrap bg-white py-2 px-2 rounded-md ", children: searchItems?.map((item, index) => {
|
|
43
|
+
return (_jsx(Badge, { backgroundColor: pillColor, borderRadius: "rounded-full", hasRightIcon: true, icon: _jsx("div", { className: "text-white text-xxs", "data-testid": "item-clear-icon", children: getFontAwesomeIcon("xmark", "solid") }), iconSize: "text-sm", mobileIconLabel: item, onClick: () => handleIconClick(item), text: _jsx(Text, { color: "text-white", fontFamily: "font-serif", size: "text-sm", tag: "span", text: item }), badgeContainerClasses: pillClassnames, type: "span" }));
|
|
44
44
|
}) })) : null] }) }));
|
|
45
45
|
};
|
|
46
46
|
export default SearchTextInput;
|