cemtrik-dependencies 1.0.4
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/README copy.md +54 -0
- package/README.md +29 -0
- package/components.js +47 -0
- package/hooks.js +4 -0
- package/package.json +58 -0
- package/postcss.config.js +6 -0
- package/src/assets/cemtrik-icons-v1.0/Read Me.txt +7 -0
- package/src/assets/cemtrik-icons-v1.0/demo-files/demo.css +152 -0
- package/src/assets/cemtrik-icons-v1.0/demo-files/demo.js +30 -0
- package/src/assets/cemtrik-icons-v1.0/demo.html +318 -0
- package/src/assets/cemtrik-icons-v1.0/fonts/cemtrik-icons.eot +0 -0
- package/src/assets/cemtrik-icons-v1.0/fonts/cemtrik-icons.svg +31 -0
- package/src/assets/cemtrik-icons-v1.0/fonts/cemtrik-icons.ttf +0 -0
- package/src/assets/cemtrik-icons-v1.0/fonts/cemtrik-icons.woff +0 -0
- package/src/assets/cemtrik-icons-v1.0/selection.json +1 -0
- package/src/assets/cemtrik-icons-v1.0/style.css +96 -0
- package/src/assets/icons/actions.svg +4 -0
- package/src/assets/icons/attributes.svg +5 -0
- package/src/assets/icons/bars.svg +3 -0
- package/src/assets/icons/bell.svg +4 -0
- package/src/assets/icons/cemtrik.svg +9 -0
- package/src/assets/icons/clipboardCheck.svg +4 -0
- package/src/assets/icons/clipboardList.svg +5 -0
- package/src/assets/icons/clock.svg +3 -0
- package/src/assets/icons/flag.svg +3 -0
- package/src/assets/icons/folder.svg +3 -0
- package/src/assets/icons/graph_indicators.svg +3 -0
- package/src/assets/icons/group.svg +3 -0
- package/src/assets/icons/letter.svg +3 -0
- package/src/assets/icons/measuring_points.svg +3 -0
- package/src/assets/icons/reports.svg +4 -0
- package/src/assets/icons/roboot.svg +3 -0
- package/src/assets/icons/user.svg +4 -0
- package/src/assets/icons/user_point.svg +10 -0
- package/src/assets/icons/vector.svg +3 -0
- package/src/assets/icons/vector2.svg +3 -0
- package/src/components/atoms/Alert/index.js +104 -0
- package/src/components/atoms/Alert/index.test.js +36 -0
- package/src/components/atoms/Avatar/index.js +63 -0
- package/src/components/atoms/Avatar/index.test.js +30 -0
- package/src/components/atoms/Bullets/Bullets.test.js +22 -0
- package/src/components/atoms/Bullets/index.js +51 -0
- package/src/components/atoms/ButtonOutline/index.js +43 -0
- package/src/components/atoms/ButtonOutline/index.test.js +36 -0
- package/src/components/atoms/ButtonPagination/index.js +37 -0
- package/src/components/atoms/ButtonPagination/index.test.js +36 -0
- package/src/components/atoms/ButtonSolid/index.js +74 -0
- package/src/components/atoms/ButtonSolid/index.test.js +59 -0
- package/src/components/atoms/Checkbox/index.js +52 -0
- package/src/components/atoms/Checkbox/index.test.js +15 -0
- package/src/components/atoms/ConfirmationAbandoningCreation/index.js +70 -0
- package/src/components/atoms/ConfirmationAbandoningCreation/index.test.js +70 -0
- package/src/components/atoms/Divider/index.js +10 -0
- package/src/components/atoms/Divider/index.test.js +12 -0
- package/src/components/atoms/GoBack/index.js +30 -0
- package/src/components/atoms/GoBack/index.test.js +24 -0
- package/src/components/atoms/Input/index.js +107 -0
- package/src/components/atoms/Input/index.test.js +63 -0
- package/src/components/atoms/InputDropdown/index.js +111 -0
- package/src/components/atoms/InputDropdown/index.test.js +86 -0
- package/src/components/atoms/Select/index.js +199 -0
- package/src/components/atoms/Select/index.test.js +86 -0
- package/src/components/atoms/Spinner/index.js +49 -0
- package/src/components/atoms/Spinner/index.test.js +9 -0
- package/src/components/atoms/Switch/index.js +46 -0
- package/src/components/atoms/Switch/index.test.js +18 -0
- package/src/components/atoms/Textarea/index.js +136 -0
- package/src/components/atoms/Textarea/index.spec.js +51 -0
- package/src/components/atoms/Tooltip/index.js +64 -0
- package/src/components/atoms/Tooltip/index.test.js +31 -0
- package/src/components/atoms/UploadImage/index.js +55 -0
- package/src/components/atoms/UploadImage/index.test.js +36 -0
- package/src/components/molecules/Dropdown/index.js +315 -0
- package/src/components/molecules/Dropdown/index.test.js +190 -0
- package/src/components/molecules/Modal/index.js +103 -0
- package/src/components/molecules/Modal/index.test.js +42 -0
- package/src/components/molecules/Pagination/index.js +126 -0
- package/src/components/molecules/Pagination/index.test.js +57 -0
- package/src/components/templates/Accordion/index.js +174 -0
- package/src/components/templates/Accordion/index.test.js +130 -0
- package/src/hooks/useCloseModal/index.js +27 -0
- package/src/hooks/useCloseModal/index.test.js +26 -0
- package/src/hooks/useForm/index.js +70 -0
- package/src/hooks/useForm/useForm.test.js +104 -0
- package/src/hooks/useMediaQuery/index.js +53 -0
- package/src/hooks/useMediaQuery/useMediaQuery.test.js +46 -0
- package/src/hooks/useWindowDimensions/index.js +34 -0
- package/src/hooks/useWindowDimensions/useWindowDimensions.test.js +19 -0
- package/src/utils/index.js +32 -0
- package/src/utils/index.test.js +56 -0
- package/tailwind.config.js +117 -0
- package/utils.js +8 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// 1.- libraries
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
// 2.- components
|
|
5
|
+
import ButtonPagination from "../../atoms/ButtonPagination";
|
|
6
|
+
|
|
7
|
+
// 3.- icons
|
|
8
|
+
import { ArrowLongLeftIcon, ArrowLongRightIcon } from "@heroicons/react/20/solid";
|
|
9
|
+
|
|
10
|
+
// 4.- utils
|
|
11
|
+
import { classNames } from "../../../utils";
|
|
12
|
+
|
|
13
|
+
export const rowsPerPage = 50;
|
|
14
|
+
|
|
15
|
+
const Pagination = ({ total, handleNext, handlePrev, handleSelectPage, page = null }) => {
|
|
16
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
17
|
+
const [arrOfCurrButtons, setArrOfCurrButtons] = useState([]);
|
|
18
|
+
const [numOfButtons, setNumOfButtons] = useState([]);
|
|
19
|
+
|
|
20
|
+
const numOfPages = Math.ceil(total / rowsPerPage);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const arr = [];
|
|
24
|
+
for (let i = 1; i <= numOfPages; i++) arr.push(i);
|
|
25
|
+
setNumOfButtons(arr);
|
|
26
|
+
}, [numOfPages]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
let tempNumberOfButtons = [];
|
|
30
|
+
const dotsInitial = "...";
|
|
31
|
+
const dotsLeft = "... ";
|
|
32
|
+
const dotsRight = " ...";
|
|
33
|
+
if (numOfButtons.length < 6) {
|
|
34
|
+
tempNumberOfButtons = numOfButtons;
|
|
35
|
+
} else if (currentPage >= 1 && currentPage <= 3) {
|
|
36
|
+
tempNumberOfButtons = [1, 2, 3, 4, dotsInitial, numOfButtons.length];
|
|
37
|
+
} else if (currentPage === 4) {
|
|
38
|
+
const sliced = numOfButtons.slice(0, 5);
|
|
39
|
+
tempNumberOfButtons = [...sliced, dotsInitial, numOfButtons.length];
|
|
40
|
+
} else if (currentPage > 4 && currentPage < numOfButtons.length - 2) {
|
|
41
|
+
const sliced1 = numOfButtons.slice(currentPage - 2, currentPage);
|
|
42
|
+
const sliced2 = numOfButtons.slice(currentPage, currentPage + 1);
|
|
43
|
+
tempNumberOfButtons = [
|
|
44
|
+
1,
|
|
45
|
+
dotsLeft,
|
|
46
|
+
...sliced1,
|
|
47
|
+
...sliced2,
|
|
48
|
+
dotsRight,
|
|
49
|
+
numOfButtons.length,
|
|
50
|
+
];
|
|
51
|
+
} else if (currentPage > numOfButtons.length - 3) {
|
|
52
|
+
const sliced = numOfButtons.slice(numOfButtons.length - 4);
|
|
53
|
+
tempNumberOfButtons = [1, dotsLeft, ...sliced];
|
|
54
|
+
}
|
|
55
|
+
setArrOfCurrButtons(tempNumberOfButtons);
|
|
56
|
+
}, [currentPage, numOfButtons]);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (page === null) return;
|
|
60
|
+
page !== currentPage && setCurrentPage(page);
|
|
61
|
+
}, [page, currentPage]);
|
|
62
|
+
|
|
63
|
+
const selectPage = (data) => {
|
|
64
|
+
setCurrentPage(data);
|
|
65
|
+
handleSelectPage(data);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const prev = () => {
|
|
69
|
+
currentPage === 1 ? setCurrentPage(currentPage) : setCurrentPage(currentPage - 1);
|
|
70
|
+
currentPage > 1 && handlePrev(currentPage - 1);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const next = () => {
|
|
74
|
+
currentPage === numOfButtons.length
|
|
75
|
+
? setCurrentPage(currentPage)
|
|
76
|
+
: setCurrentPage(currentPage + 1);
|
|
77
|
+
currentPage < numOfButtons.length && handleNext(currentPage + 1);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<nav className="flex items-center justify-between border-t border-gray-200 px-4 sm:px-0">
|
|
82
|
+
<div className="-mt-px flex w-0 flex-1">
|
|
83
|
+
<span
|
|
84
|
+
className="inline-flex items-center border-t-2 border-transparent pr-1 pt-4 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700 cursor-pointer"
|
|
85
|
+
onClick={prev}
|
|
86
|
+
data-testid="prev"
|
|
87
|
+
>
|
|
88
|
+
<ArrowLongLeftIcon className="mr-3 h-5 w-5 text-gray-400" aria-hidden="true" />
|
|
89
|
+
Anterior
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div className="hidden md:-mt-px md:flex">
|
|
93
|
+
{arrOfCurrButtons.map((data, index) => (
|
|
94
|
+
<ButtonPagination
|
|
95
|
+
key={index}
|
|
96
|
+
className={classNames(
|
|
97
|
+
"inline-flex items-center border-t-2 px-4 pt-4 text-sm font-medium",
|
|
98
|
+
currentPage === data
|
|
99
|
+
? "border-indigo-500 text-indigo-600"
|
|
100
|
+
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
|
101
|
+
)}
|
|
102
|
+
aria-current="page"
|
|
103
|
+
onClick={() =>
|
|
104
|
+
data !== "..." && data !== " ..." && data !== "... " && selectPage(data)
|
|
105
|
+
}
|
|
106
|
+
id={`page-${index + 1}`}
|
|
107
|
+
>
|
|
108
|
+
{data}
|
|
109
|
+
</ButtonPagination>
|
|
110
|
+
))}
|
|
111
|
+
</div>
|
|
112
|
+
<div className="-mt-px flex w-0 flex-1 justify-end">
|
|
113
|
+
<span
|
|
114
|
+
className="inline-flex items-center border-t-2 border-transparent pl-1 pt-4 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700 cursor-pointer"
|
|
115
|
+
onClick={next}
|
|
116
|
+
data-testid="next"
|
|
117
|
+
>
|
|
118
|
+
Siguiente
|
|
119
|
+
<ArrowLongRightIcon className="ml-3 h-5 w-5 text-gray-400" aria-hidden="true" />
|
|
120
|
+
</span>
|
|
121
|
+
</div>
|
|
122
|
+
</nav>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default Pagination;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { render, fireEvent } from "@testing-library/react";
|
|
2
|
+
import Pagination, { rowsPerPage } from "./";
|
|
3
|
+
|
|
4
|
+
describe("Pagination", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
jest.resetAllMocks();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("selectPage", () => {
|
|
10
|
+
const handleSelectPage = jest.fn();
|
|
11
|
+
const component = render(
|
|
12
|
+
<Pagination
|
|
13
|
+
total={rowsPerPage}
|
|
14
|
+
handleNext={() => {}}
|
|
15
|
+
handlePrev={() => {}}
|
|
16
|
+
handleSelectPage={handleSelectPage}
|
|
17
|
+
page={2}
|
|
18
|
+
/>,
|
|
19
|
+
);
|
|
20
|
+
const page = component.getByTestId("button-page-1");
|
|
21
|
+
fireEvent.click(page);
|
|
22
|
+
expect(handleSelectPage).toHaveBeenCalledWith(1);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("prev", () => {
|
|
26
|
+
const handlePrev = jest.fn();
|
|
27
|
+
const component = render(
|
|
28
|
+
<Pagination total={1150} handlePrev={handlePrev} handleSelectPage={() => {}} />,
|
|
29
|
+
);
|
|
30
|
+
const page3 = component.getByTestId("button-page-3");
|
|
31
|
+
const prev = component.getByTestId("prev");
|
|
32
|
+
|
|
33
|
+
fireEvent.click(prev);
|
|
34
|
+
fireEvent.click(page3);
|
|
35
|
+
fireEvent.click(prev);
|
|
36
|
+
expect(handlePrev).toHaveBeenCalledWith(2);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("next", () => {
|
|
40
|
+
const handleNext = jest.fn();
|
|
41
|
+
const component = render(
|
|
42
|
+
<Pagination total={450} handleNext={handleNext} handleSelectPage={() => {}} />,
|
|
43
|
+
);
|
|
44
|
+
const page4 = component.getByTestId("button-page-4");
|
|
45
|
+
const page6 = component.getByTestId("button-page-6");
|
|
46
|
+
const next = component.getByTestId("next");
|
|
47
|
+
fireEvent.click(page4);
|
|
48
|
+
fireEvent.click(next);
|
|
49
|
+
fireEvent.click(next);
|
|
50
|
+
fireEvent.click(next);
|
|
51
|
+
fireEvent.click(page6);
|
|
52
|
+
fireEvent.click(next);
|
|
53
|
+
expect(handleNext).toHaveBeenNthCalledWith(1, 5);
|
|
54
|
+
expect(handleNext).toHaveBeenNthCalledWith(2, 6);
|
|
55
|
+
expect(handleNext).toHaveBeenNthCalledWith(3, 7);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// 1.- libraries
|
|
2
|
+
import { useState, useEffect, Fragment } from "react";
|
|
3
|
+
import { Transition } from "@headlessui/react";
|
|
4
|
+
|
|
5
|
+
// 2.- components
|
|
6
|
+
import Input from "../../atoms/Input";
|
|
7
|
+
|
|
8
|
+
// 3.- icons
|
|
9
|
+
import {
|
|
10
|
+
TrashIcon,
|
|
11
|
+
ChevronDownIcon,
|
|
12
|
+
ChevronUpIcon,
|
|
13
|
+
MagnifyingGlassIcon,
|
|
14
|
+
} from "@heroicons/react/24/outline";
|
|
15
|
+
|
|
16
|
+
// 4.- utils
|
|
17
|
+
import { copyOptions, classNames } from "../../../utils";
|
|
18
|
+
|
|
19
|
+
const Accordion = ({
|
|
20
|
+
openDefault = false,
|
|
21
|
+
setForm,
|
|
22
|
+
setValuesDefault,
|
|
23
|
+
title,
|
|
24
|
+
type,
|
|
25
|
+
data,
|
|
26
|
+
setData,
|
|
27
|
+
Component,
|
|
28
|
+
id,
|
|
29
|
+
defaultData,
|
|
30
|
+
dispatch,
|
|
31
|
+
isAdd = true,
|
|
32
|
+
search = null,
|
|
33
|
+
isPadding = true,
|
|
34
|
+
}) => {
|
|
35
|
+
const [open, setOpen] = useState(false);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
setOpen(openDefault);
|
|
39
|
+
}, [openDefault]);
|
|
40
|
+
|
|
41
|
+
const handleChange = ({ e, index }) => {
|
|
42
|
+
const copyData = copyOptions(data);
|
|
43
|
+
const { name, value } = e.target;
|
|
44
|
+
copyData[index][name] = value;
|
|
45
|
+
setData(copyData);
|
|
46
|
+
setValuesDefault(id, copyData);
|
|
47
|
+
setForm((state) => ({ ...state, [id]: copyData }));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const add = () => {
|
|
51
|
+
const isEmpty = data.some((item) => {
|
|
52
|
+
const items = Object.values(item);
|
|
53
|
+
const isEmptyValues = items.some((v) => v.value === -1);
|
|
54
|
+
return isEmptyValues;
|
|
55
|
+
});
|
|
56
|
+
if (isEmpty || data.length === 0)
|
|
57
|
+
return dispatch({
|
|
58
|
+
type: "ALERT",
|
|
59
|
+
payload: {
|
|
60
|
+
title: "",
|
|
61
|
+
message: "Todos los campos son requeridos",
|
|
62
|
+
typeAlert: "warning",
|
|
63
|
+
active: true,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
setForm((state) => ({ ...state, data }));
|
|
67
|
+
setValuesDefault(id, data);
|
|
68
|
+
setData([...data, defaultData]);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const deleteData = (index) => {
|
|
72
|
+
if (index === data.length - 1) return;
|
|
73
|
+
const copyData = copyOptions(data);
|
|
74
|
+
copyData.splice(index, 1);
|
|
75
|
+
setForm((state) => ({ ...state, copyData }));
|
|
76
|
+
setValuesDefault(id, copyData);
|
|
77
|
+
setData(copyData);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className="divide-y divide-gray-200 rounded-lg bg-white shadow mt-7">
|
|
82
|
+
<div
|
|
83
|
+
className="px-4 py-3.5 sm:px-6 flex justify-between items-center cursor-pointer"
|
|
84
|
+
onClick={() => setOpen(!open)}
|
|
85
|
+
data-testid="open-accordion"
|
|
86
|
+
>
|
|
87
|
+
<p className="text-gray-500 uppercase text-xs font-bold">{title}</p>
|
|
88
|
+
<div className="flex items-center">
|
|
89
|
+
{search && (
|
|
90
|
+
<Input
|
|
91
|
+
htmlFor="search"
|
|
92
|
+
id="search"
|
|
93
|
+
name="search"
|
|
94
|
+
type="text"
|
|
95
|
+
autoComplete="search"
|
|
96
|
+
placeholder="Buscar..."
|
|
97
|
+
handleChange={search}
|
|
98
|
+
icon={<MagnifyingGlassIcon className="w-6 h-6 text-indigo-300" />}
|
|
99
|
+
className="h-9"
|
|
100
|
+
classNameContainerInput="w-52 2xs:w-64 max-w-64 mr-8 hidden sm:block"
|
|
101
|
+
value=""
|
|
102
|
+
onClick={(e) => e.stopPropagation()}
|
|
103
|
+
onFocus={() => setOpen(true)}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
106
|
+
{open ? (
|
|
107
|
+
<ChevronUpIcon className="w-5 h-5 text-default-5" />
|
|
108
|
+
) : (
|
|
109
|
+
<ChevronDownIcon className="w-5 h-5 text-default-5" />
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<Transition
|
|
114
|
+
as={Fragment}
|
|
115
|
+
show={open}
|
|
116
|
+
enter="transition duration-[400ms]"
|
|
117
|
+
enterFrom="opacity-0"
|
|
118
|
+
enterTo="opacity-100"
|
|
119
|
+
leave="duration-200 transition ease-in-out"
|
|
120
|
+
leaveFrom="opacity-100"
|
|
121
|
+
leaveTo="opacity-0"
|
|
122
|
+
>
|
|
123
|
+
<div className={classNames("sm:mt-2.5 py-3.5", isPadding && "px-4 sm:p-6")}>
|
|
124
|
+
{data.map((item, index) => (
|
|
125
|
+
<div
|
|
126
|
+
key={index}
|
|
127
|
+
className={classNames(
|
|
128
|
+
"flex flex-col 3xs:flex-row mb-4",
|
|
129
|
+
isPadding && "ml-0 3xs:ml-3",
|
|
130
|
+
)}
|
|
131
|
+
>
|
|
132
|
+
<Component item={item} index={index} handleChange={handleChange} />
|
|
133
|
+
{isAdd && (
|
|
134
|
+
<div
|
|
135
|
+
style={{
|
|
136
|
+
backgroundColor: "#B0B8BF",
|
|
137
|
+
width: "25px",
|
|
138
|
+
height: "25px",
|
|
139
|
+
}}
|
|
140
|
+
className={classNames(
|
|
141
|
+
"rounded-full flex justify-center items-center mt-4 3xs:mt-7 ml-2 3xs:ml-5 self-start 3xs:self-center",
|
|
142
|
+
index === data.length - 1 && "3xs:opacity-0",
|
|
143
|
+
)}
|
|
144
|
+
>
|
|
145
|
+
<TrashIcon
|
|
146
|
+
className={classNames(
|
|
147
|
+
"w-6 h-4",
|
|
148
|
+
index !== data.length - 1 && "cursor-pointer",
|
|
149
|
+
)}
|
|
150
|
+
style={{ color: "#637381" }}
|
|
151
|
+
strokeWidth={2.5}
|
|
152
|
+
onClick={() => deleteData(index)}
|
|
153
|
+
data-testid={`delete-data-${index}`}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
))}
|
|
159
|
+
{isAdd && (
|
|
160
|
+
<p
|
|
161
|
+
className="text-secondary-2 text-sm font-medium ml-3 cursor-pointer"
|
|
162
|
+
onClick={add}
|
|
163
|
+
data-testid="add"
|
|
164
|
+
>
|
|
165
|
+
{`+ Añadir ${type}`}
|
|
166
|
+
</p>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
</Transition>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export default Accordion;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { render, fireEvent, act } from "@testing-library/react";
|
|
2
|
+
import Accordion from "./";
|
|
3
|
+
|
|
4
|
+
describe("Accordion", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
jest.resetAllMocks();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("renders Accordion component without errors", async () => {
|
|
10
|
+
let component;
|
|
11
|
+
await act(async () => {
|
|
12
|
+
component = render(
|
|
13
|
+
<Accordion title="title" data={[]} openDefault id={1} dispatch={() => {}} />,
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
const add = component.getByTestId("add");
|
|
17
|
+
fireEvent.click(add);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("search", async () => {
|
|
21
|
+
const handleSearch = jest.fn();
|
|
22
|
+
const component = render(<Accordion title="title" search={handleSearch} data={[]} />);
|
|
23
|
+
const open = component.getByTestId("open-accordion");
|
|
24
|
+
const input = component.getByTestId("input-search");
|
|
25
|
+
await act(async () => fireEvent.click(open));
|
|
26
|
+
fireEvent.change(input, { target: { value: "search..." } });
|
|
27
|
+
fireEvent.click(input);
|
|
28
|
+
fireEvent.focus(input);
|
|
29
|
+
expect(handleSearch).toHaveBeenCalled();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("handleChange Component", async () => {
|
|
33
|
+
const MockComponent = ({ item, index, handleChange }) => {
|
|
34
|
+
return (
|
|
35
|
+
<button
|
|
36
|
+
onClick={() =>
|
|
37
|
+
handleChange({
|
|
38
|
+
e: { target: { name: "scope", value: { label: "label-2", id: 2 } } },
|
|
39
|
+
index,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
data-testid="component-mock"
|
|
43
|
+
>
|
|
44
|
+
Click me
|
|
45
|
+
</button>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
const mockSetValuesDefault = jest.fn();
|
|
49
|
+
const mockSetData = jest.fn();
|
|
50
|
+
let component;
|
|
51
|
+
await act(async () => {
|
|
52
|
+
component = render(
|
|
53
|
+
<Accordion
|
|
54
|
+
id={3}
|
|
55
|
+
title="title"
|
|
56
|
+
isPadding
|
|
57
|
+
openDefault
|
|
58
|
+
data={[{ scope: { label: "label", id: 1 } }]}
|
|
59
|
+
Component={MockComponent}
|
|
60
|
+
setValuesDefault={mockSetValuesDefault}
|
|
61
|
+
setForm={(fn) => fn()}
|
|
62
|
+
setData={mockSetData}
|
|
63
|
+
/>,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
const button = component.getByTestId("component-mock");
|
|
67
|
+
fireEvent.click(button);
|
|
68
|
+
expect(mockSetValuesDefault).toHaveBeenCalledWith(3, [
|
|
69
|
+
{ scope: { id: 2, label: "label-2" } },
|
|
70
|
+
]);
|
|
71
|
+
expect(mockSetData).toHaveBeenCalledWith([{ scope: { id: 2, label: "label-2" } }]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("delete data", async () => {
|
|
75
|
+
const mockSetData = jest.fn();
|
|
76
|
+
const mockSetValuesDefault = jest.fn();
|
|
77
|
+
let component;
|
|
78
|
+
await act(async () => {
|
|
79
|
+
component = render(
|
|
80
|
+
<Accordion
|
|
81
|
+
id={4}
|
|
82
|
+
title="title"
|
|
83
|
+
openDefault
|
|
84
|
+
data={[
|
|
85
|
+
{ scope: { label: "label", id: 1 } },
|
|
86
|
+
{ scope: { label: "label", id: 2 } },
|
|
87
|
+
]}
|
|
88
|
+
Component={() => {}}
|
|
89
|
+
isAdd
|
|
90
|
+
setValuesDefault={mockSetValuesDefault}
|
|
91
|
+
setForm={(fn) => fn()}
|
|
92
|
+
setData={mockSetData}
|
|
93
|
+
/>,
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
const buttonDelete = component.getByTestId("delete-data-0");
|
|
97
|
+
const buttonDelete2 = component.getByTestId("delete-data-1");
|
|
98
|
+
fireEvent.click(buttonDelete);
|
|
99
|
+
fireEvent.click(buttonDelete2);
|
|
100
|
+
expect(mockSetValuesDefault).toHaveBeenCalledWith(4, [
|
|
101
|
+
{ scope: { id: 2, label: "label" } },
|
|
102
|
+
]);
|
|
103
|
+
expect(mockSetData).toHaveBeenCalledWith([{ scope: { id: 2, label: "label" } }]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("add data", async () => {
|
|
107
|
+
const mockSetData = jest.fn();
|
|
108
|
+
const mockSetValuesDefault = jest.fn();
|
|
109
|
+
let component;
|
|
110
|
+
await act(async () => {
|
|
111
|
+
component = render(
|
|
112
|
+
<Accordion
|
|
113
|
+
id={5}
|
|
114
|
+
title="title"
|
|
115
|
+
data={[{ item: { value: 1 } }]}
|
|
116
|
+
type="rol"
|
|
117
|
+
openDefault
|
|
118
|
+
Component={() => {}}
|
|
119
|
+
setValuesDefault={mockSetValuesDefault}
|
|
120
|
+
setForm={(fn) => fn()}
|
|
121
|
+
setData={mockSetData}
|
|
122
|
+
/>,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
const add = component.getByTestId("add");
|
|
126
|
+
fireEvent.click(add);
|
|
127
|
+
expect(mockSetValuesDefault).toHaveBeenCalledWith(5, [{ item: { value: 1 } }]);
|
|
128
|
+
expect(mockSetData).toHaveBeenCalledWith([{ item: { value: 1 } }, undefined]);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
export const useCloseModal = ({ id, close, isPathsTest = false }) => {
|
|
4
|
+
const closeWindow = useCallback(
|
|
5
|
+
(paths) => {
|
|
6
|
+
const isClose = paths.some((v) => {
|
|
7
|
+
return v.getAttribute === undefined ? false : v.getAttribute("id") === id;
|
|
8
|
+
});
|
|
9
|
+
if (!isClose) close();
|
|
10
|
+
},
|
|
11
|
+
[close, id],
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const closeModal = (e) => {
|
|
16
|
+
const paths = e.composedPath();
|
|
17
|
+
if (paths === undefined || isPathsTest) return;
|
|
18
|
+
closeWindow(paths);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
window.addEventListener("click", closeModal);
|
|
22
|
+
|
|
23
|
+
return () => {
|
|
24
|
+
window.removeEventListener("click", closeModal);
|
|
25
|
+
};
|
|
26
|
+
}, [closeWindow, isPathsTest]);
|
|
27
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { renderHook, act } from "@testing-library/react";
|
|
2
|
+
import { useCloseModal } from "./";
|
|
3
|
+
|
|
4
|
+
describe("useCloseModal", () => {
|
|
5
|
+
it("close", () => {
|
|
6
|
+
const closeMock = jest.fn();
|
|
7
|
+
renderHook(useCloseModal, {
|
|
8
|
+
initialProps: { id: "", close: closeMock },
|
|
9
|
+
});
|
|
10
|
+
act(() => {
|
|
11
|
+
window.dispatchEvent(new MouseEvent("click", { composedPath: undefined }));
|
|
12
|
+
});
|
|
13
|
+
expect(closeMock.mock.calls.length).toBe(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("paths undefined", () => {
|
|
17
|
+
const closeMock = jest.fn();
|
|
18
|
+
renderHook(useCloseModal, {
|
|
19
|
+
initialProps: { id: "", close: closeMock, isPathsTest: true },
|
|
20
|
+
});
|
|
21
|
+
act(() => {
|
|
22
|
+
window.dispatchEvent(new Event("click"));
|
|
23
|
+
});
|
|
24
|
+
expect(closeMock.mock.calls.length).toBe(0);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
export const useForm = () => {
|
|
4
|
+
const [model, setModel] = useState({});
|
|
5
|
+
const [messageError, setMessageError] = useState({});
|
|
6
|
+
const [errors, setErrors] = useState([]);
|
|
7
|
+
|
|
8
|
+
const handleSubmit = (onSubmit) => (e) => {
|
|
9
|
+
e.preventDefault();
|
|
10
|
+
e.stopPropagation();
|
|
11
|
+
onSubmit(model);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const handleChange = (e, setForm, form) => {
|
|
15
|
+
const { checked, value, name, type, id } = e.target;
|
|
16
|
+
const dict = {
|
|
17
|
+
radio: id,
|
|
18
|
+
checkbox: checked,
|
|
19
|
+
};
|
|
20
|
+
setModel({
|
|
21
|
+
...model,
|
|
22
|
+
[name]: dict[type] ?? value,
|
|
23
|
+
});
|
|
24
|
+
setForm({
|
|
25
|
+
...form,
|
|
26
|
+
[name]: dict[type] ?? value,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const setValuesDefault = useCallback((name, value) => {
|
|
31
|
+
setModel((obj) => ({ ...obj, [name]: value }));
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
const validateFields = useCallback((obj, fieldRequerids) => {
|
|
35
|
+
const fields = fieldRequerids.filter(
|
|
36
|
+
(field) => obj[field] === "" || obj[field] === undefined,
|
|
37
|
+
);
|
|
38
|
+
fields.forEach((v) =>
|
|
39
|
+
setMessageError((state) => ({
|
|
40
|
+
...state,
|
|
41
|
+
[v]: "Este campo es requerido",
|
|
42
|
+
})),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
setErrors(fields);
|
|
46
|
+
return fields.length > 0;
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
const handleError = (message, fields) => {
|
|
50
|
+
fields.forEach((v) =>
|
|
51
|
+
setMessageError((state) => ({
|
|
52
|
+
...state,
|
|
53
|
+
[v]: message,
|
|
54
|
+
})),
|
|
55
|
+
);
|
|
56
|
+
setErrors(fields);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
handleSubmit,
|
|
61
|
+
handleChange,
|
|
62
|
+
setValuesDefault,
|
|
63
|
+
validateFields,
|
|
64
|
+
errors,
|
|
65
|
+
model,
|
|
66
|
+
handleError,
|
|
67
|
+
messageError,
|
|
68
|
+
setModel,
|
|
69
|
+
};
|
|
70
|
+
};
|