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

Fix crash in HelmChartDetails

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-06-17 10:14:52 -04:00
parent fc770b4b44
commit a623c59b7e
13 changed files with 609 additions and 86 deletions

View File

@ -315,6 +315,8 @@ export class HelmChart implements HelmChartData {
autoBind(this); autoBind(this);
} }
static create(data: RawHelmChart): HelmChart;
static create(data: RawHelmChart, opts?: HelmChartCreateOpts): HelmChart | undefined;
static create(data: RawHelmChart, { onError = "throw" }: HelmChartCreateOpts = {}): HelmChart | undefined { static create(data: RawHelmChart, { onError = "throw" }: HelmChartCreateOpts = {}): HelmChart | undefined {
const result = helmChartValidator.validate(data, { const result = helmChartValidator.validate(data, {
abortEarly: false, abortEarly: false,

View File

@ -0,0 +1,387 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<HelmChartDetails /> before getChartDetails resolves renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
<div
class="Animate slide-right Drawer HelmChartDetails right enter"
style="--size: 725px; --enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="drawer-wrapper flex column"
>
<div
class="drawer-title flex align-center"
>
<div
class="drawer-title-text flex gaps align-center"
>
Chart: a galaxy far far away/a name
<i
class="Icon material interactive focusable"
id="tooltip_target_1"
tabindex="0"
>
<span
class="icon"
data-icon-name="content_copy"
>
content_copy
</span>
<div />
</i>
</div>
<i
class="Icon material interactive focusable"
id="tooltip_target_2"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
<div />
</i>
</div>
<div
class="drawer-content flex column box grow"
>
<div
class="Spinner singleColor center"
/>
</div>
</div>
<div
class="ResizingAnchor horizontal leading"
/>
</div>
</body>
`;
exports[`<HelmChartDetails /> before getChartDetails resolves when getChartDetails resolves with one version renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
<div
class="Animate slide-right Drawer HelmChartDetails right enter"
style="--size: 725px; --enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="drawer-wrapper flex column"
>
<div
class="drawer-title flex align-center"
>
<div
class="drawer-title-text flex gaps align-center"
>
Chart: a galaxy far far away/a name
<i
class="Icon material interactive focusable"
id="tooltip_target_3"
tabindex="0"
>
<span
class="icon"
data-icon-name="content_copy"
>
content_copy
</span>
<div />
</i>
</div>
<i
class="Icon material interactive focusable"
id="tooltip_target_4"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
<div />
</i>
</div>
<div
class="drawer-content flex column box grow"
>
<div
class="box grow"
>
<div
class="introduction flex align-flex-start"
>
<img
class="intro-logo"
src=""
/>
<div
class="intro-contents box grow"
>
<div
class="description flex align-center justify-space-between"
data-testid="selected-chart-description"
>
<button
class="Button primary"
type="button"
>
Install
</button>
</div>
<div
class="DrawerItem version"
>
<span
class="name"
>
Version
</span>
<span
class="value"
>
<div
class="Select theme-outlined css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-chart-version-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__single-value css-qc6sy-singleValue"
>
1
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="chart-version-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Home
</span>
<span
class="value"
>
<a
rel="noreferrer"
target="_blank"
/>
</span>
</div>
<div
class="DrawerItem maintainers"
>
<span
class="name"
>
Maintainers
</span>
<span
class="value"
/>
</div>
</div>
</div>
<div
class="chart-description"
data-testid="helmchart-readme"
>
<div
class="MarkDownViewer"
>
<p>
I am a readme
</p>
</div>
</div>
</div>
</div>
</div>
<div
class="ResizingAnchor horizontal leading"
/>
</div>
</body>
`;
exports[`<HelmChartDetails /> before getChartDetails resolves with getChartDetails rejects renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
>
<div
class="Animate opacity notification flex error enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Error: some error
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_20"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
</div>
<div
class="Animate slide-right Drawer HelmChartDetails right enter"
style="--size: 725px; --enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="drawer-wrapper flex column"
>
<div
class="drawer-title flex align-center"
>
<div
class="drawer-title-text flex gaps align-center"
>
Chart: a galaxy far far away/a name
<i
class="Icon material interactive focusable"
id="tooltip_target_18"
tabindex="0"
>
<span
class="icon"
data-icon-name="content_copy"
>
content_copy
</span>
<div />
</i>
</div>
<i
class="Icon material interactive focusable"
id="tooltip_target_19"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
<div />
</i>
</div>
<div
class="drawer-content flex column box grow"
>
<div
class="Spinner singleColor center"
/>
</div>
</div>
<div
class="ResizingAnchor horizontal leading"
/>
</div>
</body>
`;

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 { getInjectable } from "@ogre-tools/injectable";
import type { GetChartDetailsOptions, IHelmChartDetails } from "../../../common/k8s-api/endpoints/helm-charts.api";
import { getChartDetails } from "../../../common/k8s-api/endpoints/helm-charts.api";
export type GetChartDetails = (repo: string, name: string, opts?: GetChartDetailsOptions) => Promise<IHelmChartDetails>;
const getChartDetailsInjectable = getInjectable({
id: "get-chart-details",
instantiate: (): GetChartDetails => getChartDetails,
causesSideEffects: true,
});
export default getChartDetailsInjectable;

View File

@ -0,0 +1,92 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { DiContainer } from "@ogre-tools/injectable";
import type { RenderResult } from "@testing-library/react";
import React from "react";
import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import { noop } from "../../utils";
import type { CreateInstallChartTab } from "../dock/install-chart/create-install-chart-tab.injectable";
import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable";
import { Notifications } from "../notifications";
import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor";
import type { GetChartDetails } from "./get-char-details.injectable";
import getChartDetailsInjectable from "./get-char-details.injectable";
import { HelmChartDetails } from "./helm-chart-details";
describe("<HelmChartDetails />", () => {
let di: DiContainer;
let getChartDetails: AsyncFnMock<GetChartDetails>;
let chart: HelmChart;
let render: DiRender;
let result: RenderResult;
let createInstallChartTab: jest.MockedFunction<CreateInstallChartTab>;
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
getChartDetails = asyncFn<GetChartDetails>();
createInstallChartTab = jest.fn();
chart = HelmChart.create({
apiVersion: "some-api-version",
created: "a long time ago",
name: "a name",
repo: "a galaxy far far away",
version: "1",
});
di.override(directoryForLensLocalStorageInjectable, () => "some-directory-for-lens-local-storage");
di.override(getChartDetailsInjectable, () => getChartDetails);
di.override(createInstallChartTabInjectable, () => createInstallChartTab);
render = renderFor(di);
result = render((
<>
<HelmChartDetails chart={chart} hideDetails={noop} />
<Notifications />
</>
));
});
describe("before getChartDetails resolves", () => {
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
describe("when getChartDetails resolves with one version", () => {
beforeEach(async () => {
await getChartDetails.resolve({
readme: "I am a readme",
versions: [chart],
});
});
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
it("shows the readme", () => {
expect(result.queryByTestId("helmchart-readme")).not.toBeNull();
});
it("shows the selected chart", () => {
expect(result.queryByTestId("selected-chart-description")).not.toBeNull();
});
});
describe("with getChartDetails rejects", () => {
beforeEach(async () => {
await getChartDetails.reject(new Error("some error"));
});
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
});
});
});

View File

@ -7,7 +7,6 @@ import "./helm-chart-details.scss";
import React, { Component } from "react"; import React, { Component } from "react";
import type { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api"; import type { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api";
import { getChartDetails } from "../../../common/k8s-api/endpoints/helm-charts.api";
import { computed, observable, reaction, runInAction } from "mobx"; import { computed, observable, reaction, runInAction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { Drawer, DrawerItem } from "../drawer"; import { Drawer, DrawerItem } from "../drawer";
@ -20,10 +19,13 @@ import { Badge } from "../badge";
import { Tooltip, withStyles } from "@material-ui/core"; import { Tooltip, withStyles } from "@material-ui/core";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable"; import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable";
import { Notifications } from "../notifications"; import type { ShowCheckedErrorNotification } from "../notifications";
import HelmLogoPlaceholder from "./helm-placeholder.svg"; import HelmLogoPlaceholder from "./helm-placeholder.svg";
import type { SingleValue } from "react-select"; import type { SingleValue } from "react-select";
import { AbortController } from "abort-controller"; import AbortController from "abort-controller";
import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable";
import type { GetChartDetails } from "./get-char-details.injectable";
import getChartDetailsInjectable from "./get-char-details.injectable";
export interface HelmChartDetailsProps { export interface HelmChartDetailsProps {
chart: HelmChart; chart: HelmChart;
@ -38,6 +40,8 @@ const LargeTooltip = withStyles({
interface Dependencies { interface Dependencies {
createInstallChartTab: (helmChart: HelmChart) => void; createInstallChartTab: (helmChart: HelmChart) => void;
showCheckedErrorNotification: ShowCheckedErrorNotification;
getChartDetails: GetChartDetails;
} }
@observer @observer
@ -73,7 +77,7 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
}); });
try { try {
const { readme, versions } = await getChartDetails(repo, name, { version }); const { readme, versions } = await this.props.getChartDetails(repo, name, { version });
runInAction(() => { runInAction(() => {
this.readme.set(readme); this.readme.set(readme);
@ -81,7 +85,7 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
this.selectedChart.set(versions[0]); this.selectedChart.set(versions[0]);
}); });
} catch (error) { } catch (error) {
Notifications.checkedError(error, "Unknown error occured while getting chart details"); this.props.showCheckedErrorNotification(error, "Unknown error occured while getting chart details");
} }
}, { }, {
fireImmediately: true, fireImmediately: true,
@ -101,11 +105,11 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
this.abortController.abort(); this.abortController.abort();
this.abortController = new AbortController(); this.abortController = new AbortController();
const { chart: { name, repo }} = this.props; const { chart: { name, repo }} = this.props;
const { readme } = await getChartDetails(repo, name, { version: chart.version, reqInit: { signal: this.abortController.signal }}); const { readme } = await this.props.getChartDetails(repo, name, { version: chart.version, reqInit: { signal: this.abortController.signal }});
this.readme.set(readme); this.readme.set(readme);
} catch (error) { } catch (error) {
Notifications.checkedError(error, "Unknown error occured while getting chart details"); this.props.showCheckedErrorNotification(error, "Unknown error occured while getting chart details");
} }
} }
@ -123,7 +127,7 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
onError={(event) => event.currentTarget.src = HelmLogoPlaceholder} onError={(event) => event.currentTarget.src = HelmLogoPlaceholder}
/> />
<div className="intro-contents box grow"> <div className="intro-contents box grow">
<div className="description flex align-center justify-space-between"> <div className="description flex align-center justify-space-between" data-testid="selected-chart-description">
{selectedChart.getDescription()} {selectedChart.getDescription()}
<Button <Button
primary primary
@ -194,7 +198,7 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
} }
return ( return (
<div className="chart-description"> <div className="chart-description" data-testid="helmchart-readme">
<MarkdownViewer markdown={readme} /> <MarkdownViewer markdown={readme} />
</div> </div>
); );
@ -232,13 +236,11 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
} }
} }
export const HelmChartDetails = withInjectables<Dependencies, HelmChartDetailsProps>( export const HelmChartDetails = withInjectables<Dependencies, HelmChartDetailsProps>(NonInjectedHelmChartDetails, {
NonInjectedHelmChartDetails,
{
getProps: (di, props) => ({ getProps: (di, props) => ({
createInstallChartTab: di.inject(createInstallChartTabInjectable),
...props, ...props,
createInstallChartTab: di.inject(createInstallChartTabInjectable),
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
getChartDetails: di.inject(getChartDetailsInjectable),
}), }),
}, });
);

View File

@ -9,9 +9,7 @@ import type {
DockTab, DockTab,
DockTabCreate, DockTabCreate,
DockTabCreateSpecific } from "../dock/store"; DockTabCreateSpecific } from "../dock/store";
import { import { TabKind } from "../dock/store";
TabKind,
} from "../dock/store";
import type { InstallChartTabStore } from "./store"; import type { InstallChartTabStore } from "./store";
import createDockTabInjectable from "../dock/create-dock-tab.injectable"; import createDockTabInjectable from "../dock/create-dock-tab.injectable";
@ -20,6 +18,8 @@ interface Dependencies {
installChartStore: InstallChartTabStore; installChartStore: InstallChartTabStore;
} }
export type CreateInstallChartTab = (chart: HelmChart, tabParams?: DockTabCreateSpecific) => DockTab;
const createInstallChartTab = ({ createDockTab, installChartStore }: Dependencies) => (chart: HelmChart, tabParams: DockTabCreateSpecific = {}) => { const createInstallChartTab = ({ createDockTab, installChartStore }: Dependencies) => (chart: HelmChart, tabParams: DockTabCreateSpecific = {}) => {
const { name, repo, version } = chart; const { name, repo, version } = chart;

View File

@ -5,6 +5,7 @@
import type React from "react"; import type React from "react";
import { action, observable, makeObservable } from "mobx"; import { action, observable, makeObservable } from "mobx";
import type { Disposer } from "../../utils";
import { autoBind } from "../../utils"; import { autoBind } from "../../utils";
import uniqueId from "lodash/uniqueId"; import uniqueId from "lodash/uniqueId";
import type { JsonApiErrorParsed } from "../../../common/k8s-api/json-api"; import type { JsonApiErrorParsed } from "../../../common/k8s-api/json-api";
@ -19,6 +20,15 @@ export enum NotificationStatus {
INFO = "info", INFO = "info",
} }
export type ShowNotification = (message: NotificationMessage, opts?: CreateNotificationOptions) => Disposer;
export type ShowCheckedErrorNotification = (message: unknown, fallback: string, opts?: CreateNotificationOptions) => Disposer;
export interface CreateNotificationOptions {
id?: NotificationId;
timeout?: number;
onClose?(): void;
}
export interface Notification { export interface Notification {
id?: NotificationId; id?: NotificationId;
message: NotificationMessage; message: NotificationMessage;

View File

@ -10,13 +10,17 @@ import { reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { JsonApiErrorParsed } from "../../../common/k8s-api/json-api"; import { JsonApiErrorParsed } from "../../../common/k8s-api/json-api";
import { cssNames, prevDefault } from "../../utils"; import { cssNames, prevDefault } from "../../utils";
import type { Notification, NotificationMessage, NotificationsStore } from "./notifications.store"; import type { Notification, NotificationsStore, ShowCheckedErrorNotification, ShowNotification } from "./notifications.store";
import { NotificationStatus } from "./notifications.store";
import { Animate } from "../animate"; import { Animate } from "../animate";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import notificationsStoreInjectable from "./notifications-store.injectable"; import notificationsStoreInjectable from "./notifications-store.injectable";
import { asLegacyGlobalFunctionForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
import showSuccessNotificationInjectable from "./show-success-notification.injectable";
import showCheckedErrorNotificationInjectable from "./show-checked-error.injectable";
import showErrorNotificationInjectable from "./show-error-notification.injectable";
import showInfoNotificationInjectable from "./show-info-notification.injectable";
import showShortInfoNotificationInjectable from "./show-short-info.injectable";
interface Dependencies { interface Dependencies {
store: NotificationsStore; store: NotificationsStore;
@ -103,58 +107,16 @@ export const Notifications = withInjectables<Dependencies>(
}), }),
}, },
) as React.FC & { ) as React.FC & {
ok: (message: NotificationMessage) => () => void; ok: ShowNotification;
checkedError: (message: unknown, fallback: string, customOpts?: Partial<Omit<Notification, "message">>) => () => void; checkedError: ShowCheckedErrorNotification;
error: (message: NotificationMessage, customOpts?: Partial<Omit<Notification, "message">>) => () => void; error: ShowNotification;
shortInfo: (message: NotificationMessage, customOpts?: Partial<Omit<Notification, "message">>) => () => void; shortInfo: ShowNotification;
info: (message: NotificationMessage, customOpts?: Partial<Omit<Notification, "message">>) => () => void; info: ShowNotification;
}; };
/** Notifications.ok = asLegacyGlobalFunctionForExtensionApi(showSuccessNotificationInjectable);
* @deprecated Notifications.error = asLegacyGlobalFunctionForExtensionApi(showErrorNotificationInjectable);
*/ Notifications.checkedError = asLegacyGlobalFunctionForExtensionApi(showCheckedErrorNotificationInjectable);
const _notificationStore = asLegacyGlobalForExtensionApi(notificationsStoreInjectable); Notifications.info = asLegacyGlobalFunctionForExtensionApi(showInfoNotificationInjectable);
Notifications.shortInfo = asLegacyGlobalFunctionForExtensionApi(showShortInfoNotificationInjectable);
Notifications.ok = (message: NotificationMessage) => {
return _notificationStore.add({
message,
timeout: 2_500,
status: NotificationStatus.OK,
});
};
Notifications.checkedError = (message, fallback, customOpts = {}) => {
if (typeof message === "string" || message instanceof Error || message instanceof JsonApiErrorParsed) {
return Notifications.error(message, customOpts);
}
console.warn("Unknown notification error message, falling back to default", message);
return Notifications.error(fallback, customOpts);
};
Notifications.error = (message, customOpts= {}) => {
return _notificationStore.add({
message,
timeout: 10_000,
status: NotificationStatus.ERROR,
...customOpts,
});
};
Notifications.shortInfo = (message, customOpts = {}) => {
return Notifications.info(message, {
timeout: 5_000,
...customOpts,
});
};
Notifications.info = (message, customOpts = {}) => {
return _notificationStore.add({
status: NotificationStatus.INFO,
timeout: 0,
message,
...customOpts,
});
};

View File

@ -0,0 +1,27 @@
/**
* 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 { JsonApiErrorParsed } from "../../../common/k8s-api/json-api";
import type { ShowCheckedErrorNotification } from "./notifications.store";
import showErrorNotificationInjectable from "./show-error-notification.injectable";
const showCheckedErrorNotificationInjectable = getInjectable({
id: "show-checked-error-notififcation",
instantiate: (di): ShowCheckedErrorNotification => {
const showErrorNotification = di.inject(showErrorNotificationInjectable);
return (message, fallback, opts) => {
if (typeof message === "string" || message instanceof Error || message instanceof JsonApiErrorParsed) {
return showErrorNotification(message, opts);
}
console.warn("Unknown notification error message, falling back to default", message);
return showErrorNotification(fallback, opts);
};
},
});
export default showCheckedErrorNotificationInjectable;

View File

@ -3,17 +3,17 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { NotificationMessage, Notification } from "./notifications.store"; import type { ShowNotification } from "./notifications.store";
import { NotificationStatus } from "./notifications.store"; import { NotificationStatus } from "./notifications.store";
import notificationsStoreInjectable from "./notifications-store.injectable"; import notificationsStoreInjectable from "./notifications-store.injectable";
const showErrorNotificationInjectable = getInjectable({ const showErrorNotificationInjectable = getInjectable({
id: "show-error-notification", id: "show-error-notification",
instantiate: (di) => { instantiate: (di): ShowNotification => {
const notificationsStore = di.inject(notificationsStoreInjectable); const notificationsStore = di.inject(notificationsStoreInjectable);
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) => return (message, customOpts = {}) =>
notificationsStore.add({ notificationsStore.add({
status: NotificationStatus.ERROR, status: NotificationStatus.ERROR,
timeout: 5000, timeout: 5000,

View File

@ -3,17 +3,17 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { NotificationMessage, Notification } from "./notifications.store"; import type { ShowNotification } from "./notifications.store";
import { NotificationStatus } from "./notifications.store"; import { NotificationStatus } from "./notifications.store";
import notificationsStoreInjectable from "./notifications-store.injectable"; import notificationsStoreInjectable from "./notifications-store.injectable";
const showInfoNotificationInjectable = getInjectable({ const showInfoNotificationInjectable = getInjectable({
id: "show-info-notification", id: "show-info-notification",
instantiate: (di) => { instantiate: (di): ShowNotification => {
const notificationsStore = di.inject(notificationsStoreInjectable); const notificationsStore = di.inject(notificationsStoreInjectable);
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) => return (message, customOpts = {}) =>
notificationsStore.add({ notificationsStore.add({
status: NotificationStatus.INFO, status: NotificationStatus.INFO,
timeout: 5000, timeout: 5000,

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 type { ShowNotification } from "./notifications.store";
import showInfoNotificationInjectable from "./show-info-notification.injectable";
const showShortInfoNotificationInjectable = getInjectable({
id: "show-short-info-notification",
instantiate: (di): ShowNotification => {
const showInfoNotification = di.inject(showInfoNotificationInjectable);
return (message, customOpts = {}) => {
return showInfoNotification(message, {
timeout: 5_000,
...customOpts,
});
};
},
});
export default showShortInfoNotificationInjectable;

View File

@ -3,17 +3,17 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { NotificationMessage, Notification } from "./notifications.store"; import type { ShowNotification } from "./notifications.store";
import { NotificationStatus } from "./notifications.store"; import { NotificationStatus } from "./notifications.store";
import notificationsStoreInjectable from "./notifications-store.injectable"; import notificationsStoreInjectable from "./notifications-store.injectable";
const showSuccessNotificationInjectable = getInjectable({ const showSuccessNotificationInjectable = getInjectable({
id: "show-success-notification", id: "show-success-notification",
instantiate: (di) => { instantiate: (di): ShowNotification => {
const notificationsStore = di.inject(notificationsStoreInjectable); const notificationsStore = di.inject(notificationsStoreInjectable);
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) => return (message, customOpts = {}) =>
notificationsStore.add({ notificationsStore.add({
status: NotificationStatus.OK, status: NotificationStatus.OK,
timeout: 5000, timeout: 5000,