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,315 @@
|
|
|
1
|
+
// 1.- libraries
|
|
2
|
+
import { Fragment, useState, useEffect } from "react";
|
|
3
|
+
import { Menu, Transition } from "@headlessui/react";
|
|
4
|
+
|
|
5
|
+
// 2.- components
|
|
6
|
+
import { SpinnerIcon } from "../../atoms/Spinner";
|
|
7
|
+
import Input from "../../atoms/Input";
|
|
8
|
+
import ButtonSolid from "../../atoms/ButtonSolid";
|
|
9
|
+
|
|
10
|
+
// 3.- icons
|
|
11
|
+
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/solid";
|
|
12
|
+
|
|
13
|
+
// 4.- utils
|
|
14
|
+
import { classNames } from "../../../utils";
|
|
15
|
+
|
|
16
|
+
const Dropdown = ({
|
|
17
|
+
children,
|
|
18
|
+
data,
|
|
19
|
+
className = "",
|
|
20
|
+
classNameTooltip = "w-full",
|
|
21
|
+
classNameMenu = "",
|
|
22
|
+
classNameMenuItems = "",
|
|
23
|
+
classNameContainerSearch = "px-5",
|
|
24
|
+
isSelectMultiple = false,
|
|
25
|
+
optionsSelect,
|
|
26
|
+
propObject = "name",
|
|
27
|
+
propId = "id",
|
|
28
|
+
textNewItem = "",
|
|
29
|
+
defaultId = null,
|
|
30
|
+
loading = false,
|
|
31
|
+
search = null,
|
|
32
|
+
searchPlaceHolder = "Buscar...",
|
|
33
|
+
valueSearch = undefined,
|
|
34
|
+
idsOptionSelect = null,
|
|
35
|
+
isComponent,
|
|
36
|
+
hiddenMenu = false,
|
|
37
|
+
id,
|
|
38
|
+
onClick = () => {},
|
|
39
|
+
isRelative = true,
|
|
40
|
+
isMaxWidth = false,
|
|
41
|
+
isTag = false,
|
|
42
|
+
isClear = false,
|
|
43
|
+
clear = null,
|
|
44
|
+
applyButton = null,
|
|
45
|
+
applyButtonText = "Aplicar",
|
|
46
|
+
tagsParent = [],
|
|
47
|
+
isReadOnly,
|
|
48
|
+
isDisabledApplyButton = true,
|
|
49
|
+
isOpenMenu = undefined,
|
|
50
|
+
}) => {
|
|
51
|
+
const [ids, setIds] = useState(new Set());
|
|
52
|
+
const [tags, setTags] = useState(new Map());
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (idsOptionSelect === null) return;
|
|
56
|
+
setIds(new Set(new Set(idsOptionSelect)));
|
|
57
|
+
}, [idsOptionSelect]);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (tagsParent.length === 0) return;
|
|
61
|
+
const map = new Map();
|
|
62
|
+
tagsParent.forEach((item) => map.set(item.id, item));
|
|
63
|
+
setTags(map);
|
|
64
|
+
}, [tagsParent]);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!isClear) return;
|
|
68
|
+
setIds(new Set());
|
|
69
|
+
setTags(new Map());
|
|
70
|
+
clear && clear();
|
|
71
|
+
}, [clear, isClear]);
|
|
72
|
+
|
|
73
|
+
const selectOption = (item) => {
|
|
74
|
+
let tagsMap = new Map(tags);
|
|
75
|
+
let set = new Set(ids);
|
|
76
|
+
let uuid = null;
|
|
77
|
+
|
|
78
|
+
if (defaultId !== null) {
|
|
79
|
+
item[propId] === defaultId && (uuid = crypto.randomUUID());
|
|
80
|
+
uuid !== null && (item[propId] = uuid);
|
|
81
|
+
uuid !== null && (item[propObject] = item[propObject].replace(textNewItem, "").trim());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
set.has(item[propId]) ? set.delete(item[propId]) : set.add(item[propId]);
|
|
85
|
+
tagsMap.has(item[propId]) ? tagsMap.delete(item[propId]) : tagsMap.set(item[propId], item);
|
|
86
|
+
|
|
87
|
+
if (!isSelectMultiple) {
|
|
88
|
+
const arrSet = Array.from(set);
|
|
89
|
+
const arrMap = Array.from(tagsMap);
|
|
90
|
+
arrSet.shift();
|
|
91
|
+
arrMap.shift();
|
|
92
|
+
set = set.size === 2 ? new Set(arrSet) : set;
|
|
93
|
+
tagsMap = tagsMap.size === 2 ? new Map(arrMap) : tagsMap;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
optionsSelect(item, Array.from(set), uuid !== null);
|
|
97
|
+
setIds(set);
|
|
98
|
+
setTags(tagsMap);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const handleClick = (isClose) => {
|
|
102
|
+
onClick(isClose === true ? -1 : id);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const deleteTag = (item) => {
|
|
106
|
+
const set = new Set(ids);
|
|
107
|
+
const tagsMap = new Map(tags);
|
|
108
|
+
set.delete(item[propId]);
|
|
109
|
+
tagsMap.delete(item[propId]);
|
|
110
|
+
|
|
111
|
+
optionsSelect(item, Array.from(set));
|
|
112
|
+
setIds(set);
|
|
113
|
+
setTags(tagsMap);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
function capitalizeFirstLetter(text) {
|
|
117
|
+
if (typeof text !== "string") return text;
|
|
118
|
+
const textLowerCase = text.toLowerCase();
|
|
119
|
+
return textLowerCase.charAt(0).toUpperCase() + textLowerCase.slice(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Menu
|
|
124
|
+
as="div"
|
|
125
|
+
className={classNames(
|
|
126
|
+
"inline-block text-left",
|
|
127
|
+
isRelative && "relative",
|
|
128
|
+
classNameMenu !== "" && classNameMenu,
|
|
129
|
+
)}
|
|
130
|
+
onClick={() => handleClick(true)}
|
|
131
|
+
data-testid={`menu-${id}`}
|
|
132
|
+
id={id}
|
|
133
|
+
>
|
|
134
|
+
<style>{`
|
|
135
|
+
.blue {
|
|
136
|
+
background-color: #3758F9;
|
|
137
|
+
}
|
|
138
|
+
.w-custom {
|
|
139
|
+
width: 320px;
|
|
140
|
+
}
|
|
141
|
+
@media(min-width: 375px) {
|
|
142
|
+
.w-custom {
|
|
143
|
+
width: 375px;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
@media(min-width: 500px) {
|
|
147
|
+
.w-custom {
|
|
148
|
+
width: 414px;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
`}</style>
|
|
152
|
+
<Menu.Button
|
|
153
|
+
as="div"
|
|
154
|
+
onClick={() => handleClick(hiddenMenu)}
|
|
155
|
+
data-testid={`menu-button-${id}`}
|
|
156
|
+
>
|
|
157
|
+
{children}
|
|
158
|
+
</Menu.Button>
|
|
159
|
+
<div
|
|
160
|
+
className={classNames(
|
|
161
|
+
"absolute right-0 z-10",
|
|
162
|
+
classNameTooltip,
|
|
163
|
+
hiddenMenu && "hidden",
|
|
164
|
+
loading && "w-full",
|
|
165
|
+
)}
|
|
166
|
+
>
|
|
167
|
+
<Transition
|
|
168
|
+
as={Fragment}
|
|
169
|
+
enter="transition ease-out duration-100"
|
|
170
|
+
enterFrom="transform opacity-0 scale-95"
|
|
171
|
+
enterTo="transform opacity-100 scale-100"
|
|
172
|
+
leave="transition ease-in duration-75"
|
|
173
|
+
leaveFrom="transform opacity-100 scale-100"
|
|
174
|
+
leaveTo="transform opacity-0 scale-95"
|
|
175
|
+
show={isOpenMenu}
|
|
176
|
+
>
|
|
177
|
+
<Menu.Items
|
|
178
|
+
className={classNames(
|
|
179
|
+
"mt-1 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none",
|
|
180
|
+
loading ? "w-full" : classNameMenuItems === "" && "w-max",
|
|
181
|
+
classNameMenuItems !== "" && classNameMenuItems,
|
|
182
|
+
)}
|
|
183
|
+
>
|
|
184
|
+
{loading ? (
|
|
185
|
+
<div className="flex justify-center py-4">
|
|
186
|
+
<SpinnerIcon />
|
|
187
|
+
</div>
|
|
188
|
+
) : (
|
|
189
|
+
<div
|
|
190
|
+
className={classNames(
|
|
191
|
+
!isComponent && "pb-1",
|
|
192
|
+
"divide-y divide-gray-100",
|
|
193
|
+
className !== "" && className,
|
|
194
|
+
isMaxWidth && "w-custom",
|
|
195
|
+
)}
|
|
196
|
+
data-testid="dropdown-menu"
|
|
197
|
+
>
|
|
198
|
+
<div className="flex justify-between items-center sticky top-0 bg-white w-full">
|
|
199
|
+
{search !== null && (
|
|
200
|
+
<div
|
|
201
|
+
className={classNames(
|
|
202
|
+
"py-2 w-full",
|
|
203
|
+
classNameContainerSearch,
|
|
204
|
+
)}
|
|
205
|
+
>
|
|
206
|
+
<Input
|
|
207
|
+
htmlFor="search"
|
|
208
|
+
id={`dropdown-search-${id}`}
|
|
209
|
+
name="search"
|
|
210
|
+
type="text"
|
|
211
|
+
placeholder={searchPlaceHolder}
|
|
212
|
+
handleChange={(e) => search(e, id)}
|
|
213
|
+
icon={
|
|
214
|
+
searchPlaceHolder !== "" && (
|
|
215
|
+
<MagnifyingGlassIcon className="w-4 h-4 text-indigo-300" />
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
className="pl-5 pr-4 h-8"
|
|
219
|
+
classNameContainerInput="w-full"
|
|
220
|
+
value={valueSearch}
|
|
221
|
+
onKeyDown={(e) =>
|
|
222
|
+
e.key === " " && e.stopPropagation()
|
|
223
|
+
}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
{applyButton !== null && (
|
|
228
|
+
<div className="pl-1 pr-2 pt-1.5">
|
|
229
|
+
<ButtonSolid
|
|
230
|
+
classNameButton="bg-secondary-2 hover:bg-secondary-3 font-medium px-5 py-1.5 rounded-md"
|
|
231
|
+
outline={
|
|
232
|
+
ids.size === 0 && isDisabledApplyButton
|
|
233
|
+
? ""
|
|
234
|
+
: "0px 0px 0px 2px #3758F9, 0px 0px 0px 2px #243BBD, 0px 1px 2px 0px rgba(0, 0, 0, 0.05)"
|
|
235
|
+
}
|
|
236
|
+
classNameText="text-sm text-white text-center"
|
|
237
|
+
id="apply"
|
|
238
|
+
disabled={ids.size === 0 && isDisabledApplyButton}
|
|
239
|
+
onClick={() =>
|
|
240
|
+
applyButton({
|
|
241
|
+
id,
|
|
242
|
+
items: Array.from(tags.values()),
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
>
|
|
246
|
+
{applyButtonText}
|
|
247
|
+
</ButtonSolid>
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
{isTag && !isReadOnly && (
|
|
252
|
+
<div className="flex flex-wrap ml-7 pt-1">
|
|
253
|
+
{Array.from(tags).map((item, index) => (
|
|
254
|
+
<div
|
|
255
|
+
key={item}
|
|
256
|
+
className={classNames(index > 0 && "ml-1")}
|
|
257
|
+
>
|
|
258
|
+
<span className="inline-flex items-center gap-x-0.5 rounded-md bg-gray-100 px-2 py-1 text-xs font-medium text-gray-600 mb-1">
|
|
259
|
+
{item[1][propObject]}
|
|
260
|
+
<button
|
|
261
|
+
type="button"
|
|
262
|
+
className="p-0.5 rounded-sm bg-gray-100 hover:bg-gray-300"
|
|
263
|
+
onClick={() => deleteTag(item[1])}
|
|
264
|
+
data-testid={`tag-${item[0]}`}
|
|
265
|
+
>
|
|
266
|
+
<XMarkIcon className="h-2.5 w-2.5 stroke-gray-700/50 group-hover:stroke-gray-700/75" />
|
|
267
|
+
</button>
|
|
268
|
+
</span>
|
|
269
|
+
</div>
|
|
270
|
+
))}
|
|
271
|
+
</div>
|
|
272
|
+
)}
|
|
273
|
+
{(isReadOnly ? [] : data).map((item, index) => (
|
|
274
|
+
<div key={index} className={classNames(!isComponent && "py-1")}>
|
|
275
|
+
{isComponent ? (
|
|
276
|
+
<div
|
|
277
|
+
onClick={(e) => {
|
|
278
|
+
e.preventDefault();
|
|
279
|
+
optionsSelect();
|
|
280
|
+
}}
|
|
281
|
+
data-testid={`item-component-${index}`}
|
|
282
|
+
>
|
|
283
|
+
{item}
|
|
284
|
+
</div>
|
|
285
|
+
) : (
|
|
286
|
+
<Menu.Item>
|
|
287
|
+
<span
|
|
288
|
+
className={classNames(
|
|
289
|
+
ids.has(item[propId]) && "blue text-white",
|
|
290
|
+
!ids.has(item[propId]) &&
|
|
291
|
+
"text-gray-700 hover:bg-gray-100 hover:text-gray-900",
|
|
292
|
+
"block px-4 py-2 text-13 lg:text-sm cursor-pointer font-normal",
|
|
293
|
+
)}
|
|
294
|
+
data-testid={`item-${index}`}
|
|
295
|
+
onClick={(e) => {
|
|
296
|
+
e.preventDefault();
|
|
297
|
+
selectOption(item);
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
{capitalizeFirstLetter(item[propObject])}
|
|
301
|
+
</span>
|
|
302
|
+
</Menu.Item>
|
|
303
|
+
)}
|
|
304
|
+
</div>
|
|
305
|
+
))}
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
</Menu.Items>
|
|
309
|
+
</Transition>
|
|
310
|
+
</div>
|
|
311
|
+
</Menu>
|
|
312
|
+
);
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
export default Dropdown;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { render, fireEvent, act } from "@testing-library/react";
|
|
2
|
+
import Dropdown from "./index";
|
|
3
|
+
|
|
4
|
+
describe("Dropdown", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
jest.resetAllMocks();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("renders Dropdown component without errors", () => {
|
|
10
|
+
render(
|
|
11
|
+
<Dropdown
|
|
12
|
+
isRelative={false}
|
|
13
|
+
data={[
|
|
14
|
+
{ name: "data1", id: 1 },
|
|
15
|
+
{ name: "data2", id: 2 },
|
|
16
|
+
]}
|
|
17
|
+
loading
|
|
18
|
+
classNameMenuItems="text-xs"
|
|
19
|
+
hiddenMenu
|
|
20
|
+
>
|
|
21
|
+
<p>List1</p>
|
|
22
|
+
<p>List2</p>
|
|
23
|
+
<p>List3</p>
|
|
24
|
+
</Dropdown>,
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("select option", async () => {
|
|
29
|
+
const mockOptionsSelect = jest.fn();
|
|
30
|
+
const mockOnClick = jest.fn();
|
|
31
|
+
const component = render(
|
|
32
|
+
<Dropdown
|
|
33
|
+
data={[
|
|
34
|
+
{ name: "data1", id: 1 },
|
|
35
|
+
{ name: "data2", id: 2 },
|
|
36
|
+
{ name: "data3", id: 3 },
|
|
37
|
+
{ name: 4, id: 4 },
|
|
38
|
+
]}
|
|
39
|
+
defaultId={1}
|
|
40
|
+
propObject="name"
|
|
41
|
+
optionsSelect={mockOptionsSelect}
|
|
42
|
+
className="w-full"
|
|
43
|
+
isMaxWidth
|
|
44
|
+
id="dropdown-test"
|
|
45
|
+
onClick={mockOnClick}
|
|
46
|
+
classNameMenu="text-xs"
|
|
47
|
+
textNewItem="data6"
|
|
48
|
+
>
|
|
49
|
+
<p>dropdown</p>
|
|
50
|
+
</Dropdown>,
|
|
51
|
+
);
|
|
52
|
+
const menuButton = component.getByTestId("menu-button-dropdown-test");
|
|
53
|
+
const menu = component.getByTestId("menu-dropdown-test");
|
|
54
|
+
await act(async () => fireEvent.click(menu));
|
|
55
|
+
await act(async () => fireEvent.click(menuButton));
|
|
56
|
+
expect(mockOnClick).toHaveBeenCalledWith(-1);
|
|
57
|
+
|
|
58
|
+
const item1 = component.getByTestId("item-0");
|
|
59
|
+
const item2 = component.getByTestId("item-1");
|
|
60
|
+
const item3 = component.getByTestId("item-2");
|
|
61
|
+
await act(async () => fireEvent.click(item1));
|
|
62
|
+
expect(item1).toHaveClass("blue text-white");
|
|
63
|
+
await act(async () => fireEvent.click(item1));
|
|
64
|
+
await act(async () => fireEvent.click(item2));
|
|
65
|
+
expect(item2).toHaveClass("blue text-white");
|
|
66
|
+
expect(item1).toHaveClass("text-gray-700 hover:bg-gray-100 hover:text-gray-900");
|
|
67
|
+
await act(async () => fireEvent.click(item3));
|
|
68
|
+
expect(item3).toHaveClass("blue text-white");
|
|
69
|
+
expect(mockOptionsSelect).toHaveBeenCalledTimes(4);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("select multiple option", async () => {
|
|
73
|
+
const component = render(
|
|
74
|
+
<Dropdown
|
|
75
|
+
data={[
|
|
76
|
+
{ name: "data1", id: 1 },
|
|
77
|
+
{ name: "data2", id: 2 },
|
|
78
|
+
{ name: "data3", id: 3 },
|
|
79
|
+
{ name: "data4", id: 4 },
|
|
80
|
+
]}
|
|
81
|
+
propObject="name"
|
|
82
|
+
isSelectMultiple
|
|
83
|
+
optionsSelect={() => {}}
|
|
84
|
+
id="dropdown-test"
|
|
85
|
+
isTag
|
|
86
|
+
>
|
|
87
|
+
<p>dropdown</p>
|
|
88
|
+
</Dropdown>,
|
|
89
|
+
);
|
|
90
|
+
const menuButton = component.getByTestId("menu-button-dropdown-test");
|
|
91
|
+
await act(async () => fireEvent.click(menuButton));
|
|
92
|
+
|
|
93
|
+
const item1 = component.getByTestId("item-1");
|
|
94
|
+
const item2 = component.getByTestId("item-2");
|
|
95
|
+
const item3 = component.getByTestId("item-3");
|
|
96
|
+
await act(async () => fireEvent.click(item1));
|
|
97
|
+
await act(async () => fireEvent.click(item2));
|
|
98
|
+
await act(async () => fireEvent.click(item3));
|
|
99
|
+
|
|
100
|
+
expect(item1).toHaveClass("blue text-white");
|
|
101
|
+
expect(item2).toHaveClass("blue text-white");
|
|
102
|
+
expect(item3).toHaveClass("blue text-white");
|
|
103
|
+
|
|
104
|
+
const tags = component.getAllByTestId(/tag-/i);
|
|
105
|
+
expect(tags.length).toBe(3);
|
|
106
|
+
await act(async () => fireEvent.click(tags[1]));
|
|
107
|
+
|
|
108
|
+
const tags2 = component.getAllByTestId(/tag-/i);
|
|
109
|
+
expect(tags2.length).toBe(2);
|
|
110
|
+
|
|
111
|
+
await act(async () => fireEvent.click(item3));
|
|
112
|
+
expect(item3).toHaveClass("text-gray-700 hover:bg-gray-100 hover:text-gray-900");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("search input", async () => {
|
|
116
|
+
const mockSearch = jest.fn();
|
|
117
|
+
const component = render(
|
|
118
|
+
<Dropdown
|
|
119
|
+
data={["item1", "item2", "item3"]}
|
|
120
|
+
isComponent
|
|
121
|
+
optionsSelect={() => {}}
|
|
122
|
+
id="dropdown-test"
|
|
123
|
+
isTag
|
|
124
|
+
idOptionSelect={0}
|
|
125
|
+
search={mockSearch}
|
|
126
|
+
>
|
|
127
|
+
<p>dropdown</p>
|
|
128
|
+
</Dropdown>,
|
|
129
|
+
);
|
|
130
|
+
const menuButton = component.getByTestId("menu-button-dropdown-test");
|
|
131
|
+
await act(async () => fireEvent.click(menuButton));
|
|
132
|
+
const item = component.getByTestId("item-component-1");
|
|
133
|
+
fireEvent.click(item);
|
|
134
|
+
|
|
135
|
+
const input = component.getByTestId("input-dropdown-search-dropdown-test");
|
|
136
|
+
fireEvent.change(input, { target: { value: "data2" } });
|
|
137
|
+
fireEvent.keyDown(input, { key: " ", keyCode: 32 });
|
|
138
|
+
expect(mockSearch).toHaveBeenCalled();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("applyButton", async () => {
|
|
142
|
+
const mockApplyButton = jest.fn();
|
|
143
|
+
const component = render(
|
|
144
|
+
<Dropdown
|
|
145
|
+
data={[
|
|
146
|
+
{ name: "data1", id: 1 },
|
|
147
|
+
{ name: "data2", id: 2 },
|
|
148
|
+
{ name: "data3", id: 3 },
|
|
149
|
+
]}
|
|
150
|
+
optionsSelect={() => {}}
|
|
151
|
+
id="dropdown-test"
|
|
152
|
+
onClick={() => {}}
|
|
153
|
+
applyButton={mockApplyButton}
|
|
154
|
+
>
|
|
155
|
+
<p>dropdown</p>
|
|
156
|
+
</Dropdown>,
|
|
157
|
+
);
|
|
158
|
+
const menuButton = component.getByTestId("menu-button-dropdown-test");
|
|
159
|
+
await act(async () => fireEvent.click(menuButton));
|
|
160
|
+
const item = component.getByTestId("item-1");
|
|
161
|
+
fireEvent.click(item);
|
|
162
|
+
|
|
163
|
+
const button = component.getByTestId("button-solid-apply");
|
|
164
|
+
fireEvent.click(button);
|
|
165
|
+
expect(mockApplyButton).toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("clear ids and tags", async () => {
|
|
169
|
+
const mockClear = jest.fn();
|
|
170
|
+
render(
|
|
171
|
+
<Dropdown
|
|
172
|
+
data={[
|
|
173
|
+
{ name: "data1", id: 1 },
|
|
174
|
+
{ name: "data2", id: 2 },
|
|
175
|
+
{ name: "data3", id: 3 },
|
|
176
|
+
]}
|
|
177
|
+
id="dropdown-test"
|
|
178
|
+
idsOptionSelect={[1]}
|
|
179
|
+
isChangeOptionIdsOptionSelect
|
|
180
|
+
tagsParent={[{ id: 1 }]}
|
|
181
|
+
clear={mockClear}
|
|
182
|
+
isClear
|
|
183
|
+
isReadOnly
|
|
184
|
+
>
|
|
185
|
+
<p>dropdown</p>
|
|
186
|
+
</Dropdown>,
|
|
187
|
+
);
|
|
188
|
+
expect(mockClear).toHaveBeenCalled();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// 1.- libraries
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { Dialog, Transition } from "@headlessui/react";
|
|
4
|
+
import { CheckIcon } from "@heroicons/react/24/outline";
|
|
5
|
+
|
|
6
|
+
// 2.- components
|
|
7
|
+
import ButtonSolid from "../../atoms/ButtonSolid";
|
|
8
|
+
|
|
9
|
+
// 3.- utils
|
|
10
|
+
import { classNames } from "../../../utils";
|
|
11
|
+
|
|
12
|
+
export default function Modal({
|
|
13
|
+
children,
|
|
14
|
+
className = "sm:max-w-sm w-11/12 sm:my-8",
|
|
15
|
+
classNameContainer = "",
|
|
16
|
+
isOpen = false,
|
|
17
|
+
setIsOpen,
|
|
18
|
+
showIconSuccess = true,
|
|
19
|
+
showButton = true,
|
|
20
|
+
}) {
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<style>{`
|
|
24
|
+
.bg-opacity-35 {
|
|
25
|
+
opacity: .35;
|
|
26
|
+
}
|
|
27
|
+
`}</style>
|
|
28
|
+
<Transition.Root show={isOpen} as={Fragment}>
|
|
29
|
+
<Dialog
|
|
30
|
+
as="div"
|
|
31
|
+
className="relative z-50 relative"
|
|
32
|
+
onClose={setIsOpen}
|
|
33
|
+
data-testid="modal"
|
|
34
|
+
>
|
|
35
|
+
<Transition.Child
|
|
36
|
+
as={Fragment}
|
|
37
|
+
enter="ease-out duration-300"
|
|
38
|
+
enterFrom="opacity-0"
|
|
39
|
+
enterTo="opacity-100"
|
|
40
|
+
leave="ease-in duration-200"
|
|
41
|
+
leaveFrom="opacity-100"
|
|
42
|
+
leaveTo="opacity-0"
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
className="fixed inset-0 bg-opacity-35 bg-gray-900 transition-opacity"
|
|
46
|
+
data-testid="window-close"
|
|
47
|
+
onClick={() => setIsOpen(false)}
|
|
48
|
+
/>
|
|
49
|
+
</Transition.Child>
|
|
50
|
+
|
|
51
|
+
<div className="fixed inset-0 z-10 overflow-y-auto">
|
|
52
|
+
<div className="flex min-h-full items-center justify-center xs:p-2 p-4 text-center sm:items-center sm:p-0">
|
|
53
|
+
<Transition.Child
|
|
54
|
+
as={Fragment}
|
|
55
|
+
enter="ease-out duration-300"
|
|
56
|
+
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
57
|
+
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
58
|
+
leave="ease-in duration-200"
|
|
59
|
+
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
60
|
+
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
61
|
+
>
|
|
62
|
+
<Dialog.Panel
|
|
63
|
+
className={classNames(
|
|
64
|
+
"relative transform rounded-lg bg-white px-4 pb-3 pt-5 text-left shadow-xl transition-all sm:w-full sm:pt-3 sm:px-3",
|
|
65
|
+
className !== "" && className,
|
|
66
|
+
)}
|
|
67
|
+
>
|
|
68
|
+
{showIconSuccess && (
|
|
69
|
+
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
|
70
|
+
<CheckIcon
|
|
71
|
+
className="h-6 w-6 text-green-600"
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
<div
|
|
77
|
+
className={classNames(
|
|
78
|
+
classNameContainer !== "" && classNameContainer,
|
|
79
|
+
)}
|
|
80
|
+
data-testid="content"
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
</div>
|
|
84
|
+
{showButton && (
|
|
85
|
+
<div className="mt-5 sm:mt-6">
|
|
86
|
+
<ButtonSolid
|
|
87
|
+
id="close-modal"
|
|
88
|
+
onClick={() => setIsOpen(false)}
|
|
89
|
+
>
|
|
90
|
+
Cerrar
|
|
91
|
+
</ButtonSolid>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
<button className="opacity-0 absolute"></button>
|
|
95
|
+
</Dialog.Panel>
|
|
96
|
+
</Transition.Child>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</Dialog>
|
|
100
|
+
</Transition.Root>
|
|
101
|
+
</>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { render, fireEvent, act } from "@testing-library/react";
|
|
2
|
+
import Modal from "./index";
|
|
3
|
+
|
|
4
|
+
global.ResizeObserver = class ResizeObserver {
|
|
5
|
+
observe() {}
|
|
6
|
+
unobserve() {}
|
|
7
|
+
disconnect() {}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe("Modal", () => {
|
|
11
|
+
it("renders Modal component without errors", () => {
|
|
12
|
+
render(
|
|
13
|
+
<Modal open={false}>
|
|
14
|
+
<p>text modal</p>
|
|
15
|
+
</Modal>,
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("close modal window", async () => {
|
|
20
|
+
const setSidebarOpenMock = jest.fn();
|
|
21
|
+
const component = render(
|
|
22
|
+
<Modal isOpen={true} setIsOpen={setSidebarOpenMock} classNameContainer="w-full">
|
|
23
|
+
<p>text modal</p>
|
|
24
|
+
</Modal>,
|
|
25
|
+
);
|
|
26
|
+
const close = component.getByTestId("window-close");
|
|
27
|
+
await act(async () => fireEvent.click(close));
|
|
28
|
+
expect(setSidebarOpenMock).toHaveBeenCalled();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("close modal", async () => {
|
|
32
|
+
const setSidebarOpenMock = jest.fn();
|
|
33
|
+
const component = render(
|
|
34
|
+
<Modal isOpen={true} setIsOpen={setSidebarOpenMock} classNameContainer="w-full">
|
|
35
|
+
<p>text modal</p>
|
|
36
|
+
</Modal>,
|
|
37
|
+
);
|
|
38
|
+
const close = component.getByTestId("button-solid-close-modal");
|
|
39
|
+
await act(async () => fireEvent.click(close));
|
|
40
|
+
expect(setSidebarOpenMock).toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
});
|