@allurereport/web-awesome 3.2.0 → 3.3.0

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 (113) hide show
  1. package/dist/multi/173.app-8be6acc0a596a2197dbf.js +1 -0
  2. package/dist/multi/174.app-8be6acc0a596a2197dbf.js +1 -0
  3. package/dist/multi/252.app-8be6acc0a596a2197dbf.js +1 -0
  4. package/dist/multi/282.app-8be6acc0a596a2197dbf.js +1 -0
  5. package/dist/multi/29.app-8be6acc0a596a2197dbf.js +1 -0
  6. package/dist/multi/416.app-8be6acc0a596a2197dbf.js +1 -0
  7. package/dist/multi/527.app-8be6acc0a596a2197dbf.js +1 -0
  8. package/dist/multi/600.app-8be6acc0a596a2197dbf.js +1 -0
  9. package/dist/multi/605.app-8be6acc0a596a2197dbf.js +1 -0
  10. package/dist/multi/638.app-8be6acc0a596a2197dbf.js +1 -0
  11. package/dist/multi/672.app-8be6acc0a596a2197dbf.js +1 -0
  12. package/dist/multi/686.app-8be6acc0a596a2197dbf.js +1 -0
  13. package/dist/multi/725.app-8be6acc0a596a2197dbf.js +1 -0
  14. package/dist/multi/741.app-8be6acc0a596a2197dbf.js +1 -0
  15. package/dist/multi/749.app-8be6acc0a596a2197dbf.js +1 -0
  16. package/dist/multi/755.app-8be6acc0a596a2197dbf.js +1 -0
  17. package/dist/multi/894.app-8be6acc0a596a2197dbf.js +1 -0
  18. package/dist/multi/943.app-8be6acc0a596a2197dbf.js +1 -0
  19. package/dist/multi/980.app-8be6acc0a596a2197dbf.js +1 -0
  20. package/dist/multi/app-8be6acc0a596a2197dbf.js +2 -0
  21. package/dist/multi/manifest.json +21 -21
  22. package/dist/multi/{styles-13107bbe6906beabc50f.css → styles-0b84e1ef76554ad2db9a.css} +15 -6
  23. package/dist/single/app-8221eb856e47b4ef50d6.js +2 -0
  24. package/dist/single/manifest.json +1 -1
  25. package/package.json +6 -6
  26. package/src/components/BaseLayout/index.tsx +5 -4
  27. package/src/components/Categories/CategoriesTree/index.tsx +14 -0
  28. package/src/components/Categories/CategoriesTree/styles.scss +14 -0
  29. package/src/components/Categories/CategoryHeaderItem/index.tsx +50 -0
  30. package/src/components/Categories/CategoryHeaderItem/styles.scss +32 -0
  31. package/src/components/Categories/CategoryTreeItem/index.tsx +309 -0
  32. package/src/components/Categories/CategoryTreeItem/styles.scss +47 -0
  33. package/src/components/Categories/GroupTreeItem/index.tsx +76 -0
  34. package/src/components/Categories/GroupTreeItem/styles.scss +47 -0
  35. package/src/components/Categories/HistoryTreeItem/index.tsx +71 -0
  36. package/src/components/Categories/HistoryTreeItem/styles.scss +53 -0
  37. package/src/components/Categories/LabelTreeItem/index.tsx +150 -0
  38. package/src/components/Categories/LabelTreeItem/styles.scss +102 -0
  39. package/src/components/Categories/MessageTreeItem/index.tsx +107 -0
  40. package/src/components/Categories/MessageTreeItem/styles.scss +178 -0
  41. package/src/components/Categories/SeverityTreeItem/index.tsx +50 -0
  42. package/src/components/Categories/SeverityTreeItem/styles.scss +12 -0
  43. package/src/components/Categories/sticky.ts +12 -0
  44. package/src/components/Header/index.tsx +4 -2
  45. package/src/components/MainReport/index.tsx +106 -10
  46. package/src/components/ReportBody/styles.scss +1 -1
  47. package/src/components/ReportCategories/index.tsx +26 -0
  48. package/src/components/ReportCategories/styles.scss +55 -0
  49. package/src/components/ReportFilters/CategoriesFilter.tsx +41 -0
  50. package/src/components/ReportFilters/index.tsx +12 -1
  51. package/src/components/SplitLayout/index.tsx +4 -2
  52. package/src/components/SplitLayout/styles.scss +1 -0
  53. package/src/components/TestResult/TrInfo/index.tsx +8 -1
  54. package/src/components/TestResult/TrInfo/styles.scss +4 -0
  55. package/src/components/TestResult/TrSeverity/index.tsx +13 -4
  56. package/src/components/TestResult/TrSeverity/styles.scss +1 -0
  57. package/src/components/TestResult/TrTabs/index.tsx +5 -0
  58. package/src/index.tsx +6 -2
  59. package/src/locales/az.json +106 -78
  60. package/src/locales/de.json +23 -3
  61. package/src/locales/en.json +23 -3
  62. package/src/locales/es.json +23 -3
  63. package/src/locales/fr.json +23 -3
  64. package/src/locales/he.json +23 -3
  65. package/src/locales/hy.json +23 -3
  66. package/src/locales/it.json +23 -3
  67. package/src/locales/ja.json +23 -3
  68. package/src/locales/ka.json +23 -3
  69. package/src/locales/kr.json +23 -3
  70. package/src/locales/nl.json +23 -3
  71. package/src/locales/pl.json +23 -3
  72. package/src/locales/pt.json +23 -3
  73. package/src/locales/ru.json +23 -3
  74. package/src/locales/sv.json +23 -3
  75. package/src/locales/tr.json +23 -3
  76. package/src/locales/uk.json +23 -3
  77. package/src/locales/zh.json +23 -3
  78. package/src/stores/categories.ts +44 -0
  79. package/src/stores/router.ts +55 -3
  80. package/src/stores/testResult.ts +14 -3
  81. package/src/stores/treeFilters/actions.ts +10 -1
  82. package/src/stores/treeFilters/constants.ts +1 -0
  83. package/src/stores/treeFilters/model.ts +2 -0
  84. package/src/stores/treeFilters/store.ts +45 -0
  85. package/src/stores/treeFilters/utils.ts +10 -0
  86. package/src/stores/treeSwitcher.ts +9 -0
  87. package/src/utils/treeFilters.ts +16 -9
  88. package/test/utils/treeFilters.test.ts +39 -0
  89. package/types.d.ts +1 -0
  90. package/dist/multi/173.app-d0210ed2e64d38a2ee8e.js +0 -1
  91. package/dist/multi/174.app-d0210ed2e64d38a2ee8e.js +0 -1
  92. package/dist/multi/252.app-d0210ed2e64d38a2ee8e.js +0 -1
  93. package/dist/multi/282.app-d0210ed2e64d38a2ee8e.js +0 -1
  94. package/dist/multi/29.app-d0210ed2e64d38a2ee8e.js +0 -1
  95. package/dist/multi/416.app-d0210ed2e64d38a2ee8e.js +0 -1
  96. package/dist/multi/527.app-d0210ed2e64d38a2ee8e.js +0 -1
  97. package/dist/multi/600.app-d0210ed2e64d38a2ee8e.js +0 -1
  98. package/dist/multi/605.app-d0210ed2e64d38a2ee8e.js +0 -1
  99. package/dist/multi/638.app-d0210ed2e64d38a2ee8e.js +0 -1
  100. package/dist/multi/672.app-d0210ed2e64d38a2ee8e.js +0 -1
  101. package/dist/multi/686.app-d0210ed2e64d38a2ee8e.js +0 -1
  102. package/dist/multi/725.app-d0210ed2e64d38a2ee8e.js +0 -1
  103. package/dist/multi/741.app-d0210ed2e64d38a2ee8e.js +0 -1
  104. package/dist/multi/749.app-d0210ed2e64d38a2ee8e.js +0 -1
  105. package/dist/multi/755.app-d0210ed2e64d38a2ee8e.js +0 -1
  106. package/dist/multi/894.app-d0210ed2e64d38a2ee8e.js +0 -1
  107. package/dist/multi/943.app-d0210ed2e64d38a2ee8e.js +0 -1
  108. package/dist/multi/980.app-d0210ed2e64d38a2ee8e.js +0 -1
  109. package/dist/multi/app-d0210ed2e64d38a2ee8e.js +0 -2
  110. package/dist/single/app-01fed10ad5f9083fd39c.js +0 -2
  111. package/src/components/SectionTabs/index.tsx +0 -0
  112. /package/dist/multi/{app-d0210ed2e64d38a2ee8e.js.LICENSE.txt → app-8be6acc0a596a2197dbf.js.LICENSE.txt} +0 -0
  113. /package/dist/single/{app-01fed10ad5f9083fd39c.js.LICENSE.txt → app-8221eb856e47b4ef50d6.js.LICENSE.txt} +0 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- "main.js": "app-01fed10ad5f9083fd39c.js"
2
+ "main.js": "app-8221eb856e47b4ef50d6.js"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/web-awesome",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "The static files for Allure Awesome Report",
5
5
  "keywords": [
6
6
  "allure",
@@ -31,11 +31,11 @@
31
31
  "IE 11"
32
32
  ],
33
33
  "dependencies": {
34
- "@allurereport/charts-api": "3.2.0",
35
- "@allurereport/core-api": "3.2.0",
36
- "@allurereport/plugin-api": "3.2.0",
37
- "@allurereport/web-commons": "3.2.0",
38
- "@allurereport/web-components": "3.2.0",
34
+ "@allurereport/charts-api": "3.3.0",
35
+ "@allurereport/core-api": "3.3.0",
36
+ "@allurereport/plugin-api": "3.3.0",
37
+ "@allurereport/web-commons": "3.3.0",
38
+ "@allurereport/web-components": "3.3.0",
39
39
  "@preact/signals": "^2.6.1",
40
40
  "clsx": "^2.1.1",
41
41
  "d3-shape": "^3.2.0",
@@ -1,7 +1,7 @@
1
1
  import { Loadable, PageLoader } from "@allurereport/web-components";
2
2
  import MainReport from "@/components/MainReport";
3
3
  import TestResult from "@/components/TestResult";
4
- import { testResultRoute } from "@/stores/router";
4
+ import { rootTabRoute, testResultRoute } from "@/stores/router";
5
5
  import { testResultStore } from "@/stores/testResults";
6
6
  import { treeStore } from "@/stores/tree";
7
7
  import * as styles from "./styles.scss";
@@ -11,11 +11,12 @@ export type BaseLayoutProps = {
11
11
  };
12
12
 
13
13
  export const BaseLayout = () => {
14
- const { matches, params } = testResultRoute.value;
14
+ const matches = testResultRoute.value.matches || Boolean(rootTabRoute.value.params.testResultId);
15
+ const testResultId = testResultRoute.value.matches
16
+ ? testResultRoute.value.params.testResultId
17
+ : rootTabRoute.value.params.testResultId;
15
18
 
16
19
  if (matches) {
17
- const testResultId = params.testResultId;
18
-
19
20
  return (
20
21
  <div className={styles.layout} data-testid="base-layout">
21
22
  <Loadable
@@ -0,0 +1,14 @@
1
+ import type { TestCategories } from "@allurereport/core-api";
2
+ import type { FC } from "preact/compat";
3
+ import { CategoryTreeItem } from "@/components/Categories/CategoryTreeItem";
4
+ import * as styles from "./styles.scss";
5
+
6
+ export const CategoriesTree: FC<{ store: TestCategories }> = ({ store }) => {
7
+ return (
8
+ <div className={styles["categories-tree-view"]}>
9
+ {store.roots.map((id: string) => (
10
+ <CategoryTreeItem key={id} nodeId={id} store={store} />
11
+ ))}
12
+ </div>
13
+ );
14
+ };
@@ -0,0 +1,14 @@
1
+ .categories-tree-view {
2
+ display: block;
3
+ min-height: 480px;
4
+ padding-bottom: 48px;
5
+ padding-top: 16px;
6
+ --categories-sticky-step: 32px;
7
+ }
8
+
9
+ .categories-tree-controls {
10
+ display: flex;
11
+ justify-content: flex-end;
12
+ gap: 8px;
13
+ margin-bottom: 12px;
14
+ }
@@ -0,0 +1,50 @@
1
+ import type { CategoryNode, CategoryNodeProps, Statistic } from "@allurereport/core-api";
2
+ import { TreeHeader } from "@allurereport/web-components";
3
+ import type { ComponentChildren } from "preact";
4
+ import type { FC } from "preact/compat";
5
+ import { createCategoriesStickyStyle } from "@/components/Categories/sticky";
6
+ import * as styles from "./styles.scss";
7
+
8
+ type CategoryHeaderItemProps = CategoryNodeProps & {
9
+ node: CategoryNode;
10
+ isOpened: boolean;
11
+ onToggle: () => void;
12
+ children: ComponentChildren;
13
+ depth: number;
14
+ subtreeToggle?: ComponentChildren;
15
+ reportStatistic?: Statistic;
16
+ };
17
+
18
+ export const CategoryHeaderItem: FC<CategoryHeaderItemProps> = ({
19
+ node,
20
+ nodeId,
21
+ isOpened,
22
+ onToggle,
23
+ children,
24
+ depth,
25
+ subtreeToggle,
26
+ reportStatistic,
27
+ }) => {
28
+ const stickyStyle = createCategoriesStickyStyle(depth);
29
+ const categoryTitle = (
30
+ <div className={styles["tree-item-category-title"]}>
31
+ <span className={styles["tree-item-category-name"]}>{node.name}</span>
32
+ {subtreeToggle}
33
+ </div>
34
+ );
35
+ return (
36
+ <div className={styles["tree-item-category"]} id={nodeId}>
37
+ <TreeHeader
38
+ categoryTitle={categoryTitle}
39
+ isOpened={isOpened}
40
+ toggleTree={onToggle}
41
+ data-tree-header
42
+ style={stickyStyle}
43
+ statistic={node.statistic}
44
+ reportStatistic={reportStatistic}
45
+ statusFilter={"total"}
46
+ />
47
+ {isOpened && children}
48
+ </div>
49
+ );
50
+ };
@@ -0,0 +1,32 @@
1
+ .tree-item-category {
2
+ font-size: 24px;
3
+ margin-bottom: 16px;
4
+ &:not(:first-child) {
5
+ border-top: 1px solid var(--on-border-muted);
6
+ }
7
+ }
8
+
9
+ .tree-item-category :global(.styles_tree-section-title) {
10
+ display: flex;
11
+ align-items: center;
12
+ flex: 1 1 auto;
13
+ min-width: 0;
14
+ margin-right: 8px;
15
+ }
16
+
17
+ .tree-item-category-title {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 8px;
21
+ width: 100%;
22
+ }
23
+
24
+ .tree-item-category-name {
25
+ color: var(--on-text-primary);
26
+ flex: 1 1 auto;
27
+ min-width: 0;
28
+ overflow: hidden;
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ }
32
+
@@ -0,0 +1,309 @@
1
+ import type { CategoryNode, CategoryNodeProps, TestCategories } from "@allurereport/core-api";
2
+ import { IconButton, TreeItem, allureIcons } from "@allurereport/web-components";
3
+ import type { FC } from "preact/compat";
4
+ import { useState } from "preact/hooks";
5
+ import { CategoryHeaderItem } from "@/components/Categories/CategoryHeaderItem";
6
+ import { GroupTreeItem } from "@/components/Categories/GroupTreeItem";
7
+ import { HistoryTreeItem } from "@/components/Categories/HistoryTreeItem";
8
+ import { LabelTreeItem } from "@/components/Categories/LabelTreeItem";
9
+ import { MessageTreeItem } from "@/components/Categories/MessageTreeItem";
10
+ import { SeverityTreeItem } from "@/components/Categories/SeverityTreeItem";
11
+ import { reportStatsStore } from "@/stores";
12
+ import { useI18n } from "@/stores/locale";
13
+ import { navigateToTestResult } from "@/stores/router";
14
+ import { currentTrId } from "@/stores/testResult";
15
+ import { collapsedTrees, toggleTree } from "@/stores/tree";
16
+ import * as styles from "./styles.scss";
17
+
18
+ type CategoryTreeItemProps = CategoryNodeProps & {
19
+ order?: number;
20
+ depth?: number;
21
+ };
22
+
23
+ const getDefaultOpened = (node?: CategoryNode) => (node?.type === "category" ? Boolean(node.expand) : true);
24
+
25
+ const getSubtreeExpandableIds = (rootId: string, store: TestCategories) => {
26
+ const result: string[] = [];
27
+ const stack = [rootId];
28
+
29
+ while (stack.length) {
30
+ const currentId = stack.pop();
31
+ if (!currentId) {
32
+ continue;
33
+ }
34
+ const currentNode = store.nodes[currentId];
35
+ if (!currentNode) {
36
+ continue;
37
+ }
38
+ if (currentNode.childrenIds?.length) {
39
+ result.push(currentId);
40
+ }
41
+ (currentNode.childrenIds ?? []).forEach((childId: string) => stack.push(childId));
42
+ }
43
+
44
+ return result;
45
+ };
46
+
47
+ const getNodeOpenedState = (nodeId: string, store: TestCategories) => {
48
+ const node = store.nodes[nodeId];
49
+ const defaultOpened = getDefaultOpened(node);
50
+ return collapsedTrees.value.has(nodeId) ? !defaultOpened : defaultOpened;
51
+ };
52
+
53
+ export const CategoryTreeItem: FC<CategoryTreeItemProps> = ({ nodeId, store, order, depth = 0 }) => {
54
+ const node: CategoryNode = store.nodes[nodeId];
55
+ const trId = currentTrId.value;
56
+ const { t: tTransitions } = useI18n("transitions");
57
+ const { t: tEnvironments } = useI18n("environments");
58
+ const { t: tEmpty } = useI18n("empty");
59
+ const hasSavedState = collapsedTrees.value.has(nodeId);
60
+ const defaultOpened = getDefaultOpened(node);
61
+ const [isOpened, setIsOpen] = useState<boolean>(hasSavedState ? !defaultOpened : defaultOpened);
62
+ const [subtreeVersion, setSubtreeVersion] = useState(0);
63
+ const [lastSubtreeToggle, setLastSubtreeToggle] = useState<"first" | "all" | "none" | null>(null);
64
+
65
+ if (!node) {
66
+ return null;
67
+ }
68
+
69
+ const hasChildren = Boolean(node.childrenIds?.length);
70
+ const hasOnlyLeafResults = hasChildren
71
+ ? (node.childrenIds ?? []).every((childId) => store.nodes[childId]?.type === "tr")
72
+ : false;
73
+ const expandableNodeIds = hasChildren ? getSubtreeExpandableIds(nodeId, store) : [];
74
+ const isSubtreeCollapsedAll = !getNodeOpenedState(nodeId, store);
75
+ const isSubtreeFirstLevelOnly =
76
+ getNodeOpenedState(nodeId, store) &&
77
+ expandableNodeIds.filter((id) => id !== nodeId).every((id) => !getNodeOpenedState(id, store));
78
+ const isSubtreeExpandedAll =
79
+ getNodeOpenedState(nodeId, store) && expandableNodeIds.every((id) => getNodeOpenedState(id, store));
80
+
81
+ const onClick = () => {
82
+ setIsOpen(!isOpened);
83
+ toggleTree(nodeId);
84
+ setLastSubtreeToggle(null);
85
+ };
86
+
87
+ const setSubtreeState = (state: "first" | "all" | "none") => {
88
+ const nextCollapsed = new Set(collapsedTrees.value);
89
+
90
+ expandableNodeIds.forEach((id) => {
91
+ const currentNode = store.nodes[id];
92
+ const isDefaultOpened = getDefaultOpened(currentNode);
93
+ const isRoot = id === nodeId;
94
+ let shouldBeOpened = false;
95
+
96
+ if (state === "all") {
97
+ shouldBeOpened = true;
98
+ } else if (state === "first") {
99
+ shouldBeOpened = isRoot;
100
+ } else {
101
+ shouldBeOpened = false;
102
+ }
103
+
104
+ if (isDefaultOpened === shouldBeOpened) {
105
+ nextCollapsed.delete(id);
106
+ } else {
107
+ nextCollapsed.add(id);
108
+ }
109
+ });
110
+
111
+ collapsedTrees.value = nextCollapsed;
112
+ };
113
+
114
+ const onToggleSubtree = (event: MouseEvent) => {
115
+ event.stopPropagation();
116
+ if (hasOnlyLeafResults) {
117
+ if (isSubtreeCollapsedAll) {
118
+ setSubtreeState("all");
119
+ setIsOpen(true);
120
+ setLastSubtreeToggle("all");
121
+ } else {
122
+ setSubtreeState("none");
123
+ setIsOpen(false);
124
+ setLastSubtreeToggle("none");
125
+ }
126
+ setSubtreeVersion((value: number) => value + 1);
127
+ return;
128
+ }
129
+ let nextState: "first" | "all" | "none" = "first";
130
+ if (isSubtreeCollapsedAll) {
131
+ nextState = "first";
132
+ } else if (isSubtreeFirstLevelOnly) {
133
+ nextState = lastSubtreeToggle === "all" ? "none" : "all";
134
+ } else if (isSubtreeExpandedAll) {
135
+ nextState = "first";
136
+ } else {
137
+ nextState = "all";
138
+ }
139
+ setSubtreeState(nextState);
140
+ setIsOpen(nextState !== "none");
141
+ setLastSubtreeToggle(nextState);
142
+ setSubtreeVersion((value: number) => value + 1);
143
+ };
144
+
145
+ const subtreeToggleIcon = (() => {
146
+ if (hasOnlyLeafResults) {
147
+ return isSubtreeCollapsedAll ? allureIcons.lineArrowsChevronDown : allureIcons.lineArrowsChevronUp;
148
+ }
149
+ if (isSubtreeCollapsedAll) {
150
+ return allureIcons.lineArrowsChevronDown;
151
+ }
152
+ if (isSubtreeFirstLevelOnly) {
153
+ return allureIcons.lineArrowsChevronDownDouble;
154
+ }
155
+ return allureIcons.lineArrowsChevronUpDouble;
156
+ })();
157
+
158
+ const subtreeToggle = hasChildren ? (
159
+ <IconButton
160
+ size="xs"
161
+ style="ghost"
162
+ icon={subtreeToggleIcon}
163
+ onClick={onToggleSubtree}
164
+ className={styles["tree-item-subtree-toggle"]}
165
+ />
166
+ ) : null;
167
+
168
+ const renderChildren = () => (
169
+ <div className={styles["tree-children"]} key={subtreeVersion}>
170
+ {(node.childrenIds ?? []).map((cid: string, index: number) => (
171
+ <div key={cid} className={styles["tree-content"]} data-tree-content>
172
+ <CategoryTreeItem key={cid} nodeId={cid} store={store} order={index} depth={depth + 1} />
173
+ </div>
174
+ ))}
175
+ </div>
176
+ );
177
+
178
+ if (node.type === "category") {
179
+ return (
180
+ <CategoryHeaderItem
181
+ node={node}
182
+ nodeId={nodeId}
183
+ store={store}
184
+ isOpened={isOpened}
185
+ onToggle={onClick}
186
+ depth={depth}
187
+ subtreeToggle={subtreeToggle}
188
+ reportStatistic={reportStatsStore.value.data}
189
+ >
190
+ {renderChildren()}
191
+ </CategoryHeaderItem>
192
+ );
193
+ }
194
+ if (node.type === "history") {
195
+ return (
196
+ <HistoryTreeItem
197
+ node={node}
198
+ nodeId={nodeId}
199
+ store={store}
200
+ isOpened={isOpened}
201
+ onToggle={onClick}
202
+ depth={depth}
203
+ subtreeToggle={subtreeToggle}
204
+ reportStatistic={reportStatsStore.value.data}
205
+ >
206
+ {renderChildren()}
207
+ </HistoryTreeItem>
208
+ );
209
+ }
210
+ if (node.type === "message" || node.type === "group") {
211
+ if (node.type === "message") {
212
+ return (
213
+ <MessageTreeItem
214
+ node={node}
215
+ nodeId={nodeId}
216
+ store={store}
217
+ isOpened={isOpened}
218
+ onToggle={onClick}
219
+ depth={depth}
220
+ subtreeToggle={subtreeToggle}
221
+ reportStatistic={reportStatsStore.value.data}
222
+ >
223
+ {renderChildren()}
224
+ </MessageTreeItem>
225
+ );
226
+ }
227
+ if (node.key === "severity") {
228
+ return (
229
+ <SeverityTreeItem
230
+ node={node}
231
+ nodeId={nodeId}
232
+ store={store}
233
+ isOpened={isOpened}
234
+ onToggle={onClick}
235
+ depth={depth}
236
+ subtreeToggle={subtreeToggle}
237
+ reportStatistic={reportStatsStore.value.data}
238
+ >
239
+ {renderChildren()}
240
+ </SeverityTreeItem>
241
+ );
242
+ }
243
+ if (node.key) {
244
+ return (
245
+ <LabelTreeItem
246
+ node={node}
247
+ nodeId={nodeId}
248
+ store={store}
249
+ isOpened={isOpened}
250
+ onToggle={onClick}
251
+ depth={depth}
252
+ subtreeToggle={subtreeToggle}
253
+ reportStatistic={reportStatsStore.value.data}
254
+ >
255
+ {renderChildren()}
256
+ </LabelTreeItem>
257
+ );
258
+ }
259
+ return (
260
+ <GroupTreeItem
261
+ node={node}
262
+ nodeId={nodeId}
263
+ store={store}
264
+ isOpened={isOpened}
265
+ onToggle={onClick}
266
+ depth={depth}
267
+ subtreeToggle={subtreeToggle}
268
+ reportStatistic={reportStatsStore.value.data}
269
+ >
270
+ {renderChildren()}
271
+ </GroupTreeItem>
272
+ );
273
+ }
274
+ if (node.type === "tr") {
275
+ const isEnvLeaf = node.key === "environment";
276
+ let envValue = node.value ?? node.name;
277
+ if (typeof envValue === "string" && envValue.toLowerCase().startsWith("environment:")) {
278
+ envValue = envValue.slice("environment:".length).trim();
279
+ }
280
+ if (node.value === "<Empty>") {
281
+ envValue = tEmpty("no-environment");
282
+ }
283
+ const displayName = isEnvLeaf ? `${tEnvironments("environment", { count: 1 })}: ${envValue}` : node.name;
284
+ const leafTooltips =
285
+ node.type === "tr"
286
+ ? {
287
+ transition:
288
+ node.tooltips?.transition ??
289
+ (node.transition ? tTransitions(`description.${node.transition}`) : undefined),
290
+ flaky: node.tooltips?.flaky ?? (node.flaky ? tTransitions("description.flaky") : undefined),
291
+ retries:
292
+ node.tooltips?.retries ??
293
+ (node.retriesCount ? tTransitions("description.retries", { count: node.retriesCount }) : undefined),
294
+ }
295
+ : node.tooltips;
296
+ return (
297
+ <div className={styles["tree-item"]} id={nodeId}>
298
+ <TreeItem
299
+ {...node}
300
+ name={displayName}
301
+ groupOrder={(order ?? 0) + 1}
302
+ marked={node.id === trId}
303
+ navigateTo={() => navigateToTestResult({ testResultId: nodeId })}
304
+ tooltips={leafTooltips}
305
+ />
306
+ </div>
307
+ );
308
+ }
309
+ };
@@ -0,0 +1,47 @@
1
+ .tree-content {
2
+ padding-left: 24px;
3
+ position: relative;
4
+
5
+ &::before {
6
+ content: "";
7
+ position: absolute;
8
+ top: 0;
9
+ bottom: 0;
10
+ left: 12px;
11
+ width: 1px;
12
+ background: var(--on-border-muted);
13
+ opacity: 0;
14
+ transition: opacity 200ms;
15
+ }
16
+ }
17
+
18
+ .tree-item {
19
+ border-top: 1px solid var(--on-border-muted);
20
+ // padding-left: 24px;
21
+
22
+ }
23
+
24
+ .tree-children {
25
+ display: contents;
26
+ }
27
+
28
+ .tree-item-subtree-toggle {
29
+ flex: 0 0 auto;
30
+ margin-left: auto;
31
+ opacity: 0;
32
+ pointer-events: none;
33
+ transition: opacity 200ms;
34
+ }
35
+
36
+ :global([data-tree-header]:hover) .tree-item-subtree-toggle {
37
+ opacity: 1;
38
+ pointer-events: auto;
39
+ }
40
+
41
+ .tree-children > [data-tree-content]::before {
42
+ opacity: 0;
43
+ }
44
+
45
+ .tree-content:hover::before {
46
+ opacity: 1;
47
+ }
@@ -0,0 +1,76 @@
1
+ import type { CategoryNode, CategoryNodeProps, Statistic } from "@allurereport/core-api";
2
+ import { TreeHeader } from "@allurereport/web-components";
3
+ import clsx from "clsx";
4
+ import type { ComponentChildren, FC } from "preact/compat";
5
+ import { createCategoriesStickyStyle } from "@/components/Categories/sticky";
6
+ import { useI18n } from "@/stores";
7
+ import * as styles from "./styles.scss";
8
+
9
+ type GroupTreeItemProps = CategoryNodeProps & {
10
+ node: CategoryNode;
11
+ isOpened: boolean;
12
+ onToggle: () => void;
13
+ children: ComponentChildren;
14
+ depth: number;
15
+ reportStatistic?: Statistic;
16
+ className?: string;
17
+ title?: ComponentChildren;
18
+ subtreeToggle?: ComponentChildren;
19
+ };
20
+
21
+ export const GroupTreeItem: FC<GroupTreeItemProps> = ({
22
+ node,
23
+ nodeId,
24
+ isOpened,
25
+ onToggle,
26
+ children,
27
+ depth,
28
+ reportStatistic,
29
+ className,
30
+ title,
31
+ subtreeToggle,
32
+ }) => {
33
+ const { t: tEmpty } = useI18n("empty");
34
+ const { t: tFilters } = useI18n("filters");
35
+ const { t: tEnvironments } = useI18n("environments");
36
+ const stickyStyle = createCategoriesStickyStyle(depth);
37
+ const emptyKeyByGroup: Partial<Record<string, string>> = {
38
+ transition: "no-transition",
39
+ layer: "no-layer",
40
+ owner: "no-owner",
41
+ severity: "no-severity",
42
+ status: "no-status",
43
+ environment: "no-environment",
44
+ flaky: "no-flaky",
45
+ };
46
+ const defaultTitle =
47
+ node.name === "<Empty>" ? tEmpty(node.key ? (emptyKeyByGroup[node.key] ?? "no-value") : "no-value") : node.name;
48
+ const headerTitle = subtreeToggle ? (
49
+ <span className={styles["tree-item-group-title"]}>
50
+ <span className={styles["tree-item-group-title-content"]}>{title ?? defaultTitle}</span>
51
+ {subtreeToggle}
52
+ </span>
53
+ ) : (
54
+ (title ?? defaultTitle)
55
+ );
56
+
57
+ return (
58
+ <div
59
+ className={clsx(styles["tree-content"], styles["tree-item-group"], className)}
60
+ id={nodeId}
61
+ data-group-key={node.key}
62
+ >
63
+ <TreeHeader
64
+ isOpened={isOpened}
65
+ categoryTitle={headerTitle}
66
+ toggleTree={onToggle}
67
+ data-tree-header
68
+ style={stickyStyle}
69
+ reportStatistic={reportStatistic}
70
+ statistic={node.statistic}
71
+ statusFilter={"total"}
72
+ />
73
+ {isOpened && children}
74
+ </div>
75
+ );
76
+ };
@@ -0,0 +1,47 @@
1
+ .tree-content {
2
+ padding-left: 24px;
3
+ }
4
+
5
+ .tree-item-group {
6
+ padding: 0;
7
+ }
8
+
9
+ .tree-item-group :global(.styles_tree-section-title) {
10
+ display: flex;
11
+ align-items: center;
12
+ flex: 1 1 auto;
13
+ min-width: 0;
14
+ margin-right: 8px;
15
+ }
16
+
17
+ .tree-item-group-title {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 8px;
21
+ width: 100%;
22
+ }
23
+
24
+ .tree-item-group-title-content {
25
+ flex: 1 1 auto;
26
+ min-width: 0;
27
+ overflow: hidden;
28
+ text-overflow: ellipsis;
29
+ white-space: nowrap;
30
+ }
31
+
32
+ .tree-item-group[data-group-key="flaky"] :global(.styles_tree-section-title) {
33
+ color: var(--on-support-atlas);
34
+ }
35
+
36
+ .tree-item-group[data-group-key="transition"] :global(.styles_tree-section-title) {
37
+ color: var(--on-support-aldebaran);
38
+ }
39
+
40
+ .tree-item-group[data-group-key="owner"] :global(.styles_tree-section-title) {
41
+ color: var(--on-support-sirius);
42
+ }
43
+
44
+ .tree-item-group[data-group-key="layer"] :global(.styles_tree-section-title) {
45
+ color: var(--on-support-castor);
46
+ }
47
+