@agilant/toga-blox 1.0.32 → 1.0.33
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/Dropdown/Dropdown.d.ts +4 -0
- package/dist/components/Dropdown/Dropdown.js +20 -0
- package/dist/components/Dropdown/Dropdown.stories.d.ts +8 -0
- package/dist/components/Dropdown/Dropdown.stories.js +110 -0
- package/dist/components/Dropdown/Dropdown.test.d.ts +1 -0
- package/dist/components/Dropdown/Dropdown.test.js +43 -0
- package/dist/components/Dropdown/Dropdown.types.d.ts +15 -0
- package/dist/components/Dropdown/Dropdown.types.js +1 -0
- package/dist/components/GenericList/GenericList.d.ts +2 -15
- package/dist/components/GenericList/GenericList.js +64 -51
- package/dist/components/GenericList/GenericList.stories.d.ts +8 -35
- package/dist/components/GenericList/GenericList.stories.js +46 -78
- package/dist/components/GenericList/GenericList.test.d.ts +1 -1
- package/dist/components/GenericList/GenericList.test.js +112 -22
- package/dist/components/GenericList/index.d.ts +2 -0
- package/dist/components/GenericList/index.js +2 -0
- package/dist/components/GenericList/types.d.ts +16 -0
- package/dist/components/GenericList/types.js +1 -0
- package/dist/components/Header/Header.stories.js +2 -4
- package/dist/components/Input/Input.d.ts +30 -3
- package/dist/components/Input/Input.js +70 -48
- package/dist/components/Input/Input.stories.js +3 -4
- package/dist/components/Input/Input.test.js +74 -42
- package/dist/components/InputAndCheck/InputAndCheck.d.ts +47 -0
- package/dist/components/InputAndCheck/InputAndCheck.js +74 -0
- package/dist/components/InputAndCheck/InputAndCheck.stories.d.ts +9 -0
- package/dist/components/InputAndCheck/InputAndCheck.stories.js +201 -0
- package/dist/components/InputAndCheck/InputAndCheck.test.d.ts +1 -0
- package/dist/components/InputAndCheck/InputAndCheck.test.js +307 -0
- package/dist/components/InputAndCheck/index.d.ts +0 -0
- package/dist/components/InputAndCheck/index.js +0 -0
- package/dist/components/InputAndCheck/types.d.ts +35 -0
- package/dist/components/InputAndCheck/types.js +1 -0
- package/dist/components/MagnifyingIcon/MagnifyingIcon.d.ts +4 -0
- package/dist/components/MagnifyingIcon/MagnifyingIcon.js +60 -0
- package/dist/components/MagnifyingIcon/MagnifyingIcon.stories.d.ts +9 -0
- package/dist/components/MagnifyingIcon/MagnifyingIcon.stories.js +72 -0
- package/dist/components/MagnifyingIcon/MagnifyingIcon.test.d.ts +1 -0
- package/dist/components/MagnifyingIcon/MagnifyingIcon.test.js +101 -0
- package/dist/components/MagnifyingIcon/index.d.ts +2 -0
- package/dist/components/MagnifyingIcon/index.js +2 -0
- package/dist/components/MagnifyingIcon/types.d.ts +20 -0
- package/dist/components/MagnifyingIcon/types.js +2 -0
- package/dist/components/MultiSelect/MultiSelect.d.ts +4 -0
- package/dist/components/MultiSelect/MultiSelect.js +30 -0
- package/dist/components/MultiSelect/MultiSelect.stories.d.ts +10 -0
- package/dist/components/MultiSelect/MultiSelect.stories.js +162 -0
- package/dist/components/MultiSelect/MultiSelect.test.d.ts +1 -0
- package/dist/components/MultiSelect/MultiSelect.test.js +107 -0
- package/dist/components/MultiSelect/MultiSelect.types.d.ts +28 -0
- package/dist/components/MultiSelect/MultiSelect.types.js +1 -0
- package/dist/components/Page/ViewPageTemplate.stories.js +2 -3
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.d.ts +3 -0
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.js +72 -0
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.stories.d.ts +4 -0
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.stories.js +99 -0
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.test.d.ts +1 -0
- package/dist/components/PrimaryTableHeader/PrimaryTableHeader.test.js +124 -0
- package/dist/components/PrimaryTableHeader/index.d.ts +0 -0
- package/dist/components/PrimaryTableHeader/index.js +0 -0
- package/dist/components/PrimaryTableHeader/types.d.ts +35 -0
- package/dist/components/PrimaryTableHeader/types.js +2 -0
- package/dist/components/SearchInput/SearchInput.d.ts +1 -2
- package/dist/components/SearchInput/SearchInput.js +61 -11
- package/dist/components/SearchInput/SearchInput.stories.d.ts +2 -4
- package/dist/components/SearchInput/SearchInput.stories.js +80 -93
- package/dist/components/SearchInput/SearchInput.types.d.ts +37 -24
- package/dist/components/SearchInput/SearchNumberInput.d.ts +31 -0
- package/dist/components/SearchInput/SearchNumberInput.js +60 -0
- package/dist/components/SearchInput/SearchTextInput.d.ts +24 -0
- package/dist/components/SearchInput/SearchTextInput.js +65 -0
- package/dist/components/SortArrowIcon/SortArrowIcon.d.ts +4 -0
- package/dist/components/SortArrowIcon/SortArrowIcon.js +12 -0
- package/dist/components/SortArrowIcon/SortArrowIcon.stories.d.ts +17 -0
- package/dist/components/SortArrowIcon/SortArrowIcon.stories.js +77 -0
- package/dist/components/SortArrowIcon/SortArrowIcon.test.d.ts +1 -0
- package/dist/components/SortArrowIcon/SortArrowIcon.test.js +44 -0
- package/dist/components/SortArrowIcon/index.d.ts +2 -0
- package/dist/components/SortArrowIcon/index.js +2 -0
- package/dist/components/SortArrowIcon/types.d.ts +15 -0
- package/dist/components/SortArrowIcon/types.js +1 -0
- package/dist/components/SortArrows/SortArrows.d.ts +3 -0
- package/dist/components/SortArrows/SortArrows.js +33 -0
- package/dist/components/SortArrows/SortArrows.stories.d.ts +7 -0
- package/dist/components/SortArrows/SortArrows.stories.js +41 -0
- package/dist/components/SortArrows/SortArrows.test.d.ts +1 -0
- package/dist/components/SortArrows/SortArrows.test.js +150 -0
- package/dist/components/SortArrows/index.d.ts +2 -0
- package/dist/components/SortArrows/index.js +2 -0
- package/dist/components/SortArrows/types.d.ts +21 -0
- package/dist/components/SortArrows/types.js +1 -0
- package/dist/components/SortArrows/useSortArrowsViewModel.d.ts +30 -0
- package/dist/components/SortArrows/useSortArrowsViewModel.js +114 -0
- package/dist/components/SortArrows/useSortArrowsViewModel.test.d.ts +1 -0
- package/dist/components/SortArrows/useSortArrowsViewModel.test.js +100 -0
- package/dist/components/TableCell/TableCell.d.ts +3 -0
- package/dist/components/TableCell/TableCell.js +13 -0
- package/dist/components/TableCell/TableCell.stories.d.ts +16 -0
- package/dist/components/TableCell/TableCell.stories.js +99 -0
- package/dist/components/TableCell/TableCell.test.d.ts +1 -0
- package/dist/components/TableCell/TableCell.test.js +84 -0
- package/dist/components/TableCell/index.d.ts +2 -0
- package/dist/components/TableCell/index.js +2 -0
- package/dist/components/TableCell/types.d.ts +12 -0
- package/dist/components/TableCell/types.js +1 -0
- package/dist/components/TableHeaderContent/TableHeaderContent.d.ts +3 -0
- package/dist/components/TableHeaderContent/TableHeaderContent.js +5 -0
- package/dist/components/TableHeaderContent/TableHeaderContent.stories.d.ts +6 -0
- package/dist/components/TableHeaderContent/TableHeaderContent.stories.js +62 -0
- package/dist/components/TableHeaderContent/TableHeaderContent.test.d.ts +1 -0
- package/dist/components/TableHeaderContent/TableHeaderContent.test.js +41 -0
- package/dist/components/TableHeaderContent/index.d.ts +0 -0
- package/dist/components/TableHeaderContent/index.js +0 -0
- package/dist/components/TableHeaderContent/types.d.ts +5 -0
- package/dist/components/TableHeaderContent/types.js +1 -0
- package/dist/components/TableHeaderInput/TableHeaderInput.d.ts +3 -0
- package/dist/components/TableHeaderInput/TableHeaderInput.js +80 -0
- package/dist/components/TableHeaderInput/TableHeaderInput.stories.d.ts +10 -0
- package/dist/components/TableHeaderInput/TableHeaderInput.stories.js +82 -0
- package/dist/components/TableHeaderInput/TableHeaderInput.test.d.ts +1 -0
- package/dist/components/TableHeaderInput/TableHeaderInput.test.js +84 -0
- package/dist/components/TableHeaderInput/index.d.ts +1 -0
- package/dist/components/TableHeaderInput/index.js +1 -0
- package/dist/components/TableHeaderInput/types.d.ts +30 -0
- package/dist/components/TableHeaderInput/types.js +1 -0
- package/dist/components/TableRow/TableRow.d.ts +15 -0
- package/dist/components/TableRow/TableRow.js +21 -0
- package/dist/components/TableRow/TableRow.stories.d.ts +9 -0
- package/dist/components/TableRow/TableRow.stories.js +195 -0
- package/dist/components/TableRow/TableRow.test.d.ts +1 -0
- package/dist/components/TableRow/TableRow.test.js +44 -0
- package/dist/components/TableRow/index.d.ts +2 -0
- package/dist/components/TableRow/index.js +2 -0
- package/dist/components/TableRow/types.d.ts +11 -0
- package/dist/components/TableRow/types.js +1 -0
- package/dist/components/ToggleButton/ToggleButton.d.ts +4 -0
- package/dist/components/ToggleButton/ToggleButton.js +41 -0
- package/dist/components/ToggleButton/ToggleButton.stories.d.ts +11 -0
- package/dist/components/ToggleButton/ToggleButton.stories.js +111 -0
- package/dist/components/ToggleButton/ToggleButton.test.d.ts +1 -0
- package/dist/components/ToggleButton/ToggleButton.test.js +106 -0
- package/dist/components/ToggleButton/ToggleButton.types.d.ts +22 -0
- package/dist/components/ToggleButton/ToggleButton.types.js +1 -0
- package/package.json +10 -3
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
4
|
+
import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
|
|
5
|
+
const Dropdown = ({ options, selectedOption, onOptionSelect, optionClasses = "flex items-center cursor-pointer px-4 py-1 text-sm text-white transition duration-200", menuClasses = "absolute top-10 right-0 bg-white shadow-md rounded-lg w-40 z-10", dropdownClasses = "", icon = {
|
|
6
|
+
name: "chevronDown",
|
|
7
|
+
weight: "solid",
|
|
8
|
+
iconClasses: "text-navy-400",
|
|
9
|
+
}, selectedOptionBgColor = "bg-gray-50", optionHoverBgColor = "hover:bg-gray-50", }) => {
|
|
10
|
+
const [showMenu, setShowMenu] = useState(false);
|
|
11
|
+
const toggleMenu = () => setShowMenu(!showMenu);
|
|
12
|
+
return (_jsxs("div", { className: `flex items-center justify-between relative min-w-32 border-2 ${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"}`, children: _jsx("span", { children: getFontAwesomeIcon(icon.name, icon.weight) }) })] }), 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
|
+
? `{${selectedOptionBgColor} font-semibold}`
|
|
14
|
+
: `${optionHoverBgColor} text-black`}`, onClick: () => {
|
|
15
|
+
onOptionSelect(action);
|
|
16
|
+
setShowMenu(false); // Close the menu
|
|
17
|
+
}, children: [selectedOption === action &&
|
|
18
|
+
getFontAwesomeIcon("check", "solid"), _jsx("span", { className: "pl-2", children: action })] }, action))) }) })) }))] }));
|
|
19
|
+
};
|
|
20
|
+
export default Dropdown;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Dropdown from "./Dropdown";
|
|
2
|
+
import { Meta } from "@storybook/react";
|
|
3
|
+
declare const _default: Meta<typeof Dropdown>;
|
|
4
|
+
export default _default;
|
|
5
|
+
export declare const Default: any;
|
|
6
|
+
export declare const GreenTheme: any;
|
|
7
|
+
export declare const CustomIcon: any;
|
|
8
|
+
export declare const LargeDropdown: any;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import Dropdown from "./Dropdown";
|
|
4
|
+
export default {
|
|
5
|
+
title: "Components/Dropdown",
|
|
6
|
+
component: Dropdown,
|
|
7
|
+
argTypes: {
|
|
8
|
+
options: {
|
|
9
|
+
control: "array",
|
|
10
|
+
description: "List of options available in the dropdown.",
|
|
11
|
+
},
|
|
12
|
+
selectedOption: {
|
|
13
|
+
control: "text",
|
|
14
|
+
description: "Currently selected option.",
|
|
15
|
+
},
|
|
16
|
+
onOptionSelect: {
|
|
17
|
+
action: "selected",
|
|
18
|
+
description: "Callback function triggered when an option is selected.",
|
|
19
|
+
},
|
|
20
|
+
optionClasses: {
|
|
21
|
+
control: "text",
|
|
22
|
+
description: "Tailwind classes for dropdown options.",
|
|
23
|
+
},
|
|
24
|
+
menuClasses: {
|
|
25
|
+
control: "text",
|
|
26
|
+
description: "Tailwind classes for the dropdown menu container.",
|
|
27
|
+
},
|
|
28
|
+
dropdownClasses: {
|
|
29
|
+
control: "text",
|
|
30
|
+
description: "Tailwind classes for the dropdown container.",
|
|
31
|
+
},
|
|
32
|
+
icon: {
|
|
33
|
+
control: "object",
|
|
34
|
+
description: "name - FontAwesome icon name. weight - FontAwesome icon style (solid, regular, etc.). iconClasses - Tailwind classes for the dropdown icon.",
|
|
35
|
+
},
|
|
36
|
+
selectedOptionBgColor: {
|
|
37
|
+
control: "text",
|
|
38
|
+
description: "Tailwind classes for the dropdown menu option background color.",
|
|
39
|
+
},
|
|
40
|
+
optionHoverBgColor: {
|
|
41
|
+
control: "text",
|
|
42
|
+
description: "Tailwind classes for the dropdown menu option hover background color.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
tags: ["autodocs"],
|
|
46
|
+
parameters: {
|
|
47
|
+
layout: "centered",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const Template = (args) => {
|
|
51
|
+
const [selectedOption, setSelectedOption] = useState(args.selectedOption);
|
|
52
|
+
return (_jsx(Dropdown, { ...args, selectedOption: selectedOption, onOptionSelect: (option) => setSelectedOption(option) }));
|
|
53
|
+
};
|
|
54
|
+
export const Default = Template.bind({});
|
|
55
|
+
Default.args = {
|
|
56
|
+
options: ["Option 1", "Option 2", "Option 3"],
|
|
57
|
+
selectedOption: "Option 1",
|
|
58
|
+
optionClasses: "flex items-center cursor-pointer px-4 py-1 text-md text-black transition duration-200",
|
|
59
|
+
menuClasses: "absolute top-10 right-0 bg-white shadow-md rounded-lg w-40 z-10",
|
|
60
|
+
icon: {
|
|
61
|
+
name: "chevronDown",
|
|
62
|
+
weight: "solid",
|
|
63
|
+
iconClasses: "text-navy-400",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
export const GreenTheme = Template.bind({});
|
|
67
|
+
GreenTheme.args = {
|
|
68
|
+
options: ["Green Option 1", "Green Option 2", "Green Option 3"],
|
|
69
|
+
selectedOption: "Green Option 1",
|
|
70
|
+
optionClasses: "flex items-center cursor-pointer px-4 py-1 text-md text-white transition duration-200 bg-green-500 hover:bg-green-600",
|
|
71
|
+
menuClasses: "absolute top-10 right-0 bg-green-200 shadow-md rounded-lg w-44 z-10",
|
|
72
|
+
icon: {
|
|
73
|
+
name: "chevronDown",
|
|
74
|
+
weight: "solid",
|
|
75
|
+
iconClasses: "text-green-800 text-white",
|
|
76
|
+
},
|
|
77
|
+
dropdownClasses: "bg-green-700 border-none",
|
|
78
|
+
};
|
|
79
|
+
export const CustomIcon = Template.bind({});
|
|
80
|
+
CustomIcon.args = {
|
|
81
|
+
options: ["Custom Option 1", "Custom Option 2", "Custom Option 3"],
|
|
82
|
+
selectedOption: "Custom Option 1",
|
|
83
|
+
optionClasses: "flex items-center cursor-pointer px-4 py-1 text-md text-black transition duration-200 hover:bg-red-200",
|
|
84
|
+
menuClasses: "absolute top-10 right-0 shadow-md rounded-lg w-40 z-10",
|
|
85
|
+
dropdownClasses: "text-black border-2 border-red-500",
|
|
86
|
+
icon: {
|
|
87
|
+
name: "arrowDown",
|
|
88
|
+
weight: "regular",
|
|
89
|
+
iconClasses: "text-red-700 hover:bg-red-200",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
export const LargeDropdown = Template.bind({});
|
|
93
|
+
LargeDropdown.args = {
|
|
94
|
+
options: [
|
|
95
|
+
"Large Option 1",
|
|
96
|
+
"Large Option 2",
|
|
97
|
+
"Large Option 3",
|
|
98
|
+
"Large Option 4",
|
|
99
|
+
"Large Option 5",
|
|
100
|
+
],
|
|
101
|
+
selectedOption: "Large Option 1",
|
|
102
|
+
optionClasses: "flex items-center cursor-pointer px-4 py-3 text-lg text-black transition duration-200 ",
|
|
103
|
+
menuClasses: "absolute top-10 right-0 shadow-md rounded-lg w-56 z-10",
|
|
104
|
+
icon: {
|
|
105
|
+
name: "chevronDown",
|
|
106
|
+
weight: "solid",
|
|
107
|
+
iconClasses: "text-black",
|
|
108
|
+
},
|
|
109
|
+
dropdownClasses: "",
|
|
110
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, test, vi } from "vitest";
|
|
4
|
+
import Dropdown from "./Dropdown";
|
|
5
|
+
const mockOnOptionSelect = vi.fn();
|
|
6
|
+
const options = ["Option 1", "Option 2", "Option 3"];
|
|
7
|
+
describe("Dropdown Component", () => {
|
|
8
|
+
test("renders the dropdown with the selected option", () => {
|
|
9
|
+
render(_jsx(Dropdown, { options: options, selectedOption: options[0], onOptionSelect: mockOnOptionSelect }));
|
|
10
|
+
expect(screen.getByText("Option 1")).toBeInTheDocument();
|
|
11
|
+
});
|
|
12
|
+
test("opens the dropdown menu when clicked", () => {
|
|
13
|
+
render(_jsx(Dropdown, { options: options, selectedOption: options[0], onOptionSelect: mockOnOptionSelect }));
|
|
14
|
+
const dropdownButton = screen.getByText("Option 1");
|
|
15
|
+
fireEvent.click(dropdownButton);
|
|
16
|
+
expect(screen.getByText("Option 2")).toBeInTheDocument();
|
|
17
|
+
expect(screen.getByText("Option 3")).toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
test("selects an option and calls onOptionSelect", () => {
|
|
20
|
+
render(_jsx(Dropdown, { options: options, selectedOption: options[0], onOptionSelect: mockOnOptionSelect }));
|
|
21
|
+
const dropdownButton = screen.getByText("Option 1");
|
|
22
|
+
fireEvent.click(dropdownButton);
|
|
23
|
+
const optionToSelect = screen.getByText("Option 2");
|
|
24
|
+
fireEvent.click(optionToSelect);
|
|
25
|
+
expect(mockOnOptionSelect).toHaveBeenCalledWith("Option 2");
|
|
26
|
+
});
|
|
27
|
+
test("closes the dropdown menu when an option is selected", () => {
|
|
28
|
+
render(_jsx(Dropdown, { options: options, selectedOption: options[0], onOptionSelect: mockOnOptionSelect }));
|
|
29
|
+
const dropdownButton = screen.getByText("Option 1");
|
|
30
|
+
fireEvent.click(dropdownButton);
|
|
31
|
+
const optionToSelect = screen.getByText("Option 2");
|
|
32
|
+
fireEvent.click(optionToSelect);
|
|
33
|
+
expect(screen.queryByText("Option 2")).not.toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
test("toggles the dropdown menu visibility when clicked", () => {
|
|
36
|
+
render(_jsx(Dropdown, { options: options, selectedOption: options[0], onOptionSelect: mockOnOptionSelect }));
|
|
37
|
+
const dropdownButton = screen.getByText("Option 1");
|
|
38
|
+
fireEvent.click(dropdownButton);
|
|
39
|
+
expect(screen.getByText("Option 2")).toBeInTheDocument();
|
|
40
|
+
fireEvent.click(dropdownButton);
|
|
41
|
+
expect(screen.queryByText("Option 2")).not.toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type DropdownProps = {
|
|
2
|
+
options: string[];
|
|
3
|
+
selectedOption: string;
|
|
4
|
+
onOptionSelect: (action: string) => void;
|
|
5
|
+
optionClasses?: string;
|
|
6
|
+
menuClasses?: string;
|
|
7
|
+
dropdownClasses?: string;
|
|
8
|
+
icon?: {
|
|
9
|
+
name: string;
|
|
10
|
+
weight: "solid" | "regular" | "light" | "duotone" | "brands";
|
|
11
|
+
iconClasses?: string;
|
|
12
|
+
};
|
|
13
|
+
selectedOptionBgColor?: string;
|
|
14
|
+
optionHoverBgColor?: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
[key: string]: any;
|
|
4
|
-
};
|
|
5
|
-
export type GenericListTypes<T> = {
|
|
6
|
-
data: T[];
|
|
7
|
-
renderItem: (item: T) => JSX.Element;
|
|
8
|
-
itemHeight?: number;
|
|
9
|
-
containerHeight?: number;
|
|
10
|
-
containerClasses?: string;
|
|
11
|
-
listDirection?: "horizontal" | "vertical";
|
|
12
|
-
hasVirtualization?: boolean;
|
|
13
|
-
listType: "ordered" | "unordered" | "none";
|
|
14
|
-
};
|
|
15
|
-
declare const GenericList: <T extends GenericData>({ data, renderItem, itemHeight, containerHeight, containerClasses, listDirection, hasVirtualization, listType, }: GenericListTypes<T>) => import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import { GenericData, GenericListTypes } from "./types";
|
|
2
|
+
declare const GenericList: <T extends GenericData>({ data, renderItem, itemHeight, containerHeight, containerClasses, listDirection, hasVirtualization, listType, parentRef, isVirtualizer, }: GenericListTypes<T>) => import("react/jsx-runtime").JSX.Element;
|
|
16
3
|
export default GenericList;
|
|
@@ -1,59 +1,72 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useRef, forwardRef } from "react";
|
|
3
3
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
// New wrapper component for proper list semantics
|
|
5
|
+
const ListWrapper = forwardRef(({ listType, className, style, children }, ref) => {
|
|
6
|
+
const combinedClassName = `${className} ${listType !== "none" ? "list-none p-0 m-0" : ""}`.trim();
|
|
7
|
+
if (listType === "ordered") {
|
|
8
|
+
return (_jsx("ol", { ref: ref, className: combinedClassName, style: style, children: children }));
|
|
9
|
+
}
|
|
10
|
+
if (listType === "unordered") {
|
|
11
|
+
return (_jsx("ul", { ref: ref, className: combinedClassName, style: style, children: children }));
|
|
12
|
+
}
|
|
13
|
+
return (_jsx("div", { ref: ref, className: className, style: style, children: children }));
|
|
14
|
+
});
|
|
15
|
+
const getListDirectionClasses = (listDirection) => {
|
|
16
|
+
if (listDirection === "horizontal") {
|
|
17
|
+
return "flex overflow-x-auto";
|
|
18
|
+
}
|
|
19
|
+
if (listDirection === "vertical") {
|
|
20
|
+
return "flex flex-col";
|
|
21
|
+
}
|
|
22
|
+
return "";
|
|
23
|
+
};
|
|
24
|
+
// Improved virtualization positioning
|
|
25
|
+
const getVirtualItemPosition = (listDirection, start) => {
|
|
26
|
+
if (listDirection === "horizontal") {
|
|
27
|
+
return { left: start, top: 0 };
|
|
28
|
+
}
|
|
29
|
+
return { top: start, left: 0 };
|
|
30
|
+
};
|
|
31
|
+
const GenericList = ({ data, renderItem, itemHeight = 35, containerHeight, containerClasses = "", listDirection, hasVirtualization = false, listType = "none", parentRef, isVirtualizer = false, }) => {
|
|
32
|
+
const listClasses = getListDirectionClasses(listDirection);
|
|
33
|
+
const internalRef = useRef(null);
|
|
34
|
+
const scrollContainerRef = parentRef || internalRef;
|
|
11
35
|
const count = data.length;
|
|
36
|
+
const estimateSize = useCallback(() => itemHeight, [itemHeight]);
|
|
37
|
+
// Virtualization logic
|
|
12
38
|
const virtualizer = useVirtualizer({
|
|
13
39
|
count,
|
|
14
40
|
estimateSize,
|
|
15
|
-
overscan:
|
|
16
|
-
getScrollElement: () =>
|
|
41
|
+
overscan: 5,
|
|
42
|
+
getScrollElement: () => scrollContainerRef.current,
|
|
43
|
+
horizontal: listDirection === "horizontal",
|
|
17
44
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
? virtualItems.map((virtualItem) => (_jsx("li", { style: { height: itemHeight }, children: renderItem(data[virtualItem.index]) }, virtualItem.index)))
|
|
45
|
-
: nonVirtualItems.map((item) => (_jsx("li", { children: renderItem(item) }, item.id))) }));
|
|
46
|
-
case "none":
|
|
47
|
-
default:
|
|
48
|
-
return (_jsx("div", { ref: parentRef, style: hasVirtualization
|
|
49
|
-
? {
|
|
50
|
-
height: containerHeight,
|
|
51
|
-
overflow: "scroll",
|
|
52
|
-
}
|
|
53
|
-
: {}, className: `${listClasses} ${containerClasses}`, children: hasVirtualization
|
|
54
|
-
? virtualItems.map((virtualItem) => (_jsx("div", { style: { height: itemHeight }, children: renderItem(data[virtualItem.index]) }, virtualItem.index)))
|
|
55
|
-
: nonVirtualItems.map((item) => (_jsx("div", { children: renderItem(item) }, item.id))) }));
|
|
56
|
-
}
|
|
57
|
-
})() }));
|
|
45
|
+
const totalSize = virtualizer.getTotalSize();
|
|
46
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
47
|
+
// Shared container props
|
|
48
|
+
const containerProps = {
|
|
49
|
+
ref: scrollContainerRef,
|
|
50
|
+
style: {
|
|
51
|
+
height: containerHeight,
|
|
52
|
+
overflow: "auto",
|
|
53
|
+
position: "relative",
|
|
54
|
+
},
|
|
55
|
+
className: `${listClasses} ${containerClasses}`.trim(),
|
|
56
|
+
};
|
|
57
|
+
return (_jsx(ListWrapper, { listType: listType, ...containerProps, children: !hasVirtualization || isVirtualizer ? (
|
|
58
|
+
// Non-virtualized render
|
|
59
|
+
data.map((item, index) => (_jsx("div", { children: renderItem(item, index) }, `${item.id}-${index}`)))) : (
|
|
60
|
+
// Virtualized render
|
|
61
|
+
_jsx("div", { style: {
|
|
62
|
+
height: listDirection === "vertical" ? totalSize : "100%",
|
|
63
|
+
width: listDirection === "horizontal" ? totalSize : "100%",
|
|
64
|
+
position: "relative",
|
|
65
|
+
}, children: virtualItems.map((virtualItem) => (_jsx("div", { style: {
|
|
66
|
+
position: "absolute",
|
|
67
|
+
width: "100%",
|
|
68
|
+
height: itemHeight,
|
|
69
|
+
...getVirtualItemPosition(listDirection, virtualItem.start),
|
|
70
|
+
}, children: renderItem(data[virtualItem.index], virtualItem.index) }, virtualItem.key))) })) }));
|
|
58
71
|
};
|
|
59
72
|
export default GenericList;
|
|
@@ -1,35 +1,8 @@
|
|
|
1
|
-
import { Meta } from "@storybook/react";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
icon: string;
|
|
10
|
-
hoverBorder?: "border" | "none" | string;
|
|
11
|
-
limitCharacters?: (text: string) => string;
|
|
12
|
-
indicatorSize?: "sm" | "md" | "lg" | string;
|
|
13
|
-
iconBorder?: "border" | "none" | string;
|
|
14
|
-
hoverBackground: string;
|
|
15
|
-
hoverColor: string;
|
|
16
|
-
limitIndicator: boolean;
|
|
17
|
-
indicatorNumber: string;
|
|
18
|
-
additionalContainerClasses: string;
|
|
19
|
-
onClick?: () => void;
|
|
20
|
-
text: {
|
|
21
|
-
size: string;
|
|
22
|
-
color: string;
|
|
23
|
-
text: string;
|
|
24
|
-
fontFamily: string;
|
|
25
|
-
tag: TagName;
|
|
26
|
-
additionalClasses: string;
|
|
27
|
-
};
|
|
28
|
-
to?: string;
|
|
29
|
-
href?: string;
|
|
30
|
-
};
|
|
31
|
-
declare const _default: Meta;
|
|
32
|
-
export default _default;
|
|
33
|
-
export declare const Default: any;
|
|
34
|
-
export declare const VerticalList: any;
|
|
35
|
-
export declare const LargeDataSet: any;
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import GenericList from "./GenericList";
|
|
3
|
+
declare const meta: Meta<typeof GenericList>;
|
|
4
|
+
export default meta;
|
|
5
|
+
export declare const HoverList: StoryObj<typeof GenericList>;
|
|
6
|
+
export declare const ClickableList: StoryObj<typeof GenericList>;
|
|
7
|
+
export declare const ActionListExample: StoryObj<typeof GenericList>;
|
|
8
|
+
export declare const HorizontalClickable: StoryObj<typeof GenericList>;
|
|
@@ -1,87 +1,55 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import GenericList from "./GenericList";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const sampleData = Array.from({ length: 1000 }, (_, i) => ({
|
|
4
|
+
id: `item-${i}`,
|
|
5
|
+
name: `Item ${i}`,
|
|
6
|
+
value: i,
|
|
7
|
+
}));
|
|
8
|
+
const meta = {
|
|
7
9
|
title: "Components/GenericList",
|
|
8
10
|
component: GenericList,
|
|
9
|
-
argTypes: {
|
|
10
|
-
listDirection: {
|
|
11
|
-
control: "select",
|
|
12
|
-
options: ["horizontal", "vertical"],
|
|
13
|
-
description: "Whether the list is horizontal or vertical",
|
|
14
|
-
},
|
|
15
|
-
listType: {
|
|
16
|
-
table: {
|
|
17
|
-
disable: true,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
hasVirtualization: {
|
|
21
|
-
table: {
|
|
22
|
-
disable: true,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
containerClasses: {
|
|
26
|
-
table: {
|
|
27
|
-
disable: true,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
itemHeight: {
|
|
31
|
-
table: {
|
|
32
|
-
disable: true,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
containerHeight: {
|
|
36
|
-
table: {
|
|
37
|
-
disable: true,
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
renderItem: {
|
|
41
|
-
table: {
|
|
42
|
-
disable: true,
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
data: {
|
|
46
|
-
table: {
|
|
47
|
-
disable: true,
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
11
|
tags: ["autodocs"],
|
|
52
|
-
|
|
53
|
-
|
|
12
|
+
};
|
|
13
|
+
export default meta;
|
|
14
|
+
// ... existing stories ...
|
|
15
|
+
// Hover state story
|
|
16
|
+
export const HoverList = {
|
|
17
|
+
args: {
|
|
18
|
+
data: sampleData.slice(0, 10),
|
|
19
|
+
renderItem: (item) => (_jsx("div", { className: "p-2 border-b hover:bg-gray-100 transition-colors duration-200", children: item.name })),
|
|
20
|
+
containerHeight: 300,
|
|
21
|
+
listType: "unordered",
|
|
54
22
|
},
|
|
55
23
|
};
|
|
56
|
-
|
|
57
|
-
export const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
hasVirtualization: true,
|
|
64
|
-
listDirection: "horizontal",
|
|
65
|
-
listType: "none",
|
|
24
|
+
// Clickable rows story
|
|
25
|
+
export const ClickableList = {
|
|
26
|
+
args: {
|
|
27
|
+
data: sampleData.slice(0, 10),
|
|
28
|
+
renderItem: (item) => (_jsx("div", { className: "p-2 border-b hover:bg-blue-50 cursor-pointer transition-colors", onClick: () => alert(`Clicked: ${item.name}`), children: item.name })),
|
|
29
|
+
containerHeight: 300,
|
|
30
|
+
},
|
|
66
31
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
32
|
+
// Interactive list with both hover and click (like ActionList example)
|
|
33
|
+
export const ActionListExample = {
|
|
34
|
+
args: {
|
|
35
|
+
data: sampleData.slice(0, 5).map((item) => ({
|
|
36
|
+
...item,
|
|
37
|
+
icon: "faUser",
|
|
38
|
+
color: "blue-500",
|
|
39
|
+
setActionModal: () => console.log(`Action for ${item.name}`),
|
|
40
|
+
})),
|
|
41
|
+
renderItem: (item) => (_jsxs("div", { className: "flex items-center p-2 hover:bg-gray-100 cursor-pointer group", onClick: () => alert(`Action triggered for: ${item.name}`), children: [_jsx("span", { className: `text-${item.color} w-6 text-center`, children: "\u26A1" }), _jsx("span", { className: "ml-2 group-hover:text-blue-600 transition-colors", children: item.name })] })),
|
|
42
|
+
containerClasses: "border rounded-lg w-64",
|
|
43
|
+
listType: "unordered",
|
|
44
|
+
},
|
|
76
45
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
listType: "unordered",
|
|
46
|
+
// Horizontal clickable items
|
|
47
|
+
export const HorizontalClickable = {
|
|
48
|
+
args: {
|
|
49
|
+
data: sampleData.slice(0, 10),
|
|
50
|
+
renderItem: (item) => (_jsx("div", { className: "p-4 border-r hover:bg-orange-50 cursor-pointer h-full flex items-center", onClick: () => alert(`Selected: ${item.name}`), children: _jsx("span", { className: "hover:scale-105 transition-transform", children: item.name }) })),
|
|
51
|
+
listDirection: "horizontal",
|
|
52
|
+
containerHeight: 80,
|
|
53
|
+
containerClasses: "w-full border-y",
|
|
54
|
+
},
|
|
87
55
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import "@testing-library/jest-dom";
|