@allurereport/web-awesome 3.0.0-beta.22 → 3.0.0-beta.24

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 (58) hide show
  1. package/dist/multi/{173.app-08a07d0f676c912297d1.js → 173.app-9744a06934ffbef2c892.js} +1 -1
  2. package/dist/multi/{174.app-08a07d0f676c912297d1.js → 174.app-9744a06934ffbef2c892.js} +1 -1
  3. package/dist/multi/{252.app-08a07d0f676c912297d1.js → 252.app-9744a06934ffbef2c892.js} +1 -1
  4. package/dist/multi/{282.app-08a07d0f676c912297d1.js → 282.app-9744a06934ffbef2c892.js} +1 -1
  5. package/dist/multi/{29.app-08a07d0f676c912297d1.js → 29.app-9744a06934ffbef2c892.js} +1 -1
  6. package/dist/multi/{416.app-08a07d0f676c912297d1.js → 416.app-9744a06934ffbef2c892.js} +1 -1
  7. package/dist/multi/{527.app-08a07d0f676c912297d1.js → 527.app-9744a06934ffbef2c892.js} +1 -1
  8. package/dist/multi/{600.app-08a07d0f676c912297d1.js → 600.app-9744a06934ffbef2c892.js} +1 -1
  9. package/dist/multi/{605.app-08a07d0f676c912297d1.js → 605.app-9744a06934ffbef2c892.js} +1 -1
  10. package/dist/multi/{638.app-08a07d0f676c912297d1.js → 638.app-9744a06934ffbef2c892.js} +1 -1
  11. package/dist/multi/{672.app-08a07d0f676c912297d1.js → 672.app-9744a06934ffbef2c892.js} +1 -1
  12. package/dist/multi/{686.app-08a07d0f676c912297d1.js → 686.app-9744a06934ffbef2c892.js} +1 -1
  13. package/dist/multi/{725.app-08a07d0f676c912297d1.js → 725.app-9744a06934ffbef2c892.js} +1 -1
  14. package/dist/multi/{741.app-08a07d0f676c912297d1.js → 741.app-9744a06934ffbef2c892.js} +1 -1
  15. package/dist/multi/{755.app-08a07d0f676c912297d1.js → 755.app-9744a06934ffbef2c892.js} +1 -1
  16. package/dist/multi/{894.app-08a07d0f676c912297d1.js → 894.app-9744a06934ffbef2c892.js} +1 -1
  17. package/dist/multi/{943.app-08a07d0f676c912297d1.js → 943.app-9744a06934ffbef2c892.js} +1 -1
  18. package/dist/multi/{980.app-08a07d0f676c912297d1.js → 980.app-9744a06934ffbef2c892.js} +1 -1
  19. package/dist/multi/app-9744a06934ffbef2c892.js +2 -0
  20. package/dist/multi/manifest.json +20 -20
  21. package/dist/multi/styles-c0d22dac0f1d971a76eb.css +48 -0
  22. package/dist/single/app-bfa8df3fad4158726f8e.js +2 -0
  23. package/dist/single/manifest.json +1 -1
  24. package/package.json +6 -6
  25. package/src/assets/scss/theme.scss +73 -68
  26. package/src/components/Charts/index.tsx +72 -16
  27. package/src/components/Charts/styles.scss +1 -13
  28. package/src/components/SectionPicker/index.tsx +1 -0
  29. package/src/components/SectionSwitcher/index.tsx +2 -0
  30. package/src/components/Timeline/index.tsx +108 -0
  31. package/src/components/Timeline/styles.scss +45 -0
  32. package/src/locales/az.json +32 -3
  33. package/src/locales/de.json +32 -3
  34. package/src/locales/en.json +32 -3
  35. package/src/locales/es.json +32 -3
  36. package/src/locales/fr.json +32 -3
  37. package/src/locales/he.json +32 -3
  38. package/src/locales/hy.json +32 -3
  39. package/src/locales/it.json +32 -3
  40. package/src/locales/ja.json +32 -3
  41. package/src/locales/ka.json +32 -3
  42. package/src/locales/kr.json +32 -3
  43. package/src/locales/nl.json +32 -3
  44. package/src/locales/pl.json +40 -3
  45. package/src/locales/pt.json +32 -3
  46. package/src/locales/ru.json +40 -3
  47. package/src/locales/sv.json +32 -3
  48. package/src/locales/tr.json +32 -3
  49. package/src/locales/zh.json +32 -3
  50. package/src/stores/locale.ts +13 -3
  51. package/src/stores/theme.ts +14 -16
  52. package/src/stores/timeline.ts +34 -0
  53. package/types.d.ts +3 -3
  54. package/dist/multi/app-08a07d0f676c912297d1.js +0 -2
  55. package/dist/multi/styles-5b8b40733448845bbda5.css +0 -47
  56. package/dist/single/app-0e422701ed9861ee2c14.js +0 -2
  57. /package/dist/multi/{app-08a07d0f676c912297d1.js.LICENSE.txt → app-9744a06934ffbef2c892.js.LICENSE.txt} +0 -0
  58. /package/dist/single/{app-0e422701ed9861ee2c14.js.LICENSE.txt → app-bfa8df3fad4158726f8e.js.LICENSE.txt} +0 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- "main.js": "app-0e422701ed9861ee2c14.js"
2
+ "main.js": "app-bfa8df3fad4158726f8e.js"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/web-awesome",
3
- "version": "3.0.0-beta.22",
3
+ "version": "3.0.0-beta.24",
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.0.0-beta.22",
35
- "@allurereport/core-api": "3.0.0-beta.22",
36
- "@allurereport/plugin-api": "3.0.0-beta.22",
37
- "@allurereport/web-commons": "3.0.0-beta.22",
38
- "@allurereport/web-components": "3.0.0-beta.22",
34
+ "@allurereport/charts-api": "3.0.0-beta.24",
35
+ "@allurereport/core-api": "3.0.0-beta.24",
36
+ "@allurereport/plugin-api": "3.0.0-beta.24",
37
+ "@allurereport/web-commons": "3.0.0-beta.24",
38
+ "@allurereport/web-components": "3.0.0-beta.24",
39
39
  "@preact/signals": "^1.3.0",
40
40
  "clsx": "^2.1.1",
41
41
  "d3-shape": "^3.2.0",
@@ -30,6 +30,7 @@
30
30
  --constant-on-border-heavy: var(--constant-on-border-heavy-day);
31
31
  --constant-on-icon-primary: var(--constant-on-icon-primary-day);
32
32
  --constant-on-text-primary: var(--constant-on-text-primary-day);
33
+ --constant-on-text-secondary: var(--gray-neutral-alpha-night-300);
33
34
  --on-border-control: var(--on-border-control-day);
34
35
  --on-border-heavy: var(--on-border-heavy-day);
35
36
  --on-border-medium: var(--on-border-medium-day);
@@ -59,73 +60,77 @@
59
60
  color-scheme: light;
60
61
  }
61
62
 
62
- :root[data-theme="dark"] {
63
- --ad-other-focus: var(--ad-other-focus-night);
64
- --ad-other-modal-overlay: var(--ad-other-modal-overlay-night);
65
- --bg-alpha-capella-heavy: var(--bg-alpha-capella-heavy-night);
66
- --bg-alpha-capella-medium: var(--bg-alpha-capella-medium-night);
67
- --bg-alpha-capella: var(--bg-alpha-capella-night);
68
- --bg-alpha-castor-heavy: var(--bg-alpha-castor-heavy-night);
69
- --bg-base-modal: var(--bg-base-modal-night);
70
- --bg-base-neutral: var(--bg-base-neutral-night);
71
- --bg-base-primary: var(--bg-base-primary-night);
72
- --bg-base-secondary: var(--bg-base-secondary-night);
73
- --bg-control-flat-heavy: var(--bg-control-flat-heavy-night);
74
- --bg-control-flat-medium: var(--bg-control-flat-medium-night);
75
- --bg-control-flat: var(--bg-control-flat-night);
76
- --bg-control-secondary-heavy: var(--bg-control-secondary-heavy-night);
77
- --bg-control-secondary-medium: var(--bg-control-secondary-medium-night);
78
- --bg-control-secondary: var(--bg-control-secondary-night);
79
- --bg-support-aldebaran-heavy: var(--bg-support-aldebaran-heavy-night);
80
- --bg-support-aldebaran-medium: var(--bg-support-aldebaran-medium-night);
81
- --bg-support-aldebaran: var(--bg-support-aldebaran-night);
82
- --bg-support-atlas-heavy: var(--bg-support-atlas-heavy-night);
83
- --bg-support-atlas-medium: var(--bg-support-atlas-medium-night);
84
- --bg-support-atlas: var(--bg-support-atlas-night);
85
- --bg-support-capella-heavy: var(--bg-support-capella-heavy-night);
86
- --bg-support-capella-medium: var(--bg-support-capella-medium-night);
87
- --bg-support-capella: var(--bg-support-capella-night);
88
- --bg-support-castor-heavy: var(--bg-support-castor-heavy-night);
89
- --bg-support-castor-medium: var(--bg-support-castor-medium-night);
90
- --bg-support-castor: var(--bg-support-castor-night);
91
- --bg-support-rau: var(--bg-support-rau-night);
92
- --bg-support-skat: var(--bg-support-skat-night);
93
- --bg-support-sirius: var(--bg-support-sirius-night);
94
- --constant-bg-base-modal: var(--constant-bg-base-modal-night);
95
- --constant-bg-base-primary: var(--constant-bg-base-primary-night);
96
- --constant-on-border-heavy: var(--constant-on-border-heavy-night);
97
- --constant-on-icon-primary: var(--constant-on-icon-primary-night);
98
- --constant-on-text-primary: var(--constant-on-text-primary-night);
99
- --on-border-control: var(--on-border-control-night);
100
- --on-border-heavy: var(--on-border-heavy-night);
101
- --on-border-medium: var(--on-border-medium-night);
102
- --on-border-muted: var(--on-border-muted-night);
103
- --on-border-primary: var(--on-border-primary-night);
104
- --on-icon-hint: var(--on-icon-hint-night);
105
- --on-icon-primary: var(--on-icon-primary-night);
106
- --on-icon-secondary: var(--on-icon-secondary-night);
107
- --on-support-aldebaran-heavy: var(--on-support-aldebaran-heavy-night);
108
- --on-support-aldebaran-medium: var(--on-support-aldebaran-medium-night);
109
- --on-support-aldebaran: var(--on-support-aldebaran-night);
110
- --on-support-atlas-heavy: var(--on-support-atlas-heavy-night);
111
- --on-support-atlas-medium: var(--on-support-atlas-medium-night);
112
- --on-support-atlas: var(--on-support-atlas-night);
113
- --on-support-capella-heavy: var(--on-support-capella-heavy-night);
114
- --on-support-capella-medium: var(--on-support-capella-medium-night);
115
- --on-support-capella: var(--on-support-capella-night);
116
- --on-support-castor-heavy: var(--on-support-castor-heavy-night);
117
- --on-support-castor-medium: var(--on-support-castor-medium-night);
118
- --on-support-castor: var(--on-support-castor-night);
119
- --on-support-sirius: var(--on-support-sirius-night);
120
- --on-text-hint: var(--on-text-hint-night);
121
- --on-text-primary: var(--on-text-primary-night);
122
- --on-text-secondary: var(--on-text-secondary-night);
123
- --on-text-universal-primary: var(--on-text-universal-primary-night);
63
+ @media (prefers-color-scheme: dark) {
64
+ :root[data-theme="auto"],
65
+ :root[data-theme="dark"] {
66
+ --ad-other-focus: var(--ad-other-focus-night);
67
+ --ad-other-modal-overlay: var(--ad-other-modal-overlay-night);
68
+ --bg-alpha-capella-heavy: var(--bg-alpha-capella-heavy-night);
69
+ --bg-alpha-capella-medium: var(--bg-alpha-capella-medium-night);
70
+ --bg-alpha-capella: var(--bg-alpha-capella-night);
71
+ --bg-alpha-castor-heavy: var(--bg-alpha-castor-heavy-night);
72
+ --bg-base-modal: var(--bg-base-modal-night);
73
+ --bg-base-neutral: var(--bg-base-neutral-night);
74
+ --bg-base-primary: var(--bg-base-primary-night);
75
+ --bg-base-secondary: var(--bg-base-secondary-night);
76
+ --bg-control-flat-heavy: var(--bg-control-flat-heavy-night);
77
+ --bg-control-flat-medium: var(--bg-control-flat-medium-night);
78
+ --bg-control-flat: var(--bg-control-flat-night);
79
+ --bg-control-secondary-heavy: var(--bg-control-secondary-heavy-night);
80
+ --bg-control-secondary-medium: var(--bg-control-secondary-medium-night);
81
+ --bg-control-secondary: var(--bg-control-secondary-night);
82
+ --bg-support-aldebaran-heavy: var(--bg-support-aldebaran-heavy-night);
83
+ --bg-support-aldebaran-medium: var(--bg-support-aldebaran-medium-night);
84
+ --bg-support-aldebaran: var(--bg-support-aldebaran-night);
85
+ --bg-support-atlas-heavy: var(--bg-support-atlas-heavy-night);
86
+ --bg-support-atlas-medium: var(--bg-support-atlas-medium-night);
87
+ --bg-support-atlas: var(--bg-support-atlas-night);
88
+ --bg-support-capella-heavy: var(--bg-support-capella-heavy-night);
89
+ --bg-support-capella-medium: var(--bg-support-capella-medium-night);
90
+ --bg-support-capella: var(--bg-support-capella-night);
91
+ --bg-support-castor-heavy: var(--bg-support-castor-heavy-night);
92
+ --bg-support-castor-medium: var(--bg-support-castor-medium-night);
93
+ --bg-support-castor: var(--bg-support-castor-night);
94
+ --bg-support-rau: var(--bg-support-rau-night);
95
+ --bg-support-skat: var(--bg-support-skat-night);
96
+ --bg-support-sirius: var(--bg-support-sirius-night);
97
+ --constant-bg-base-modal: var(--constant-bg-base-modal-night);
98
+ --constant-bg-base-primary: var(--constant-bg-base-primary-night);
99
+ --constant-on-border-heavy: var(--constant-on-border-heavy-night);
100
+ --constant-on-icon-primary: var(--constant-on-icon-primary-night);
101
+ --constant-on-text-primary: var(--constant-on-text-primary-night);
102
+ --constant-on-text-secondary: var(--gray-neutral-alpha-night-300);
103
+ --on-border-control: var(--on-border-control-night);
104
+ --on-border-heavy: var(--on-border-heavy-night);
105
+ --on-border-medium: var(--on-border-medium-night);
106
+ --on-border-muted: var(--on-border-muted-night);
107
+ --on-border-primary: var(--on-border-primary-night);
108
+ --on-icon-hint: var(--on-icon-hint-night);
109
+ --on-icon-primary: var(--on-icon-primary-night);
110
+ --on-icon-secondary: var(--on-icon-secondary-night);
111
+ --on-support-aldebaran-heavy: var(--on-support-aldebaran-heavy-night);
112
+ --on-support-aldebaran-medium: var(--on-support-aldebaran-medium-night);
113
+ --on-support-aldebaran: var(--on-support-aldebaran-night);
114
+ --on-support-atlas-heavy: var(--on-support-atlas-heavy-night);
115
+ --on-support-atlas-medium: var(--on-support-atlas-medium-night);
116
+ --on-support-atlas: var(--on-support-atlas-night);
117
+ --on-support-capella-heavy: var(--on-support-capella-heavy-night);
118
+ --on-support-capella-medium: var(--on-support-capella-medium-night);
119
+ --on-support-capella: var(--on-support-capella-night);
120
+ --on-support-castor-heavy: var(--on-support-castor-heavy-night);
121
+ --on-support-castor-medium: var(--on-support-castor-medium-night);
122
+ --on-support-castor: var(--on-support-castor-night);
123
+ --on-support-sirius: var(--on-support-sirius-night);
124
+ --on-text-hint: var(--on-text-hint-night);
125
+ --on-text-primary: var(--on-text-primary-night);
126
+ --on-text-secondary: var(--on-text-secondary-night);
127
+ --on-text-universal-primary: var(--on-text-universal-primary-night);
124
128
 
125
- --scrollbar-color: var(--gray-400);
126
- --bg-alpha-atlas: var(--atlas-night-darken-5-12);
127
- --bg-alpha-rau: var(--rau-night-darken-5-12);
128
- --bg-alpha-castor: var(--castor-night-darken-5-20);
129
- --bg-alpha-skat: var(--skat-night-darken-5-12);
130
- color-scheme: dark;
129
+ --scrollbar-color: var(--gray-400);
130
+ --bg-alpha-atlas: var(--atlas-night-darken-5-12);
131
+ --bg-alpha-rau: var(--rau-night-darken-5-12);
132
+ --bg-alpha-castor: var(--castor-night-darken-5-20);
133
+ --bg-alpha-skat: var(--skat-night-darken-5-12);
134
+ color-scheme: dark;
135
+ }
131
136
  }
@@ -1,21 +1,20 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
2
- import { BarChartType, ChartType, FunnelChartType } from "@allurereport/charts-api";
3
- import { capitalize } from "@allurereport/core-api";
2
+ import { BarChartType, ChartDataType, ChartType, FunnelChartType } from "@allurereport/charts-api";
3
+ import { capitalize, statusesList } from "@allurereport/core-api";
4
4
  import { type UIChartData } from "@allurereport/web-commons";
5
5
  import {
6
6
  BarChartWidget,
7
7
  ComingSoonChartWidget,
8
+ CurrentStatusChartWidget,
8
9
  Grid,
9
10
  GridItem,
10
11
  HeatMapWidget,
11
12
  Loadable,
12
13
  PageLoader,
13
14
  StabilityRateDistributionWidget,
14
- SuccessRatePieChart,
15
15
  TestingPyramidWidget,
16
16
  TreeMapChartWidget,
17
17
  TrendChartWidget,
18
- Widget,
19
18
  } from "@allurereport/web-components";
20
19
  import { useEffect } from "preact/hooks";
21
20
  import { chartsStore, fetchChartsData } from "@/stores/chart";
@@ -31,12 +30,17 @@ const getChartWidgetByType = (
31
30
  case ChartType.Trend: {
32
31
  const type = t(`trend.type.${chartData.dataType}`);
33
32
  const title = chartData.title ?? t("trend.title", { type: capitalize(type) });
33
+ let isDataEmpty = false;
34
+
35
+ if (chartData.dataType === ChartDataType.Status) {
36
+ isDataEmpty = !chartData.slices.some((slice) => slice.min > 0 || slice.max > 0);
37
+ }
34
38
 
35
39
  return (
36
40
  <TrendChartWidget
37
41
  title={title}
38
42
  mode={chartData.mode}
39
- items={chartData.items}
43
+ items={isDataEmpty ? [] : chartData.items}
40
44
  slices={chartData.slices}
41
45
  min={chartData.min}
42
46
  max={chartData.max}
@@ -44,22 +48,29 @@ const getChartWidgetByType = (
44
48
  />
45
49
  );
46
50
  }
47
- case ChartType.Pie: {
48
- const title = chartData.title ?? t("pie.title");
51
+ case ChartType.CurrentStatus: {
52
+ const title = chartData.title ?? t("currentStatus.title");
49
53
 
50
54
  return (
51
- <Widget title={title}>
52
- <div className={styles["overview-grid-item-pie-chart-wrapper"]}>
53
- <div className={styles["overview-grid-item-pie-chart-wrapper-squeezer"]}>
54
- <SuccessRatePieChart slices={chartData.slices} percentage={chartData.percentage} />
55
- </div>
56
- </div>
57
- </Widget>
55
+ <CurrentStatusChartWidget
56
+ title={title}
57
+ data={chartData.data}
58
+ statuses={chartData.statuses}
59
+ metric={chartData.metric}
60
+ i18n={(key, props = {}) => t(`currentStatus.${key}`, props)}
61
+ />
58
62
  );
59
63
  }
60
64
  case ChartType.Bar: {
61
65
  const type = t(`bar.type.${chartData.dataType}`);
62
66
  const title = chartData.title ?? t("bar.title", { type: capitalize(type) });
67
+ /**
68
+ * A flag that indicates whether the chart data should be considered empty.
69
+ * For some chart types, simply checking the `data` array length is not sufficient,
70
+ * because additional logic may be needed to assess the presence of meaningful data.
71
+ * This flag ensures that specific conditions for data emptiness are handled per chart type.
72
+ */
73
+ let isDataEmpty = false;
63
74
 
64
75
  if (chartData.dataType === BarChartType.StabilityRateDistribution) {
65
76
  return (
@@ -80,11 +91,54 @@ const getChartWidgetByType = (
80
91
  );
81
92
  }
82
93
 
94
+ if (chartData.dataType === BarChartType.StatusChangeTrend) {
95
+ /*
96
+ * For StatusChangeTrend data:
97
+ * - The data consists of keys such as newPassed, newFailed, newBroken, newSkipped, etc., all with numeric values.
98
+ * - We must check if any of these metrics in any group are greater than 0.
99
+ * - If at least one statistic is greater than 0, we should display the chart.
100
+ */
101
+ isDataEmpty = !chartData.data.some((item) => chartData.keys.some((key) => (item[key] ?? 0) > 0));
102
+ }
103
+
104
+ if (chartData.dataType === BarChartType.StatusBySeverity) {
105
+ /*
106
+ * Data for this chart type consists of statistics where each group contains
107
+ * metrics such as passed, failed, broken, etc., represented as numbers.
108
+ * To determine if the chart should be displayed, we need to check if any of these
109
+ * metrics in any group is greater than 0. If at least one of the statistics is
110
+ * greater than 0, we display the chart; otherwise, we consider the data empty.
111
+ */
112
+ isDataEmpty = !chartData.data.some(
113
+ (item) => chartData.keys.includes(item.groupId) && statusesList.some((status) => (item[status] ?? 0) > 0),
114
+ );
115
+ }
116
+
117
+ if (chartData.dataType === BarChartType.StatusTrend) {
118
+ /*
119
+ * The data structure contains keys such as passed, failed, and broken, each associated with numeric values.
120
+ * To determine whether the chart should be displayed, we need to check if any value for these keys
121
+ * is greater than 0 in any of the data groups. If at least one of those metrics is greater than 0,
122
+ * the chart will be displayed; otherwise, it should be considered empty.
123
+ */
124
+ isDataEmpty = !chartData.data.some((item) => chartData.keys.some((key) => (item[key] ?? 0) > 0));
125
+ }
126
+
127
+ if (chartData.dataType === BarChartType.FbsuAgePyramid) {
128
+ /*
129
+ * For FbsuAgePyramid data:
130
+ * - Each group contains keys such as passed, failed, broken, etc., with numeric values.
131
+ * - To determine if the chart should be shown, check if any statistic in any group is greater than 0.
132
+ * - If at least one such statistic exists, display the chart; otherwise, consider the data empty.
133
+ */
134
+ isDataEmpty = !chartData.data.some((item) => chartData.keys.some((key) => (item[key] ?? 0) > 0));
135
+ }
136
+
83
137
  return (
84
138
  <BarChartWidget
85
139
  title={title}
86
140
  mode={chartData.mode}
87
- data={chartData.data}
141
+ data={isDataEmpty ? [] : chartData.data}
88
142
  keys={chartData.keys}
89
143
  indexBy={chartData.indexBy}
90
144
  colors={chartData.colors}
@@ -124,10 +178,12 @@ const getChartWidgetByType = (
124
178
  return null;
125
179
  }
126
180
 
181
+ const isDataEmpty = !chartData.data.some((item) => item.testCount > 0);
182
+
127
183
  return (
128
184
  <TestingPyramidWidget
129
185
  title={chartData.title}
130
- data={chartData.data}
186
+ data={isDataEmpty ? [] : chartData.data}
131
187
  translations={{ "no-results": empty("no-results") }}
132
188
  />
133
189
  );
@@ -14,16 +14,4 @@
14
14
  .overview-grid-item {
15
15
  padding: 4px;
16
16
  width: 100%;
17
- }
18
-
19
- .overview-grid-item-pie-chart-wrapper {
20
- display: flex;
21
- justify-content: center;
22
- align-items: center;
23
- width: 100%;
24
- height: 100%;
25
- }
26
-
27
- .overview-grid-item-pie-chart-wrapper-squeezer {
28
- width: 50%;
29
- }
17
+ }
@@ -13,6 +13,7 @@ const defaultSection: SectionItem = { name: "report", logo: allureIcons.reportLo
13
13
  const sectionMap: Record<string, SectionItem> = {
14
14
  default: defaultSection,
15
15
  charts: { name: "charts", logo: allureIcons.lineChartsBarChartSquare },
16
+ timeline: { name: "timeline", logo: allureIcons.lineChartsTimeline },
16
17
  };
17
18
 
18
19
  export const SectionPicker = () => {
@@ -2,12 +2,14 @@ import type { VNode } from "preact";
2
2
  import { Charts } from "@/components/Charts";
3
3
  import { Report } from "@/components/Report";
4
4
  import { currentSection } from "@/stores/sections";
5
+ import { Timeline } from "../Timeline";
5
6
  import * as styles from "./styles.scss";
6
7
 
7
8
  export const SectionSwitcher = () => {
8
9
  const sectionMap: Record<string, VNode> = {
9
10
  report: <Report />,
10
11
  charts: <Charts />,
12
+ timeline: <Timeline />,
11
13
  };
12
14
 
13
15
  return <div className={styles.layout}>{sectionMap[currentSection.value] || sectionMap.report}</div>;
@@ -0,0 +1,108 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
2
+ import type { TestResult } from "@allurereport/core-api";
3
+ import { Timeline as AllureTimeline, Grid, GridItem, Loadable, PageLoader, Widget } from "@allurereport/web-components";
4
+ import { computed } from "@preact/signals";
5
+ import { useEffect, useMemo } from "preact/hooks";
6
+ import { useI18n } from "@/stores";
7
+ import { currentEnvironment } from "@/stores/env";
8
+ import { fetchTimelineData, timelineStore } from "@/stores/timeline";
9
+ import * as styles from "./styles.scss";
10
+
11
+ const getHosts = (tests: TestResult[]) => [
12
+ ...new Set(tests.map((test) => test.labels.find((label) => label.name === "host")?.value).filter(Boolean)),
13
+ ];
14
+
15
+ const filterTestsByHost = (tests: TestResult[], host: string) =>
16
+ tests.filter((test) => test.labels.find((label) => label.name === "host")?.value === host);
17
+
18
+ const currentTimelineData = computed(() => {
19
+ if (!timelineStore.value.data) {
20
+ return [];
21
+ }
22
+
23
+ if (currentEnvironment.value) {
24
+ const testsToEnv = timelineStore.value.data.filter((test) => test.environment === currentEnvironment.value);
25
+ const hostsByEnv = getHosts(testsToEnv);
26
+
27
+ return hostsByEnv.map((host) => ({
28
+ data: filterTestsByHost(testsToEnv, host),
29
+ host,
30
+ }));
31
+ }
32
+
33
+ const hosts = getHosts(timelineStore.value.data);
34
+
35
+ return hosts.map((host) => ({
36
+ data: filterTestsByHost(timelineStore.value.data, host),
37
+ host,
38
+ }));
39
+ });
40
+
41
+ // Detects if the user prefers reduced motion to disable animations for accessibility
42
+ const prefersLessMovement = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
43
+ export const Timeline = () => {
44
+ const { t } = useI18n("timeline");
45
+
46
+ useEffect(() => {
47
+ fetchTimelineData();
48
+ }, []);
49
+
50
+ const translations = useMemo(
51
+ () => ({
52
+ empty: t("empty"),
53
+ selected: (props: { count: number; percentage: string; minDuration: string; maxDuration: string }) =>
54
+ t("selected", {
55
+ count: props.count,
56
+ percentage: props.percentage,
57
+ minDuration: props.minDuration,
58
+ maxDuration: props.maxDuration,
59
+ }),
60
+ }),
61
+ [t],
62
+ );
63
+
64
+ return (
65
+ <Loadable
66
+ source={timelineStore}
67
+ renderLoader={() => <PageLoader />}
68
+ renderData={() => {
69
+ if (currentTimelineData.value.length === 0) {
70
+ return (
71
+ <div className={styles.overview}>
72
+ <Grid kind="swap" className={styles["overview-grid"]}>
73
+ <GridItem className={styles["overview-grid-item"]}>
74
+ <Widget>
75
+ <div className={styles.empty}>{t("empty")}</div>
76
+ </Widget>
77
+ </GridItem>
78
+ </Grid>
79
+ </div>
80
+ );
81
+ }
82
+
83
+ return (
84
+ <div className={styles.overview}>
85
+ <Grid kind="swap" className={styles["overview-grid"]}>
86
+ {currentTimelineData.value.map(({ data, host }) => (
87
+ <GridItem key={host} className={styles["overview-grid-item"]}>
88
+ <Widget title={t("host", { host })}>
89
+ {data.length > 0 && (
90
+ <AllureTimeline
91
+ data={data}
92
+ dataId={host}
93
+ width={100}
94
+ enableAnimations={!prefersLessMovement}
95
+ translations={translations}
96
+ />
97
+ )}
98
+ {data.length === 0 && <div className={styles.empty}>{t("empty_host", { host })}</div>}
99
+ </Widget>
100
+ </GridItem>
101
+ ))}
102
+ </Grid>
103
+ </div>
104
+ );
105
+ }}
106
+ />
107
+ );
108
+ };
@@ -0,0 +1,45 @@
1
+ .overview {
2
+ padding: 0 24px;
3
+ width: 100%;
4
+ height: 100%;
5
+ overflow-y: auto;
6
+
7
+ --timeline-font-family: var(--font-family);
8
+ --timeline-grid: var(--on-border-control);
9
+ --timeline-border-primary: var(--on-border-primary);
10
+ --timeline-axis-text: var(--on-text-secondary);
11
+ --timeline-bg: var(--bg-base-primary);
12
+ --timeline-selection-bg: var(--bg-control-flat-medium);
13
+ }
14
+
15
+ .overview-grid {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 12px;
19
+ }
20
+
21
+ .overview-grid-item {
22
+ padding: 4px;
23
+ width: 100%;
24
+ }
25
+
26
+ .overview-grid-item-pie-chart-wrapper {
27
+ display: flex;
28
+ justify-content: center;
29
+ align-items: center;
30
+ width: 100%;
31
+ height: 100%;
32
+ }
33
+
34
+ .overview-grid-item-pie-chart-wrapper-squeezer {
35
+ width: 50%;
36
+ }
37
+
38
+ .empty {
39
+ display: flex;
40
+ justify-content: center;
41
+ align-items: center;
42
+ width: 100%;
43
+ min-height: 320px;
44
+ color: var(--on-text-hint);
45
+ }
@@ -162,7 +162,15 @@
162
162
  },
163
163
  "sections": {
164
164
  "report": "Report",
165
- "charts": "Qrafiklər"
165
+ "charts": "Qrafiklər",
166
+ "timeline": "Zaman xətti"
167
+ },
168
+ "timeline": {
169
+ "empty": "Məlumat yoxdur",
170
+ "empty_host": "{{ host }} üçün məlumat yoxdur",
171
+ "selected_one": "{{ count }} test ({{ percentage }}%) {{ minDuration }}-dən çox və {{ maxDuration }}-dən az müddətlə seçildi",
172
+ "selected_other": "{{ count }} test ({{ percentage }}%) {{ minDuration }}-dən çox və {{ maxDuration }}-dən az müddətlə seçildi",
173
+ "host": "Host: {{ host }}"
166
174
  },
167
175
  "charts": {
168
176
  "trend": {
@@ -172,8 +180,29 @@
172
180
  "severity": "Ciddilik"
173
181
  }
174
182
  },
175
- "pie": {
176
- "title": "Test Uğur Dərəcəsi"
183
+ "currentStatus": {
184
+ "title": "Cari Status",
185
+ "status": {
186
+ "passed": "$t(statuses:passed, capitalize)",
187
+ "failed": "$t(statuses:failed, capitalize)",
188
+ "broken": "$t(statuses:broken, capitalize)",
189
+ "skipped": "$t(statuses:skipped, capitalize)",
190
+ "unknown": "$t(statuses:unknown, capitalize)"
191
+ },
192
+ "percentage": "{{percentage}}%",
193
+ "of": "{{total}} dən",
194
+ "total": "cəmi",
195
+ "tests": {
196
+ "new_zero": "Yeni test yoxdur",
197
+ "new_one": "{{count}} yeni test",
198
+ "new_other": "{{count}} yeni test",
199
+ "flaky_zero": "Qeyri-sabit test yoxdur",
200
+ "flaky_one": "{{count}} qeyri-sabit test",
201
+ "flaky_other": "{{count}} qeyri-sabit test",
202
+ "retries_zero": "Yenidən cəhd edilmiş test yoxdur",
203
+ "retries_one": "{{count}} yenidən cəhd edilmiş test",
204
+ "retries_other": "{{count}} yenidən cəhd edilmiş test"
205
+ }
177
206
  }
178
207
  },
179
208
  "transitions.description": {
@@ -162,7 +162,15 @@
162
162
  },
163
163
  "sections": {
164
164
  "report": "Report",
165
- "charts": "Graphen"
165
+ "charts": "Graphen",
166
+ "timeline": "Zeitleiste"
167
+ },
168
+ "timeline": {
169
+ "empty": "Keine Daten",
170
+ "empty_host": "Keine Daten für {{ host }}",
171
+ "selected_one": "{{ count }} Test ({{ percentage }}%) mit Dauer mehr als {{ minDuration }} und weniger als {{ maxDuration }} ausgewählt",
172
+ "selected_other": "{{ count }} Tests ({{ percentage }}%) mit Dauer mehr als {{ minDuration }} und weniger als {{ maxDuration }} ausgewählt",
173
+ "host": "Host: {{ host }}"
166
174
  },
167
175
  "charts": {
168
176
  "trend": {
@@ -172,8 +180,29 @@
172
180
  "severity": "Schweregrad"
173
181
  }
174
182
  },
175
- "pie": {
176
- "title": "Testerfolgsrate"
183
+ "currentStatus": {
184
+ "title": "Aktueller Status",
185
+ "status": {
186
+ "passed": "$t(statuses:passed, capitalize)",
187
+ "failed": "$t(statuses:failed, capitalize)",
188
+ "broken": "$t(statuses:broken, capitalize)",
189
+ "skipped": "$t(statuses:skipped, capitalize)",
190
+ "unknown": "$t(statuses:unknown, capitalize)"
191
+ },
192
+ "percentage": "{{percentage}}%",
193
+ "of": "von {{total}}",
194
+ "total": "gesamt",
195
+ "tests": {
196
+ "new_zero": "Keine neuen Tests",
197
+ "new_one": "{{count}} neuer Test",
198
+ "new_other": "{{count}} neue Tests",
199
+ "flaky_zero": "Keine instabilen Tests",
200
+ "flaky_one": "{{count}} instabiler Test",
201
+ "flaky_other": "{{count}} instabile Tests",
202
+ "retries_zero": "Keine wiederholten Tests",
203
+ "retries_one": "{{count}} wiederholter Test",
204
+ "retries_other": "{{count}} wiederholte Tests"
205
+ }
177
206
  }
178
207
  },
179
208
  "transitions.description": {