@allurereport/web-awesome 3.0.0-beta.12 → 3.0.0-beta.14

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 (107) hide show
  1. package/dist/multi/{173.app-52d1decd.js → 173.app-1829fb8f.js} +1 -1
  2. package/dist/multi/174.app-1829fb8f.js +1 -0
  3. package/dist/multi/252.app-1829fb8f.js +1 -0
  4. package/dist/multi/{282.app-52d1decd.js → 282.app-1829fb8f.js} +1 -1
  5. package/dist/multi/29.app-1829fb8f.js +1 -0
  6. package/dist/multi/416.app-1829fb8f.js +1 -0
  7. package/dist/multi/527.app-1829fb8f.js +1 -0
  8. package/dist/multi/{600.app-52d1decd.js → 600.app-1829fb8f.js} +1 -1
  9. package/dist/multi/605.app-1829fb8f.js +1 -0
  10. package/dist/multi/{638.app-52d1decd.js → 638.app-1829fb8f.js} +1 -1
  11. package/dist/multi/672.app-1829fb8f.js +1 -0
  12. package/dist/multi/{686.app-52d1decd.js → 686.app-1829fb8f.js} +1 -1
  13. package/dist/multi/725.app-1829fb8f.js +1 -0
  14. package/dist/multi/{741.app-52d1decd.js → 741.app-1829fb8f.js} +1 -1
  15. package/dist/multi/755.app-1829fb8f.js +1 -0
  16. package/dist/multi/{894.app-52d1decd.js → 894.app-1829fb8f.js} +1 -1
  17. package/dist/multi/943.app-1829fb8f.js +1 -0
  18. package/dist/multi/980.app-1829fb8f.js +1 -0
  19. package/dist/multi/app-1829fb8f.js +2 -0
  20. package/dist/multi/manifest.json +20 -20
  21. package/dist/multi/{styles-52d1decd.css → styles-1829fb8f.css} +13 -11
  22. package/dist/single/app-3a6e31c7.js +2 -0
  23. package/dist/single/manifest.json +1 -1
  24. package/package.json +4 -4
  25. package/src/components/BaseLayout/index.tsx +1 -4
  26. package/src/components/BaseLayout/styles.scss +4 -2
  27. package/src/components/Charts/index.tsx +89 -0
  28. package/src/components/Charts/styles.scss +29 -0
  29. package/src/components/Footer/index.tsx +6 -2
  30. package/src/components/Header/index.tsx +5 -2
  31. package/src/components/Header/styles.scss +2 -0
  32. package/src/components/MainReport/index.tsx +0 -2
  33. package/src/components/MetadataButton/index.tsx +2 -2
  34. package/src/components/Report/index.tsx +7 -0
  35. package/src/components/ReportBody/Filters.tsx +2 -2
  36. package/src/components/ReportBody/index.tsx +24 -13
  37. package/src/components/ReportBody/styles.scss +1 -0
  38. package/src/components/ReportHeader/index.tsx +3 -0
  39. package/src/components/ReportMetadata/MetadataWithIcon.tsx +1 -1
  40. package/src/components/SectionPicker/index.tsx +54 -0
  41. package/src/components/SectionPicker/styles.scss +5 -0
  42. package/src/components/SectionSwitcher/index.tsx +14 -0
  43. package/src/components/SectionSwitcher/styles.scss +4 -0
  44. package/src/components/SectionTabs/index.tsx +0 -0
  45. package/src/components/SideBySide/styles.scss +1 -1
  46. package/src/components/SplitLayout/index.tsx +1 -3
  47. package/src/components/SplitLayout/styles.scss +1 -3
  48. package/src/components/TestResult/TrDropdown/index.tsx +1 -2
  49. package/src/components/TestResult/TrEnvironmentItem/index.tsx +3 -3
  50. package/src/components/TestResult/TrEnvironmentsView/index.tsx +2 -2
  51. package/src/components/TestResult/TrHistory/TrHistoryItem.tsx +1 -2
  52. package/src/components/TestResult/TrInfo/index.tsx +16 -12
  53. package/src/components/TestResult/TrPwTraces/PwTraceButton.tsx +0 -1
  54. package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +5 -4
  55. package/src/components/TestResult/TrSteps/TrAttachment.tsx +33 -6
  56. package/src/components/TestResult/TrSteps/TrStep.tsx +7 -5
  57. package/src/components/TestResult/TrSteps/styles.scss +4 -6
  58. package/src/components/TestResult/TrTabs/index.tsx +18 -6
  59. package/src/components/TestResult/TrTabs/styles.scss +0 -3
  60. package/src/components/TestResult/index.tsx +0 -1
  61. package/src/components/Tree/index.tsx +6 -6
  62. package/src/components/Tree/styles.scss +0 -4
  63. package/src/index.tsx +26 -12
  64. package/src/locales/az.json +16 -0
  65. package/src/locales/de.json +17 -0
  66. package/src/locales/en.json +17 -1
  67. package/src/locales/es.json +17 -1
  68. package/src/locales/fr.json +17 -1
  69. package/src/locales/he.json +16 -0
  70. package/src/locales/hy.json +16 -0
  71. package/src/locales/it.json +17 -1
  72. package/src/locales/ja.json +17 -1
  73. package/src/locales/ka.json +16 -0
  74. package/src/locales/kr.json +17 -1
  75. package/src/locales/nl.json +17 -1
  76. package/src/locales/pl.json +17 -1
  77. package/src/locales/pt.json +16 -0
  78. package/src/locales/ru.json +16 -0
  79. package/src/locales/sv.json +17 -1
  80. package/src/locales/tr.json +17 -1
  81. package/src/locales/zh.json +17 -1
  82. package/src/stores/chart.ts +32 -0
  83. package/src/stores/locale.ts +2 -0
  84. package/src/stores/router.ts +76 -16
  85. package/src/stores/sections.ts +63 -0
  86. package/src/stores/tree.ts +9 -6
  87. package/src/styles.scss +21 -0
  88. package/src/utils/charts.ts +169 -0
  89. package/src/utils/time.ts +1 -0
  90. package/types.d.ts +3 -1
  91. package/dist/multi/174.app-52d1decd.js +0 -1
  92. package/dist/multi/252.app-52d1decd.js +0 -1
  93. package/dist/multi/29.app-52d1decd.js +0 -1
  94. package/dist/multi/416.app-52d1decd.js +0 -1
  95. package/dist/multi/527.app-52d1decd.js +0 -1
  96. package/dist/multi/605.app-52d1decd.js +0 -1
  97. package/dist/multi/672.app-52d1decd.js +0 -1
  98. package/dist/multi/725.app-52d1decd.js +0 -1
  99. package/dist/multi/755.app-52d1decd.js +0 -1
  100. package/dist/multi/943.app-52d1decd.js +0 -1
  101. package/dist/multi/980.app-52d1decd.js +0 -1
  102. package/dist/multi/app-52d1decd.js +0 -2
  103. package/dist/single/app-83b0c4fc.js +0 -2
  104. package/src/components/ArrowButton/index.tsx +0 -36
  105. package/src/components/ArrowButton/styles.scss +0 -35
  106. /package/dist/multi/{app-52d1decd.js.LICENSE.txt → app-1829fb8f.js.LICENSE.txt} +0 -0
  107. /package/dist/single/{app-83b0c4fc.js.LICENSE.txt → app-3a6e31c7.js.LICENSE.txt} +0 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- "main.js": "app-83b0c4fc.js"
2
+ "main.js": "app-3a6e31c7.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.12",
3
+ "version": "3.0.0-beta.14",
4
4
  "description": "The static files for Allure Awesome Report",
5
5
  "keywords": [
6
6
  "allure",
@@ -31,9 +31,9 @@
31
31
  "IE 11"
32
32
  ],
33
33
  "dependencies": {
34
- "@allurereport/core-api": "3.0.0-beta.12",
35
- "@allurereport/web-commons": "3.0.0-beta.12",
36
- "@allurereport/web-components": "3.0.0-beta.12",
34
+ "@allurereport/core-api": "3.0.0-beta.14",
35
+ "@allurereport/web-commons": "3.0.0-beta.14",
36
+ "@allurereport/web-components": "3.0.0-beta.14",
37
37
  "@preact/signals": "^1.3.0",
38
38
  "clsx": "^2.1.1",
39
39
  "d3-shape": "^3.2.0",
@@ -1,5 +1,4 @@
1
1
  import { Loadable, PageLoader } from "@allurereport/web-components";
2
- import { Footer } from "@/components/Footer";
3
2
  import MainReport from "@/components/MainReport";
4
3
  import TestResult from "@/components/TestResult";
5
4
  import { route } from "@/stores/router";
@@ -12,7 +11,7 @@ export type BaseLayoutProps = {
12
11
  };
13
12
 
14
13
  export const BaseLayout = () => {
15
- const { id: testResultId } = route.value;
14
+ const testResultId = route.value.params?.testResultId ?? null;
16
15
 
17
16
  const content = testResultId ? (
18
17
  <Loadable
@@ -23,7 +22,6 @@ export const BaseLayout = () => {
23
22
  <>
24
23
  <div className={styles.wrapper} key={testResult?.id}>
25
24
  <TestResult testResult={testResult} />
26
- <Footer />
27
25
  </div>
28
26
  </>
29
27
  )}
@@ -31,7 +29,6 @@ export const BaseLayout = () => {
31
29
  ) : (
32
30
  <div className={styles.wrapper}>
33
31
  <Loadable source={treeStore} renderLoader={() => <PageLoader />} renderData={() => <MainReport />} />
34
- <Footer />
35
32
  </div>
36
33
  );
37
34
 
@@ -4,7 +4,8 @@
4
4
  background: var(--bg-base-secondary);
5
5
  color: var(--on-text-primary);
6
6
  font-size: 14px;
7
- min-height: 100vh;
7
+ height: 100%;
8
+ overflow: auto;
8
9
  }
9
10
 
10
11
  .wrapper {
@@ -51,7 +52,8 @@
51
52
  display: flex;
52
53
  justify-content: space-between;
53
54
  align-items: center;
54
- padding: 8px;
55
+ padding: 8px 24px;
56
+ width: 100%;
55
57
  }
56
58
 
57
59
  .test-result-errors {
@@ -0,0 +1,89 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
2
+ import { capitalize } from "@allurereport/web-commons";
3
+ import {
4
+ Grid,
5
+ GridItem,
6
+ Loadable,
7
+ PageLoader,
8
+ SuccessRatePieChart,
9
+ TrendChartWidget,
10
+ Widget,
11
+ } from "@allurereport/web-components";
12
+ import { useEffect } from "preact/hooks";
13
+ import { chartsStore, fetchChartsData } from "@/stores/chart";
14
+ import { useI18n } from "@/stores/locale";
15
+ import type { ChartData } from "@/utils/charts";
16
+ import { ChartType } from "@/utils/charts";
17
+ import * as styles from "./styles.scss";
18
+
19
+ const getChartWidgetByType = (
20
+ chartData: ChartData,
21
+ { t, empty }: Record<string, (key: string, options?: any) => string>,
22
+ ) => {
23
+ switch (chartData.type) {
24
+ case ChartType.Trend: {
25
+ const type = t(`trend.type.${chartData.dataType}`);
26
+ const title = chartData.title ?? t("trend.title", { type: capitalize(type) });
27
+ const translations = empty("no-results");
28
+
29
+ return (
30
+ <TrendChartWidget
31
+ title={title}
32
+ items={chartData.items}
33
+ slices={chartData.slices}
34
+ min={chartData.min}
35
+ max={chartData.max}
36
+ translations={{ "no-results": translations }}
37
+ />
38
+ );
39
+ }
40
+ case ChartType.Pie: {
41
+ const title = chartData.title ?? t("pie.title");
42
+
43
+ return (
44
+ <Widget title={title}>
45
+ <div className={styles["overview-grid-item-pie-chart-wrapper"]}>
46
+ <div className={styles["overview-grid-item-pie-chart-wrapper-squeezer"]}>
47
+ <SuccessRatePieChart slices={chartData.slices} percentage={chartData.percentage} />
48
+ </div>
49
+ </div>
50
+ </Widget>
51
+ );
52
+ }
53
+ }
54
+ };
55
+
56
+ export const Charts = () => {
57
+ const { t } = useI18n("charts");
58
+ const { t: empty } = useI18n("empty");
59
+
60
+ useEffect(() => {
61
+ fetchChartsData();
62
+ }, []);
63
+
64
+ return (
65
+ <Loadable
66
+ source={chartsStore}
67
+ renderLoader={() => <PageLoader />}
68
+ renderData={(data) => {
69
+ const charts = Object.entries(data).map(([chartId, value]) => {
70
+ const chartWidget = getChartWidgetByType(value, { t, empty });
71
+
72
+ return (
73
+ <GridItem key={chartId} className={styles["overview-grid-item"]}>
74
+ {chartWidget}
75
+ </GridItem>
76
+ );
77
+ });
78
+
79
+ return (
80
+ <div className={styles.overview}>
81
+ <Grid kind="swap" className={styles["overview-grid"]}>
82
+ {charts}
83
+ </Grid>
84
+ </div>
85
+ );
86
+ }}
87
+ />
88
+ );
89
+ };
@@ -0,0 +1,29 @@
1
+ .overview {
2
+ padding: 0 24px;
3
+ width: 100%;
4
+ height: 100%;
5
+ overflow-y: auto;
6
+ }
7
+
8
+ .overview-grid {
9
+ display: grid;
10
+ gap: 24px;
11
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(50% - 12px)), 1fr));
12
+ }
13
+
14
+ .overview-grid-item {
15
+ padding: 12px;
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
+ }
@@ -1,11 +1,15 @@
1
+ import type { ClassValue } from "clsx";
1
2
  import { clsx } from "clsx";
2
3
  import * as styles from "@/components/BaseLayout/styles.scss";
3
4
  import { FooterLogo } from "@/components/Footer/FooterLogo";
4
5
  import { FooterVersion } from "@/components/Footer/FooterVersion";
5
6
 
6
- export const Footer = () => {
7
+ interface FooterProps {
8
+ className?: ClassValue;
9
+ }
10
+ export const Footer = ({ className }: FooterProps) => {
7
11
  return (
8
- <div className={clsx(styles.below)}>
12
+ <div className={clsx(styles.below, className)}>
9
13
  <FooterLogo />
10
14
  <FooterVersion />
11
15
  </div>
@@ -1,8 +1,10 @@
1
1
  import type { ClassValue } from "clsx";
2
2
  import clsx from "clsx";
3
3
  import { HeaderControls } from "@/components/HeaderControls";
4
+ import { SectionPicker } from "@/components/SectionPicker";
4
5
  import { TrBreadcrumbs } from "@/components/TestResult/TrHeader/TrBreadcrumbs";
5
6
  import { route } from "@/stores/router";
7
+ import { availableSections } from "@/stores/sections";
6
8
  import { testResultStore } from "@/stores/testResults";
7
9
  import * as styles from "./styles.scss";
8
10
 
@@ -11,11 +13,12 @@ interface HeaderProps {
11
13
  }
12
14
 
13
15
  export const Header = ({ className }: HeaderProps) => {
14
- const { id } = route.value;
16
+ const testResultId = route.value.params?.testResultId ?? null;
15
17
 
16
18
  return (
17
19
  <div className={clsx(styles.above, className)}>
18
- {id && <TrBreadcrumbs testResult={testResultStore.value?.data?.[id]} />}
20
+ {Boolean(availableSections.value?.length) && <SectionPicker />}
21
+ {testResultId && <TrBreadcrumbs testResult={testResultStore.value?.data?.[testResultId]} />}
19
22
  <HeaderControls className={styles.right} />
20
23
  </div>
21
24
  );
@@ -5,6 +5,8 @@
5
5
  justify-content: space-between;
6
6
  align-items: center;
7
7
  gap: 12px;
8
+ padding: 0 24px;
9
+ padding-top: 8px;
8
10
  }
9
11
 
10
12
  .logoWrapper {
@@ -1,5 +1,4 @@
1
1
  import clsx from "clsx";
2
- import { Header } from "@/components/Header";
3
2
  import { ReportBody } from "@/components/ReportBody";
4
3
  import { ReportHeader } from "@/components/ReportHeader";
5
4
  import { ReportMetadata } from "@/components/ReportMetadata";
@@ -9,7 +8,6 @@ import * as styles from "./styles.scss";
9
8
  const MainReport = () => {
10
9
  return (
11
10
  <>
12
- {!isSplitMode.value && <Header />}
13
11
  <div className={clsx(styles.content, isSplitMode.value ? styles["scroll-inside"] : "")}>
14
12
  <ReportHeader />
15
13
  <ReportMetadata />
@@ -1,7 +1,6 @@
1
- import { Counter, Text } from "@allurereport/web-components";
1
+ import { ArrowButton, Counter, Text } from "@allurereport/web-components";
2
2
  import clsx from "clsx";
3
3
  import type { FunctionalComponent } from "preact";
4
- import { ArrowButton } from "@/components/ArrowButton";
5
4
  import * as styles from "./styles.scss";
6
5
 
7
6
  interface MetadataButtonProps {
@@ -34,6 +33,7 @@ export const MetadataButton: FunctionalComponent<MetadataButtonProps> = ({
34
33
  iconSize={"s"}
35
34
  buttonSize={"s"}
36
35
  className={styles["report-metadata-header-arrow"]}
36
+ tag={"div"}
37
37
  />
38
38
  </button>
39
39
  );
@@ -0,0 +1,7 @@
1
+ import { BaseLayout } from "@/components/BaseLayout";
2
+ import { SplitLayout } from "@/components/SplitLayout";
3
+ import { isSplitMode } from "@/stores/layout";
4
+
5
+ export const Report = () => {
6
+ return isSplitMode.value ? <SplitLayout /> : <BaseLayout />;
7
+ };
@@ -24,14 +24,14 @@ export const Filters = () => {
24
24
  </div>
25
25
  )}
26
26
  >
27
- <Menu.Section>
27
+ <Menu.Section data-testid="filters-menu">
28
28
  <Menu.Item
29
29
  closeMenuOnClick={false}
30
30
  ariaLabel={t("enable-filter", { filter: t("flaky") })}
31
31
  onClick={() => {
32
32
  setTreeFilter("flaky", !flaky);
33
33
  }}
34
- leadingIcon={allureIcons.lineGeneralZap}
34
+ leadingIcon={allureIcons.lineIconBomb2}
35
35
  rightSlot={
36
36
  <div className={styles.filterToggle}>
37
37
  <Toggle
@@ -1,10 +1,11 @@
1
1
  import { statusesList } from "@allurereport/core-api";
2
2
  import { Counter, Loadable } from "@allurereport/web-components";
3
- import { reportStatsStore } from "@/stores";
3
+ import { reportStatsStore, statsByEnvStore } from "@/stores";
4
+ import { currentEnvironment } from "@/stores/env";
4
5
  import { useI18n } from "@/stores/locale";
5
- import { treeFiltersStore } from "@/stores/tree";
6
+ import { setTreeStatus, treeFiltersStore } from "@/stores/tree";
6
7
  import { capitalize } from "@/utils/capitalize";
7
- import { Tab, Tabs, TabsList } from "../Tabs";
8
+ import { Tab, Tabs, TabsList, useTabsContext } from "../Tabs";
8
9
  import { TreeList } from "../Tree";
9
10
  import { HeaderActions } from "./HeaderActions";
10
11
  import { SortBy } from "./SortBy";
@@ -15,6 +16,7 @@ const ALL_TAB = "total";
15
16
 
16
17
  const Header = () => {
17
18
  const { t } = useI18n("statuses");
19
+ const { currentTab, setCurrentTab } = useTabsContext();
18
20
 
19
21
  return (
20
22
  <header className={styles.header}>
@@ -22,21 +24,30 @@ const Header = () => {
22
24
  <div className={styles.headerRow}>
23
25
  <TabsList>
24
26
  <Loadable
25
- source={reportStatsStore}
27
+ source={statsByEnvStore}
26
28
  renderData={(stats) => {
27
- const allStatuses = statusesList
28
- .map((status) => ({ status, value: stats[status] }))
29
- .filter(({ value }) => value)
30
- .map(({ status, value }) => (
31
- <Tab data-testid={`tab-${status}`} key={status} id={status}>
32
- {capitalize(t(status) ?? status)} <Counter count={value} size="s" status={status} />
33
- </Tab>
34
- ));
29
+ const currentEnv = stats[currentEnvironment.value] || reportStatsStore.value.data;
30
+ const statList = statusesList
31
+ .map((status) => {
32
+ return { status, value: currentEnv[status] };
33
+ })
34
+ .filter(({ value }) => value);
35
+ const isStatListHaveCurrentTab = statList.filter(({ status }) => status === currentTab);
36
+ if (!isStatListHaveCurrentTab.length && currentTab !== "total") {
37
+ setCurrentTab("total");
38
+ setTreeStatus("total");
39
+ }
40
+
41
+ const allStatuses = statList.map(({ status, value }) => (
42
+ <Tab data-testid={`tab-${status}`} key={status} id={status}>
43
+ {capitalize(t(status) ?? status)} <Counter count={value} size="s" status={status} />
44
+ </Tab>
45
+ ));
35
46
 
36
47
  return (
37
48
  <>
38
49
  <Tab data-testid="tab-all" id={ALL_TAB}>
39
- {capitalize(t("total"))} <Counter count={stats?.total ?? 0} size="s" />
50
+ {capitalize(t("total"))} <Counter count={currentEnv?.total ?? 0} size="s" />
40
51
  </Tab>
41
52
  {allStatuses}
42
53
  </>
@@ -23,6 +23,7 @@
23
23
 
24
24
  .body {
25
25
  padding: 16px 24px;
26
+ min-height: 320px;
26
27
  }
27
28
 
28
29
  .headerActions {
@@ -12,6 +12,9 @@ export const ReportHeader = () => {
12
12
  month: "long",
13
13
  day: "numeric",
14
14
  year: "numeric",
15
+ hour: "numeric",
16
+ minute: "numeric",
17
+ second: "numeric",
15
18
  });
16
19
 
17
20
  return (
@@ -4,7 +4,7 @@ import type { MetadataProps } from "@/components/ReportMetadata/MetadataItem";
4
4
  import * as styles from "./styles.scss";
5
5
 
6
6
  const icons: Record<string, string> = {
7
- flaky: allureIcons.lineGeneralZap,
7
+ flaky: allureIcons.lineIconBomb2,
8
8
  retries: allureIcons.lineArrowsRefreshCcw1,
9
9
  new: allureIcons.lineAlertsNotificationBox,
10
10
  };
@@ -0,0 +1,54 @@
1
+ import { DropdownButton, Menu, SvgIcon, allureIcons } from "@allurereport/web-components";
2
+ import { useI18n } from "@/stores";
3
+ import { availableSections, currentSection, setSection } from "@/stores/sections";
4
+ import * as styles from "./styles.scss";
5
+
6
+ export type SectionItem = {
7
+ name: string;
8
+ logo: string;
9
+ };
10
+
11
+ const defaultSection: SectionItem = { name: "report", logo: allureIcons.reportLogo };
12
+
13
+ const sectionMap: Record<string, SectionItem> = {
14
+ default: defaultSection,
15
+ charts: { name: "charts", logo: allureIcons.lineChartsBarChartSquare },
16
+ };
17
+
18
+ export const SectionPicker = () => {
19
+ const selectedSection = sectionMap[currentSection.value] || defaultSection;
20
+ const { t } = useI18n("sections");
21
+
22
+ return (
23
+ <Menu
24
+ size="m"
25
+ placement={"bottom-start"}
26
+ menuTrigger={({ isOpened, onClick }) => (
27
+ <DropdownButton
28
+ style="ghost"
29
+ size="m"
30
+ text={t(selectedSection.name)}
31
+ icon={selectedSection.logo}
32
+ isExpanded={isOpened}
33
+ onClick={onClick}
34
+ iconSize={"xs"}
35
+ />
36
+ )}
37
+ >
38
+ <Menu.Section>
39
+ {["default", ...availableSections.value].map((value) => (
40
+ <Menu.ItemWithCheckmark
41
+ onClick={() => setSection(value)}
42
+ key={value}
43
+ isChecked={currentSection.value === value}
44
+ >
45
+ <div className={styles["menu-item"]}>
46
+ <SvgIcon id={sectionMap[value]?.logo} size={"s"} />
47
+ {t(sectionMap[value]?.name) || value}
48
+ </div>
49
+ </Menu.ItemWithCheckmark>
50
+ ))}
51
+ </Menu.Section>
52
+ </Menu>
53
+ );
54
+ };
@@ -0,0 +1,5 @@
1
+ .menu-item {
2
+ display: flex;
3
+ gap: 4px;
4
+ align-items: center;
5
+ }
@@ -0,0 +1,14 @@
1
+ import type { VNode } from "preact";
2
+ import { Charts } from "@/components/Charts";
3
+ import { Report } from "@/components/Report";
4
+ import { currentSection } from "@/stores/sections";
5
+ import * as styles from "./styles.scss";
6
+
7
+ export const SectionSwitcher = () => {
8
+ const sectionMap: Record<string, VNode> = {
9
+ report: <Report />,
10
+ charts: <Charts />,
11
+ };
12
+
13
+ return <div className={styles.layout}>{sectionMap[currentSection.value] || sectionMap.report}</div>;
14
+ };
@@ -0,0 +1,4 @@
1
+ .layout {
2
+ flex: 1 1 auto;
3
+ overflow: hidden;
4
+ }
File without changes
@@ -56,7 +56,7 @@
56
56
 
57
57
  .gutter {
58
58
  background: var(--bg-base-secondary) no-repeat 50%;
59
- min-height: 100vh;
59
+ height: 100%;
60
60
  }
61
61
 
62
62
  .gutter:hover {
@@ -31,7 +31,7 @@ const Loader = () => {
31
31
  };
32
32
 
33
33
  export const SplitLayout = () => {
34
- const { id: testResultId } = route.value;
34
+ const testResultId = route.value.params?.testResultId ?? null;
35
35
  const [cachedMain, setCachedMain] = useState<JSX.Element | null>(null);
36
36
 
37
37
  const { t } = useI18n("controls");
@@ -69,9 +69,7 @@ export const SplitLayout = () => {
69
69
 
70
70
  return (
71
71
  <div className={styles["side-by-side"]} data-testId={"split-layout"}>
72
- <Header className={styles.header} />
73
72
  <SideBySide left={cachedMain} right={<TrView />} />
74
- <Footer />
75
73
  </div>
76
74
  );
77
75
  };
@@ -4,7 +4,6 @@
4
4
  background: var(--bg-base-secondary);
5
5
  color: var(--on-text-primary);
6
6
  font-size: 14px;
7
- min-height: 100vh;
8
7
  }
9
8
 
10
9
  .wrapper {
@@ -22,7 +21,6 @@
22
21
  width: 100%;
23
22
  overflow: hidden;
24
23
  overflow-y: scroll;
25
- height: calc(100vh - var(--footer-header-sizes));
26
24
  position: relative;
27
25
  }
28
26
 
@@ -69,8 +67,8 @@
69
67
  .side-by-side {
70
68
  display: flex;
71
69
  flex-direction: column;
72
- height: 100vh;
73
70
  justify-content: space-between;
71
+ height: 100%;
74
72
  }
75
73
 
76
74
  .empty {
@@ -1,8 +1,7 @@
1
- import { Counter, SvgIcon, Text, allureIcons } from "@allurereport/web-components";
1
+ import { ArrowButton, Counter, SvgIcon, Text, allureIcons } from "@allurereport/web-components";
2
2
  import type { ClassValue } from "clsx";
3
3
  import clsx from "clsx";
4
4
  import { type FunctionalComponent } from "preact";
5
- import { ArrowButton } from "@/components/ArrowButton";
6
5
  import * as styles from "./styles.scss";
7
6
 
8
7
  export const TrDropdown: FunctionalComponent<{
@@ -1,9 +1,8 @@
1
1
  import { formatDuration } from "@allurereport/core-api";
2
- import { IconButton, Text, TooltipWrapper, TreeItemIcon, allureIcons } from "@allurereport/web-components";
2
+ import { ArrowButton, IconButton, Text, TooltipWrapper, TreeItemIcon, allureIcons } from "@allurereport/web-components";
3
3
  import cx from "clsx";
4
4
  import { type FunctionalComponent } from "preact";
5
5
  import { useState } from "preact/hooks";
6
- import { ArrowButton } from "@/components/ArrowButton";
7
6
  import { TrError } from "@/components/TestResult/TrError";
8
7
  import { useI18n } from "@/stores";
9
8
  import { navigateTo, openInNewTab } from "@/stores/router";
@@ -19,7 +18,7 @@ export const TrEnvironmentItem: FunctionalComponent<{
19
18
  const { status, error, stop, duration, id } = testResult;
20
19
  const [isOpened, setIsOpen] = useState(false);
21
20
  const hasEmptyError = !error || !Object.keys(error).length;
22
- const convertedStop = timestampToDate(stop);
21
+ const convertedStop = stop ? timestampToDate(stop) : "";
23
22
  const formattedDuration = formatDuration(duration as number);
24
23
  const { t } = useI18n("controls");
25
24
  const navigateUrl = id;
@@ -36,6 +35,7 @@ export const TrEnvironmentItem: FunctionalComponent<{
36
35
  className={cx(styles["test-result-environment-item-wrap"], {
37
36
  [styles.current]: current,
38
37
  })}
38
+ role={current ? undefined : "button"}
39
39
  onClick={(e) => {
40
40
  if (current) {
41
41
  return;
@@ -1,4 +1,4 @@
1
- import type { TestEnvGroup } from "@allurereport/core-api";
1
+ import { type TestEnvGroup, getRealEnvsCount } from "@allurereport/core-api";
2
2
  import { Loadable } from "@allurereport/web-components";
3
3
  import type { FunctionalComponent } from "preact";
4
4
  import { useEffect } from "preact/hooks";
@@ -39,7 +39,7 @@ export const TrEnvironmentsView: FunctionalComponent<{
39
39
  <Loadable<Record<string, TestEnvGroup>, TestEnvGroup | undefined>
40
40
  source={testEnvGroupsStore}
41
41
  renderData={(group) => {
42
- if (!group) {
42
+ if (!getRealEnvsCount(group)) {
43
43
  return <div className={styles["test-result-empty"]}>{t("no-environments-results")}</div>;
44
44
  }
45
45
 
@@ -1,8 +1,7 @@
1
1
  import { type HistoryTestResult, formatDuration } from "@allurereport/core-api";
2
- import { IconButton, Text, TooltipWrapper, TreeItemIcon, allureIcons } from "@allurereport/web-components";
2
+ import { ArrowButton, IconButton, Text, TooltipWrapper, TreeItemIcon, allureIcons } from "@allurereport/web-components";
3
3
  import { type FunctionalComponent } from "preact";
4
4
  import { useState } from "preact/hooks";
5
- import { ArrowButton } from "@/components/ArrowButton";
6
5
  import { TrError } from "@/components/TestResult/TrError";
7
6
  import * as styles from "@/components/TestResult/TrHistory/styles.scss";
8
7
  import { useI18n } from "@/stores";