mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix doughnut graphs on workload overview sometimes not showing (#5727)
This commit is contained in:
parent
7767216167
commit
4b1c9fb5fd
@ -6,8 +6,6 @@
|
|||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
|
|
||||||
export const allowedResourcesInjectionToken = getInjectionToken<
|
export const allowedResourcesInjectionToken = getInjectionToken<IComputedValue<Set<string>>>({
|
||||||
IComputedValue<Set<string>>
|
|
||||||
>({
|
|
||||||
id: "allowed-resources",
|
id: "allowed-resources",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,11 +3,17 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
import activeThemeInjectable from "../../renderer/themes/active.injectable";
|
||||||
|
import type { LensTheme } from "../../renderer/themes/store";
|
||||||
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
|
|
||||||
const themeStore = asLegacyGlobalForExtensionApi(themeStoreInjectable);
|
export const activeTheme = asLegacyGlobalForExtensionApi(activeThemeInjectable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This hides the reactivity of active theme, use {@link activeTheme} instead
|
||||||
|
*/
|
||||||
export function getActiveTheme() {
|
export function getActiveTheme() {
|
||||||
return themeStore.activeTheme;
|
return activeTheme.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type { LensTheme };
|
||||||
|
|||||||
@ -350,6 +350,7 @@ exports[`cluster - order of sidebar items when rendered renders 1`] = `
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -987,6 +988,7 @@ exports[`cluster - order of sidebar items when rendered when parent is expanded
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -319,6 +319,7 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -867,6 +868,7 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -1437,6 +1439,7 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -2886,6 +2889,7 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -3434,6 +3438,7 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -319,6 +319,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -867,6 +868,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -1455,6 +1457,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -3526,6 +3529,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -4074,6 +4078,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -289,6 +289,7 @@ exports[`cluster - visibility of sidebar items given kube resource for route is
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -851,6 +852,7 @@ exports[`cluster - visibility of sidebar items given kube resource for route is
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -0,0 +1,576 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`workload overview when navigating to workload overview renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="mainLayout"
|
||||||
|
style="--sidebar-width: 200px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sidebar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col"
|
||||||
|
data-testid="cluster-sidebar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SidebarCluster"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Avatar rounded loadingAvatar"
|
||||||
|
style="width: 40px; height: 40px;"
|
||||||
|
>
|
||||||
|
??
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="loadingClusterName"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sidebarNav sidebar-active-status"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="true"
|
||||||
|
data-testid="sidebar-item-workloads"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
aria-current="page"
|
||||||
|
class="nav-item flex gaps align-center expandable active"
|
||||||
|
data-testid="sidebar-item-link-for-workloads"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon svg focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Workloads
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
class="Icon expand-icon box right material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="keyboard_arrow_down"
|
||||||
|
>
|
||||||
|
keyboard_arrow_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="sidebar-item-config"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-item flex gaps align-center"
|
||||||
|
data-testid="sidebar-item-link-for-config"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="list"
|
||||||
|
>
|
||||||
|
list
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Config
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="sidebar-item-network"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-item flex gaps align-center expandable"
|
||||||
|
data-testid="sidebar-item-link-for-network"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="device_hub"
|
||||||
|
>
|
||||||
|
device_hub
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Network
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
class="Icon expand-icon box right material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="keyboard_arrow_down"
|
||||||
|
>
|
||||||
|
keyboard_arrow_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="sidebar-item-storage"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-item flex gaps align-center"
|
||||||
|
data-testid="sidebar-item-link-for-storage"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="storage"
|
||||||
|
>
|
||||||
|
storage
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Storage
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="sidebar-item-helm"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-item flex gaps align-center expandable"
|
||||||
|
data-testid="sidebar-item-link-for-helm"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon svg focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Helm
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
class="Icon expand-icon box right material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="keyboard_arrow_down"
|
||||||
|
>
|
||||||
|
keyboard_arrow_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="sidebar-item-user-management"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-item flex gaps align-center"
|
||||||
|
data-testid="sidebar-item-link-for-user-management"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="security"
|
||||||
|
>
|
||||||
|
security
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Access Control
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="SidebarItem"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="sidebar-item-custom-resources"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-item flex gaps align-center expandable"
|
||||||
|
data-testid="sidebar-item-link-for-custom-resources"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="extension"
|
||||||
|
>
|
||||||
|
extension
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<span
|
||||||
|
class="link-text box grow"
|
||||||
|
>
|
||||||
|
Custom Resources
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
class="Icon expand-icon box right material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="keyboard_arrow_down"
|
||||||
|
>
|
||||||
|
keyboard_arrow_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ResizingAnchor horizontal trailing"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="contents"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="TabLayout"
|
||||||
|
data-testid="tab-layout"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Tabs center scrollable"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center active"
|
||||||
|
data-is-active-test="true"
|
||||||
|
data-testid="tab-link-for-overview"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
Overview
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center"
|
||||||
|
data-is-active-test="false"
|
||||||
|
data-testid="tab-link-for-pods"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
Pods
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<div
|
||||||
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="header flex gaps align-center"
|
||||||
|
>
|
||||||
|
<h5
|
||||||
|
class="box grow"
|
||||||
|
>
|
||||||
|
Overview
|
||||||
|
</h5>
|
||||||
|
<div
|
||||||
|
class="NamespaceSelectFilterParent"
|
||||||
|
data-testid="namespace-select-filter"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select theme-dark NamespaceSelect NamespaceSelectFilter css-b62m3t-container"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
id="react-select-overview-namespace-select-filter-input-live-region"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
aria-atomic="false"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-relevant="additions text"
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="Select__control css-1s2u09g-control"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__value-container Select__value-container--is-multi css-319lph-ValueContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
|
id="react-select-overview-namespace-select-filter-input-placeholder"
|
||||||
|
>
|
||||||
|
All namespaces
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
|
data-value=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-describedby="react-select-overview-namespace-select-filter-input-placeholder"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
class="Select__input"
|
||||||
|
id="overview-namespace-select-filter-input"
|
||||||
|
role="combobox"
|
||||||
|
spellcheck="false"
|
||||||
|
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||||
|
tabindex="0"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="css-tj5bde-Svg"
|
||||||
|
focusable="false"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="OverviewStatuses"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="workloads"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="workload"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="title"
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
Pods (0)
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="OverviewWorkloadStatus"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex column align-center box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Chart PieChart flex column align-center"
|
||||||
|
data-testid="workload-overview-status-chart-pods"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="chart-container"
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
class="chartjs-render-monitor"
|
||||||
|
height="0"
|
||||||
|
style="display: block;"
|
||||||
|
width="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="chartjs-tooltip flex column"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="legend flex wrap"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="footer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Dock"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ResizingAnchor vertical leading"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="tabs-container flex align-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="dockTabs"
|
||||||
|
role="tablist"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Tabs tabs"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center DockTab TerminalTab active"
|
||||||
|
data-testid="dock-tab-for-terminal"
|
||||||
|
id="tab-terminal"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="terminal"
|
||||||
|
>
|
||||||
|
terminal
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex align-center"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="title"
|
||||||
|
>
|
||||||
|
Terminal
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="close"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable small"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div>
|
||||||
|
Close ⌘+W
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="toolbar flex gaps align-center box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="dock-menu box grow"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon new-dock-tab material interactive focusable"
|
||||||
|
id="menu-actions-for-dock"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="add"
|
||||||
|
>
|
||||||
|
add
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div>
|
||||||
|
New tab
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="fullscreen"
|
||||||
|
>
|
||||||
|
fullscreen
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div>
|
||||||
|
Fit to window
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="keyboard_arrow_up"
|
||||||
|
>
|
||||||
|
keyboard_arrow_up
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div>
|
||||||
|
Open
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
@ -691,6 +691,7 @@ exports[`disable-cluster-pages-when-cluster-is-not-relevant given extension shou
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -1211,6 +1212,7 @@ exports[`disable-cluster-pages-when-cluster-is-not-relevant given not yet known
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -310,6 +310,7 @@ exports[`disable sidebar items when cluster is not relevant given extension shou
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -830,6 +831,7 @@ exports[`disable sidebar items when cluster is not relevant given extension shou
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -1350,6 +1352,7 @@ exports[`disable sidebar items when cluster is not relevant given not yet known
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -317,6 +317,7 @@ exports[`cluster/namespaces - edit namespaces from previously opened tab given t
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -874,6 +875,7 @@ exports[`cluster/namespaces - edit namespaces from previously opened tab given t
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
37
src/features/cluster/workload-overview.test.tsx
Normal file
37
src/features/cluster/workload-overview.test.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import navigateToWorkloadsOverviewInjectable from "../../common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable";
|
||||||
|
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
|
||||||
|
describe("workload overview", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
|
||||||
|
applicationBuilder.allowKubeResource("pods");
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to workload overview", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder.navigateWith(navigateToWorkloadsOverviewInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows workload overview", () => {
|
||||||
|
expect(rendered.queryByTestId("page-for-workloads-overview")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows pods pie chart", () => {
|
||||||
|
expect(rendered.queryByTestId("workload-overview-status-chart-pods")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -290,6 +290,7 @@ exports[`disable workloads overview details when cluster is not relevant given e
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -815,6 +816,7 @@ exports[`disable workloads overview details when cluster is not relevant given e
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -1335,6 +1337,7 @@ exports[`disable workloads overview details when cluster is not relevant given n
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -290,6 +290,7 @@ exports[`installing helm chart from previously opened tab given tab for installi
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
@ -820,6 +821,7 @@ exports[`installing helm chart from previously opened tab given tab for installi
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import styles from "./cluster-issues.module.scss";
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
import { computed, makeObservable } from "mobx";
|
import { computed, makeObservable } from "mobx";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { SubHeader } from "../layout/sub-header";
|
import { SubHeader } from "../layout/sub-header";
|
||||||
@ -14,11 +15,9 @@ import { Table, TableCell, TableHead, TableRow } from "../table";
|
|||||||
import { cssNames, prevDefault } from "../../utils";
|
import { cssNames, prevDefault } from "../../utils";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "../../../common/item.store";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
|
||||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||||
import { KubeObjectAge } from "../kube-object/age";
|
import { KubeObjectAge } from "../kube-object/age";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
|
||||||
import type { NodeStore } from "../+nodes/store";
|
import type { NodeStore } from "../+nodes/store";
|
||||||
import type { EventStore } from "../+events/store";
|
import type { EventStore } from "../+events/store";
|
||||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||||
@ -28,6 +27,8 @@ import type { PageParam } from "../../navigation";
|
|||||||
import type { ToggleKubeDetailsPane } from "../kube-detail-params/toggle-details.injectable";
|
import type { ToggleKubeDetailsPane } from "../kube-detail-params/toggle-details.injectable";
|
||||||
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
|
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
|
||||||
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
|
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
|
||||||
|
import type { LensTheme } from "../../themes/store";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export interface ClusterIssuesProps {
|
export interface ClusterIssuesProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -48,7 +49,7 @@ enum sortBy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
nodeStore: NodeStore;
|
nodeStore: NodeStore;
|
||||||
eventStore: EventStore;
|
eventStore: EventStore;
|
||||||
apiManager: ApiManager;
|
apiManager: ApiManager;
|
||||||
@ -166,7 +167,7 @@ class NonInjectedClusterIssues extends React.Component<ClusterIssuesProps & Depe
|
|||||||
sortByDefault={{ sortBy: sortBy.object, orderBy: "asc" }}
|
sortByDefault={{ sortBy: sortBy.object, orderBy: "asc" }}
|
||||||
sortSyncWithUrl={false}
|
sortSyncWithUrl={false}
|
||||||
getTableRow={this.getTableRow}
|
getTableRow={this.getTableRow}
|
||||||
className={cssNames("box grow", this.props.themeStore.activeTheme.type)}
|
className={cssNames("box grow", this.props.activeTheme.get().type)}
|
||||||
>
|
>
|
||||||
<TableHead nowrap>
|
<TableHead nowrap>
|
||||||
<TableCell className="message">Message</TableCell>
|
<TableCell className="message">Message</TableCell>
|
||||||
@ -191,7 +192,7 @@ class NonInjectedClusterIssues extends React.Component<ClusterIssuesProps & Depe
|
|||||||
export const ClusterIssues = withInjectables<Dependencies, ClusterIssuesProps>(NonInjectedClusterIssues, {
|
export const ClusterIssues = withInjectables<Dependencies, ClusterIssuesProps>(NonInjectedClusterIssues, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
apiManager: di.inject(apiManagerInjectable),
|
apiManager: di.inject(apiManagerInjectable),
|
||||||
eventStore: di.inject(eventStoreInjectable),
|
eventStore: di.inject(eventStoreInjectable),
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
nodeStore: di.inject(nodeStoreInjectable),
|
||||||
|
|||||||
@ -16,12 +16,13 @@ import type { PieChartData } from "../chart";
|
|||||||
import { PieChart } from "../chart";
|
import { PieChart } from "../chart";
|
||||||
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
||||||
import { bytesToUnits, cssNames } from "../../utils";
|
import { bytesToUnits, cssNames } from "../../utils";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
||||||
import nodeStoreInjectable from "../+nodes/store.injectable";
|
import nodeStoreInjectable from "../+nodes/store.injectable";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
||||||
return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`);
|
return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`);
|
||||||
@ -30,13 +31,13 @@ function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
clusterOverviewStore: ClusterOverviewStore;
|
||||||
nodeStore: NodeStore;
|
nodeStore: NodeStore;
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedClusterPieCharts = observer(({
|
const NonInjectedClusterPieCharts = observer(({
|
||||||
clusterOverviewStore,
|
clusterOverviewStore,
|
||||||
nodeStore,
|
nodeStore,
|
||||||
themeStore,
|
activeTheme,
|
||||||
}: Dependencies) => {
|
}: Dependencies) => {
|
||||||
const renderLimitWarning = () => {
|
const renderLimitWarning = () => {
|
||||||
return (
|
return (
|
||||||
@ -54,7 +55,7 @@ const NonInjectedClusterPieCharts = observer(({
|
|||||||
const { podUsage, podAllocatableCapacity, podCapacity } = data;
|
const { podUsage, podAllocatableCapacity, podCapacity } = data;
|
||||||
const cpuLimitsOverload = cpuLimits > cpuAllocatableCapacity;
|
const cpuLimitsOverload = cpuLimits > cpuAllocatableCapacity;
|
||||||
const memoryLimitsOverload = memoryLimits > memoryAllocatableCapacity;
|
const memoryLimitsOverload = memoryLimits > memoryAllocatableCapacity;
|
||||||
const defaultColor = themeStore.activeTheme.colors.pieChartDefaultColor;
|
const defaultColor = activeTheme.get().colors.pieChartDefaultColor;
|
||||||
|
|
||||||
if (!memoryCapacity || !cpuCapacity || !podCapacity || !memoryAllocatableCapacity || !cpuAllocatableCapacity || !podAllocatableCapacity) return null;
|
if (!memoryCapacity || !cpuCapacity || !podCapacity || !memoryAllocatableCapacity || !cpuAllocatableCapacity || !podAllocatableCapacity) return null;
|
||||||
const cpuData: PieChartData = {
|
const cpuData: PieChartData = {
|
||||||
@ -261,6 +262,6 @@ export const ClusterPieCharts = withInjectables<Dependencies>(NonInjectedCluster
|
|||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
nodeStore: di.inject(nodeStoreInjectable),
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,13 +3,12 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import type { IObservableValue } from "mobx";
|
import type { IComputedValue, IObservableValue } from "mobx";
|
||||||
import { runInAction, action, observable, computed } from "mobx";
|
import { runInAction, action, observable, computed } from "mobx";
|
||||||
import type { TargetHelmRelease } from "../target-helm-release.injectable";
|
import type { TargetHelmRelease } from "../target-helm-release.injectable";
|
||||||
import type { CallForHelmRelease, DetailedHelmRelease } from "./call-for-helm-release/call-for-helm-release.injectable";
|
import type { CallForHelmRelease, DetailedHelmRelease } from "./call-for-helm-release/call-for-helm-release.injectable";
|
||||||
import callForHelmReleaseInjectable from "./call-for-helm-release/call-for-helm-release.injectable";
|
import callForHelmReleaseInjectable from "./call-for-helm-release/call-for-helm-release.injectable";
|
||||||
import type { ThemeStore } from "../../../../themes/store";
|
import type { LensTheme } from "../../../../themes/store";
|
||||||
import themeStoreInjectable from "../../../../themes/store.injectable";
|
|
||||||
import type { CallForHelmReleaseConfiguration } from "./call-for-helm-release-configuration/call-for-helm-release-configuration.injectable";
|
import type { CallForHelmReleaseConfiguration } from "./call-for-helm-release-configuration/call-for-helm-release-configuration.injectable";
|
||||||
import callForHelmReleaseConfigurationInjectable from "./call-for-helm-release-configuration/call-for-helm-release-configuration.injectable";
|
import callForHelmReleaseConfigurationInjectable from "./call-for-helm-release-configuration/call-for-helm-release-configuration.injectable";
|
||||||
import { toHelmRelease } from "../../releases.injectable";
|
import { toHelmRelease } from "../../releases.injectable";
|
||||||
@ -31,6 +30,7 @@ import type { NavigateToHelmReleases } from "../../../../../common/front-end-rou
|
|||||||
import navigateToHelmReleasesInjectable from "../../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
import navigateToHelmReleasesInjectable from "../../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import withOrphanPromiseInjectable from "../../../../../common/utils/with-orphan-promise/with-orphan-promise.injectable";
|
import withOrphanPromiseInjectable from "../../../../../common/utils/with-orphan-promise/with-orphan-promise.injectable";
|
||||||
|
import activeThemeInjectable from "../../../../themes/active.injectable";
|
||||||
|
|
||||||
const releaseDetailsModelInjectable = getInjectable({
|
const releaseDetailsModelInjectable = getInjectable({
|
||||||
id: "release-details-model",
|
id: "release-details-model",
|
||||||
@ -38,7 +38,7 @@ const releaseDetailsModelInjectable = getInjectable({
|
|||||||
instantiate: (di, targetRelease: TargetHelmRelease) => {
|
instantiate: (di, targetRelease: TargetHelmRelease) => {
|
||||||
const callForHelmRelease = di.inject(callForHelmReleaseInjectable);
|
const callForHelmRelease = di.inject(callForHelmReleaseInjectable);
|
||||||
const callForHelmReleaseConfiguration = di.inject(callForHelmReleaseConfigurationInjectable);
|
const callForHelmReleaseConfiguration = di.inject(callForHelmReleaseConfigurationInjectable);
|
||||||
const themeStore = di.inject(themeStoreInjectable);
|
const activeTheme = di.inject(activeThemeInjectable);
|
||||||
const getResourceDetailsUrl = di.inject(getResourceDetailsUrlInjectable);
|
const getResourceDetailsUrl = di.inject(getResourceDetailsUrlInjectable);
|
||||||
const updateRelease = di.inject(updateReleaseInjectable);
|
const updateRelease = di.inject(updateReleaseInjectable);
|
||||||
const showCheckedErrorNotification = di.inject(showCheckedErrorNotificationInjectable);
|
const showCheckedErrorNotification = di.inject(showCheckedErrorNotificationInjectable);
|
||||||
@ -50,7 +50,7 @@ const releaseDetailsModelInjectable = getInjectable({
|
|||||||
const model = new ReleaseDetailsModel({
|
const model = new ReleaseDetailsModel({
|
||||||
callForHelmRelease,
|
callForHelmRelease,
|
||||||
targetRelease,
|
targetRelease,
|
||||||
themeStore,
|
activeTheme,
|
||||||
callForHelmReleaseConfiguration,
|
callForHelmReleaseConfiguration,
|
||||||
getResourceDetailsUrl,
|
getResourceDetailsUrl,
|
||||||
updateRelease,
|
updateRelease,
|
||||||
@ -92,7 +92,7 @@ export interface ConfigurationInput {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
callForHelmRelease: CallForHelmRelease;
|
callForHelmRelease: CallForHelmRelease;
|
||||||
targetRelease: TargetHelmRelease;
|
targetRelease: TargetHelmRelease;
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
callForHelmReleaseConfiguration: CallForHelmReleaseConfiguration;
|
callForHelmReleaseConfiguration: CallForHelmReleaseConfiguration;
|
||||||
getResourceDetailsUrl: GetResourceDetailsUrl;
|
getResourceDetailsUrl: GetResourceDetailsUrl;
|
||||||
updateRelease: CallForHelmReleaseUpdate;
|
updateRelease: CallForHelmReleaseUpdate;
|
||||||
@ -259,7 +259,7 @@ export class ReleaseDetailsModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get activeTheme() {
|
@computed get activeTheme() {
|
||||||
return this.dependencies.themeStore.activeTheme.type;
|
return this.dependencies.activeTheme.get().type;
|
||||||
}
|
}
|
||||||
|
|
||||||
close = () => {
|
close = () => {
|
||||||
|
|||||||
@ -12,18 +12,19 @@ import { ResourceMetricsContext } from "../resource-metrics";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { mapValues } from "lodash";
|
import { mapValues } from "lodash";
|
||||||
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export interface NodeChartsProps {}
|
export interface NodeChartsProps {}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedNodeCharts = observer(({
|
const NonInjectedNodeCharts = observer(({
|
||||||
themeStore,
|
activeTheme,
|
||||||
}: Dependencies & NodeChartsProps) => {
|
}: Dependencies & NodeChartsProps) => {
|
||||||
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ const NonInjectedNodeCharts = observer(({
|
|||||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||||
|
|
||||||
const id = object.getId();
|
const id = object.getId();
|
||||||
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
const { chartCapacityColor } = activeTheme.get().colors;
|
||||||
const {
|
const {
|
||||||
memoryUsage,
|
memoryUsage,
|
||||||
workloadMemoryUsage,
|
workloadMemoryUsage,
|
||||||
@ -162,6 +163,6 @@ const NonInjectedNodeCharts = observer(({
|
|||||||
export const NodeCharts = withInjectables<Dependencies, NodeChartsProps>(NonInjectedNodeCharts, {
|
export const NodeCharts = withInjectables<Dependencies, NodeChartsProps>(NonInjectedNodeCharts, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,18 +10,19 @@ import { BarChart, memoryOptions } from "../chart";
|
|||||||
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { NoMetrics } from "../resource-metrics/no-metrics";
|
import { NoMetrics } from "../resource-metrics/no-metrics";
|
||||||
import { ResourceMetricsContext } from "../resource-metrics";
|
import { ResourceMetricsContext } from "../resource-metrics";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export interface VolumeClaimDiskChartProps {}
|
export interface VolumeClaimDiskChartProps {}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedVolumeClaimDiskChart = observer(({
|
const NonInjectedVolumeClaimDiskChart = observer(({
|
||||||
themeStore,
|
activeTheme,
|
||||||
}: Dependencies & VolumeClaimDiskChartProps) => {
|
}: Dependencies & VolumeClaimDiskChartProps) => {
|
||||||
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ const NonInjectedVolumeClaimDiskChart = observer(({
|
|||||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||||
|
|
||||||
const id = object.getId();
|
const id = object.getId();
|
||||||
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
const { chartCapacityColor } = activeTheme.get().colors;
|
||||||
const { diskUsage, diskCapacity } = metrics;
|
const { diskUsage, diskCapacity } = metrics;
|
||||||
const usage = normalizeMetrics(diskUsage).data.result[0].values;
|
const usage = normalizeMetrics(diskUsage).data.result[0].values;
|
||||||
const capacity = normalizeMetrics(diskCapacity).data.result[0].values;
|
const capacity = normalizeMetrics(diskCapacity).data.result[0].values;
|
||||||
@ -65,6 +66,6 @@ const NonInjectedVolumeClaimDiskChart = observer(({
|
|||||||
export const VolumeClaimDiskChart = withInjectables<Dependencies, VolumeClaimDiskChartProps>(NonInjectedVolumeClaimDiskChart, {
|
export const VolumeClaimDiskChart = withInjectables<Dependencies, VolumeClaimDiskChartProps>(NonInjectedVolumeClaimDiskChart, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -31,7 +31,7 @@ const NonInjectedOverviewStatuses = observer(
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OverviewWorkloadStatus status={workload.status.get()} />
|
<OverviewWorkloadStatus workload={workload} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,92 +8,101 @@ import "./overview-workload-status.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import capitalize from "lodash/capitalize";
|
import capitalize from "lodash/capitalize";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { DatasetTooltipLabel, PieChartData } from "../chart";
|
import type { PieChartData } from "../chart";
|
||||||
import { PieChart } from "../chart";
|
import { PieChart } from "../chart";
|
||||||
import { cssVar, object } from "../../utils";
|
import { object } from "../../utils";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import type { PascalCase } from "type-fest";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
import type { Workload } from "./workloads/workload-injection-token";
|
||||||
|
|
||||||
|
export type LowercaseOrPascalCase<T extends string> = Lowercase<T> | PascalCase<T>;
|
||||||
|
|
||||||
|
export type WorkloadStatus = Partial<Record<LowercaseOrPascalCase<keyof typeof statusBackgroundColorMapping>, number>>;
|
||||||
|
|
||||||
|
function toLowercase<T extends string>(src: T): Lowercase<T> {
|
||||||
|
return src.toLowerCase() as Lowercase<T>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface OverviewWorkloadStatusProps {
|
export interface OverviewWorkloadStatusProps {
|
||||||
status: Partial<Record<string, number>>;
|
workload: Workload;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
const statusBackgroundColorMapping = {
|
||||||
class NonInjectedOverviewWorkloadStatus extends React.Component<OverviewWorkloadStatusProps & Dependencies> {
|
"running": "colorOk",
|
||||||
private elem: HTMLElement | null = null;
|
"scheduled": "colorOk",
|
||||||
|
"pending": "colorWarning",
|
||||||
|
"suspended": "colorWarning",
|
||||||
|
"evicted": "colorError",
|
||||||
|
"succeeded": "colorSuccess",
|
||||||
|
"failed": "colorError",
|
||||||
|
"terminated": "colorTerminated",
|
||||||
|
"terminating": "colorTerminated",
|
||||||
|
"unknown": "colorVague",
|
||||||
|
"complete": "colorSuccess",
|
||||||
|
} as const;
|
||||||
|
|
||||||
renderChart() {
|
const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatusProps & Dependencies) => {
|
||||||
if (!this.elem) {
|
const {
|
||||||
return null;
|
workload,
|
||||||
}
|
activeTheme,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const cssVars = cssVar(this.elem);
|
const statusesToBeShown = object.entries(workload.status.get()).filter(([, val]) => val > 0);
|
||||||
const chartData: Required<PieChartData> = {
|
const theme = activeTheme.get();
|
||||||
labels: [],
|
|
||||||
datasets: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const statuses = object.entries(this.props.status).filter(([, val]) => val > 0);
|
const emptyDataSet = {
|
||||||
|
data: [1],
|
||||||
|
backgroundColor: [theme.colors.pieChartDefaultColor],
|
||||||
|
label: "Empty",
|
||||||
|
};
|
||||||
|
const statusDataSet = {
|
||||||
|
label: "Status",
|
||||||
|
data: statusesToBeShown.map(([, value]) => value),
|
||||||
|
backgroundColor: statusesToBeShown.map(([status]) => (
|
||||||
|
theme.colors[statusBackgroundColorMapping[toLowercase(status)]]
|
||||||
|
)),
|
||||||
|
tooltipLabels: statusesToBeShown.map(([status]) => (
|
||||||
|
(percent: string) => `${capitalize(status)}: ${percent}`
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
if (statuses.length === 0) {
|
const chartData: Required<PieChartData> = {
|
||||||
chartData.datasets.push({
|
datasets: [statusesToBeShown.length > 0 ? statusDataSet : emptyDataSet],
|
||||||
data: [1],
|
|
||||||
backgroundColor: [this.props.themeStore.activeTheme.colors.pieChartDefaultColor],
|
|
||||||
label: "Empty",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const data: number[] = [];
|
|
||||||
const backgroundColor: string[] = [];
|
|
||||||
const tooltipLabels: DatasetTooltipLabel[] = [];
|
|
||||||
|
|
||||||
for (const [status, value] of statuses) {
|
labels: statusesToBeShown.map(
|
||||||
data.push(value);
|
([status, value]) => `${capitalize(status)}: ${value}`,
|
||||||
backgroundColor.push(cssVars.get(`--workload-status-${status.toLowerCase()}`).toString());
|
),
|
||||||
tooltipLabels.push(percent => `${capitalize(status)}: ${percent}`);
|
};
|
||||||
chartData.labels.push(`${capitalize(status)}: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
chartData.datasets.push({
|
return (
|
||||||
data,
|
<div className="OverviewWorkloadStatus">
|
||||||
backgroundColor,
|
<div className="flex column align-center box grow">
|
||||||
label: "Status",
|
<PieChart
|
||||||
tooltipLabels,
|
data={chartData}
|
||||||
});
|
options={{
|
||||||
}
|
elements: {
|
||||||
|
arc: {
|
||||||
return (
|
borderWidth: 0,
|
||||||
<PieChart
|
},
|
||||||
data={chartData}
|
|
||||||
options={{
|
|
||||||
elements: {
|
|
||||||
arc: {
|
|
||||||
borderWidth: 0,
|
|
||||||
},
|
},
|
||||||
},
|
}}
|
||||||
}}
|
data-testid={`workload-overview-status-chart-${workload.title.toLowerCase().replace(/\s+/, "-")}`}
|
||||||
/>
|
/>
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="OverviewWorkloadStatus" ref={e => this.elem = e}>
|
|
||||||
<div className="flex column align-center box grow">
|
|
||||||
{this.renderChart()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
}
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
export const OverviewWorkloadStatus = withInjectables<Dependencies, OverviewWorkloadStatusProps>(NonInjectedOverviewWorkloadStatus, {
|
export const OverviewWorkloadStatus = withInjectables<Dependencies, OverviewWorkloadStatusProps>(NonInjectedOverviewWorkloadStatus, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -104,7 +104,7 @@ class NonInjectedWorkloadsOverview extends React.Component<Dependencies> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SiblingsInTabLayout>
|
<SiblingsInTabLayout>
|
||||||
<div className="WorkloadsOverview flex column gaps">
|
<div className="WorkloadsOverview flex column gaps" data-testid="page-for-workloads-overview">
|
||||||
<div className="header flex gaps align-center">
|
<div className="header flex gaps align-center">
|
||||||
<h5 className="box grow">Overview</h5>
|
<h5 className="box grow">Overview</h5>
|
||||||
{this.renderLoadErrors()}
|
{this.renderLoadErrors()}
|
||||||
|
|||||||
@ -4,12 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import type { WorkloadStatus } from "../overview-workload-status";
|
||||||
|
|
||||||
export interface Workload {
|
export interface Workload {
|
||||||
resourceName: string;
|
resourceName: string;
|
||||||
open: () => void;
|
open: () => void;
|
||||||
amountOfItems: IComputedValue<number>;
|
amountOfItems: IComputedValue<number>;
|
||||||
status: IComputedValue<Partial<Record<string, number>>>;
|
status: IComputedValue<WorkloadStatus>;
|
||||||
title: string;
|
title: string;
|
||||||
orderNumber: number;
|
orderNumber: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,27 +10,28 @@ import { BarChart } from "../chart";
|
|||||||
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { NoMetrics } from "../resource-metrics/no-metrics";
|
import { NoMetrics } from "../resource-metrics/no-metrics";
|
||||||
import { ResourceMetricsContext } from "../resource-metrics";
|
import { ResourceMetricsContext } from "../resource-metrics";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { mapValues } from "lodash";
|
import { mapValues } from "lodash";
|
||||||
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
|
|
||||||
export interface ContainerChartsProps {}
|
export interface ContainerChartsProps {}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedContainerCharts = observer(({
|
const NonInjectedContainerCharts = observer(({
|
||||||
themeStore,
|
activeTheme,
|
||||||
}: Dependencies & ContainerChartsProps) => {
|
}: Dependencies & ContainerChartsProps) => {
|
||||||
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
||||||
|
|
||||||
if (!metrics || !object || !tab) return null;
|
if (!metrics || !object || !tab) return null;
|
||||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||||
|
|
||||||
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
const { chartCapacityColor } = activeTheme.get().colors;
|
||||||
const {
|
const {
|
||||||
cpuUsage,
|
cpuUsage,
|
||||||
cpuRequests,
|
cpuRequests,
|
||||||
@ -127,6 +128,6 @@ const NonInjectedContainerCharts = observer(({
|
|||||||
export const ContainerCharts = withInjectables<Dependencies, ContainerChartsProps>(NonInjectedContainerCharts, {
|
export const ContainerCharts = withInjectables<Dependencies, ContainerChartsProps>(NonInjectedContainerCharts, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,11 +13,12 @@ import type { ChartProps } from "./chart";
|
|||||||
import { Chart, ChartKind } from "./chart";
|
import { Chart, ChartKind } from "./chart";
|
||||||
import { bytesToUnits, cssNames, isObject } from "../../utils";
|
import { bytesToUnits, cssNames, isObject } from "../../utils";
|
||||||
import { ZebraStripesPlugin } from "./zebra-stripes.plugin";
|
import { ZebraStripesPlugin } from "./zebra-stripes.plugin";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { NoMetrics } from "../resource-metrics/no-metrics";
|
import { NoMetrics } from "../resource-metrics/no-metrics";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export interface BarChartProps extends ChartProps {
|
export interface BarChartProps extends ChartProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
@ -27,11 +28,11 @@ export interface BarChartProps extends ChartProps {
|
|||||||
const getBarColor: Scriptable<string> = ({ dataset }) => Color(dataset?.borderColor).alpha(0.2).string();
|
const getBarColor: Scriptable<string> = ({ dataset }) => Color(dataset?.borderColor).alpha(0.2).string();
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedBarChart = observer(({
|
const NonInjectedBarChart = observer(({
|
||||||
themeStore,
|
activeTheme,
|
||||||
name,
|
name,
|
||||||
data,
|
data,
|
||||||
className,
|
className,
|
||||||
@ -40,7 +41,7 @@ const NonInjectedBarChart = observer(({
|
|||||||
options: customOptions,
|
options: customOptions,
|
||||||
...settings
|
...settings
|
||||||
}: Dependencies & BarChartProps) => {
|
}: Dependencies & BarChartProps) => {
|
||||||
const { textColorPrimary, borderFaintColor, chartStripesColor } = themeStore.activeTheme.colors;
|
const { textColorPrimary, borderFaintColor, chartStripesColor } = activeTheme.get().colors;
|
||||||
const { datasets: rawDatasets = [], ...rest } = data;
|
const { datasets: rawDatasets = [], ...rest } = data;
|
||||||
const datasets = rawDatasets
|
const datasets = rawDatasets
|
||||||
.filter(set => set.data?.length)
|
.filter(set => set.data?.length)
|
||||||
@ -168,7 +169,7 @@ const NonInjectedBarChart = observer(({
|
|||||||
export const BarChart = withInjectables<Dependencies, BarChartProps>(NonInjectedBarChart, {
|
export const BarChart = withInjectables<Dependencies, BarChartProps>(NonInjectedBarChart, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export interface ChartProps {
|
|||||||
redraw?: boolean; // If true - recreate chart instance with no animation
|
redraw?: boolean; // If true - recreate chart instance with no animation
|
||||||
title?: string;
|
title?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
"data-testid"?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ChartKind {
|
export enum ChartKind {
|
||||||
@ -212,25 +213,26 @@ export class Chart extends React.Component<ChartProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { width, height, showChart, title, className } = this.props;
|
const { width, height, showChart, title, className, "data-testid": dataTestId } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<div className={cssNames("Chart", className)}>
|
className={cssNames("Chart", className)}
|
||||||
{title && <div className="chart-title">{title}</div>}
|
data-testid={dataTestId}
|
||||||
{showChart && (
|
>
|
||||||
<div className="chart-container">
|
{title && <div className="chart-title">{title}</div>}
|
||||||
<canvas
|
{showChart && (
|
||||||
ref={this.canvas}
|
<div className="chart-container">
|
||||||
width={width}
|
<canvas
|
||||||
height={height}
|
ref={this.canvas}
|
||||||
/>
|
width={width}
|
||||||
<div className="chartjs-tooltip flex column"></div>
|
height={height}
|
||||||
</div>
|
/>
|
||||||
)}
|
<div className="chartjs-tooltip flex column"></div>
|
||||||
{this.renderLegend()}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</>
|
{this.renderLegend()}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,10 @@ import ChartJS from "chart.js";
|
|||||||
import type { ChartProps } from "./chart";
|
import type { ChartProps } from "./chart";
|
||||||
import { Chart } from "./chart";
|
import { Chart } from "./chart";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export interface PieChartProps extends ChartProps {
|
export interface PieChartProps extends ChartProps {
|
||||||
}
|
}
|
||||||
@ -44,18 +45,18 @@ function getCutout(length: number | undefined): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedPieChart = observer(({
|
const NonInjectedPieChart = observer(({
|
||||||
themeStore,
|
activeTheme,
|
||||||
data,
|
data,
|
||||||
className,
|
className,
|
||||||
options,
|
options,
|
||||||
showChart,
|
showChart,
|
||||||
...chartProps
|
...chartProps
|
||||||
}: Dependencies & PieChartProps) => {
|
}: Dependencies & PieChartProps) => {
|
||||||
const { contentColor } = themeStore.activeTheme.colors;
|
const { contentColor } = activeTheme.get().colors;
|
||||||
const opts: ChartOptions = {
|
const opts: ChartOptions = {
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
tooltips: {
|
tooltips: {
|
||||||
@ -68,18 +69,11 @@ const NonInjectedPieChart = observer(({
|
|||||||
const total = datasetData.reduce((acc, cur) => acc + cur, 0);
|
const total = datasetData.reduce((acc, cur) => acc + cur, 0);
|
||||||
const percent = Math.round((datasetData[tooltipItem.index] as number / total) * 100);
|
const percent = Math.round((datasetData[tooltipItem.index] as number / total) * 100);
|
||||||
const percentLabel = isNaN(percent) ? "N/A" : `${percent}%`;
|
const percentLabel = isNaN(percent) ? "N/A" : `${percent}%`;
|
||||||
const tooltipLabel = dataset.tooltipLabels?.[tooltipItem.index];
|
const tooltipLabelCustomizer = dataset.tooltipLabels?.[tooltipItem.index];
|
||||||
let tooltip = `${dataset.label}: ${percentLabel}`;
|
|
||||||
|
|
||||||
if (tooltipLabel) {
|
return tooltipLabelCustomizer
|
||||||
if (typeof tooltipLabel === "function") {
|
? tooltipLabelCustomizer(percentLabel)
|
||||||
tooltip = tooltipLabel(percentLabel);
|
: `${dataset.label}: ${percentLabel}`;
|
||||||
} else {
|
|
||||||
tooltip = tooltipLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tooltip;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
filter: ({ datasetIndex, index }, { datasets = [] }) => {
|
filter: ({ datasetIndex, index }, { datasets = [] }) => {
|
||||||
@ -120,7 +114,7 @@ const NonInjectedPieChart = observer(({
|
|||||||
export const PieChart = withInjectables<Dependencies, PieChartProps>(NonInjectedPieChart, {
|
export const PieChart = withInjectables<Dependencies, PieChartProps>(NonInjectedPieChart, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -10,13 +10,14 @@ import { disposeOnUnmount, observer } from "mobx-react";
|
|||||||
import { cssNames } from "../../../utils";
|
import { cssNames } from "../../../utils";
|
||||||
import type { Terminal } from "./terminal";
|
import type { Terminal } from "./terminal";
|
||||||
import type { TerminalStore } from "./store";
|
import type { TerminalStore } from "./store";
|
||||||
import type { ThemeStore } from "../../../themes/store";
|
import type { LensTheme } from "../../../themes/store";
|
||||||
import type { DockTab, DockStore } from "../dock/store";
|
import type { DockTab, DockStore } from "../dock/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import dockStoreInjectable from "../dock/store.injectable";
|
import dockStoreInjectable from "../dock/store.injectable";
|
||||||
import terminalStoreInjectable from "./store.injectable";
|
import terminalStoreInjectable from "./store.injectable";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import themeStoreInjectable from "../../../themes/store.injectable";
|
import activeThemeInjectable from "../../../themes/active.injectable";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
|
|
||||||
export interface TerminalWindowProps {
|
export interface TerminalWindowProps {
|
||||||
tab: DockTab;
|
tab: DockTab;
|
||||||
@ -25,7 +26,7 @@ export interface TerminalWindowProps {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
dockStore: DockStore;
|
dockStore: DockStore;
|
||||||
terminalStore: TerminalStore;
|
terminalStore: TerminalStore;
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -68,7 +69,7 @@ class NonInjectedTerminalWindow extends React.Component<TerminalWindowProps & De
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cssNames("TerminalWindow", this.props.themeStore.activeTheme.type)}
|
className={cssNames("TerminalWindow", this.props.activeTheme.get().type)}
|
||||||
ref={elem => this.elem = elem}
|
ref={elem => this.elem = elem}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -80,7 +81,7 @@ export const TerminalWindow = withInjectables<Dependencies, TerminalWindowProps>
|
|||||||
...props,
|
...props,
|
||||||
dockStore: di.inject(dockStoreInjectable),
|
dockStore: di.inject(dockStoreInjectable),
|
||||||
terminalStore: di.inject(terminalStoreInjectable),
|
terminalStore: di.inject(terminalStoreInjectable),
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import "./item-list-layout.scss";
|
|||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
import { computed, makeObservable } from "mobx";
|
import { computed, makeObservable } from "mobx";
|
||||||
import { Observer, observer } from "mobx-react";
|
import { Observer, observer } from "mobx-react";
|
||||||
import type { ConfirmDialogParams } from "../confirm-dialog";
|
import type { ConfirmDialogParams } from "../confirm-dialog";
|
||||||
@ -20,18 +21,18 @@ import { NoItems } from "../no-items";
|
|||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "../../../common/item.store";
|
||||||
import type { Filter, PageFiltersStore } from "./page-filters/store";
|
import type { Filter, PageFiltersStore } from "./page-filters/store";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { MenuActions } from "../menu/menu-actions";
|
import { MenuActions } from "../menu/menu-actions";
|
||||||
import { MenuItem } from "../menu";
|
import { MenuItem } from "../menu";
|
||||||
import { Checkbox } from "../checkbox";
|
import { Checkbox } from "../checkbox";
|
||||||
import type { UserStore } from "../../../common/user-store";
|
import type { UserStore } from "../../../common/user-store";
|
||||||
import type { ItemListStore } from "./list-layout";
|
import type { ItemListStore } from "./list-layout";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
|
||||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||||
import pageFiltersStoreInjectable from "./page-filters/store.injectable";
|
import pageFiltersStoreInjectable from "./page-filters/store.injectable";
|
||||||
import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable";
|
import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable";
|
||||||
import openConfirmDialogInjectable from "../confirm-dialog/open.injectable";
|
import openConfirmDialogInjectable from "../confirm-dialog/open.injectable";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export interface ItemListLayoutContentProps<Item extends ItemObject, PreLoadStores extends boolean> {
|
export interface ItemListLayoutContentProps<Item extends ItemObject, PreLoadStores extends boolean> {
|
||||||
getFilters: () => Filter[];
|
getFilters: () => Filter[];
|
||||||
@ -71,7 +72,7 @@ export interface ItemListLayoutContentProps<Item extends ItemObject, PreLoadStor
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
userStore: UserStore;
|
userStore: UserStore;
|
||||||
pageFiltersStore: PageFiltersStore;
|
pageFiltersStore: PageFiltersStore;
|
||||||
openConfirmDialog: OpenConfirmDialog;
|
openConfirmDialog: OpenConfirmDialog;
|
||||||
@ -291,10 +292,10 @@ class NonInjectedItemListLayoutContent<
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
||||||
detailsItem, className, tableProps = {}, tableId, getItems, themeStore,
|
detailsItem, className, tableProps = {}, tableId, getItems, activeTheme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const selectedItemId = detailsItem && detailsItem.getId();
|
const selectedItemId = detailsItem && detailsItem.getId();
|
||||||
const classNames = cssNames(className, "box", "grow", themeStore.activeTheme.type);
|
const classNames = cssNames(className, "box", "grow", activeTheme.get().type);
|
||||||
const items = getItems();
|
const items = getItems();
|
||||||
const selectedItems = store.pickOnlySelected(items);
|
const selectedItems = store.pickOnlySelected(items);
|
||||||
|
|
||||||
@ -377,7 +378,7 @@ class NonInjectedItemListLayoutContent<
|
|||||||
export const ItemListLayoutContent = withInjectables<Dependencies, ItemListLayoutContentProps<ItemObject, boolean>>(NonInjectedItemListLayoutContent, {
|
export const ItemListLayoutContent = withInjectables<Dependencies, ItemListLayoutContentProps<ItemObject, boolean>>(NonInjectedItemListLayoutContent, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
userStore: di.inject(userStoreInjectable),
|
userStore: di.inject(userStoreInjectable),
|
||||||
pageFiltersStore: di.inject(pageFiltersStoreInjectable),
|
pageFiltersStore: di.inject(pageFiltersStoreInjectable),
|
||||||
openConfirmDialog: di.inject(openConfirmDialogInjectable),
|
openConfirmDialog: di.inject(openConfirmDialogInjectable),
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
import styles from "./monaco-editor.module.scss";
|
import styles from "./monaco-editor.module.scss";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
import { action, computed, makeObservable, observable, reaction } from "mobx";
|
import { action, computed, makeObservable, observable, reaction } from "mobx";
|
||||||
import { editor, Uri } from "monaco-editor";
|
import { editor, Uri } from "monaco-editor";
|
||||||
import type { MonacoTheme } from "./monaco-themes";
|
import type { MonacoTheme } from "./monaco-themes";
|
||||||
@ -13,10 +14,10 @@ import { type MonacoValidator, monacoValidators } from "./monaco-validators";
|
|||||||
import { debounce, merge } from "lodash";
|
import { debounce, merge } from "lodash";
|
||||||
import { autoBind, cssNames, disposer } from "../../utils";
|
import { autoBind, cssNames, disposer } from "../../utils";
|
||||||
import type { UserStore } from "../../../common/user-store";
|
import type { UserStore } from "../../../common/user-store";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
|
||||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||||
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
export type MonacoEditorId = string;
|
export type MonacoEditorId = string;
|
||||||
|
|
||||||
@ -39,8 +40,8 @@ export interface MonacoEditorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
|
||||||
userStore: UserStore;
|
userStore: UserStore;
|
||||||
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createMonacoUri(id: MonacoEditorId): Uri {
|
export function createMonacoUri(id: MonacoEditorId): Uri {
|
||||||
@ -83,7 +84,7 @@ class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Depend
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get theme() {
|
@computed get theme() {
|
||||||
return this.props.theme ?? this.props.themeStore.activeTheme.monacoTheme;
|
return this.props.theme ?? this.props.activeTheme.get().monacoTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get model(): editor.ITextModel {
|
@computed get model(): editor.ITextModel {
|
||||||
@ -306,8 +307,8 @@ export const MonacoEditor = withInjectables<Dependencies, MonacoEditorProps, Mon
|
|||||||
{
|
{
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
|
||||||
userStore: di.inject(userStoreInjectable),
|
userStore: di.inject(userStoreInjectable),
|
||||||
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,15 +8,15 @@
|
|||||||
import "./select.scss";
|
import "./select.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { ObservableSet } from "mobx";
|
import type { IComputedValue, ObservableSet } from "mobx";
|
||||||
import { action, computed, makeObservable } from "mobx";
|
import { action, computed, makeObservable } from "mobx";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import ReactSelect, { components, createFilter } from "react-select";
|
import ReactSelect, { components, createFilter } from "react-select";
|
||||||
import type { Props as ReactSelectProps, GroupBase, MultiValue, OptionsOrGroups, PropsValue, SingleValue } from "react-select";
|
import type { Props as ReactSelectProps, GroupBase, MultiValue, OptionsOrGroups, PropsValue, SingleValue } from "react-select";
|
||||||
import type { ThemeStore } from "../../themes/store";
|
import type { LensTheme } from "../../themes/store";
|
||||||
import { autoBind, cssNames } from "../../utils";
|
import { autoBind, cssNames } from "../../utils";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import themeStoreInjectable from "../../themes/store.injectable";
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
|
|
||||||
const { Menu } = components;
|
const { Menu } = components;
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ const defaultFilter = createFilter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
themeStore: ThemeStore;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onMultiSelectFor<Value, Option extends SelectOption<Value>, Group extends GroupBase<Option> = GroupBase<Option>>(collection: Set<Value> | ObservableSet<Value>): SelectProps<Value, Option, true, Group>["onChange"] {
|
export function onMultiSelectFor<Value, Option extends SelectOption<Value>, Group extends GroupBase<Option> = GroupBase<Option>>(collection: Set<Value> | ObservableSet<Value>): SelectProps<Value, Option, true, Group>["onChange"] {
|
||||||
@ -124,7 +124,7 @@ class NonInjectedSelect<
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get themeClass() {
|
@computed get themeClass() {
|
||||||
const themeName = this.props.themeName || this.props.themeStore.activeTheme.type;
|
const themeName = this.props.themeName || this.props.activeTheme.get().type;
|
||||||
|
|
||||||
return `theme-${themeName}`;
|
return `theme-${themeName}`;
|
||||||
}
|
}
|
||||||
@ -248,7 +248,7 @@ class NonInjectedSelect<
|
|||||||
export const Select = withInjectables<Dependencies, SelectProps<unknown, SelectOption<unknown>, boolean>>(NonInjectedSelect, {
|
export const Select = withInjectables<Dependencies, SelectProps<unknown, SelectOption<unknown>, boolean>>(NonInjectedSelect, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
}),
|
}),
|
||||||
}) as <
|
}) as <
|
||||||
Value,
|
Value,
|
||||||
|
|||||||
@ -125,7 +125,7 @@ export interface ApplicationBuilder {
|
|||||||
helmCharts: {
|
helmCharts: {
|
||||||
navigate: NavigateToHelmCharts;
|
navigate: NavigateToHelmCharts;
|
||||||
};
|
};
|
||||||
|
navigateWith: (token: Injectable<() => void, any, void>) => void;
|
||||||
select: {
|
select: {
|
||||||
openMenu: (id: string) => { selectOption: (labelText: string) => void };
|
openMenu: (id: string) => { selectOption: (labelText: string) => void };
|
||||||
selectOption: (menuId: string, labelText: string) => void;
|
selectOption: (menuId: string, labelText: string) => void;
|
||||||
@ -441,6 +441,13 @@ export const getApplicationBuilder = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
navigateWith: (token) => {
|
||||||
|
const windowDi = builder.applicationWindow.only.di;
|
||||||
|
const navigate = windowDi.inject(token);
|
||||||
|
|
||||||
|
navigate();
|
||||||
|
},
|
||||||
|
|
||||||
setEnvironmentToClusterFrame: () => {
|
setEnvironmentToClusterFrame: () => {
|
||||||
environment = environments.clusterFrame;
|
environment = environments.clusterFrame;
|
||||||
|
|
||||||
|
|||||||
@ -1281,6 +1281,7 @@ exports[`<ClusterFrame /> given cluster without list nodes, but with namespaces
|
|||||||
<main>
|
<main>
|
||||||
<div
|
<div
|
||||||
class="WorkloadsOverview flex column gaps"
|
class="WorkloadsOverview flex column gaps"
|
||||||
|
data-testid="page-for-workloads-overview"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="header flex gaps align-center"
|
class="header flex gaps align-center"
|
||||||
|
|||||||
18
src/renderer/themes/active.injectable.ts
Normal file
18
src/renderer/themes/active.injectable.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { computed } from "mobx";
|
||||||
|
import themeStoreInjectable from "./store.injectable";
|
||||||
|
|
||||||
|
const activeThemeInjectable = getInjectable({
|
||||||
|
id: "active-theme",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const store = di.inject(themeStoreInjectable);
|
||||||
|
|
||||||
|
return computed(() => store.activeTheme);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default activeThemeInjectable;
|
||||||
@ -3,9 +3,9 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Theme } from "./store";
|
import type { LensTheme } from "./store";
|
||||||
|
|
||||||
const lensDarkTheme: Theme = {
|
const lensDarkTheme: LensTheme = {
|
||||||
"name": "Dark",
|
"name": "Dark",
|
||||||
"type": "dark",
|
"type": "dark",
|
||||||
"description": "Original Lens dark theme",
|
"description": "Original Lens dark theme",
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import type { Theme } from "./store";
|
import type { LensTheme } from "./store";
|
||||||
|
|
||||||
const lensLightTheme: Theme = {
|
const lensLightTheme: LensTheme = {
|
||||||
"name": "Light",
|
"name": "Light",
|
||||||
"type": "light",
|
"type": "light",
|
||||||
"description": "Original Lens light theme",
|
"description": "Original Lens light theme",
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import assert from "assert";
|
|||||||
|
|
||||||
export type ThemeId = string;
|
export type ThemeId = string;
|
||||||
|
|
||||||
export interface Theme {
|
export interface LensTheme {
|
||||||
name: string;
|
name: string;
|
||||||
type: "dark" | "light";
|
type: "dark" | "light";
|
||||||
colors: Record<string, string>;
|
colors: Record<string, string>;
|
||||||
@ -39,7 +39,7 @@ interface Dependencies {
|
|||||||
export class ThemeStore {
|
export class ThemeStore {
|
||||||
private terminalColorPrefix = "terminal";
|
private terminalColorPrefix = "terminal";
|
||||||
|
|
||||||
#themes = observable.map<ThemeId, Theme>({
|
#themes = observable.map<ThemeId, LensTheme>({
|
||||||
"lens-dark": lensDarkTheme,
|
"lens-dark": lensDarkTheme,
|
||||||
"lens-light": lensLightTheme,
|
"lens-light": lensLightTheme,
|
||||||
});
|
});
|
||||||
@ -66,9 +66,9 @@ export class ThemeStore {
|
|||||||
return this.dependencies.userStore.terminalTheme;
|
return this.dependencies.userStore.terminalTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly defaultTheme: Theme;
|
private readonly defaultTheme: LensTheme;
|
||||||
|
|
||||||
@computed get activeTheme(): Theme {
|
@computed get activeTheme(): LensTheme {
|
||||||
return this.themes.get(this.activeThemeId) ?? this.defaultTheme;
|
return this.themes.get(this.activeThemeId) ?? this.defaultTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ export class ThemeStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get themes() {
|
get themes() {
|
||||||
return this.#themes as ReadonlyDeep<Map<string, Theme>>;
|
return this.#themes as ReadonlyDeep<Map<string, LensTheme>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(protected readonly dependencies: Dependencies) {
|
constructor(protected readonly dependencies: Dependencies) {
|
||||||
@ -130,7 +130,7 @@ export class ThemeStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getThemeById(themeId: ThemeId): Theme | undefined {
|
getThemeById(themeId: ThemeId): LensTheme | undefined {
|
||||||
return this.themes.get(themeId);
|
return this.themes.get(themeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user