@allurereport/web-awesome 3.0.0-beta.3 → 3.0.0-beta.5

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.
Files changed (89) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/CONTRIBUTING.md +34 -0
  3. package/dist/multi/{141.app-b6362ca0.js → 141.app-71d7f77e.js} +1 -1
  4. package/dist/multi/222.app-71d7f77e.js +1 -0
  5. package/dist/multi/335.app-71d7f77e.js +1 -0
  6. package/dist/multi/{34.app-b6362ca0.js → 34.app-71d7f77e.js} +1 -1
  7. package/dist/multi/349.app-71d7f77e.js +1 -0
  8. package/dist/multi/378.app-71d7f77e.js +1 -0
  9. package/dist/multi/{406.app-b6362ca0.js → 406.app-71d7f77e.js} +1 -1
  10. package/dist/multi/476.app-71d7f77e.js +1 -0
  11. package/dist/multi/{53.app-b6362ca0.js → 53.app-71d7f77e.js} +1 -1
  12. package/dist/multi/{584.app-b6362ca0.js → 584.app-71d7f77e.js} +1 -1
  13. package/dist/multi/690.app-71d7f77e.js +1 -0
  14. package/dist/multi/{747.app-b6362ca0.js → 747.app-71d7f77e.js} +1 -1
  15. package/dist/multi/{767.app-b6362ca0.js → 767.app-71d7f77e.js} +1 -1
  16. package/dist/multi/{816.app-b6362ca0.js → 816.app-71d7f77e.js} +1 -1
  17. package/dist/multi/83.app-71d7f77e.js +1 -0
  18. package/dist/multi/{873.app-b6362ca0.js → 873.app-71d7f77e.js} +1 -1
  19. package/dist/multi/{920.app-b6362ca0.js → 920.app-71d7f77e.js} +1 -1
  20. package/dist/multi/{991.app-b6362ca0.js → 991.app-71d7f77e.js} +1 -1
  21. package/dist/multi/app-71d7f77e.js +2 -0
  22. package/dist/multi/manifest.json +20 -20
  23. package/dist/multi/{styles-b6362ca0.css → styles-71d7f77e.css} +6 -6
  24. package/dist/single/app-7aa8b012.js +2 -0
  25. package/dist/single/manifest.json +1 -1
  26. package/package.json +11 -4
  27. package/src/assets/scss/_common.scss +9 -0
  28. package/src/components/app/ArrowButton/index.tsx +3 -2
  29. package/src/components/app/BaseLayout/index.tsx +5 -5
  30. package/src/components/app/ReportBody/Filters.tsx +12 -10
  31. package/src/components/app/ReportBody/HeaderActions.tsx +3 -3
  32. package/src/components/app/ReportBody/SortBy.tsx +10 -10
  33. package/src/components/app/ReportBody/context.tsx +0 -1
  34. package/src/components/app/ReportHeader/index.tsx +1 -1
  35. package/src/components/app/Tabs/index.tsx +2 -3
  36. package/src/components/app/TestResult/TestResultDescription/index.tsx +3 -3
  37. package/src/components/app/TestResult/TestResultNavigation/index.tsx +34 -37
  38. package/src/components/app/TestResult/TestResultNavigation/styles.scss +1 -1
  39. package/src/components/app/TestResult/TestResultSteps/attachment.tsx +4 -6
  40. package/src/components/app/Tree/Tree.tsx +54 -101
  41. package/src/components/app/Tree/TreeHeader.tsx +13 -12
  42. package/src/components/app/Tree/TreeItem.tsx +3 -1
  43. package/src/components/app/Tree/index.tsx +31 -7
  44. package/src/components/app/Tree/styles.scss +9 -3
  45. package/src/components/commons/Menu/index.tsx +44 -19
  46. package/src/components/commons/SearchBox/index.tsx +8 -5
  47. package/src/components/commons/SuccessRatePieChart/styles.scss +0 -1
  48. package/src/components/commons/Toggle/index.tsx +3 -2
  49. package/src/components/commons/Tooltip/index.tsx +3 -3
  50. package/src/i18n/constants.ts +21 -2
  51. package/src/i18n/locales/am.json +3 -1
  52. package/src/i18n/locales/az.json +3 -1
  53. package/src/i18n/locales/de.json +3 -1
  54. package/src/i18n/locales/en.json +4 -2
  55. package/src/i18n/locales/es.json +3 -0
  56. package/src/i18n/locales/fr.json +3 -1
  57. package/src/i18n/locales/he.json +3 -1
  58. package/src/i18n/locales/it.json +3 -1
  59. package/src/i18n/locales/ja.json +3 -1
  60. package/src/i18n/locales/ka.json +3 -1
  61. package/src/i18n/locales/kr.json +3 -1
  62. package/src/i18n/locales/nl.json +3 -1
  63. package/src/i18n/locales/pl.json +3 -1
  64. package/src/i18n/locales/pt.json +3 -1
  65. package/src/i18n/locales/ru.json +3 -1
  66. package/src/i18n/locales/sv.json +3 -1
  67. package/src/i18n/locales/tr.json +3 -1
  68. package/src/i18n/locales/zh.json +4 -2
  69. package/src/index.html +1 -0
  70. package/src/stores/chart.ts +2 -2
  71. package/src/stores/testResults.ts +26 -4
  72. package/src/stores/tree.ts +98 -4
  73. package/src/types/globals.d.ts +6 -1
  74. package/src/utils/capitalize.ts +5 -3
  75. package/src/utils/treeFilters.ts +73 -120
  76. package/test/utils/treeFilters.test.ts +424 -0
  77. package/types.d.ts +25 -4
  78. package/vitest.config.ts +12 -0
  79. package/dist/multi/222.app-b6362ca0.js +0 -1
  80. package/dist/multi/335.app-b6362ca0.js +0 -1
  81. package/dist/multi/349.app-b6362ca0.js +0 -1
  82. package/dist/multi/378.app-b6362ca0.js +0 -1
  83. package/dist/multi/476.app-b6362ca0.js +0 -1
  84. package/dist/multi/690.app-b6362ca0.js +0 -1
  85. package/dist/multi/83.app-b6362ca0.js +0 -1
  86. package/dist/multi/app-b6362ca0.js +0 -2
  87. package/dist/single/app-57ae0a60.js +0 -2
  88. /package/dist/multi/{app-b6362ca0.js.LICENSE.txt → app-71d7f77e.js.LICENSE.txt} +0 -0
  89. /package/dist/single/{app-57ae0a60.js.LICENSE.txt → app-7aa8b012.js.LICENSE.txt} +0 -0
@@ -1,121 +1,74 @@
1
- import type { Statistic, TestStatus, WithChildren } from "@allurereport/core-api";
1
+ import type { Statistic } from "@allurereport/core-api";
2
2
  import cx from "clsx";
3
3
  import type { FunctionComponent } from "preact";
4
4
  import { useState } from "preact/hooks";
5
- import { useReportContentContext } from "@/components/app/ReportBody/context";
6
5
  import TreeItem from "@/components/app/Tree/TreeItem";
7
- import { Loadable } from "@/components/commons/Loadable";
8
- import { PageLoader } from "@/components/commons/PageLoader";
9
- import { Text } from "@/components/commons/Typography";
10
- import { useI18n } from "@/stores";
11
- import { treeStore } from "@/stores/tree";
12
- import { filterGroups, filterLeaves } from "@/utils/treeFilters";
6
+ import type { AllureAwesomeRecursiveTree, AllureAwesomeStatus } from "../../../../types";
13
7
  import TreeHeader from "./TreeHeader";
14
8
  import * as styles from "./styles.scss";
15
9
 
16
10
  interface TreeProps {
17
11
  statistic?: Statistic;
18
- leaves?: WithChildren["leaves"];
19
- groups?: WithChildren["groups"];
12
+ tree: AllureAwesomeRecursiveTree;
20
13
  name?: string;
21
14
  root?: boolean;
22
- statusFilter?: TestStatus;
15
+ statusFilter?: AllureAwesomeStatus;
23
16
  }
24
17
 
25
- const Tree: FunctionComponent<TreeProps> = ({ statusFilter, root, name, leaves = [], groups = [], statistic }) => {
18
+ const Tree: FunctionComponent<TreeProps> = ({ tree, statusFilter, root, name, statistic }) => {
26
19
  const [isOpened, setIsOpen] = useState(statistic === undefined || !!statistic.failed || !!statistic.broken);
27
- const { t } = useI18n("empty");
20
+ const toggleTree = () => {
21
+ setIsOpen(!isOpened);
22
+ };
23
+ const emptyTree = !tree?.trees?.length && !tree?.leaves?.length;
28
24
 
29
- return (
30
- <Loadable
31
- source={treeStore}
32
- renderLoader={() => <PageLoader />}
33
- renderData={(treeData) => {
34
- const reportContext = useReportContentContext();
35
- const toggleTree = () => {
36
- setIsOpen(!isOpened);
37
- };
38
- const leavesToRender = filterLeaves(leaves, treeData?.leavesById, statusFilter, reportContext);
39
- const groupsToRender = filterGroups(
40
- groups,
41
- treeData?.groupsById,
42
- treeData?.leavesById,
43
- statusFilter,
44
- reportContext,
45
- );
46
- if (!groupsToRender.length && !leavesToRender.length) {
47
- return (
48
- <div className={styles["tree-list"]}>
49
- <div className={styles["tree-empty-results"]}>
50
- <Text className={styles["tree-empty-results-title"]}>{t("no-results")}</Text>
51
- </div>
52
- </div>
53
- );
54
- }
55
-
56
- const treeContent = isOpened && (
57
- <div
58
- className={cx({
59
- [styles["tree-content"]]: true,
60
- [styles.root]: root,
61
- })}
62
- >
63
- {groupsToRender.map((groupId) => {
64
- const group = treeData?.groupsById?.[groupId];
65
-
66
- if (!group) {
67
- return null;
68
- }
69
-
70
- return (
71
- <Tree
72
- key={group.nodeId}
73
- name={group.name}
74
- leaves={group.leaves}
75
- groups={group.groups}
76
- statistic={group.statistic}
77
- statusFilter={statusFilter}
78
- />
79
- );
80
- })}
81
- {leavesToRender.map((leafId) => {
82
- const leaf = treeData?.leavesById?.[leafId];
25
+ if (emptyTree) {
26
+ return null;
27
+ }
83
28
 
84
- if (!leaf) {
85
- return null;
86
- }
87
-
88
- return (
89
- <TreeItem
90
- data-testid="tree-leaf"
91
- key={leaf.nodeId}
92
- id={leaf.nodeId}
93
- name={leaf.name}
94
- status={leaf.status}
95
- duration={leaf.duration}
96
- />
97
- );
98
- })}
99
- </div>
100
- );
29
+ const treeContent = isOpened && (
30
+ <div
31
+ data-testid="tree-content"
32
+ className={cx({
33
+ [styles["tree-content"]]: true,
34
+ [styles.root]: root,
35
+ })}
36
+ >
37
+ {tree?.trees?.map?.((subTree) => (
38
+ <Tree
39
+ key={subTree.nodeId}
40
+ name={subTree.name}
41
+ tree={subTree}
42
+ statistic={subTree.statistic}
43
+ statusFilter={statusFilter}
44
+ />
45
+ ))}
46
+ {tree?.leaves?.map?.((leaf) => (
47
+ <TreeItem
48
+ data-testid="tree-leaf"
49
+ key={leaf.nodeId}
50
+ id={leaf.nodeId}
51
+ name={leaf.name}
52
+ status={leaf.status}
53
+ groupOrder={leaf.groupOrder}
54
+ duration={leaf.duration}
55
+ />
56
+ ))}
57
+ </div>
58
+ );
101
59
 
102
- return (
103
- <div className={styles.tree}>
104
- {name && (
105
- <TreeHeader
106
- categoryTitle={name}
107
- isOpened={isOpened}
108
- toggleTree={toggleTree}
109
- statusFilter={statusFilter}
110
- statistic={statistic}
111
- data-testid="tree-group-header"
112
- />
113
- )}
114
- {treeContent}
115
- </div>
116
- );
117
- }}
118
- />
60
+ return (
61
+ <div className={styles.tree}>
62
+ {name && (
63
+ <TreeHeader
64
+ categoryTitle={name}
65
+ isOpened={isOpened}
66
+ toggleTree={toggleTree}
67
+ statistic={statistic}
68
+ />
69
+ )}
70
+ {treeContent}
71
+ </div>
119
72
  );
120
73
  };
121
74
 
@@ -1,10 +1,11 @@
1
- import { Statistic, statusesList } from "@allurereport/core-api";
1
+ import { type Statistic, statusesList } from "@allurereport/core-api";
2
2
  import { clsx } from "clsx";
3
- import { FunctionComponent } from "preact";
3
+ import { type FunctionComponent } from "preact";
4
4
  import { ArrowButton } from "@/components/app/ArrowButton";
5
5
  import { Loadable } from "@/components/commons/Loadable";
6
6
  import { Text } from "@/components/commons/Typography";
7
7
  import { statsStore } from "@/stores";
8
+ import { treeFiltersStore } from "@/stores/tree";
8
9
  import * as styles from "./styles.scss";
9
10
 
10
11
  interface TreeHeaderProps {
@@ -12,15 +13,14 @@ interface TreeHeaderProps {
12
13
  categoryTitle: string;
13
14
  isOpened: boolean;
14
15
  toggleTree: () => void;
15
- statusFilter?: string;
16
16
  }
17
17
 
18
- const maxWidthTab: number = 140;
19
- const minWidthTab: number = 46;
18
+ const maxWidthTab = 140;
19
+ const minWidthTab = 46;
20
20
  // to make the progress bar more visually responsive for smaller values,
21
21
  // we can adjust the formula by adding an offset to stretch the lower part
22
22
  // of the logarithmic scale
23
- const offset: number = 10;
23
+ const offset = 10;
24
24
 
25
25
  const progress = (current: number, total: number) => {
26
26
  const logOffset = Math.log(offset);
@@ -31,10 +31,11 @@ const TreeHeader: FunctionComponent<TreeHeaderProps> = ({
31
31
  categoryTitle,
32
32
  isOpened,
33
33
  toggleTree,
34
- statusFilter = "total",
35
34
  statistic,
36
35
  ...rest
37
36
  }) => {
37
+ const { status: statusFilter } = treeFiltersStore.value;
38
+
38
39
  return (
39
40
  <Loadable
40
41
  source={statsStore}
@@ -49,8 +50,8 @@ const TreeHeader: FunctionComponent<TreeHeaderProps> = ({
49
50
  value !== undefined && (statusFilter === "total" || (statusFilter === status && value > 0)),
50
51
  )
51
52
  .map(({ status, value }) => {
52
- const className = clsx(styles[`tree-header-bar-item`], styles[status]);
53
- const style = { flexGrow: `${value}` };
53
+ const className = clsx(styles["tree-header-bar-item"], styles[status]);
54
+ const style = { flexGrow: value };
54
55
 
55
56
  return (
56
57
  <div key={status} className={className} style={style}>
@@ -61,9 +62,9 @@ const TreeHeader: FunctionComponent<TreeHeaderProps> = ({
61
62
  : null;
62
63
 
63
64
  return (
64
- <div {...rest} className={styles["tree-header"]} onClick={toggleTree}>
65
- <ArrowButton isOpened={isOpened} />
66
- <Text size="m" bold className={styles["tree-header-title"]}>
65
+ <div data-testid="tree-header" {...rest} className={styles["tree-header"]} onClick={toggleTree}>
66
+ <ArrowButton data-testid="tree-arrow" isOpened={isOpened} />
67
+ <Text data-testid="tree-header-title" size="m" bold className={styles["tree-header-title"]}>
67
68
  {categoryTitle}
68
69
  </Text>
69
70
  {treeHeaderBar && (
@@ -10,14 +10,16 @@ interface TreeItemProps {
10
10
  status: TestStatus;
11
11
  duration?: number;
12
12
  id: string;
13
+ groupOrder: number;
13
14
  }
14
15
 
15
- export const TreeItem: FunctionComponent<TreeItemProps> = ({ name, status, duration, id, ...rest }) => {
16
+ export const TreeItem: FunctionComponent<TreeItemProps> = ({ name, groupOrder, status, duration, id, ...rest }) => {
16
17
  const formattedDuration = formatDuration(duration);
17
18
 
18
19
  return (
19
20
  <div {...rest} className={styles["tree-item"]} onClick={() => navigateTo(id)}>
20
21
  <TreeItemIcon status={status} />
22
+ <span data-testid="tree-leaf-order" class={styles.order}>#{groupOrder}</span>
21
23
  <Text data-testid="tree-leaf-title" className={styles["item-title"]}>
22
24
  {name}
23
25
  </Text>
@@ -1,25 +1,29 @@
1
- import type { TestStatus } from "@allurereport/core-api";
1
+ import { useEffect } from "preact/hooks";
2
2
  import { useTabsContext } from "@/components/app/Tabs";
3
3
  import Tree from "@/components/app/Tree/Tree";
4
+ import { Button } from "@/components/commons/Button";
4
5
  import { Loadable } from "@/components/commons/Loadable";
5
6
  import { PageLoader } from "@/components/commons/PageLoader";
6
7
  import { Text } from "@/components/commons/Typography";
7
8
  import { useI18n } from "@/stores/locale";
8
- import { treeStore } from "@/stores/tree";
9
+ import { clearTreeFilters, filteredTree, noTests, noTestsFound, setTreeStatus, treeStore } from "@/stores/tree";
10
+ import type { AllureAwesomeStatus } from "../../../../types";
9
11
  import * as styles from "./styles.scss";
10
12
 
11
13
  export const TreeList = () => {
12
14
  const { t } = useI18n("empty");
13
15
  const { currentTab } = useTabsContext();
14
16
 
17
+ useEffect(() => {
18
+ setTreeStatus(currentTab as AllureAwesomeStatus);
19
+ }, [currentTab]);
20
+
15
21
  return (
16
22
  <Loadable
17
23
  source={treeStore}
18
24
  renderLoader={() => <PageLoader />}
19
- renderData={(treeData) => {
20
- const { groups, leaves } = treeData?.root ?? {};
21
-
22
- if (!groups && !leaves) {
25
+ renderData={() => {
26
+ if (noTests.value) {
23
27
  return (
24
28
  <div className={styles["tree-list"]}>
25
29
  <div className={styles["tree-empty-results"]}>
@@ -29,9 +33,29 @@ export const TreeList = () => {
29
33
  );
30
34
  }
31
35
 
36
+ if (noTestsFound.value) {
37
+ return (
38
+ <div className={styles["tree-list"]}>
39
+ <div className={styles["tree-empty-results"]}>
40
+ <Text tag="p" className={styles["tree-empty-results-title"]}>
41
+ {t("no-tests-found")}
42
+ </Text>
43
+ <Button
44
+ className={styles["tree-empty-results-clear-button"]}
45
+ type="button"
46
+ text={t("clear-filters")}
47
+ size={"s"}
48
+ style={"outline"}
49
+ onClick={() => clearTreeFilters()}
50
+ />
51
+ </div>
52
+ </div>
53
+ );
54
+ }
55
+
32
56
  return (
33
57
  <div className={styles["tree-list"]}>
34
- <Tree groups={groups} leaves={leaves} statusFilter={currentTab as TestStatus} root />
58
+ <Tree tree={filteredTree.value} statusFilter={currentTab as AllureAwesomeStatus} root />
35
59
  </div>
36
60
  );
37
61
  }}
@@ -159,12 +159,18 @@
159
159
  }
160
160
 
161
161
  .tree-empty-results {
162
- display: flex;
163
162
  padding: 44px 24px;
164
- align-items: center;
165
- justify-content: center;
163
+ text-align: center;
166
164
  }
167
165
 
168
166
  .tree-empty-results-title {
169
167
  color: var(--on-text-secondary);
170
168
  }
169
+
170
+ .tree-empty-results-clear-button {
171
+ margin-top: 4px;
172
+ }
173
+
174
+ .order {
175
+ user-select: none;
176
+ }
@@ -1,12 +1,29 @@
1
1
  import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
2
2
  import { clsx } from "clsx";
3
3
  import type { ComponentChildren, VNode } from "preact";
4
- import { useEffect, useRef, useState } from "preact/hooks";
4
+ import { createContext } from "preact";
5
+ import { useContext, useEffect, useRef, useState } from "preact/hooks";
5
6
  import check from "@/assets/svg/line-general-check.svg";
6
7
  import { SvgIcon } from "@/components/commons/SvgIcon";
7
8
  import { Text } from "@/components/commons/Typography";
8
9
  import * as styles from "./styles.scss";
9
10
 
11
+ type MenuContextT = {
12
+ setIsOpened: (isOpened: boolean) => void;
13
+ };
14
+
15
+ const MenuContext = createContext<MenuContextT | null>(null);
16
+
17
+ export const useMenuContext = () => {
18
+ const context = useContext(MenuContext);
19
+
20
+ if (!context) {
21
+ throw new Error("useMenuContext must be used within a Menu");
22
+ }
23
+
24
+ return context;
25
+ };
26
+
10
27
  export const Menu = (props: {
11
28
  children: ComponentChildren;
12
29
  isInitialOpened?: boolean;
@@ -82,9 +99,9 @@ export const Menu = (props: {
82
99
  }).then(({ x, y }) => {
83
100
  if (menuRef.current) {
84
101
  Object.assign(menuRef.current.style, {
85
- left: `${x}px`,
86
- top: `${y}px`,
87
- position: "absolute",
102
+ "left": `${x}px`,
103
+ "top": `${y}px`,
104
+ "position": "absolute",
88
105
  "z-index": 10,
89
106
  });
90
107
  }
@@ -96,20 +113,26 @@ export const Menu = (props: {
96
113
  }, [menuRef.current, triggerRef.current]);
97
114
 
98
115
  return (
99
- <>
100
- {typeof menuTrigger === "function" && (
101
- <MenuTriggerWrapper ref={triggerRef}>
102
- {menuTrigger({
103
- isOpened,
104
- onClick: handleTriggerClick,
105
- setIsOpened,
106
- })}
107
- </MenuTriggerWrapper>
108
- )}
109
- <div ref={menuRef}>
110
- {isOpened && <aside className={clsx(styles.menu, styles[`size-${size}`])}>{children}</aside>}
111
- </div>
112
- </>
116
+ <MenuContext.Provider
117
+ value={{
118
+ setIsOpened,
119
+ }}
120
+ >
121
+ <>
122
+ {typeof menuTrigger === "function" && (
123
+ <MenuTriggerWrapper ref={triggerRef}>
124
+ {menuTrigger({
125
+ isOpened,
126
+ onClick: handleTriggerClick,
127
+ setIsOpened,
128
+ })}
129
+ </MenuTriggerWrapper>
130
+ )}
131
+ <div ref={menuRef}>
132
+ {isOpened && <aside className={clsx(styles.menu, styles[`size-${size}`])}>{children}</aside>}
133
+ </div>
134
+ </>
135
+ </MenuContext.Provider>
113
136
  );
114
137
  };
115
138
 
@@ -124,10 +147,11 @@ type ItemProps = {
124
147
  rightSlot?: ComponentChildren;
125
148
  closeMenuOnClick?: boolean;
126
149
  ariaLabel?: string;
127
- setIsOpened: (isOpened: boolean) => void;
150
+ setIsOpened?: (isOpened: boolean) => void;
128
151
  };
129
152
 
130
153
  Menu.Item = (props: ItemProps) => {
154
+ const { setIsOpened } = useMenuContext();
131
155
  const { children, onClick, leadingIcon, rightSlot, ariaLabel, closeMenuOnClick = true } = props;
132
156
  const isInteractive = typeof onClick === "function";
133
157
  const hasLeadingIcon = typeof leadingIcon === "string";
@@ -135,6 +159,7 @@ Menu.Item = (props: ItemProps) => {
135
159
  const handleItemClick = (e: MouseEvent) => {
136
160
  if (isInteractive && closeMenuOnClick) {
137
161
  e.stopPropagation();
162
+ setIsOpened(false);
138
163
  }
139
164
 
140
165
  if (isInteractive) {
@@ -1,5 +1,5 @@
1
1
  import { clsx } from "clsx";
2
- import { useState } from "preact/hooks";
2
+ import {useEffect, useState} from "preact/hooks";
3
3
  import searchIcon from "@/assets/svg/line-general-search-md.svg";
4
4
  import closeIcon from "@/assets/svg/line-general-x-close.svg";
5
5
  import { useDebouncedCallback } from "@/hooks/useDebouncedCallback";
@@ -20,25 +20,27 @@ type Props = {
20
20
  export const SearchBox = (props: Props) => {
21
21
  const { placeholder, value, onChange, changeDebounce = 300 } = props;
22
22
  const [localValue, setLocalValue] = useState(value);
23
-
24
23
  const onChangeDebounced = useDebouncedCallback(onChange, changeDebounce);
25
-
26
24
  const handleChange = (e: Event) => {
27
25
  const newValue = (e.target as HTMLInputElement).value;
28
26
 
29
27
  setLocalValue(newValue);
30
28
  onChangeDebounced(newValue);
31
29
  };
32
-
33
30
  const handleClear = (e: PointerEvent) => {
34
31
  e.preventDefault();
35
32
  e.stopPropagation();
36
33
  setLocalValue("");
37
34
  onChangeDebounced("");
38
35
  };
39
-
40
36
  const showClear = !!localValue;
41
37
 
38
+ useEffect(() => {
39
+ if (localValue !== value) {
40
+ setLocalValue(value);
41
+ }
42
+ }, [value]);
43
+
42
44
  return (
43
45
  <Text className={styles.inputWrap} type="ui" size="m" tag="div">
44
46
  <SvgIcon id={searchIcon.id} size="s" className={styles.leadingIcon} />
@@ -50,6 +52,7 @@ export const SearchBox = (props: Props) => {
50
52
  value={localValue}
51
53
  name="search"
52
54
  autocomplete="off"
55
+ data-testid="search-input"
53
56
  />
54
57
  {showClear && (
55
58
  <div className={styles.clearButton}>
@@ -8,5 +8,4 @@
8
8
 
9
9
  .chart {
10
10
  position: relative;
11
- display: flex;
12
11
  }
@@ -8,7 +8,7 @@ type Props = {
8
8
  };
9
9
 
10
10
  export const Toggle = (props: Props) => {
11
- const { value, label, onChange, focusable = true } = props;
11
+ const { value, label, onChange, focusable = true, ...rest } = props;
12
12
 
13
13
  const handleChange = (e: Event) => {
14
14
  const newValue = !(e.target as HTMLInputElement).checked;
@@ -17,13 +17,14 @@ export const Toggle = (props: Props) => {
17
17
 
18
18
  return (
19
19
  <input
20
+ {...rest}
20
21
  tabIndex={focusable ? 0 : -1}
21
22
  className={styles.toggle}
22
23
  role="switch"
23
24
  type="checkbox"
24
25
  checked={value}
25
26
  aria-label={label}
26
- onChange={handleChange}
27
+ onToggle={handleChange}
27
28
  />
28
29
  );
29
30
  };
@@ -49,9 +49,9 @@ export const TooltipWrapper: FunctionalComponent<TooltipWrapperProps> = ({
49
49
  }).then(({ x, y }) => {
50
50
  if (tooltipRef.current) {
51
51
  Object.assign(tooltipRef.current.style, {
52
- left: `${x}px`,
53
- top: `${y}px`,
54
- position: "absolute",
52
+ "left": `${x}px`,
53
+ "top": `${y}px`,
54
+ "position": "absolute",
55
55
  "z-index": 100,
56
56
  });
57
57
  }
@@ -1,4 +1,23 @@
1
- export const AVAILABLE_LOCALES = ["en", "ru", "pl", "es", "pt", "de", "am", "az", "fr", "it", "ja", "he", "ka", "kr", "nl", "sv", "tr", "zh"] as const;
1
+ export const AVAILABLE_LOCALES = [
2
+ "en",
3
+ "ru",
4
+ "pl",
5
+ "es",
6
+ "pt",
7
+ "de",
8
+ "am",
9
+ "az",
10
+ "fr",
11
+ "it",
12
+ "ja",
13
+ "he",
14
+ "ka",
15
+ "kr",
16
+ "nl",
17
+ "sv",
18
+ "tr",
19
+ "zh",
20
+ ] as const;
2
21
 
3
22
  export const DEFAULT_LOCALE = "en";
4
23
 
@@ -101,5 +120,5 @@ export const LANG_LOCALE: Record<
101
120
  short: "Zh",
102
121
  full: "中文",
103
122
  iso: "zh-CN",
104
- }
123
+ },
105
124
  };
@@ -60,7 +60,9 @@
60
60
  "status-desc-short": "Հակադարձված"
61
61
  },
62
62
  "empty": {
63
- "no-results": "Արդյունքներ չեն գտնվել",
63
+ "no-results": "Ոչ մի արդյունք",
64
+ "no-tests-found": "Արդյունքներ չեն գտնվել",
65
+ "clear-filters": "Մաքրել ֆիլտրները",
64
66
  "no-attachments-results": "Կցորդների մասին տեղեկություններ չկան",
65
67
  "no-history-results": "Պատմության մասին տեղեկություններ չկան",
66
68
  "no-retries-results": "Կրկնությունների մասին տեղեկություններ չկան"
@@ -61,6 +61,8 @@
61
61
  },
62
62
  "empty": {
63
63
  "no-results": "Nəticə tapılmadı",
64
+ "no-tests-found": "Nəticə tapılmadı",
65
+ "clear-filters": "Filtrləri təmizlə",
64
66
  "no-attachments-results": "Əlavə məlumatı mövcud deyil",
65
67
  "no-history-results": "Tarixçə məlumatı mövcud deyil",
66
68
  "no-retries-results": "Təkrar məlumatı mövcud deyil"
@@ -112,4 +114,4 @@
112
114
  "errors": {
113
115
  "missedAttachment": "Əlavə tapılmadı"
114
116
  }
115
- }
117
+ }
@@ -60,7 +60,9 @@
60
60
  "status-desc-short": "Umgekehrt"
61
61
  },
62
62
  "empty": {
63
- "no-results": "Keine Ergebnisse gefunden",
63
+ "no-results": "Keine Ergebnisse",
64
+ "no-tests-found": "Keine Ergebnisse gefunden",
65
+ "clear-filters": "Filter löschen",
64
66
  "no-attachments-results": "Keine Anhängeinformationen verfügbar",
65
67
  "no-history-results": "Keine Verlaufsinformationen verfügbar",
66
68
  "no-retries-results": "Keine Wiederholungsinformationen verfügbar"
@@ -60,7 +60,9 @@
60
60
  "status-desc-short": "Reversed"
61
61
  },
62
62
  "empty": {
63
- "no-results": "No results found",
63
+ "no-results": "No results",
64
+ "no-tests-found": "No results found",
65
+ "clear-filters": "Clear filters",
64
66
  "no-attachments-results": "No attachments information available",
65
67
  "no-history-results": "No history information available",
66
68
  "no-retries-results": "No retries information available"
@@ -112,4 +114,4 @@
112
114
  "errors": {
113
115
  "missedAttachment": "Attachment not found"
114
116
  }
115
- }
117
+ }
@@ -60,6 +60,9 @@
60
60
  "status-desc-short": "Invertido"
61
61
  },
62
62
  "empty": {
63
+ "no-results": "Sin resultados",
64
+ "no-tests-found": "No se encontraron resultados",
65
+ "clear-filters": "Limpiar filtros",
63
66
  "no-attachments-results": "No hay información de adjuntos disponible",
64
67
  "no-history-results": "No hay información de historial disponible",
65
68
  "no-retries-results": "No hay información de reintentos disponible"
@@ -60,7 +60,9 @@
60
60
  "status-desc-short": "Inversé"
61
61
  },
62
62
  "empty": {
63
- "no-results": "Aucun résultat trouvé",
63
+ "no-results": "Aucun résultat",
64
+ "no-tests-found": "Aucun résultat trouvé",
65
+ "clear-filters": "Effacer les filtres",
64
66
  "no-attachments-results": "Aucune information sur les pièces jointes disponible",
65
67
  "no-history-results": "Aucune information sur l'historique disponible",
66
68
  "no-retries-results": "Aucune information sur les réessais disponible"