@onewelcome/react-lib-components 0.2.4 → 0.2.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onewelcome/react-lib-components",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "license": "Apache-2.0",
5
5
  "author": "OneWelcome B.V.",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,15 @@
11
11
  padding: 0;
12
12
  min-width: 200px;
13
13
 
14
+ &.no-margin-top {
15
+ margin: 0 0 1rem;
16
+ }
17
+
14
18
  button {
15
19
  text-align: left;
16
20
  }
17
21
  }
22
+
23
+ .decorative-element {
24
+ margin-top: 1rem;
25
+ }
@@ -88,6 +88,16 @@ describe("ContextMenu should render", () => {
88
88
  expect(child.parentElement).toHaveClass("custom");
89
89
  });
90
90
 
91
+ it("renders the decorative element", () => {
92
+ const { getByText } = createContextMenu(defaultParams => ({
93
+ ...defaultParams,
94
+ show: true,
95
+ decorativeElement: <div>test</div>
96
+ }));
97
+
98
+ expect(getByText("test")).toBeInTheDocument();
99
+ });
100
+
91
101
  it("should throw an error", () => {
92
102
  // Prevent throwing an error in the console when this test is executed. We fix this and the end of this test.
93
103
  const err = console.error;
@@ -252,4 +262,37 @@ describe("accessibility controls", () => {
252
262
 
253
263
  expect(thirdContextMenuItem).toHaveFocus();
254
264
  });
265
+
266
+ it("opens correctly with enter, navigate with arrow keys skipping the decorative element", () => {
267
+ onClick.mockImplementation(e => {
268
+ expect(e.target.getAttribute("data-testid")).toBe("contextmenuitem3");
269
+ });
270
+
271
+ const { trigger, getByTestId, getByText } = createContextMenu(defaultParams => ({
272
+ ...defaultParams,
273
+ decorativeElement: <div>test</div>
274
+ }));
275
+ const thirdContextMenuItem = getByTestId("contextmenuitem3");
276
+
277
+ userEvent.tab();
278
+ userEvent.keyboard("{enter}");
279
+
280
+ expect(getByText("test")).toBeInTheDocument();
281
+
282
+ expect(trigger).toHaveAttribute("aria-expanded", "true");
283
+
284
+ userEvent.keyboard("{arrowdown}");
285
+ userEvent.keyboard("{arrowdown}");
286
+ userEvent.keyboard("{arrowdown}");
287
+
288
+ expect(thirdContextMenuItem).toHaveFocus();
289
+
290
+ userEvent.keyboard("{space}");
291
+
292
+ expect(onClick).toHaveBeenCalled();
293
+
294
+ userEvent.keyboard("{space}");
295
+
296
+ expect(thirdContextMenuItem).toHaveFocus();
297
+ });
255
298
  });
@@ -16,6 +16,7 @@ import { createPortal } from "react-dom";
16
16
 
17
17
  export interface Props extends ComponentPropsWithRef<"div"> {
18
18
  trigger: ReactElement<ButtonProps> | ReactElement<IconButtonProps>;
19
+ decorativeElement?: ReactNode;
19
20
  children: ReactNode;
20
21
  placement?: Placement;
21
22
  transformOrigin?: Placement;
@@ -32,6 +33,7 @@ export const ContextMenu = React.forwardRef<HTMLDivElement, Props>(
32
33
  {
33
34
  trigger,
34
35
  children,
36
+ decorativeElement,
35
37
  id,
36
38
  show = false,
37
39
  onShow,
@@ -183,7 +185,15 @@ export const ContextMenu = React.forwardRef<HTMLDivElement, Props>(
183
185
  anchorEl={anchorEl}
184
186
  show={showContextMenu}
185
187
  >
186
- <ul className={classes["menu"]} id={`${id}-menu`} aria-describedby={id} role="menu">
188
+ {decorativeElement && (
189
+ <div className={classes["decorative-element"]}>{decorativeElement}</div>
190
+ )}
191
+ <ul
192
+ className={`${classes["menu"]} ${decorativeElement ? classes["no-margin-top"] : ""}`}
193
+ id={`${id}-menu`}
194
+ aria-describedby={id}
195
+ role="menu"
196
+ >
187
197
  {renderChildren()}
188
198
  </ul>
189
199
  </Popover>,
@@ -17,11 +17,6 @@ $borderRadius: 2.5rem;
17
17
  display: block;
18
18
  pointer-events: none;
19
19
 
20
- &.disabled {
21
- opacity: 0.25;
22
- cursor: not-allowed;
23
- }
24
-
25
20
  &:before {
26
21
  content: "";
27
22
  width: 1rem;
@@ -42,6 +37,11 @@ $borderRadius: 2.5rem;
42
37
  transform: translateY(-50%) translateX(calc(100% - 0.25rem));
43
38
  }
44
39
  }
40
+
41
+ &.disabled {
42
+ background-color: var(--disabled);
43
+ cursor: not-allowed;
44
+ }
45
45
  }
46
46
 
47
47
  .toggle-helper {
@@ -1,4 +1,4 @@
1
- import React, { useState } from "react";
1
+ import React, { useEffect, useState } from "react";
2
2
  import {
3
3
  render,
4
4
  getByTestId,
@@ -6,20 +6,26 @@ import {
6
6
  waitFor,
7
7
  getAllByRole,
8
8
  getByText,
9
- findByText,
10
- findAllByText,
11
- queryByText
9
+ fireEvent
12
10
  } from "@testing-library/react";
13
11
  import { SnackbarProvider, Props } from "./SnackbarProvider";
14
12
  import { useSnackbar } from "../useSnackbar";
15
13
  import userEvent from "@testing-library/user-event";
16
14
 
17
15
  const successProps = {
18
- title: "success title"
16
+ title: "success title",
17
+ options: {
18
+ duration: 10,
19
+ onClose: jest.fn()
20
+ }
19
21
  };
20
22
 
21
23
  const errorProps = {
22
- title: "error title"
24
+ title: "error title",
25
+ options: {
26
+ duration: 10,
27
+ onClose: jest.fn()
28
+ }
23
29
  };
24
30
 
25
31
  const infoProps = {
@@ -43,7 +49,7 @@ const renderSnackbarProvider = (props?: Partial<Props>) => {
43
49
  <button
44
50
  data-testid="show-success"
45
51
  onClick={() => {
46
- enqueueSuccessSnackbar(successProps.title + index);
52
+ enqueueSuccessSnackbar(successProps.title + index, undefined, successProps.options);
47
53
  setIndex(index + 1);
48
54
  }}
49
55
  >
@@ -96,24 +102,24 @@ describe("SnackbarProvider", () => {
96
102
  expect(container).toHaveTextContent("content");
97
103
  });
98
104
 
99
- it("should stack 3 snackbars at one time", () => {
105
+ it("should stack 3 snackbars at one time", async () => {
100
106
  const { showSuccessSnackbarBtn } = renderSnackbarProvider();
101
107
 
102
- userEvent.click(showSuccessSnackbarBtn);
103
- userEvent.click(showSuccessSnackbarBtn);
104
- userEvent.click(showSuccessSnackbarBtn);
105
- userEvent.click(showSuccessSnackbarBtn);
108
+ await userEvent.click(showSuccessSnackbarBtn);
109
+ await userEvent.click(showSuccessSnackbarBtn);
110
+ await userEvent.click(showSuccessSnackbarBtn);
111
+ await userEvent.click(showSuccessSnackbarBtn);
106
112
 
107
113
  expect(getAllByText(document.body, new RegExp(successProps.title))).toHaveLength(3);
108
114
  });
109
115
 
110
- it("should render 3 variants of snackbars", () => {
116
+ it("should render 3 variants of snackbars", async () => {
111
117
  const { showSuccessSnackbarBtn, showErrorSnackbarBtn, showInfoSnackbarBtn } =
112
118
  renderSnackbarProvider();
113
119
 
114
- userEvent.click(showSuccessSnackbarBtn);
115
- userEvent.click(showErrorSnackbarBtn);
116
- userEvent.click(showInfoSnackbarBtn);
120
+ await userEvent.click(showSuccessSnackbarBtn);
121
+ await userEvent.click(showErrorSnackbarBtn);
122
+ await userEvent.click(showInfoSnackbarBtn);
117
123
 
118
124
  expect(getByText(document.body, new RegExp(successProps.title))).toBeDefined();
119
125
  expect(getByText(document.body, new RegExp(errorProps.title))).toBeDefined();
@@ -122,58 +128,45 @@ describe("SnackbarProvider", () => {
122
128
  const infoSnackbarActions = getAllByRole(document.body, "button", { name: /Contact/i });
123
129
  expect(infoSnackbarActions).toHaveLength(2);
124
130
 
125
- userEvent.click(infoSnackbarActions[0]);
126
- userEvent.click(infoSnackbarActions[1]);
131
+ await userEvent.click(infoSnackbarActions[0]);
132
+ await userEvent.click(infoSnackbarActions[1]);
127
133
 
128
134
  expect(infoProps.options.actions[0].onClick).toBeCalledTimes(1);
129
- waitFor(() => expect(infoProps.options.actions[1].onClick).toBeCalledTimes(1));
130
135
  });
136
+ });
131
137
 
132
- it("should stack 3 snackbars at one time and then after 3 disapear show the fourth one", () => {
133
- const { showSuccessSnackbarBtn } = renderSnackbarProvider({
134
- autoHideDuration: { long: 1, short: 1 }
135
- });
138
+ describe("handlers", () => {
139
+ it("should fire onClose", async () => {
140
+ const onCloseHandler = jest.fn();
141
+ const ExampleComponent = () => {
142
+ const { enqueueErrorSnackbar, enqueueSuccessSnackbar } = useSnackbar();
136
143
 
137
- userEvent.click(showSuccessSnackbarBtn);
138
- userEvent.click(showSuccessSnackbarBtn);
139
- userEvent.click(showSuccessSnackbarBtn);
140
- userEvent.click(showSuccessSnackbarBtn);
144
+ useEffect(() => {
145
+ enqueueErrorSnackbar("error", undefined, { onClose: onCloseHandler, duration: 1 });
146
+ enqueueSuccessSnackbar("success", undefined, { onClose: onCloseHandler, duration: 1 });
147
+ }, []);
141
148
 
142
- expect(getAllByText(document.body, new RegExp(successProps.title))).toHaveLength(3);
149
+ return <div></div>;
150
+ };
143
151
 
144
- /** Looking for fourth one to be shown */
145
- waitFor(() => expect(getAllByText(document.body, successProps.title + "3")).toHaveLength(1));
146
-
147
- /** There shouldn't be any other snackbars */
148
- waitFor(() =>
149
- expect(getAllByText(document.body, new RegExp(successProps.title))).not.toBeDefined()
152
+ const queries = render(
153
+ <SnackbarProvider closeButtonTitle="close">
154
+ <ExampleComponent />
155
+ </SnackbarProvider>
150
156
  );
151
- });
152
-
153
- it("should close snackbar after clicking X button", async () => {
154
- const { showSuccessSnackbarBtn } = renderSnackbarProvider({
155
- autoHideDuration: { long: 1_000_000, short: 1_000_000 }
156
- });
157
157
 
158
- userEvent.click(showSuccessSnackbarBtn);
159
- userEvent.click(showSuccessSnackbarBtn);
160
- userEvent.click(showSuccessSnackbarBtn);
161
- userEvent.click(showSuccessSnackbarBtn);
158
+ const errorSnackbar = await queries.findByText(/error/i);
159
+ const successSnackbar = await queries.findByText(/success/i);
162
160
 
163
- const closeButtons = getAllByRole(document.body, "button", { name: "close" });
164
-
165
- expect(closeButtons).toHaveLength(3);
166
- expect(getAllByText(document.body, new RegExp(successProps.title))).toHaveLength(3);
161
+ expect(errorSnackbar).toBeTruthy();
162
+ expect(successSnackbar).toBeTruthy();
167
163
 
168
- userEvent.click(closeButtons[0]);
169
- expect(
170
- await findAllByText(document.body, new RegExp(successProps.title + "[12]+"))
171
- ).toHaveLength(2);
164
+ const parentErrorSnackbar = errorSnackbar.closest(".snackbar")!;
165
+ const parentSuccessSnackbar = successSnackbar.closest(".snackbar")!;
172
166
 
173
- userEvent.click(closeButtons[1]);
174
- expect(await findByText(document.body, successProps.title + "2")).toBeDefined();
167
+ await fireEvent.animationEnd(parentErrorSnackbar);
168
+ await fireEvent.animationEnd(parentSuccessSnackbar);
175
169
 
176
- userEvent.click(closeButtons[2]);
177
- waitFor(() => expect(queryByText(document.body, new RegExp(successProps.title))).toBeNull());
170
+ await waitFor(() => expect(onCloseHandler).toHaveBeenCalledTimes(2));
178
171
  });
179
172
  });
@@ -29,6 +29,7 @@ interface Item {
29
29
  variant: Variant;
30
30
  content?: string;
31
31
  actions?: Actions;
32
+ onClose?: () => void;
32
33
  }
33
34
 
34
35
  export const SnackbarProvider = (
@@ -65,7 +66,8 @@ export const SnackbarProvider = (
65
66
  const {
66
67
  variant = "info",
67
68
  actions,
68
- duration = getDuration(variant, actions, content)
69
+ duration = getDuration(variant, actions, content),
70
+ onClose
69
71
  } = options;
70
72
  const item: Item = {
71
73
  title,
@@ -73,7 +75,8 @@ export const SnackbarProvider = (
73
75
  variant,
74
76
  actions,
75
77
  duration,
76
- id: generateID(15, title)
78
+ id: generateID(15, title),
79
+ onClose
77
80
  };
78
81
  addSnackbar(item);
79
82
  };
@@ -103,7 +106,10 @@ export const SnackbarProvider = (
103
106
  <SnackbarItem
104
107
  {...item}
105
108
  key={item.id}
106
- onClose={onItemClosed}
109
+ onClose={() => {
110
+ onItemClosed(item.id);
111
+ item.onClose && item.onClose();
112
+ }}
107
113
  closeButtonTitle={closeButtonTitle}
108
114
  />
109
115
  ) : null
@@ -8,4 +8,5 @@ export interface SnackbarOptionsProps {
8
8
  actions?: Actions;
9
9
  variant?: Variant;
10
10
  duration?: number;
11
+ onClose?: () => void;
11
12
  }