1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Split root and cluster frames in smaller pieces (#5737)

* Make root frame child components comply with open closed principle and include it in the behavioural unit tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make cluster frame child components comply with open closed principle and include it in behavioural unit tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove duplication

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Simplify test

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make a component more determistic in unit tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Mock uninterested, non-deterministic third party library in unit tests

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Optimize registration of injectables in unit tests to make tests faster

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update snapshots

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove import time side-effect causing memory leak

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-07-04 19:38:29 +03:00 committed by GitHub
parent 1a9fa699c9
commit 14d5a1c3cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 48472 additions and 35711 deletions

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import type {
DragDropContextProps,
DraggableProps,
DroppableProps,
} from "react-beautiful-dnd";
export const DragDropContext = ({ children }: DragDropContextProps) => <>{ children }</>;
export const Draggable = ({ children }: DraggableProps) => <>{ children }</>;
export const Droppable = ({ children }: DroppableProps) => <>{ children }</>;

View File

@ -209,11 +209,11 @@
"@hapi/subtext": "^7.0.4", "@hapi/subtext": "^7.0.4",
"@kubernetes/client-node": "^0.16.3", "@kubernetes/client-node": "^0.16.3",
"@material-ui/styles": "^4.11.5", "@material-ui/styles": "^4.11.5",
"@ogre-tools/fp": "9.0.0", "@ogre-tools/fp": "9.0.1",
"@ogre-tools/injectable": "9.0.0", "@ogre-tools/injectable": "9.0.1",
"@ogre-tools/injectable-extension-for-auto-registration": "9.0.0", "@ogre-tools/injectable-extension-for-auto-registration": "9.0.1",
"@ogre-tools/injectable-extension-for-mobx": "9.0.0", "@ogre-tools/injectable-extension-for-mobx": "9.0.1",
"@ogre-tools/injectable-react": "9.0.0", "@ogre-tools/injectable-react": "9.0.1",
"@sentry/electron": "^3.0.7", "@sentry/electron": "^3.0.7",
"@sentry/integrations": "^6.19.3", "@sentry/integrations": "^6.19.3",
"@side/jest-runtime": "^1.0.1", "@side/jest-runtime": "^1.0.1",

View File

@ -3,71 +3,206 @@
exports[`extension special characters in page registrations renders 1`] = ` exports[`extension special characters in page registrations renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
> >
<div <div
data-testid="app-update-not-available" data-testid="welcome-banner-container"
style="width: 320px;"
> >
No new updates available <i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
@ -78,74 +213,126 @@ exports[`extension special characters in page registrations renders 1`] = `
exports[`extension special characters in page registrations when navigating to route with ID having special characters renders 1`] = ` exports[`extension special characters in page registrations when navigating to route with ID having special characters renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
> >
<div <i
data-testid="app-update-not-available" class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
> >
No new updates available <span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div>
Some page
</div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div> </div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div> </div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
</div> class="leftSide"
<div> data-testid="status-bar-left"
Some page >
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"

View File

@ -3,78 +3,130 @@
exports[`navigating between routes given route with optional path parameters when navigating to route with path parameters renders 1`] = ` exports[`navigating between routes given route with optional path parameters when navigating to route with path parameters renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
> >
<div <i
data-testid="app-update-not-available" class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
> >
No new updates available <span
</div> class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div> </div>
<div
class="items"
/>
</div> </div>
<div <main>
class="rightSide" <div
data-testid="status-bar-right" id="lens-views"
/> />
</div> <pre>
<pre> {
{
"someParameter": "some-value", "someParameter": "some-value",
"someOtherParameter": "some-other-value" "someOtherParameter": "some-other-value"
} }
</pre> </pre>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
/> />
@ -84,74 +136,126 @@ exports[`navigating between routes given route with optional path parameters whe
exports[`navigating between routes given route without path parameters when navigating to route renders 1`] = ` exports[`navigating between routes given route without path parameters when navigating to route renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
> >
<div <i
data-testid="app-update-not-available" class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
> >
No new updates available <span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div>
Some component
</div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div> </div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div> </div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
</div> class="leftSide"
<div> data-testid="status-bar-left"
Some component >
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"

View File

@ -3,71 +3,206 @@
exports[`add-cluster - navigation using application menu renders 1`] = ` exports[`add-cluster - navigation using application menu renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
> >
<div <div
data-testid="app-update-not-available" data-testid="welcome-banner-container"
style="width: 320px;"
> >
No new updates available <i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
@ -78,151 +213,203 @@ exports[`add-cluster - navigation using application menu renders 1`] = `
exports[`add-cluster - navigation using application menu when navigating to add cluster using application menu renders 1`] = ` exports[`add-cluster - navigation using application menu when navigating to add cluster using application menu renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
> >
<div <i
data-testid="app-update-not-available" class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
> >
No new updates available <span
</div> class="icon"
</div> data-icon-name="home"
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
<div
class="SettingLayout AddClusters"
data-testid="add-cluster-page"
>
<div
class="contentRegion"
id="ScrollSpyRoot"
>
<div
class="content flex column gaps"
>
<h2>
Add Clusters from Kubeconfig
</h2>
<p>
Clusters added here are
<b>
not
</b>
merged into the
<code>
~/.kube/config
</code>
file.
<a
href="https://docs.k8slens.dev/main/catalog/add-cluster/"
rel="noreferrer"
target="_blank"
> >
Read more about adding clusters. home
</a> </span>
</p> </i>
<div <i
class="flex column" class="Icon material interactive disabled focusable"
/> data-testid="history-back"
<div
class="actions-panel"
> >
<button <span
class="Button primary" class="icon"
disabled="" data-icon-name="arrow_back"
type="button"
> >
Add clusters arrow_back
</button> </span>
</div> </i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div> </div>
<div <div
class="toolsRegion" class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="SettingLayout AddClusters"
data-testid="add-cluster-page"
> >
<div <div
class="fixed top-[60px]" class="contentRegion"
id="ScrollSpyRoot"
> >
<div> <div
<div class="content flex column gaps"
aria-label="Close" >
class="closeButton" <h2>
role="button" Add Clusters from Kubeconfig
> </h2>
<i <p>
class="Icon icon material focusable" Clusters added here are
<b>
not
</b>
merged into the
<code>
~/.kube/config
</code>
file.
<a
href="https://docs.k8slens.dev/main/catalog/add-cluster/"
rel="noreferrer"
target="_blank"
> >
<span Read more about adding clusters.
class="icon" </a>
data-icon-name="close" </p>
>
close
</span>
</i>
</div>
<div <div
aria-hidden="true" class="flex column"
class="esc" />
<div
class="actions-panel"
> >
ESC <button
class="Button primary"
disabled=""
type="button"
>
Add clusters
</button>
</div>
</div>
<div
class="toolsRegion"
>
<div
class="fixed top-[60px]"
>
<div>
<div
aria-label="Close"
class="closeButton"
role="button"
>
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
<div
aria-hidden="true"
class="esc"
>
ESC
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div> </div>
</div> </div>
<div <div

View File

@ -9,10 +9,6 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
import React from "react"; import React from "react";
// TODO: Make components free of side effects by making them deterministic // TODO: Make components free of side effects by making them deterministic
jest.mock("../../renderer/components/tooltip/tooltip", () => ({
Tooltip: () => null,
}));
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({ jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
withTooltip: (Target: any) => ({ tooltip, tooltipOverrideDisabled, ...props }: any) => <Target {...props} />, withTooltip: (Target: any) => ({ tooltip, tooltipOverrideDisabled, ...props }: any) => <Target {...props} />,
})); }));

View File

@ -4,71 +4,206 @@ exports[`encourage user to update when sufficient time passed since update was d
<body> <body>
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
> >
<div <div
data-testid="app-update-not-available" data-testid="welcome-banner-container"
style="width: 320px;"
> >
No new updates available <i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"

View File

@ -4,71 +4,206 @@ exports[`periodical checking of updates given updater is enabled and configurati
<body> <body>
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
> >
<div <div
data-testid="app-update-not-available" data-testid="welcome-banner-container"
style="width: 320px;"
> >
No new updates available <i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"

View File

@ -4,71 +4,206 @@ exports[`selection of update stability when started renders 1`] = `
<body> <body>
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
> >
<div <div
data-testid="app-update-not-available" data-testid="welcome-banner-container"
style="width: 320px;"
> >
No new updates available <i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"

View File

@ -22,6 +22,7 @@ import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token"; import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
import sidebarStorageInjectable from "../../renderer/components/layout/sidebar-storage/sidebar-storage.injectable"; import sidebarStorageInjectable from "../../renderer/components/layout/sidebar-storage/sidebar-storage.injectable";
import hostedClusterIdInjectable from "../../renderer/cluster-frame-context/hosted-cluster-id.injectable";
import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time";
describe("cluster - sidebar and tab navigation for core", () => { describe("cluster - sidebar and tab navigation for core", () => {
@ -38,6 +39,8 @@ describe("cluster - sidebar and tab navigation for core", () => {
applicationBuilder.setEnvironmentToClusterFrame(); applicationBuilder.setEnvironmentToClusterFrame();
applicationBuilder.beforeApplicationStart(({ rendererDi }) => { applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(hostedClusterIdInjectable, () => "some-hosted-cluster-id");
rendererDi.override( rendererDi.override(
directoryForLensLocalStorageInjectable, directoryForLensLocalStorageInjectable,
() => "/some-directory-for-lens-local-storage", () => "/some-directory-for-lens-local-storage",
@ -94,7 +97,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable);
await writeJsonFileFake( await writeJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
{ {
sidebar: { sidebar: {
expanded: { "some-parent-id": true }, expanded: { "some-parent-id": true },
@ -136,7 +139,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable);
await writeJsonFileFake( await writeJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
{ {
sidebar: { sidebar: {
expanded: { "some-unknown-parent-id": true }, expanded: { "some-unknown-parent-id": true },
@ -166,7 +169,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable);
await writeJsonFileFake( await writeJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
{ {
someThingButSidebar: {}, someThingButSidebar: {},
}, },
@ -268,7 +271,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
const pathExistsFake = rendererDi.inject(pathExistsInjectable); const pathExistsFake = rendererDi.inject(pathExistsInjectable);
const actual = await pathExistsFake( const actual = await pathExistsFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
); );
expect(actual).toBe(false); expect(actual).toBe(false);
@ -280,7 +283,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
const readJsonFileFake = rendererDi.inject(readJsonFileInjectable); const readJsonFileFake = rendererDi.inject(readJsonFileInjectable);
const actual = await readJsonFileFake( const actual = await readJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
); );
expect(actual).toEqual({ expect(actual).toEqual({

View File

@ -18,8 +18,14 @@ import { navigateToRouteInjectionToken } from "../../common/front-end-routing/na
import assert from "assert"; import assert from "assert";
import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake"; import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake"; import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import hostedClusterIdInjectable from "../../renderer/cluster-frame-context/hosted-cluster-id.injectable";
import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time";
// TODO: Make tooltips free of side effects by making it deterministic
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
withTooltip: (target: any) => target,
}));
describe("cluster - sidebar and tab navigation for extensions", () => { describe("cluster - sidebar and tab navigation for extensions", () => {
let applicationBuilder: ApplicationBuilder; let applicationBuilder: ApplicationBuilder;
let rendererDi: DiContainer; let rendererDi: DiContainer;
@ -34,6 +40,8 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
applicationBuilder.setEnvironmentToClusterFrame(); applicationBuilder.setEnvironmentToClusterFrame();
applicationBuilder.beforeApplicationStart(({ rendererDi }) => { applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(hostedClusterIdInjectable, () => "some-hosted-cluster-id");
rendererDi.override( rendererDi.override(
directoryForLensLocalStorageInjectable, directoryForLensLocalStorageInjectable,
() => "/some-directory-for-lens-local-storage", () => "/some-directory-for-lens-local-storage",
@ -96,7 +104,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable);
await writeJsonFileFake( await writeJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
{ {
sidebar: { sidebar: {
expanded: { "some-extension-name-some-parent-id": true }, expanded: { "some-extension-name-some-parent-id": true },
@ -132,7 +140,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable);
await writeJsonFileFake( await writeJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
{ {
sidebar: { sidebar: {
expanded: { "some-extension-name-some-unknown-parent-id": true }, expanded: { "some-extension-name-some-unknown-parent-id": true },
@ -162,7 +170,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable);
await writeJsonFileFake( await writeJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
{ {
someThingButSidebar: {}, someThingButSidebar: {},
}, },
@ -284,7 +292,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
const pathExistsFake = rendererDi.inject(pathExistsInjectable); const pathExistsFake = rendererDi.inject(pathExistsInjectable);
const actual = await pathExistsFake( const actual = await pathExistsFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
); );
expect(actual).toBe(false); expect(actual).toBe(false);
@ -296,7 +304,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
const readJsonFileFake = rendererDi.inject(readJsonFileInjectable); const readJsonFileFake = rendererDi.inject(readJsonFileInjectable);
const actual = await readJsonFileFake( const actual = await readJsonFileFake(
"/some-directory-for-lens-local-storage/app.json", "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json",
); );
expect(actual).toEqual({ expect(actual).toEqual({

View File

@ -3,71 +3,206 @@
exports[`extensions - navigation using application menu renders 1`] = ` exports[`extensions - navigation using application menu renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
> >
<div <div
data-testid="app-update-not-available" data-testid="welcome-banner-container"
style="width: 320px;"
> >
No new updates available <i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right" >
/> <div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
@ -78,184 +213,236 @@ exports[`extensions - navigation using application menu renders 1`] = `
exports[`extensions - navigation using application menu when navigating to extensions using application menu renders 1`] = ` exports[`extensions - navigation using application menu when navigating to extensions using application menu renders 1`] = `
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="items"
>
<i
class="Icon material interactive focusable"
data-testid="home-button"
tabindex="0"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div>
<main>
<div
id="lens-views"
/>
<div
class="SettingLayout DropFileInput Extensions"
data-testid="extensions-page"
> >
<div <div
data-testid="app-update-not-available" class="contentRegion"
id="ScrollSpyRoot"
> >
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
<div
class="SettingLayout DropFileInput Extensions"
data-testid="extensions-page"
>
<div
class="contentRegion"
id="ScrollSpyRoot"
>
<div
class="content"
>
<section>
<h1>
Extensions
</h1>
<div <div
class="notice mb-14 mt-3" class="content"
> >
<p> <section>
Add new features via Lens Extensions. Check out the <h1>
<a Extensions
href="https://docs.k8slens.dev/main/extensions/" </h1>
rel="noreferrer"
target="_blank"
>
docs
</a>
and list of
<a
href="https://github.com/lensapp/lens-extensions/blob/main/README.md"
rel="noreferrer"
target="_blank"
>
available extensions
</a>
.
</p>
</div>
<section
class="mt-2"
>
<div
class="SubTitle"
>
Name or file path or URL to an extension package (tar, tgz)
</div>
<div
class="flex"
>
<div <div
class="flex-1" class="notice mb-14 mt-3"
/>
<div
class="flex-initial"
> >
<button <p>
class="Button w-80 h-full primary" Add new features via Lens Extensions. Check out the
type="button" <a
href="https://docs.k8slens.dev/main/extensions/"
rel="noreferrer"
target="_blank"
>
docs
</a>
and list of
<a
href="https://github.com/lensapp/lens-extensions/blob/main/README.md"
rel="noreferrer"
target="_blank"
>
available extensions
</a>
.
</p>
</div>
<section
class="mt-2"
>
<div
class="SubTitle"
> >
Install Name or file path or URL to an extension package (tar, tgz)
</button>
</div>
<div
class="flex"
>
<div
class="flex-1"
/>
<div
class="flex-initial"
>
<button
class="Button w-80 h-full primary"
type="button"
>
Install
</button>
</div>
</div>
<small
class="mt-3"
>
<b>
Pro-Tip
</b>
: you can drag-n-drop tarball-file to this area
</small>
</section>
<div>
<div
class="Spinner singleColor center"
/>
</div>
</section>
</div>
<div
class="toolsRegion"
>
<div
class="fixed top-[60px]"
>
<div>
<div
aria-label="Close"
class="closeButton"
role="button"
>
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
<div
aria-hidden="true"
class="esc"
>
ESC
</div>
</div> </div>
</div> </div>
<small
class="mt-3"
>
<b>
Pro-Tip
</b>
: you can drag-n-drop tarball-file to this area
</small>
</section>
<div>
<div
class="Spinner singleColor center"
/>
</div>
</section>
</div>
<div
class="toolsRegion"
>
<div
class="fixed top-[60px]"
>
<div>
<div
aria-label="Close"
class="closeButton"
role="button"
>
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
<div
aria-hidden="true"
class="esc"
>
ESC
</div>
</div> </div>
</div> </div>
</div> </div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
/>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
>
<div
class="item"
>
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
</div>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div> </div>
</div> </div>
<div <div

View File

@ -3,451 +3,586 @@
exports[`helm-charts - navigation to Helm charts when navigating to Helm charts renders 1`] = ` exports[`helm-charts - navigation to Helm charts when navigating to Helm charts renders 1`] = `
<div> <div>
<div <div
class="flex flex-col" class="Notifications flex column align-flex-end"
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="false"
data-testid="sidebar-item-workloads"
>
<a
class="nav-item flex gaps align-center expandable"
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="true"
data-testid="sidebar-item-helm"
>
<a
aria-current="page"
class="nav-item flex gaps align-center expandable active"
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 <div
class="TabLayout" class="mainLayout"
data-testid="tab-layout" style="--sidebar-width: 200px;"
> >
<div <div
class="Tabs center scrollable" class="sidebar"
> >
<div <div
class="Tab flex gaps align-center active" class="flex flex-col"
data-is-active-test="true" data-testid="cluster-sidebar"
data-testid="tab-link-for-charts"
role="tab"
tabindex="0"
> >
<div <div
class="label" class="SidebarCluster"
>
Charts
</div>
</div>
<div
class="Tab flex gaps align-center"
data-is-active-test="false"
data-testid="tab-link-for-releases"
role="tab"
tabindex="0"
>
<div
class="label"
>
Releases
</div>
</div>
</div>
<main>
<div
data-testid="page-for-helm-charts"
style="display: none;"
/>
<div
class="ItemListLayout flex column HelmCharts"
>
<div
class="header flex gaps align-center"
> >
<div <div
class="Input SearchInput focused" class="Avatar rounded loadingAvatar"
style="width: 40px; height: 40px;"
> >
<label ??
class="input-area flex gaps align-center" </div>
id="" <div
class="loadingClusterName"
/>
</div>
<div
class="sidebarNav sidebar-active-status"
>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-workloads"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-workloads"
href="/"
> >
<input
class="input box grow"
placeholder="Search Helm Charts..."
spellcheck="false"
value=""
/>
<i <i
class="Icon material focusable small" class="Icon svg focusable"
> >
<span <span
class="icon" class="icon"
data-icon-name="search" />
</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"
> >
search keyboard_arrow_down
</span> </span>
</i> </i>
</label> </a>
<div </div>
class="input-info flex gaps" <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="true"
data-testid="sidebar-item-helm"
>
<a
aria-current="page"
class="nav-item flex gaps align-center expandable active"
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>
</div>
<div
class="ResizingAnchor horizontal trailing"
/>
</div>
<div
class="contents"
>
<div
class="TabLayout"
data-testid="tab-layout"
>
<div <div
class="items box grow flex column" class="Tabs center scrollable"
> >
<div <div
class="Table flex column HelmCharts box grow dark selectable scrollable sortable autoSize virtual" class="Tab flex gaps align-center active"
data-is-active-test="true"
data-testid="tab-link-for-charts"
role="tab"
tabindex="0"
> >
<div <div
class="TableHead sticky nowrap topLine" class="label"
>
Charts
</div>
</div>
<div
class="Tab flex gaps align-center"
data-is-active-test="false"
data-testid="tab-link-for-releases"
role="tab"
tabindex="0"
>
<div
class="label"
>
Releases
</div>
</div>
</div>
<main>
<div
data-testid="page-for-helm-charts"
style="display: none;"
/>
<div
class="ItemListLayout flex column HelmCharts"
>
<div
class="header flex gaps align-center"
> >
<div <div
class="TableCell icon nowrap" class="Input SearchInput focused"
> >
<div <label
class="content" class="input-area flex gaps align-center"
/> id=""
</div>
<div
class="TableCell name nowrap sorting"
id="name"
>
<div
class="content"
>
Name
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell description nowrap"
id="description"
>
<div
class="content"
>
Description
</div>
</div>
<div
class="TableCell version nowrap"
id="version"
>
<div
class="content"
>
Version
</div>
</div>
<div
class="TableCell app-version nowrap"
id="app-version"
>
<div
class="content"
>
App Version
</div>
</div>
<div
class="TableCell repository nowrap sorting"
id="repo"
>
<div
class="content"
>
Repository
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell menu nowrap"
>
<div
class="content"
> >
<input
class="input box grow"
placeholder="Search Helm Charts..."
spellcheck="false"
value=""
/>
<i <i
class="Icon material interactive focusable" class="Icon material focusable small"
id="menu_actions_17"
tabindex="0"
> >
<span <span
class="icon" class="icon"
data-icon-name="more_vert" data-icon-name="search"
> >
more_vert search
</span> </span>
</i> </i>
</div> </label>
<div
class="input-info flex gaps"
/>
</div> </div>
</div> </div>
<div <div
class="NoItems flex box grow" class="items box grow flex column"
> >
<div <div
class="box center" class="Table flex column HelmCharts box grow dark selectable scrollable sortable autoSize virtual"
> >
Item list is empty <div
class="TableHead sticky nowrap topLine"
>
<div
class="TableCell icon nowrap"
>
<div
class="content"
/>
</div>
<div
class="TableCell name nowrap sorting"
id="name"
>
<div
class="content"
>
Name
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell description nowrap"
id="description"
>
<div
class="content"
>
Description
</div>
</div>
<div
class="TableCell version nowrap"
id="version"
>
<div
class="content"
>
Version
</div>
</div>
<div
class="TableCell app-version nowrap"
id="app-version"
>
<div
class="content"
>
App Version
</div>
</div>
<div
class="TableCell repository nowrap sorting"
id="repo"
>
<div
class="content"
>
Repository
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell menu nowrap"
>
<div
class="content"
>
<i
class="Icon material interactive focusable"
id="menu_actions_27"
tabindex="0"
>
<span
class="icon"
data-icon-name="more_vert"
>
more_vert
</span>
</i>
</div>
</div>
</div>
<div
class="NoItems flex box grow"
>
<div
class="box center"
>
Item list is empty
</div>
</div>
</div>
<div
class="AddRemoveButtons flex gaps"
/>
</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"
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"
id="tooltip_target_13"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
<div />
</i>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div <div
class="AddRemoveButtons flex gaps" 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_14"
tabindex="0"
>
<span
class="icon"
data-icon-name="add"
>
add
</span>
<div />
</i>
</div>
<i
class="Icon material interactive focusable"
id="tooltip_target_16"
tabindex="0"
>
<span
class="icon"
data-icon-name="fullscreen"
>
fullscreen
</span>
<div />
</i>
<i
class="Icon material interactive focusable"
id="tooltip_target_17"
tabindex="0"
>
<span
class="icon"
data-icon-name="keyboard_arrow_up"
>
keyboard_arrow_up
</span>
<div />
</i>
</div>
</div> </div>
</div> </div>
</main> </div>
</div> </div>
<div
class="Notifications flex column align-flex-end"
/>
</div> </div>
`; `;

View File

@ -4,115 +4,250 @@ exports[`status-bar-items-originating-from-extensions when application starts wh
<body> <body>
<div> <div>
<div <div
class="topBar" class="ClusterManager"
> >
<div <div
class="items" class="topBar"
> >
<i <div
class="Icon material interactive focusable" class="items"
data-testid="home-button"
tabindex="0"
> >
<span <i
class="icon" class="Icon material interactive focusable"
data-icon-name="home" data-testid="home-button"
tabindex="0"
> >
home <span
</span> class="icon"
</i> data-icon-name="home"
<i >
class="Icon material interactive disabled focusable" home
data-testid="history-back" </span>
> </i>
<span <i
class="icon" class="Icon material interactive disabled focusable"
data-icon-name="arrow_back" data-testid="history-back"
> >
arrow_back <span
</span> class="icon"
</i> data-icon-name="arrow_back"
<i >
class="Icon material interactive disabled focusable" arrow_back
data-testid="history-forward" </span>
> </i>
<span <i
class="icon" class="Icon material interactive disabled focusable"
data-icon-name="arrow_forward" data-testid="history-forward"
> >
arrow_forward <span
</span> class="icon"
</i> data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="items"
/>
</div> </div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
>
<div
data-testid="welcome-banner-container"
style="width: 320px;"
>
<i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
rel="noreferrer"
target="_blank"
>
Lens Community slack channel
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div>
</div>
</main>
<div <div
class="items" class="HotbarMenu flex column"
/>
</div>
<div
class="StatusBar"
>
<div
class="leftSide"
data-testid="status-bar-left"
> >
<div <div
class="item" class="HotbarItems flex column gaps"
> />
<div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
<div <div
class="item" class="HotbarSelector"
> >
<div <i
data-testid="some-testId" class="Icon Icon previous material interactive focusable"
tabindex="0"
> >
left1 <span
</div> class="icon"
</div> data-icon-name="arrow_left"
<div >
class="item" arrow_left
> </span>
</i>
<div <div
data-testid="some-testId" class="HotbarIndex"
> >
left2 <div
class="badge Badge small clickable"
id="hotbarIndex"
>
0
</div>
</div> </div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div> </div>
</div> </div>
<div <div
class="rightSide" class="StatusBar"
data-testid="status-bar-right"
> >
<div <div
class="item" class="leftSide"
data-testid="status-bar-left"
> >
<div <div
data-testid="some-testId" class="item"
> >
right3 <div
data-testid="app-update-not-available"
>
No new updates available
</div>
</div>
<div
class="item"
>
<div
data-testid="some-testId"
>
left1
</div>
</div>
<div
class="item"
>
<div
data-testid="some-testId"
>
left2
</div>
</div> </div>
</div> </div>
<div <div
class="item" class="rightSide"
data-testid="status-bar-right"
> >
<span <div
data-testid="some-testId" class="item"
> >
right2 <div
</span> data-testid="some-testId"
</div> >
<div right3
class="item" </div>
> </div>
<span <div
data-testid="some-testId" class="item"
> >
right1 <span
</span> data-testid="some-testId"
>
right2
</span>
</div>
<div
class="item"
>
<span
data-testid="some-testId"
>
right1
</span>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -21,25 +21,41 @@ describe("welcome - navigation using application menu", () => {
expect(rendered.container).toMatchSnapshot(); expect(rendered.container).toMatchSnapshot();
}); });
it("does not show welcome page yet", () => { it("shows welcome page being front page", () => {
const actual = rendered.queryByTestId("welcome-page"); const actual = rendered.getByTestId("welcome-page");
expect(actual).toBeNull(); expect(actual).not.toBeNull();
}); });
describe("when navigating to welcome using application menu", () => { describe("when navigated somewhere else", () => {
beforeEach(() => { beforeEach(() => {
applicationBuilder.applicationMenu.click("help.welcome"); applicationBuilder.applicationMenu.click("root.preferences");
}); });
it("renders", () => { it("renders", () => {
expect(rendered.container).toMatchSnapshot(); expect(rendered.baseElement).toMatchSnapshot();
}); });
it("shows welcome page", () => { it("does not show welcome page", () => {
const actual = rendered.getByTestId("welcome-page"); const actual = rendered.queryByTestId("welcome-page");
expect(actual).not.toBeNull(); expect(actual).toBeNull();
});
describe("when navigated to welcome using application menu", () => {
beforeEach(() => {
applicationBuilder.applicationMenu.click("help.welcome");
});
it("renders", () => {
expect(rendered.container).toMatchSnapshot();
});
it("shows welcome page", () => {
const actual = rendered.getByTestId("welcome-page");
expect(actual).not.toBeNull();
});
}); });
}); });
}); });

View File

@ -4,7 +4,7 @@
*/ */
import glob from "glob"; import glob from "glob";
import { kebabCase, memoize, noop } from "lodash/fp"; import { kebabCase, memoize, noop, chunk } from "lodash/fp";
import type { DiContainer, Injectable } from "@ogre-tools/injectable"; import type { DiContainer, Injectable } from "@ogre-tools/injectable";
import { createContainer } from "@ogre-tools/injectable"; import { createContainer } from "@ogre-tools/injectable";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
@ -99,6 +99,7 @@ import updateHelmReleaseInjectable from "./helm/helm-service/update-helm-release
import waitUntilBundledExtensionsAreLoadedInjectable from "./start-main-application/lens-window/application-window/wait-until-bundled-extensions-are-loaded.injectable"; import waitUntilBundledExtensionsAreLoadedInjectable from "./start-main-application/lens-window/application-window/wait-until-bundled-extensions-are-loaded.injectable";
import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
import electronInjectable from "./utils/resolve-system-proxy/electron.injectable"; import electronInjectable from "./utils/resolve-system-proxy/electron.injectable";
import type { HotbarStore } from "../common/hotbars/store";
export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) {
const { const {
@ -111,15 +112,13 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
setLegacyGlobalDiForExtensionApi(di, Environments.main); setLegacyGlobalDiForExtensionApi(di, Environments.main);
for (const filePath of getInjectableFilePaths()) { const filePaths = getInjectableFilePaths();
// eslint-disable-next-line @typescript-eslint/no-var-requires
const injectableInstance = require(filePath).default;
di.register({ const injectables = filePaths.map(filePath => require(filePath).default);
...injectableInstance,
aliases: [injectableInstance, ...(injectableInstance.aliases || [])], chunk(100)(injectables).forEach(chunkInjectables => {
}); di.register(...chunkInjectables);
} });
di.preventSideEffects(); di.preventSideEffects();
@ -127,7 +126,13 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
di.override(electronInjectable, () => ({})); di.override(electronInjectable, () => ({}));
di.override(waitUntilBundledExtensionsAreLoadedInjectable, () => async () => {}); di.override(waitUntilBundledExtensionsAreLoadedInjectable, () => async () => {});
di.override(getRandomIdInjectable, () => () => "some-irrelevant-random-id"); di.override(getRandomIdInjectable, () => () => "some-irrelevant-random-id");
di.override(hotbarStoreInjectable, () => ({ load: () => {} }));
di.override(hotbarStoreInjectable, () => ({
load: () => {},
getActive: () => ({ name: "some-hotbar", items: [] }),
getDisplayIndex: () => "0",
}) as unknown as HotbarStore);
di.override(userStoreInjectable, () => ({ startMainReactions: () => {}, extensionRegistryUrl: { customUrl: "some-custom-url" }}) as UserStore); di.override(userStoreInjectable, () => ({ startMainReactions: () => {}, extensionRegistryUrl: { customUrl: "some-custom-url" }}) as UserStore);
di.override(extensionsStoreInjectable, () => ({ isEnabled: (opts) => (void opts, false) }) as ExtensionsStore); di.override(extensionsStoreInjectable, () => ({ isEnabled: (opts) => (void opts, false) }) as ExtensionsStore);
di.override(clusterStoreInjectable, () => ({ provideInitialFromMain: () => {}, getById: (id) => (void id, {}) as Cluster }) as ClusterStore); di.override(clusterStoreInjectable, () => ({ provideInitialFromMain: () => {}, getById: (id) => (void id, {}) as Cluster }) as ClusterStore);

View File

@ -0,0 +1,24 @@
/**
* 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 { CronJobTriggerDialog } from "./cronjob-trigger-dialog";
import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const cronJobTriggerDialogClusterFrameChildComponentInjectable = getInjectable({
id: "cron-job-trigger-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "cron-job-trigger-dialog",
shouldRender: computed(() => true),
Component: CronJobTriggerDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default cronJobTriggerDialogClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { DeploymentScaleDialog } from "./dialog";
import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const deploymentScaleDialogClusterFrameChildComponentInjectable = getInjectable({
id: "deployment-scale-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "deployment-scale-dialog",
shouldRender: computed(() => true),
Component: DeploymentScaleDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default deploymentScaleDialogClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token";
import { ReplicaSetScaleDialog } from "./dialog";
const replicasetScaleDialogClusterFrameChildComponentInjectable = getInjectable({
id: "replicaset-scale-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "replicaset-scale-dialog",
shouldRender: computed(() => true),
Component: ReplicaSetScaleDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default replicasetScaleDialogClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { StatefulSetScaleDialog } from "./dialog";
import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const statefulsetScaleDialogClusterFrameChildComponentInjectable = getInjectable({
id: "statefulset-scale-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "statefulset-scale-dialog",
shouldRender: computed(() => true),
Component: StatefulSetScaleDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default statefulsetScaleDialogClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { getInjectable } from "@ogre-tools/injectable";
import { rootFrameChildComponentInjectionToken } from "../../frames/root-frame/root-frame-child-component-injection-token";
import { ClusterManager } from "./cluster-manager";
import { computed } from "mobx";
import { ErrorBoundary } from "../error-boundary";
const clusterManagerRootFrameChildComponentInjectable = getInjectable({
id: "cluster-manager-root-frame-child-component",
instantiate: () => ({
id: "cluster-manager",
shouldRender: computed(() => true),
Component: () => (
<ErrorBoundary>
<ClusterManager />
</ErrorBoundary>
),
}),
injectionToken: rootFrameChildComponentInjectionToken,
});
export default clusterManagerRootFrameChildComponentInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { CommandContainer } from "./command-container";
import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const commandContainerClusterFrameChildComponentInjectable = getInjectable({
id: "command-container-cluster-frame-child-component",
instantiate: () => ({
id: "command-container",
shouldRender: computed(() => true),
Component: CommandContainer,
}),
causesSideEffects: true,
injectionToken: clusterFrameChildComponentInjectionToken,
});
export default commandContainerClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { rootFrameChildComponentInjectionToken } from "../../frames/root-frame/root-frame-child-component-injection-token";
import { computed } from "mobx";
import { CommandContainer } from "./command-container";
const commandContainerRootFrameChildComponentInjectable = getInjectable({
id: "command-container-root-frame-child-component",
instantiate: () => ({
id: "command-container",
shouldRender: computed(() => true),
Component: CommandContainer,
}),
causesSideEffects: true,
injectionToken: rootFrameChildComponentInjectionToken,
});
export default commandContainerRootFrameChildComponentInjectable;

View File

@ -0,0 +1,22 @@
/**
* 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 { ConfirmDialog } from "./confirm-dialog";
import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const confirmDialogClusterFrameChildComponentInjectable = getInjectable({
id: "confirm-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "confirm-dialog",
shouldRender: computed(() => true),
Component: ConfirmDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
});
export default confirmDialogClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,22 @@
/**
* 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 { rootFrameChildComponentInjectionToken } from "../../frames/root-frame/root-frame-child-component-injection-token";
import { computed } from "mobx";
import { ConfirmDialog } from "./confirm-dialog";
const confirmDialogRootFrameChildComponentInjectable = getInjectable({
id: "confirm-dialog-root-frame-child-component",
instantiate: () => ({
id: "confirm-dialog",
shouldRender: computed(() => true),
Component: ConfirmDialog,
}),
injectionToken: rootFrameChildComponentInjectionToken,
});
export default confirmDialogRootFrameChildComponentInjectable;

View File

@ -7,9 +7,7 @@ import { KubeConfig } from "@kubernetes/client-node";
import { fireEvent } from "@testing-library/react"; import { fireEvent } from "@testing-library/react";
import type { RenderResult } from "@testing-library/react"; import type { RenderResult } from "@testing-library/react";
import mockFs from "mock-fs"; import mockFs from "mock-fs";
import React from "react";
import * as selectEvent from "react-select-event"; import * as selectEvent from "react-select-event";
import { DeleteClusterDialog } from "../view";
import type { CreateCluster } from "../../../../common/cluster/create-cluster-injection-token"; import type { CreateCluster } from "../../../../common/cluster/create-cluster-injection-token";
import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token"; import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token";
import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable"; import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable";
@ -19,12 +17,6 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre
import createKubeconfigManagerInjectable from "../../../../main/kubeconfig-manager/create-kubeconfig-manager.injectable"; import createKubeconfigManagerInjectable from "../../../../main/kubeconfig-manager/create-kubeconfig-manager.injectable";
import type { ApplicationBuilder } from "../../test-utils/get-application-builder"; import type { ApplicationBuilder } from "../../test-utils/get-application-builder";
import { getApplicationBuilder } from "../../test-utils/get-application-builder"; import { getApplicationBuilder } from "../../test-utils/get-application-builder";
import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token";
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { routeSpecificComponentInjectionToken } from "../../../routes/route-specific-component-injection-token";
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
import normalizedPlatformInjectable from "../../../../common/vars/normalized-platform.injectable"; import normalizedPlatformInjectable from "../../../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../../../../main/kubectl/binary-name.injectable"; import kubectlBinaryNameInjectable from "../../../../main/kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../../../../main/kubectl/normalized-arch.injectable"; import kubectlDownloadingNormalizedArchInjectable from "../../../../main/kubectl/normalized-arch.injectable";
@ -111,22 +103,9 @@ describe("<DeleteClusterDialog />", () => {
mainDi.override(kubectlBinaryNameInjectable, () => "kubectl"); mainDi.override(kubectlBinaryNameInjectable, () => "kubectl");
mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
mainDi.override(normalizedPlatformInjectable, () => "darwin"); mainDi.override(normalizedPlatformInjectable, () => "darwin");
rendererDi.override(hotbarStoreInjectable, () => ({}));
rendererDi.override(storesAndApisCanBeCreatedInjectable, () => true); rendererDi.override(storesAndApisCanBeCreatedInjectable, () => true);
}); });
const { rendererDi } = applicationBuilder.dis;
rendererDi.register(testRouteInjectable, testRouteComponentInjectable);
applicationBuilder.beforeRender(({ rendererDi }) => {
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
const testRoute = rendererDi.inject(testRouteInjectable);
navigateToRoute(testRoute);
});
mockFs(); mockFs();
applicationBuilder.beforeRender(({ rendererDi }) => { applicationBuilder.beforeRender(({ rendererDi }) => {
@ -321,26 +300,3 @@ describe("<DeleteClusterDialog />", () => {
}); });
}); });
}); });
const testRouteInjectable = getInjectable({
id: "some-test-route",
instantiate: () => ({
path: "/some-test-path",
clusterFrame: false,
isEnabled: computed(() => true),
}),
injectionToken: frontEndRouteInjectionToken,
});
const testRouteComponentInjectable = getInjectable({
id: "some-test-component",
instantiate: (di) => ({
route: di.inject(testRouteInjectable),
Component: () => <DeleteClusterDialog />,
}),
injectionToken: routeSpecificComponentInjectionToken,
});

View File

@ -0,0 +1,24 @@
/**
* 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 { DeleteClusterDialog } from "./view";
import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const deleteClusterDialogClusterFrameChildComponentInjectable = getInjectable({
id: "delete-cluster-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "delete-cluster-dialog",
shouldRender: computed(() => true),
Component: DeleteClusterDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default deleteClusterDialogClusterFrameChildComponentInjectable;

View File

@ -14,10 +14,10 @@ import { Tab } from "../tabs";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Menu, MenuItem } from "../menu"; import { Menu, MenuItem } from "../menu";
import { observable } from "mobx"; import { observable } from "mobx";
import { isMac } from "../../../common/vars";
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 { Tooltip, TooltipPosition } from "../tooltip"; import { Tooltip, TooltipPosition } from "../tooltip";
import isMacInjectable from "../../../common/vars/is-mac.injectable";
export interface DockTabProps extends TabProps<DockTabModel> { export interface DockTabProps extends TabProps<DockTabModel> {
moreActions?: React.ReactNode; moreActions?: React.ReactNode;
@ -25,6 +25,7 @@ export interface DockTabProps extends TabProps<DockTabModel> {
interface Dependencies { interface Dependencies {
dockStore: DockStore; dockStore: DockStore;
isMac: boolean;
} }
@observer @observer
@ -73,7 +74,7 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
} }
render() { render() {
const { className, moreActions, dockStore, active, ...tabProps } = this.props; const { className, moreActions, dockStore, active, isMac, ...tabProps } = this.props;
if (!tabProps.value) { if (!tabProps.value) {
return; return;
@ -100,7 +101,7 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
<Icon <Icon
small small
material="close" material="close"
tooltip={`Close ${isMac ? "⌘+W" : "Ctrl+W"}`} tooltip={`Close ${this.props.isMac ? "⌘+W" : "Ctrl+W"}`}
onClick={close} onClick={close}
/> />
</div> </div>
@ -124,6 +125,7 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
export const DockTab = withInjectables<Dependencies, DockTabProps>(NonInjectedDockTab, { export const DockTab = withInjectables<Dependencies, DockTabProps>(NonInjectedDockTab, {
getProps: (di, props) => ({ getProps: (di, props) => ({
dockStore: di.inject(dockStoreInjectable), dockStore: di.inject(dockStoreInjectable),
isMac: di.inject(isMacInjectable),
...props, ...props,
}), }),
}); });

View File

@ -184,7 +184,9 @@ class NonInjectedHotbarMenu extends React.Component<Dependencies & HotbarMenuPro
return ( return (
<div className={cssNames("HotbarMenu flex column", { draggingOver: this.draggingOver }, className)}> <div className={cssNames("HotbarMenu flex column", { draggingOver: this.draggingOver }, className)}>
<div className="HotbarItems flex column gaps"> <div className="HotbarItems flex column gaps">
<DragDropContext onDragStart={() => this.onDragStart()} onDragEnd={(result) => this.onDragEnd(result)}> <DragDropContext
onDragStart={() => this.onDragStart()}
onDragEnd={(result) => this.onDragEnd(result)}>
{this.renderGrid()} {this.renderGrid()}
</DragDropContext> </DragDropContext>
</div> </div>

View File

@ -0,0 +1,24 @@
/**
* 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 { KubeObjectDetails } from "./kube-object-details";
import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const kubeObjectDetailsClusterFrameChildComponentInjectable = getInjectable({
id: "kube-object-details-cluster-frame-child-component",
instantiate: () => ({
id: "kube-object-details",
shouldRender: computed(() => true),
Component: KubeObjectDetails,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default kubeObjectDetailsClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
import { KubeConfigDialog } from "./kubeconfig-dialog";
const kubeconfigDialogClusterFrameChildComponentInjectable = getInjectable({
id: "kubeconfig-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "kubeconfig-dialog",
shouldRender: computed(() => true),
Component: KubeConfigDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default kubeconfigDialogClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,22 @@
/**
* 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 { Notifications } from "./notifications";
import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token";
const notificationsClusterFrameChildComponentInjectable = getInjectable({
id: "notifications-cluster-frame-child-component",
instantiate: () => ({
id: "notifications",
shouldRender: computed(() => true),
Component: Notifications,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
});
export default notificationsClusterFrameChildComponentInjectable;

View File

@ -0,0 +1,22 @@
/**
* 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 { rootFrameChildComponentInjectionToken } from "../../frames/root-frame/root-frame-child-component-injection-token";
import { computed } from "mobx";
import { Notifications } from "./notifications";
const notificationsRootFrameChildComponentInjectable = getInjectable({
id: "notifications-root-frame-child-component",
instantiate: () => ({
id: "notifications",
shouldRender: computed(() => true),
Component: Notifications,
}),
injectionToken: rootFrameChildComponentInjectionToken,
});
export default notificationsRootFrameChildComponentInjectable;

View File

@ -7,21 +7,17 @@ import rendererExtensionsInjectable from "../../../extensions/renderer-extension
import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable"; import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable";
import type { IObservableArray, ObservableSet } from "mobx"; import type { IObservableArray, ObservableSet } from "mobx";
import { computed, observable, runInAction } from "mobx"; import { computed, observable, runInAction } from "mobx";
import { renderFor } from "./renderFor";
import React from "react"; import React from "react";
import { Router } from "react-router"; import { Router } from "react-router";
import { Observer } from "mobx-react";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import allowedResourcesInjectable from "../../cluster-frame-context/allowed-resources.injectable"; import allowedResourcesInjectable from "../../cluster-frame-context/allowed-resources.injectable";
import type { RenderResult } from "@testing-library/react"; import type { RenderResult } from "@testing-library/react";
import { getByText, fireEvent } from "@testing-library/react"; import { getByText, fireEvent } from "@testing-library/react";
import type { KubeResource } from "../../../common/rbac"; import type { KubeResource } from "../../../common/rbac";
import { Sidebar } from "../layout/sidebar";
import type { DiContainer } from "@ogre-tools/injectable"; import type { DiContainer } from "@ogre-tools/injectable";
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable"; import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
import type { ClusterStore } from "../../../common/cluster-store/cluster-store"; import type { ClusterStore } from "../../../common/cluster-store/cluster-store";
import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable";
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { flatMap, compact, join, get, filter, map, matches, last } from "lodash/fp"; import { flatMap, compact, join, get, filter, map, matches, last } from "lodash/fp";
import preferenceNavigationItemsInjectable from "../+preferences/preferences-navigation/preference-navigation-items.injectable"; import preferenceNavigationItemsInjectable from "../+preferences/preferences-navigation/preference-navigation-items.injectable";
@ -44,8 +40,6 @@ import historyInjectable from "../../navigation/history.injectable";
import type { MinimalTrayMenuItem } from "../../../main/tray/electron-tray/electron-tray.injectable"; import type { MinimalTrayMenuItem } from "../../../main/tray/electron-tray/electron-tray.injectable";
import electronTrayInjectable from "../../../main/tray/electron-tray/electron-tray.injectable"; import electronTrayInjectable from "../../../main/tray/electron-tray/electron-tray.injectable";
import applicationWindowInjectable from "../../../main/start-main-application/lens-window/application-window/application-window.injectable"; import applicationWindowInjectable from "../../../main/start-main-application/lens-window/application-window/application-window.injectable";
import { Notifications } from "../notifications/notifications";
import broadcastThatRootFrameIsRenderedInjectable from "../../frames/root-frame/broadcast-that-root-frame-is-rendered.injectable";
import { getDiForUnitTesting as getRendererDi } from "../../getDiForUnitTesting"; import { getDiForUnitTesting as getRendererDi } from "../../getDiForUnitTesting";
import { getDiForUnitTesting as getMainDi } from "../../../main/getDiForUnitTesting"; import { getDiForUnitTesting as getMainDi } from "../../../main/getDiForUnitTesting";
import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels"; import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels";
@ -53,7 +47,6 @@ import trayIconPathsInjectable from "../../../main/tray/tray-icon-path.injectabl
import assert from "assert"; import assert from "assert";
import { openMenu } from "react-select-event"; import { openMenu } from "react-select-event";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { StatusBar } from "../status-bar/status-bar";
import lensProxyPortInjectable from "../../../main/lens-proxy/lens-proxy-port.injectable"; import lensProxyPortInjectable from "../../../main/lens-proxy/lens-proxy-port.injectable";
import type { Route } from "../../../common/front-end-routing/front-end-route-injection-token"; import type { Route } from "../../../common/front-end-routing/front-end-route-injection-token";
import type { NavigateToRouteOptions } from "../../../common/front-end-routing/navigate-to-route-injection-token"; import type { NavigateToRouteOptions } from "../../../common/front-end-routing/navigate-to-route-injection-token";
@ -62,7 +55,10 @@ import type { LensMainExtension } from "../../../extensions/lens-main-extension"
import type { LensExtension } from "../../../extensions/lens-extension"; import type { LensExtension } from "../../../extensions/lens-extension";
import extensionInjectable from "../../../extensions/extension-loader/extension/extension.injectable"; import extensionInjectable from "../../../extensions/extension-loader/extension/extension.injectable";
import { TopBar } from "../layout/top-bar/top-bar"; import { renderFor } from "./renderFor";
import { RootFrame } from "../../frames/root-frame/root-frame";
import { ClusterFrame } from "../../frames/cluster-frame/cluster-frame";
import hostedClusterIdInjectable from "../../cluster-frame-context/hosted-cluster-id.injectable";
type Callback = (dis: DiContainers) => void | Promise<void>; type Callback = (dis: DiContainers) => void | Promise<void>;
@ -125,10 +121,7 @@ interface DiContainers {
} }
interface Environment { interface Environment {
renderSidebar: () => React.ReactNode; RootComponent: React.ElementType;
renderTopBar: () => React.ReactNode;
renderStatusBar: () => React.ReactNode;
beforeRender: () => void;
onAllowKubeResource: () => void; onAllowKubeResource: () => void;
} }
@ -166,17 +159,7 @@ export const getApplicationBuilder = () => {
const environments = { const environments = {
application: { application: {
renderSidebar: () => null, RootComponent: RootFrame,
renderTopBar: () => <TopBar />,
renderStatusBar: () => <StatusBar />,
beforeRender: () => {
const nofifyThatRootFrameIsRendered = rendererDi.inject(broadcastThatRootFrameIsRenderedInjectable);
nofifyThatRootFrameIsRendered();
},
onAllowKubeResource: () => { onAllowKubeResource: () => {
throw new Error( throw new Error(
@ -186,10 +169,7 @@ export const getApplicationBuilder = () => {
} as Environment, } as Environment,
clusterFrame: { clusterFrame: {
renderSidebar: () => <Sidebar />, RootComponent: ClusterFrame,
renderStatusBar: () => null,
renderTopBar: () => null,
beforeRender: () => {},
onAllowKubeResource: () => {}, onAllowKubeResource: () => {},
} as Environment, } as Environment,
}; };
@ -401,6 +381,7 @@ export const getApplicationBuilder = () => {
const namespaceStoreStub = { const namespaceStoreStub = {
contextNamespaces: [], contextNamespaces: [],
items: [],
} as unknown as NamespaceStore; } as unknown as NamespaceStore;
const clusterFrameContextFake = new ClusterFrameContext( const clusterFrameContextFake = new ClusterFrameContext(
@ -413,6 +394,7 @@ export const getApplicationBuilder = () => {
rendererDi.override(namespaceStoreInjectable, () => namespaceStoreStub); rendererDi.override(namespaceStoreInjectable, () => namespaceStoreStub);
rendererDi.override(hostedClusterInjectable, () => clusterStub); rendererDi.override(hostedClusterInjectable, () => clusterStub);
rendererDi.override(hostedClusterIdInjectable, () => "irrelevant-hosted-cluster-id");
rendererDi.override(clusterFrameContextInjectable, () => clusterFrameContextFake); rendererDi.override(clusterFrameContextInjectable, () => clusterFrameContextFake);
// Todo: get rid of global state. // Todo: get rid of global state.
@ -474,35 +456,17 @@ export const getApplicationBuilder = () => {
await startFrame(); await startFrame();
const render = renderFor(rendererDi);
const history = rendererDi.inject(historyInjectable);
const currentRouteComponent = rendererDi.inject(currentRouteComponentInjectable);
for (const callback of beforeRenderCallbacks) { for (const callback of beforeRenderCallbacks) {
await callback(dis); await callback(dis);
} }
environment.beforeRender(); const history = rendererDi.inject(historyInjectable);
const render = renderFor(rendererDi);
rendered = render( rendered = render(
<Router history={history}> <Router history={history}>
{environment.renderSidebar()} <environment.RootComponent />
{environment.renderTopBar()}
{environment.renderStatusBar()}
<Observer>
{() => {
const Component = currentRouteComponent.get();
if (!Component) {
return null;
}
return <Component />;
}}
</Observer>
<Notifications />
</Router>, </Router>,
); );

View File

@ -2,6 +2,9 @@
exports[`<ClusterFrame /> given cluster with list nodes and namespaces permissions given no matching component given current url is starting url renders 1`] = ` exports[`<ClusterFrame /> given cluster with list nodes and namespaces permissions given no matching component given current url is starting url renders 1`] = `
<div> <div>
<div
class="Notifications flex column align-flex-end"
/>
<div <div
class="mainLayout" class="mainLayout"
style="--sidebar-width: 200px;" style="--sidebar-width: 200px;"
@ -467,14 +470,14 @@ exports[`<ClusterFrame /> given cluster with list nodes and namespaces permissio
</div> </div>
</div> </div>
</div> </div>
<div
class="Notifications flex column align-flex-end"
/>
</div> </div>
`; `;
exports[`<ClusterFrame /> given cluster with list nodes and namespaces permissions renders 1`] = ` exports[`<ClusterFrame /> given cluster with list nodes and namespaces permissions renders 1`] = `
<div> <div>
<div
class="Notifications flex column align-flex-end"
/>
<div <div
class="mainLayout" class="mainLayout"
style="--sidebar-width: 200px;" style="--sidebar-width: 200px;"
@ -947,14 +950,14 @@ exports[`<ClusterFrame /> given cluster with list nodes and namespaces permissio
</div> </div>
</div> </div>
</div> </div>
<div
class="Notifications flex column align-flex-end"
/>
</div> </div>
`; `;
exports[`<ClusterFrame /> given cluster without list nodes, but with namespaces permissions renders 1`] = ` exports[`<ClusterFrame /> given cluster without list nodes, but with namespaces permissions renders 1`] = `
<div> <div>
<div
class="Notifications flex column align-flex-end"
/>
<div <div
class="mainLayout" class="mainLayout"
style="--sidebar-width: 200px;" style="--sidebar-width: 200px;"
@ -1483,8 +1486,5 @@ exports[`<ClusterFrame /> given cluster without list nodes, but with namespaces
</div> </div>
</div> </div>
</div> </div>
<div
class="Notifications flex column align-flex-end"
/>
</div> </div>
`; `;

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx";
export interface ClusterFrameChildComponent {
id: string;
Component: React.ElementType;
shouldRender: IComputedValue<boolean>;
}
export const clusterFrameChildComponentInjectionToken = getInjectionToken<ClusterFrameChildComponent>({
id: "cluster-frame-child-component",
});

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { getInjectable } from "@ogre-tools/injectable";
import { clusterFrameChildComponentInjectionToken } from "./cluster-frame-child-component-injection-token";
import { MainLayout } from "../../components/layout/main-layout";
import { Sidebar } from "../../components/layout/sidebar";
import { Dock } from "../../components/dock";
import styles from "./cluster-frame.module.css";
import { computed } from "mobx";
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
import { Redirect } from "react-router";
import startUrlInjectable from "./start-url.injectable";
import currentPathInjectable from "../../routes/current-path.injectable";
import { observer } from "mobx-react";
const clusterFrameLayoutChildComponentInjectable = getInjectable({
id: "cluster-frame-layout-child-component",
instantiate: (di) => {
const currentRouteComponent = di.inject(currentRouteComponentInjectable);
const startUrl = di.inject(startUrlInjectable);
const currentPath = di.inject(currentPathInjectable);
return {
id: "cluster-frame-layout",
shouldRender: computed(() => true),
Component: observer(() => {
const Component = currentRouteComponent.get();
const starting = startUrl.get();
const current = currentPath.get();
return (
<MainLayout sidebar={<Sidebar />} footer={<Dock />}>
{Component ? (
<Component />
) : // NOTE: this check is to prevent an infinite loop
starting !== current ? (
<Redirect to={startUrl.get()} />
) : (
<div className={styles.centering}>
<div className="error">
An error has occured. No route can be found matching the
current route, which is also the starting route.
</div>
</div>
)}
</MainLayout>
);
}),
};
},
injectionToken: clusterFrameChildComponentInjectionToken,
});
export default clusterFrameLayoutChildComponentInjectable;

View File

@ -2,52 +2,30 @@
* 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 styles from "./cluster-frame.module.css";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import type { IComputedValue } from "mobx"; import { Observer, observer } from "mobx-react";
import { observer } from "mobx-react";
import { Redirect } from "react-router";
import { ConfirmDialog } from "../../components/confirm-dialog";
import { DeploymentScaleDialog } from "../../components/+workloads-deployments/scale/dialog";
import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog";
import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/scale/dialog";
import { ReplicaSetScaleDialog } from "../../components/+workloads-replicasets/scale-dialog/dialog";
import { CommandContainer } from "../../components/command-palette/command-container";
import { ErrorBoundary } from "../../components/error-boundary"; import { ErrorBoundary } from "../../components/error-boundary";
import { MainLayout } from "../../components/layout/main-layout";
import { Notifications } from "../../components/notifications";
import { KubeObjectDetails } from "../../components/kube-object-details";
import { KubeConfigDialog } from "../../components/kubeconfig-dialog";
import { Sidebar } from "../../components/layout/sidebar";
import { Dock } from "../../components/dock";
import { PortForwardDialog } from "../../port-forward";
import { DeleteClusterDialog } from "../../components/delete-cluster-dialog";
import type { NamespaceStore } from "../../components/+namespaces/store"; import type { NamespaceStore } from "../../components/+namespaces/store";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import namespaceStoreInjectable from "../../components/+namespaces/store.injectable"; import namespaceStoreInjectable from "../../components/+namespaces/store.injectable";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import { disposer } from "../../utils"; import { disposer } from "../../utils";
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
import startUrlInjectable from "./start-url.injectable";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import currentPathInjectable from "../../routes/current-path.injectable"; import type { ClusterFrameChildComponent } from "./cluster-frame-child-component-injection-token";
import { clusterFrameChildComponentInjectionToken } from "./cluster-frame-child-component-injection-token";
import watchHistoryStateInjectable from "../../remote-helpers/watch-history-state.injectable"; import watchHistoryStateInjectable from "../../remote-helpers/watch-history-state.injectable";
interface Dependencies { interface Dependencies {
namespaceStore: NamespaceStore; namespaceStore: NamespaceStore;
currentRouteComponent: IComputedValue<React.ElementType<{}> | undefined>;
startUrl: IComputedValue<string>;
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
currentPath: IComputedValue<string>; childComponents: ClusterFrameChildComponent[];
watchHistoryState: () => () => void; watchHistoryState: () => () => void;
} }
export const NonInjectedClusterFrame = observer(({ export const NonInjectedClusterFrame = observer(({
namespaceStore, namespaceStore,
currentRouteComponent,
startUrl,
subscribeStores, subscribeStores,
currentPath, childComponents,
watchHistoryState, watchHistoryState,
}: Dependencies) => { }: Dependencies) => {
useEffect(() => disposer( useEffect(() => disposer(
@ -57,43 +35,14 @@ export const NonInjectedClusterFrame = observer(({
watchHistoryState(), watchHistoryState(),
), []); ), []);
const Component = currentRouteComponent.get();
const starting = startUrl.get();
const current = currentPath.get();
return ( return (
<ErrorBoundary> <ErrorBoundary>
<MainLayout {childComponents
sidebar={<Sidebar />} .map((child) => (
footer={<Dock />} <Observer key={child.id}>
> {() => (child.shouldRender.get() ? <child.Component /> : null) }
{ </Observer>
Component ))}
? <Component />
// NOTE: this check is to prevent an infinite loop
: starting !== current
? <Redirect to={startUrl.get()} />
: (
<div className={styles.centering}>
<div className="error">
An error has occured. No route can be found matching the current route, which is also the starting route.
</div>
</div>
)
}
</MainLayout>
<Notifications />
<ConfirmDialog />
<KubeObjectDetails />
<KubeConfigDialog />
<DeploymentScaleDialog />
<StatefulSetScaleDialog />
<ReplicaSetScaleDialog />
<CronJobTriggerDialog />
<PortForwardDialog />
<DeleteClusterDialog />
<CommandContainer />
</ErrorBoundary> </ErrorBoundary>
); );
}); });
@ -102,9 +51,7 @@ export const ClusterFrame = withInjectables<Dependencies>(NonInjectedClusterFram
getProps: di => ({ getProps: di => ({
namespaceStore: di.inject(namespaceStoreInjectable), namespaceStore: di.inject(namespaceStoreInjectable),
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
startUrl: di.inject(startUrlInjectable), childComponents: di.injectMany(clusterFrameChildComponentInjectionToken),
currentRouteComponent: di.inject(currentRouteComponentInjectable),
currentPath: di.inject(currentPathInjectable),
watchHistoryState: di.inject(watchHistoryStateInjectable), watchHistoryState: di.inject(watchHistoryStateInjectable),
}), }),
}); });

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx";
export interface RootFrameChildComponent {
id: string;
Component: React.ElementType;
shouldRender: IComputedValue<boolean>;
}
export const rootFrameChildComponentInjectionToken = getInjectionToken<RootFrameChildComponent>({
id: "root-frame-child-component",
});

View File

@ -3,25 +3,18 @@
* 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 { injectSystemCAs } from "../../../common/system-ca";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { Observer } from "mobx-react";
import { ClusterManager } from "../../components/cluster-manager";
import { ErrorBoundary } from "../../components/error-boundary";
import { Notifications } from "../../components/notifications";
import { ConfirmDialog } from "../../components/confirm-dialog";
import { CommandContainer } from "../../components/command-palette/command-container";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import broadcastThatRootFrameIsRenderedInjectable from "./broadcast-that-root-frame-is-rendered.injectable"; import broadcastThatRootFrameIsRenderedInjectable from "./broadcast-that-root-frame-is-rendered.injectable";
import type { RootFrameChildComponent } from "./root-frame-child-component-injection-token";
// Todo: remove import-time side-effect. import { rootFrameChildComponentInjectionToken } from "./root-frame-child-component-injection-token";
injectSystemCAs();
interface Dependencies { interface Dependencies {
broadcastThatRootFrameIsRendered: () => void; broadcastThatRootFrameIsRendered: () => void;
childComponents: RootFrameChildComponent[];
} }
@observer
class NonInjectedRootFrame extends React.Component<Dependencies> { class NonInjectedRootFrame extends React.Component<Dependencies> {
static displayName = "RootFrame"; static displayName = "RootFrame";
@ -32,12 +25,12 @@ class NonInjectedRootFrame extends React.Component<Dependencies> {
render() { render() {
return ( return (
<> <>
<ErrorBoundary> {this.props.childComponents
<ClusterManager /> .map((child) => (
</ErrorBoundary> <Observer key={child.id}>
<Notifications /> {() => (child.shouldRender.get() ? <child.Component /> : null) }
<ConfirmDialog /> </Observer>
<CommandContainer /> ))}
</> </>
); );
} }
@ -49,6 +42,7 @@ export const RootFrame = withInjectables<Dependencies>(
{ {
getProps: (di, props) => ({ getProps: (di, props) => ({
broadcastThatRootFrameIsRendered: di.inject(broadcastThatRootFrameIsRenderedInjectable), broadcastThatRootFrameIsRendered: di.inject(broadcastThatRootFrameIsRenderedInjectable),
childComponents: di.injectMany(rootFrameChildComponentInjectionToken),
...props, ...props,
}), }),
}, },

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { injectSystemCAs } from "../../../common/system-ca";
import { getInjectable } from "@ogre-tools/injectable";
import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/before-frame-starts-injection-token";
const setupSystemCaInjectable = getInjectable({
id: "setup-system-ca",
instantiate: () => ({
run: async () => {
await injectSystemCAs();
},
}),
causesSideEffects: true,
injectionToken: beforeFrameStartsInjectionToken,
});
export default setupSystemCaInjectable;

View File

@ -4,10 +4,8 @@
*/ */
import glob from "glob"; import glob from "glob";
import { memoize, noop } from "lodash/fp"; import { memoize, noop, chunk } from "lodash/fp";
import type { import type { DiContainer, Injectable } from "@ogre-tools/injectable";
DiContainer,
Injectable } from "@ogre-tools/injectable";
import { import {
createContainer, createContainer,
} from "@ogre-tools/injectable"; } from "@ogre-tools/injectable";
@ -41,7 +39,7 @@ import apiManagerInjectable from "../common/k8s-api/api-manager/manager.injectab
import ipcRendererInjectable from "./utils/channel/ipc-renderer.injectable"; import ipcRendererInjectable from "./utils/channel/ipc-renderer.injectable";
import type { IpcRenderer } from "electron"; import type { IpcRenderer } from "electron";
import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable"; import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable";
import { observable } from "mobx"; import { observable, computed } from "mobx";
import defaultShellInjectable from "./components/+preferences/default-shell.injectable"; import defaultShellInjectable from "./components/+preferences/default-shell.injectable";
import appVersionInjectable from "../common/get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../common/get-configuration-file-model/app-version/app-version.injectable";
import provideInitialValuesForSyncBoxesInjectable from "./utils/sync-box/provide-initial-values-for-sync-boxes.injectable"; import provideInitialValuesForSyncBoxesInjectable from "./utils/sync-box/provide-initial-values-for-sync-boxes.injectable";
@ -59,6 +57,18 @@ import goForwardInjectable from "./components/layout/top-bar/go-forward.injectab
import closeWindowInjectable from "./components/layout/top-bar/close-window.injectable"; import closeWindowInjectable from "./components/layout/top-bar/close-window.injectable";
import maximizeWindowInjectable from "./components/layout/top-bar/maximize-window.injectable"; import maximizeWindowInjectable from "./components/layout/top-bar/maximize-window.injectable";
import toggleMaximizeWindowInjectable from "./components/layout/top-bar/toggle-maximize-window.injectable"; import toggleMaximizeWindowInjectable from "./components/layout/top-bar/toggle-maximize-window.injectable";
import commandContainerRootFrameChildComponentInjectable from "./components/command-palette/command-container-root-frame-child-component.injectable";
import type { HotbarStore } from "../common/hotbars/store";
import commandContainerClusterFrameChildComponentInjectable from "./components/command-palette/command-container-cluster-frame-child-component.injectable";
import cronJobTriggerDialogClusterFrameChildComponentInjectable from "./components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable";
import deploymentScaleDialogClusterFrameChildComponentInjectable from "./components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable";
import replicasetScaleDialogClusterFrameChildComponentInjectable from "./components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable";
import statefulsetScaleDialogClusterFrameChildComponentInjectable from "./components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable";
import deleteClusterDialogClusterFrameChildComponentInjectable from "./components/delete-cluster-dialog/delete-cluster-dialog-cluster-frame-child-component.injectable";
import kubeObjectDetailsClusterFrameChildComponentInjectable from "./components/kube-object-details/kube-object-details-cluster-frame-child-component.injectable";
import kubeconfigDialogClusterFrameChildComponentInjectable from "./components/kubeconfig-dialog/kubeconfig-dialog-cluster-frame-child-component.injectable";
import portForwardDialogClusterFrameChildComponentInjectable from "./port-forward/port-forward-dialog-cluster-frame-child-component.injectable";
import setupSystemCaInjectable from "./frames/root-frame/setup-system-ca.injectable";
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => { export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
const { const {
@ -71,14 +81,13 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
setLegacyGlobalDiForExtensionApi(di, Environments.renderer); setLegacyGlobalDiForExtensionApi(di, Environments.renderer);
for (const filePath of getInjectableFilePaths()) { const filePaths = getInjectableFilePaths();
const injectableInstance = require(filePath).default;
di.register({ const injectables = filePaths.map(filePath => require(filePath).default);
...injectableInstance,
aliases: [injectableInstance, ...(injectableInstance.aliases || [])], chunk(100)(injectables).forEach(chunkInjectables => {
}); di.register(...chunkInjectables);
} });
di.preventSideEffects(); di.preventSideEffects();
@ -103,8 +112,35 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
di.override(lensResourcesDirInjectable, () => "/irrelevant"); di.override(lensResourcesDirInjectable, () => "/irrelevant");
di.override(watchHistoryStateInjectable, () => () => () => {}); // TODO: Remove side-effects and shared global state
di.override(commandContainerRootFrameChildComponentInjectable, () => ({
Component: () => null,
id: "command-container",
shouldRender: computed(() => false),
}));
// TODO: Remove side-effects and shared global state
const clusterFrameChildComponentInjectables: Injectable<any, any, any>[] = [
commandContainerClusterFrameChildComponentInjectable,
cronJobTriggerDialogClusterFrameChildComponentInjectable,
deploymentScaleDialogClusterFrameChildComponentInjectable,
replicasetScaleDialogClusterFrameChildComponentInjectable,
statefulsetScaleDialogClusterFrameChildComponentInjectable,
deleteClusterDialogClusterFrameChildComponentInjectable,
kubeObjectDetailsClusterFrameChildComponentInjectable,
kubeconfigDialogClusterFrameChildComponentInjectable,
portForwardDialogClusterFrameChildComponentInjectable,
];
clusterFrameChildComponentInjectables.forEach((injectable) => {
di.override(injectable, () => ({
Component: () => null,
id: injectable.id,
shouldRender: computed(() => false),
}));
});
di.override(watchHistoryStateInjectable, () => () => () => {});
di.override(openAppContextMenuInjectable, () => () => {}); di.override(openAppContextMenuInjectable, () => () => {});
di.override(goBackInjectable, () => () => {}); di.override(goBackInjectable, () => () => {});
di.override(goForwardInjectable, () => () => {}); di.override(goForwardInjectable, () => () => {});
@ -126,13 +162,17 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
// eslint-disable-next-line unused-imports/no-unused-vars-ts // eslint-disable-next-line unused-imports/no-unused-vars-ts
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore); di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
di.override(hotbarStoreInjectable, () => ({})); di.override(hotbarStoreInjectable, () => ({
getActive: () => ({ name: "some-hotbar", items: [] }),
getDisplayIndex: () => "0",
}) as unknown as HotbarStore);
di.override(fileSystemProvisionerStoreInjectable, () => ({}) as FileSystemProvisionerStore); di.override(fileSystemProvisionerStoreInjectable, () => ({}) as FileSystemProvisionerStore);
// eslint-disable-next-line unused-imports/no-unused-vars-ts // eslint-disable-next-line unused-imports/no-unused-vars-ts
di.override(clusterStoreInjectable, () => ({ getById: (id): Cluster => ({}) as Cluster }) as ClusterStore); di.override(clusterStoreInjectable, () => ({ getById: (id): Cluster => ({}) as Cluster }) as ClusterStore);
di.override(setupSystemCaInjectable, () => ({ run: () => {} }));
di.override(setupOnApiErrorListenersInjectable, () => ({ run: () => {} })); di.override(setupOnApiErrorListenersInjectable, () => ({ run: () => {} }));
di.override(provideInitialValuesForSyncBoxesInjectable, () => ({ run: () => {} })); di.override(provideInitialValuesForSyncBoxesInjectable, () => ({ run: () => {} }));

View File

@ -0,0 +1,24 @@
/**
* 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 { PortForwardDialog } from "./port-forward-dialog";
import { clusterFrameChildComponentInjectionToken } from "../frames/cluster-frame/cluster-frame-child-component-injection-token";
const portForwardDialogClusterFrameChildComponentInjectable = getInjectable({
id: "port-forward-dialog-cluster-frame-child-component",
instantiate: () => ({
id: "port-forward-dialog",
shouldRender: computed(() => true),
Component: PortForwardDialog,
}),
injectionToken: clusterFrameChildComponentInjectionToken,
causesSideEffects: true,
});
export default portForwardDialogClusterFrameChildComponentInjectable;

View File

@ -1211,46 +1211,46 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@ogre-tools/fp@9.0.0", "@ogre-tools/fp@^9.0.0": "@ogre-tools/fp@9.0.1", "@ogre-tools/fp@^9.0.1":
version "9.0.0" version "9.0.1"
resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-9.0.0.tgz#926cd4f13b52961156161feeeafddf22a0ad39c0" resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-9.0.1.tgz#22c23bd209f6f56bf66a5ff4514930334a3a4782"
integrity sha512-kMUgzhdjHuph0UWteOfyXNGBavZJX23NOA5su6fx9NdTzWhl9yB5Uf6Q//nOvL9COftjZDwnAgIaDU4MPdjyqA== integrity sha512-7kORUcvR9DZz5/2qDSSKExGeisXzfmOafP+Z6l8C+WQXEmEQm7CmFMyBEyMkei0eNFzNghlKYgRFpsvBdU3KAg==
dependencies: dependencies:
lodash "^4.17.21" lodash "^4.17.21"
"@ogre-tools/injectable-extension-for-auto-registration@9.0.0": "@ogre-tools/injectable-extension-for-auto-registration@9.0.1":
version "9.0.0" version "9.0.1"
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-9.0.0.tgz#69463737ef4f7777db4703964b8a72a5fb82d6b3" resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-9.0.1.tgz#e00dff5bd833068faa58d8fa514b218515c55775"
integrity sha512-+3I9Z0GfA04zZoj7Nw5WhJLDFLJgr5xv8Kp1zPDuT9/OvE9EA6hzAqakMDLbvn1zZOJjkJCGk44x6UjSQJp/9w== integrity sha512-reyhW2wWPrNnGXo88QKWUsLyhjF195i0/iXfdAoR8/4lRxWmzka58bc8r6jFvTBBJ6eXaGidhUa5DqZe+Oia2Q==
dependencies: dependencies:
"@ogre-tools/fp" "^9.0.0" "@ogre-tools/fp" "^9.0.1"
"@ogre-tools/injectable" "^9.0.0" "@ogre-tools/injectable" "^9.0.1"
lodash "^4.17.21" lodash "^4.17.21"
"@ogre-tools/injectable-extension-for-mobx@9.0.0": "@ogre-tools/injectable-extension-for-mobx@9.0.1":
version "9.0.0" version "9.0.1"
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-9.0.0.tgz#ed14df39b266e521272977821d3e05bcbd647577" resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-9.0.1.tgz#3d9e6e119b7db5a4e4309e89ba6b301635c8da90"
integrity sha512-9Hrtr7AdibcD+Fqn2qNsjiOUakAACO55TB1IqNsOJMMuqQHVB5SFZTHBzdVRqqqY2MwQvWYvd4xfy+beItD/xw== integrity sha512-wNHIG73NTGfmRb0b2nNhJD9ek4RcyBp/189KfrXhTt6/5KwMEuOeJjAbh8xwbhpYJnWFcMzWKLTqePozc+Ntww==
dependencies: dependencies:
"@ogre-tools/fp" "^9.0.0" "@ogre-tools/fp" "^9.0.1"
"@ogre-tools/injectable" "^9.0.0" "@ogre-tools/injectable" "^9.0.1"
lodash "^4.17.21" lodash "^4.17.21"
"@ogre-tools/injectable-react@9.0.0": "@ogre-tools/injectable-react@9.0.1":
version "9.0.0" version "9.0.1"
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-9.0.0.tgz#c5c510e893a5c1d7994d8709f70606967cabdec2" resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-9.0.1.tgz#d72a155b914873f1a5e35ee167aa14dd7c0e7454"
integrity sha512-vGQrwkcWibRUWFPbu392riBYY4dXK051FxwyMsDYNRqmvaLo8HuumwjzS1DWS7db/P9Li+Kc+Ms670xIZepcpA== integrity sha512-GjkhhONy4hamw5Mx79ZxhL3Pbgnfnf1mrkixDkqlXFzhdPBHDtCYIZu9Y6NjSRLgEwXTrAPYv9lDqREnRbUwLA==
dependencies: dependencies:
"@ogre-tools/fp" "^9.0.0" "@ogre-tools/fp" "^9.0.1"
"@ogre-tools/injectable" "^9.0.0" "@ogre-tools/injectable" "^9.0.1"
lodash "^4.17.21" lodash "^4.17.21"
"@ogre-tools/injectable@9.0.0", "@ogre-tools/injectable@^9.0.0": "@ogre-tools/injectable@9.0.1", "@ogre-tools/injectable@^9.0.1":
version "9.0.0" version "9.0.1"
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-9.0.0.tgz#0819bc7b5fbae0a467f3250b10b4adb003268067" resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-9.0.1.tgz#611a2dd8468bfe3610f005178837d76071a44a70"
integrity sha512-z9X86Q9AEkkilLu9V33j/aXv/IUoG944AdfN6WX2zZgJqRNjESN9spoOMqdKqib6JmEjCRxpMvaMwHLQSh14fg== integrity sha512-orcERUOeUEP2oBAcr9rKYbik+r/xJEIb5uedPMMqPzPJnNwZ9JA/1rDEJcx1/I9aHgjUQpeFANjYI4eYDcwEYQ==
dependencies: dependencies:
"@ogre-tools/fp" "^9.0.0" "@ogre-tools/fp" "^9.0.1"
lodash "^4.17.21" lodash "^4.17.21"
"@panva/asn1.js@^1.0.0": "@panva/asn1.js@^1.0.0":