Add eslint rule padding-line-between-statements (#1593)

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
This commit is contained in:
Panu Horsmalahti authored and GitHub committed 2020-12-02 09:55:52 +02:00
1 parent 7b77f18376
commit dcf253e7d5
401 files changed
+2018 -40

No files matched your search

@@ -44,6 +44,7 @@ export class CreateResource extends React.Component<Props> {
.filter(v => !!v); // skip empty documents if "---" pasted at the beginning or end
const createdResources: string[] = [];
const errors: string[] = [];
await Promise.all(
resources.map(data => {
return resourceApplierApi.update(data)
@@ -51,6 +52,7 @@ export class CreateResource extends React.Component<Props> {
.catch((err: JsonApiErrorParsed) => errors.push(err.toString()));
})
);
if (errors.length) {
errors.forEach(Notifications.error);
if (!createdResources.length) throw errors[0];
@@ -61,13 +63,16 @@ export class CreateResource extends React.Component<Props> {
<Trans><b>{createdResources.join(", ")}</b> successfully created</Trans>
</p>
);
Notifications.ok(successMessage);
return successMessage;
};
render() {
const { tabId, data, error, create, onChange } = this;
const { className } = this.props;
return (
<div className={cssNames("CreateResource flex column", className)}>
<InfoPanel
@@ -17,6 +17,7 @@ export class DockTabStore<T = any> {
// auto-save to local-storage
if (storageName) {
const storage = createStorage<[TabId, T][]>(storageName, []);
this.data.replace(storage.get());
reaction(() => this.serializeData(), (data: T | any) => storage.set(data));
}
@@ -24,6 +25,7 @@ export class DockTabStore<T = any> {
// clear data for closed tabs
autorun(() => {
const currentTabs = dockStore.tabs.map(tab => tab.id);
Array.from(this.data.keys()).forEach(tabId => {
if (!currentTabs.includes(tabId)) {
this.clearData(tabId);
@@ -34,8 +36,10 @@ export class DockTabStore<T = any> {
protected serializeData() {
const { storageSerializer } = this.options;
return Array.from(this.data).map(([tabId, tabData]) => {
if (storageSerializer) return [tabId, storageSerializer(tabData)];
return [tabId, tabData];
});
}
@@ -40,6 +40,7 @@ export class DockTab extends React.Component<DockTabProps> {
)}
</div>
);
return (
<Tab
{...tabProps}
@@ -51,6 +51,7 @@ export class DockStore {
const mainLayoutMargin = 16;
const dockTabs = 33;
const preferedMax = window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs;
return Math.max(preferedMax, this.minHeight); // don't let max < min
}
@@ -74,6 +75,7 @@ export class DockStore {
if (!this.height) {
this.setHeight(this.defaultHeight || this.minHeight);
}
if (this.height > this.maxHeight) {
this.setHeight(this.maxHeight);
}
@@ -94,6 +96,7 @@ export class DockStore {
@action
open(fullSize?: boolean) {
this.isOpen = true;
if (typeof fullSize === "boolean") {
this.fullSize = fullSize;
}
@@ -125,8 +128,10 @@ export class DockStore {
.filter(tab => tab.kind === kind)
.map(tab => {
const tabNumber = +tab.title.match(/\d+/);
return tabNumber === 0 ? 1 : tabNumber; // tab without a number is first
});
for (let i = 1; ; i++) {
if (!tabNumbers.includes(i)) return i;
}
@@ -136,29 +141,36 @@ export class DockStore {
createTab(anonTab: IDockTab, addNumber = true): IDockTab {
const tabId = MD5(Math.random().toString() + Date.now()).toString();
const tab: IDockTab = { id: tabId, ...anonTab };
if (addNumber) {
const tabNumber = this.getNewTabNumber(tab.kind);
if (tabNumber > 1) tab.title += ` (${tabNumber})`;
}
this.tabs.push(tab);
this.selectTab(tab.id);
this.open();
return tab;
}
@action
async closeTab(tabId: TabId) {
const tab = this.getTabById(tabId);
if (!tab || tab.pinned) {
return;
}
this.tabs.remove(tab);
if (this.selectedTabId === tab.id) {
if (this.tabs.length) {
const newTab = this.tabs.slice(-1)[0]; // last
if (newTab.kind === TabKind.TERMINAL) {
// close the dock when selected sibling inactive terminal tab
const { terminalStore } = await import("./terminal.store");
if (!terminalStore.isConnected(newTab.id)) this.close();
}
this.selectTab(newTab.id);
+10
View File
@@ -33,11 +33,14 @@ interface Props {
export class Dock extends React.Component<Props> {
onKeydown = (evt: React.KeyboardEvent<HTMLElement>) => {
const { close, closeTab, selectedTab } = dockStore;
if (!selectedTab) return;
const { code, ctrlKey, shiftKey } = evt.nativeEvent;
if (shiftKey && code === "Escape") {
close();
}
if (ctrlKey && code === "KeyW") {
if (selectedTab.pinned) close();
else closeTab(selectedTab.id);
@@ -46,6 +49,7 @@ export class Dock extends React.Component<Props> {
onChangeTab = (tab: IDockTab) => {
const { open, selectTab } = dockStore;
open();
selectTab(tab.id);
};
@@ -55,12 +59,15 @@ export class Dock extends React.Component<Props> {
if (isTerminalTab(tab)) {
return <TerminalTab value={tab} />;
}
if (isCreateResourceTab(tab) || isEditResourceTab(tab)) {
return <DockTab value={tab} icon="edit" />;
}
if (isInstallChartTab(tab) || isUpgradeChartTab(tab)) {
return <DockTab value={tab} icon={<Icon svg="install" />} />;
}
if (isPodLogsTab(tab)) {
return <DockTab value={tab} icon="subject" />;
}
@@ -68,7 +75,9 @@ export class Dock extends React.Component<Props> {
renderTabContent() {
const { isOpen, height, selectedTab: tab } = dockStore;
if (!isOpen || !tab) return;
return (
<div className="tab-content" style={{ flexBasis: height }}>
{isCreateResourceTab(tab) && <CreateResource tab={tab} />}
@@ -84,6 +93,7 @@ export class Dock extends React.Component<Props> {
render() {
const { className } = this.props;
const { isOpen, toggle, tabs, toggleFillSize, selectedTab, hasTabs, fullSize } = dockStore;
return (
<div
className={cssNames("Dock", className, { isOpen, fullSize })}
@@ -27,9 +27,11 @@ export class EditResourceStore extends DockTabStore<KubeEditResource> {
}
this.watchers.set(tabId, autorun(() => {
const store = apiManager.getStore(resource);
if (store) {
const isActiveTab = dockStore.isOpen && dockStore.selectedTabId === tabId;
const obj = store.getByPath(resource);
// preload resource for editing
if (!obj && !store.isLoaded && !store.isLoading && isActiveTab) {
store.loadFromPath(resource).catch(noop);
@@ -50,6 +52,7 @@ export class EditResourceStore extends DockTabStore<KubeEditResource> {
const [tabId] = Array.from(this.data).find(([, { resource }]) => {
return object.selfLink === resource;
}) || [];
return dockStore.getTabById(tabId);
}
@@ -67,10 +70,12 @@ export const editResourceStore = new EditResourceStore();
export function editResourceTab(object: KubeObject, tabParams: Partial<IDockTab> = {}) {
// use existing tab if already opened
let tab = editResourceStore.getTabByResource(object);
if (tab) {
dockStore.open();
dockStore.selectTab(tab.id);
}
// or create new tab
if (!tab) {
tab = dockStore.createTab({
@@ -82,6 +87,7 @@ export function editResourceTab(object: KubeObject, tabParams: Partial<IDockTab>
resource: object.selfLink,
});
}
return tab;
}
@@ -28,6 +28,7 @@ export class EditResource extends React.Component<Props> {
@disposeOnUnmount
autoDumpResourceOnInit = autorun(() => {
if (!this.tabData) return;
if (this.tabData.draft === undefined && this.resource) {
this.saveDraft(this.resource);
}
@@ -44,6 +45,7 @@ export class EditResource extends React.Component<Props> {
get resource(): KubeObject {
const { resource } = this.tabData;
const store = apiManager.getStore(resource);
if (store) {
return store.getByPath(resource);
}
@@ -71,9 +73,11 @@ export class EditResource extends React.Component<Props> {
const { resource, draft } = this.tabData;
const store = apiManager.getStore(resource);
const updatedResource = await store.update(this.resource, jsYaml.safeLoad(draft));
this.saveDraft(updatedResource); // update with new resourceVersion to avoid further errors on save
const resourceType = updatedResource.kind;
const resourceName = updatedResource.getName();
return (
<p>
<Trans>{resourceType} <b>{resourceName}</b> updated.</Trans>
@@ -84,10 +88,12 @@ export class EditResource extends React.Component<Props> {
render() {
const { tabId, resource, tabData, error, onChange, save } = this;
const { draft } = tabData;
if (!resource || draft === undefined) {
return <Spinner center/>;
}
const { kind, getNs, getName } = resource;
return (
<div className={cssNames("EditResource flex column", this.props.className)}>
<InfoPanel
@@ -57,6 +57,7 @@ export class EditorPanel extends React.Component<Props> {
onChange = (value: string) => {
this.validate(value);
if (this.props.onChange) {
this.props.onChange(value, this.yamlError);
}
@@ -65,8 +66,10 @@ export class EditorPanel extends React.Component<Props> {
render() {
const { value, tabId } = this.props;
let { className } = this.props;
className = cssNames("EditorPanel", className);
const cursorPos = EditorPanel.cursorPos.getData(tabId);
return (
<AceEditor
autoFocus mode="yaml"
@@ -57,9 +57,12 @@ export class InfoPanel extends Component<Props> {
submit = async () => {
const { showNotifications } = this.props;
this.waiting = true;
try {
const result = await this.props.submit();
if (showNotifications) Notifications.ok(result);
} catch (error) {
if (showNotifications) Notifications.error(error.toString());
@@ -81,6 +84,7 @@ export class InfoPanel extends Component<Props> {
if (!this.props.showInlineInfo || !this.errorInfo) {
return;
}
return (
<div className="error">
<Icon material="error_outline" tooltip={this.errorInfo}/>
@@ -92,6 +96,7 @@ export class InfoPanel extends Component<Props> {
const { className, controls, submitLabel, disableSubmit, error, submittingMessage, showButtons, showSubmitClose } = this.props;
const { submit, close, submitAndClose, waiting } = this;
const isDisabled = !!(disableSubmit || waiting || error);
return (
<div className={cssNames("InfoPanel flex gaps align-center", className)}>
<div className="controls">
@@ -28,6 +28,7 @@ export class InstallChartStore extends DockTabStore<IChartInstallData> {
});
autorun(() => {
const { selectedTab, isOpen } = dockStore;
if (isInstallChartTab(selectedTab) && isOpen) {
this.loadData()
.catch(err => Notifications.error(String(err)));
@@ -53,9 +54,11 @@ export class InstallChartStore extends DockTabStore<IChartInstallData> {
@action
async loadVersions(tabId: TabId) {
const { repo, name, version } = this.getData(tabId);
this.versions.clearData(tabId); // reset
const charts = await helmChartsApi.get(repo, name, version);
const versions = charts.versions.map(chartVersion => chartVersion.version);
this.versions.setData(tabId, versions);
}
@@ -63,8 +66,8 @@ export class InstallChartStore extends DockTabStore<IChartInstallData> {
async loadValues(tabId: TabId, attempt = 0): Promise<void> {
const data = this.getData(tabId);
const { repo, name, version } = data;
const values = await helmChartsApi.getValues(repo, name, version);
if (values) {
this.setData(tabId, { ...data, values });
} else if (attempt < 4) {
@@ -77,7 +80,6 @@ export const installChartStore = new InstallChartStore();
export function createInstallChartTab(chart: HelmChart, tabParams: Partial<IDockTab> = {}) {
const { name, repo, version } = chart;
const tab = dockStore.createTab({
kind: TabKind.INSTALL_CHART,
title: _i18n._(t`Helm Install: ${repo}/${name}`),
@@ -54,6 +54,7 @@ export class InstallChart extends Component<Props> {
@autobind()
viewRelease() {
const { release } = this.releaseDetails;
navigate(releaseURL({
params: {
name: release.name,
@@ -66,12 +67,14 @@ export class InstallChart extends Component<Props> {
@autobind()
save(data: Partial<IChartInstallData>) {
const chart = { ...this.chartData, ...data };
installChartStore.setData(this.tabId, chart);
}
@autobind()
onVersionChange(option: SelectOption) {
const version = option.value;
this.save({ version, values: "" });
installChartStore.loadValues(this.tabId);
}
@@ -99,7 +102,9 @@ export class InstallChart extends Component<Props> {
chart: name,
repo, namespace, version, values,
});
installChartStore.details.setData(this.tabId, details);
return (
<p><Trans>Chart Release <b>{details.release.name}</b> successfully created.</Trans></p>
);
@@ -107,6 +112,7 @@ export class InstallChart extends Component<Props> {
render() {
const { tabId, chartData, values, versions, install } = this;
if (chartData?.values === undefined || !versions) {
return <Spinner center />;
}
@@ -39,11 +39,13 @@ export const PodLogControls = observer((props: Props) => {
const downloadLogs = () => {
const fileName = selectedContainer ? selectedContainer.name : pod.getName();
saveFileDialog(`${fileName}.log`, logs.join("\n"), "text/plain");
};
const onContainerChange = (option: SelectOption) => {
const { containers, initContainers } = tabData;
save({
selectedContainer: containers
.concat(initContainers)
@@ -54,6 +56,7 @@ export const PodLogControls = observer((props: Props) => {
const containerSelectOptions = () => {
const { containers, initContainers } = tabData;
return [
{
label: _i18n._(t`Containers`),
@@ -72,6 +75,7 @@ export const PodLogControls = observer((props: Props) => {
const formatOptionLabel = (option: SelectOption) => {
const { value, label } = option;
return label || <><Icon small material="view_carousel"/> {value}</>;
};
@@ -41,24 +41,31 @@ export class PodLogList extends React.Component<Props> {
componentDidUpdate(prevProps: Props) {
const { logs, id } = this.props;
if (id != prevProps.id) {
this.isLastLineVisible = true;
return;
}
if (logs == prevProps.logs || !this.virtualListDiv.current) return;
const newLogsLoaded = prevProps.logs.length < logs.length;
const scrolledToBeginning = this.virtualListDiv.current.scrollTop === 0;
const fewLogsLoaded = logs.length < logRange;
if (this.isLastLineVisible) {
this.scrollToBottom(); // Scroll down to keep user watching/reading experience
return;
}
if (scrolledToBeginning && newLogsLoaded) {
this.virtualListDiv.current.scrollTop = (logs.length - prevProps.logs.length) * this.lineHeight;
}
if (fewLogsLoaded) {
this.isJumpButtonVisible = false;
}
if (!logs.length) {
this.isLastLineVisible = false;
}
@@ -73,6 +80,7 @@ export class PodLogList extends React.Component<Props> {
const offset = 100 * this.lineHeight;
const { scrollHeight } = this.virtualListDiv.current;
const { scrollOffset } = props;
if (scrollHeight - scrollOffset < offset) {
this.isJumpButtonVisible = false;
} else {
@@ -88,6 +96,7 @@ export class PodLogList extends React.Component<Props> {
setLastLineVisibility = (props: ListOnScrollProps) => {
const { scrollHeight, clientHeight } = this.virtualListDiv.current;
const { scrollOffset, scrollDirection } = props;
if (scrollDirection == "backward") {
this.isLastLineVisible = false;
} else {
@@ -103,6 +112,7 @@ export class PodLogList extends React.Component<Props> {
*/
checkLoadIntent = (props: ListOnScrollProps) => {
const { scrollOffset } = props;
if (scrollOffset === 0) {
this.props.load();
}
@@ -136,6 +146,7 @@ export class PodLogList extends React.Component<Props> {
const item = this.props.logs[rowIndex];
const contents: React.ReactElement[] = [];
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(colorConverter.ansi_to_html(ansi));
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
// Case-insensitive search (lowercasing query and keywords in line)
const regex = new RegExp(searchStore.escapeRegex(searchQuery), "gi");
@@ -143,6 +154,7 @@ export class PodLogList extends React.Component<Props> {
const modified = item.replace(regex, match => match.toLowerCase());
// Splitting text line by keyword
const pieces = modified.split(searchQuery.toLowerCase());
pieces.forEach((piece, index) => {
const active = isActiveOverlay(rowIndex, index);
const lastItem = index === pieces.length - 1;
@@ -153,6 +165,7 @@ export class PodLogList extends React.Component<Props> {
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
/>
: null;
contents.push(
<React.Fragment key={piece + index}>
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
@@ -161,6 +174,7 @@ export class PodLogList extends React.Component<Props> {
);
});
}
return (
<div className={cssNames("LogRow")}>
{contents.length > 1 ? contents : (
@@ -174,9 +188,11 @@ export class PodLogList extends React.Component<Props> {
const { logs, isLoading } = this.props;
const isInitLoading = isLoading && !logs.length;
const rowHeights = new Array(logs.length).fill(this.lineHeight);
if (isInitLoading) {
return <Spinner center/>;
}
if (!logs.length) {
return (
<div className="PodLogList flex box grow align-center justify-center">
@@ -184,6 +200,7 @@ export class PodLogList extends React.Component<Props> {
</div>
);
}
return (
<div className={cssNames("PodLogList flex", { isLoading })}>
<VirtualList
@@ -26,6 +26,7 @@ export const logRange = 500;
export class PodLogsStore extends DockTabStore<IPodLogsData> {
private refresher = interval(10, () => {
const id = dockStore.selectedTabId;
if (!this.logs.get(id)) return;
this.loadMore(id);
});
@@ -39,6 +40,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
});
autorun(() => {
const { selectedTab, isOpen } = dockStore;
if (isPodLogsTab(selectedTab) && isOpen) {
this.refresher.start();
} else {
@@ -68,6 +70,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
const logs = await this.loadLogs(tabId, {
tailLines: this.lines + logRange
});
this.refresher.start();
this.logs.set(tabId, logs);
} catch ({error}) {
@@ -75,6 +78,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
_i18n._(t`Failed to load logs: ${error.message}`),
_i18n._(t`Reason: ${error.reason} (${error.code})`)
];
this.refresher.stop();
this.logs.set(tabId, message);
}
@@ -92,6 +96,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
const logs = await this.loadLogs(tabId, {
sinceTime: this.getLastSinceTime(tabId)
});
// Add newly received logs to bottom
this.logs.set(tabId, [...oldLogs, ...logs]);
};
@@ -109,6 +114,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
const pod = new Pod(data.pod);
const namespace = pod.getNs();
const name = pod.getName();
return podsApi.getLogs({ namespace, name }, {
...params,
timestamps: true, // Always setting timestampt to separate old logs from new ones
@@ -116,7 +122,9 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
previous
}).then(result => {
const logs = [...result.split("\n")]; // Transform them into array
logs.pop(); // Remove last empty element
return logs;
});
};
@@ -128,6 +136,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
setNewLogSince(tabId: TabId) {
if (!this.logs.has(tabId) || !this.logs.get(tabId).length || this.newLogSince.has(tabId)) return;
const timestamp = this.getLastSinceTime(tabId);
this.newLogSince.set(tabId, timestamp.split(".")[0]); // Removing milliseconds from string
}
@@ -139,6 +148,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
get lines() {
const id = dockStore.selectedTabId;
const logs = this.logs.get(id);
return logs ? logs.length : 0;
}
@@ -151,7 +161,9 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
const logs = this.logs.get(tabId);
const timestamps = this.getTimestamps(logs[logs.length - 1]);
const stamp = new Date(timestamps ? timestamps[0] : null);
stamp.setSeconds(stamp.getSeconds() + 1); // avoid duplicates from last second
return stamp.toISOString();
}
@@ -178,9 +190,11 @@ export const podLogsStore = new PodLogsStore();
export function createPodLogsTab(data: IPodLogsData, tabParams: Partial<IDockTab> = {}) {
const podId = data.pod.getId();
let tab = dockStore.getTabById(podId);
if (tab) {
dockStore.open();
dockStore.selectTab(tab.id);
return;
}
// If no existent tab found
@@ -191,6 +205,7 @@ export function createPodLogsTab(data: IPodLogsData, tabParams: Partial<IDockTab
...tabParams
}, false);
podLogsStore.setData(tab.id, data);
return tab;
}
@@ -66,12 +66,14 @@ export class PodLogs extends React.Component<Props> {
@autobind()
toOverlay() {
const { activeOverlayLine } = searchStore;
if (!this.logListElement.current || activeOverlayLine === undefined) return;
// Scroll vertically
this.logListElement.current.scrollToItem(activeOverlayLine, "center");
// Scroll horizontally in timeout since virtual list need some time to prepare its contents
setTimeout(() => {
const overlay = document.querySelector(".PodLogs .list span.active");
if (!overlay) return;
overlay.scrollIntoViewIfNeeded();
}, 100);
@@ -87,9 +89,11 @@ export class PodLogs extends React.Component<Props> {
const logs = podLogsStore.logs.get(this.tabId);
const { getData, removeTimestamps } = podLogsStore;
const { showTimestamps } = getData(this.tabId);
if (!showTimestamps) {
return logs.map(item => removeTimestamps(item));
}
return logs;
}
@@ -107,6 +111,7 @@ export class PodLogs extends React.Component<Props> {
toNextOverlay={this.toOverlay}
/>
);
return (
<div className="PodLogs flex column">
<InfoPanel
@@ -35,6 +35,7 @@ export class TerminalWindow extends React.Component<Props> {
render() {
const { className } = this.props;
return (
<div
className={cssNames("TerminalWindow", className, themeStore.activeTheme.type)}
@@ -32,7 +32,9 @@ export class TerminalStore {
// connect active tab
autorun(() => {
const { selectedTab, isOpen } = dockStore;
if (!isTerminalTab(selectedTab)) return;
if (isOpen) {
this.connect(selectedTab.id);
}
@@ -40,6 +42,7 @@ export class TerminalStore {
// disconnect closed tabs
autorun(() => {
const currentTabs = dockStore.tabs.map(tab => tab.id);
for (const [tabId] of this.connections) {
if (!currentTabs.includes(tabId)) this.disconnect(tabId);
}
@@ -56,6 +59,7 @@ export class TerminalStore {
node: tab.node,
});
const terminal = new Terminal(tabId, api);
this.connections.set(tabId, api);
this.terminals.set(tabId, terminal);
}
@@ -66,6 +70,7 @@ export class TerminalStore {
}
const terminal = this.terminals.get(tabId);
const terminalApi = this.connections.get(tabId);
terminal.destroy();
terminalApi.destroy();
this.connections.delete(tabId);
@@ -74,6 +79,7 @@ export class TerminalStore {
reconnect(tabId: TabId) {
const terminalApi = this.connections.get(tabId);
if (terminalApi) terminalApi.connect();
}
@@ -83,6 +89,7 @@ export class TerminalStore {
isDisconnected(tabId: TabId) {
const terminalApi = this.connections.get(tabId);
if (terminalApi) {
return terminalApi.readyState === WebSocketApiState.CLOSED;
}
@@ -91,12 +98,13 @@ export class TerminalStore {
sendCommand(command: string, options: { enter?: boolean; newTab?: boolean; tabId?: TabId } = {}) {
const { enter, newTab, tabId } = options;
const { selectTab, getTabById } = dockStore;
const tab = tabId && getTabById(tabId);
if (tab) selectTab(tabId);
if (newTab) createTerminalTab();
const terminalApi = this.connections.get(dockStore.selectedTabId);
if (terminalApi) {
terminalApi.sendCommand(command + (enter ? "\r" : ""));
}
+8
View File
@@ -14,6 +14,7 @@ export class Terminal {
// terminal element must be in DOM before attaching via xterm.open(elem)
// https://xtermjs.org/docs/api/terminal/classes/terminal/#open
const pool = document.createElement("div");
pool.className = "terminal-init";
pool.style.cssText = "position: absolute; top: 0; left: 0; height: 0; visibility: hidden; overflow: hidden";
document.body.appendChild(pool);
@@ -23,6 +24,7 @@ export class Terminal {
static async preloadFonts() {
const fontPath = require("../fonts/roboto-mono-nerd.ttf").default; // eslint-disable-line @typescript-eslint/no-var-requires
const fontFace = new FontFace("RobotoMono", `url(${fontPath})`);
await fontFace.load();
document.fonts.add(fontFace);
}
@@ -41,10 +43,13 @@ export class Terminal {
.filter(([name]) => name.startsWith(colorPrefix))
.reduce<any>((colors, [name, color]) => {
const colorName = name.split("").slice(colorPrefix.length);
colorName[0] = colorName[0].toLowerCase();
colors[colorName.join("")] = color;
return colors;
}, {});
this.xterm.setOption("theme", terminalColors);
}
@@ -62,6 +67,7 @@ export class Terminal {
get isActive() {
const { isOpen, selectedTabId } = dockStore;
return isOpen && selectedTabId === this.tabId;
}
@@ -95,6 +101,7 @@ export class Terminal {
// bind events
const onDataHandler = this.xterm.onData(this.onData);
this.viewport.addEventListener("scroll", this.onScroll);
this.api.onReady.addListener(this.onClear, { once: true }); // clear status logs (connecting..)
this.api.onData.addListener(this.onApiData);
@@ -125,6 +132,7 @@ export class Terminal {
if (!this.isActive) return;
this.fitAddon.fit();
const { cols, rows } = this.xterm;
this.api.sendTerminalSize(cols, rows);
};
@@ -23,7 +23,9 @@ export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
autorun(() => {
const { selectedTab, isOpen } = dockStore;
if (!isUpgradeChartTab(selectedTab)) return;
if (isOpen) {
this.loadData(selectedTab.id);
}
@@ -31,6 +33,7 @@ export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
autorun(() => {
const objects = [...this.data.values()];
objects.forEach(({ releaseName }) => this.createReleaseWatcher(releaseName));
});
}
@@ -41,13 +44,16 @@ export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
}
const dispose = reaction(() => {
const release = releaseStore.getByName(releaseName);
if (release) return release.getRevision(); // watch changes only by revision
},
release => {
const releaseTab = this.getTabByRelease(releaseName);
if (!releaseStore.isLoaded || !releaseTab) {
return;
}
// auto-reload values if was loaded before
if (release) {
if (dockStore.selectedTab === releaseTab && this.values.getData(releaseTab.id)) {
@@ -61,17 +67,20 @@ export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
dockStore.closeTab(releaseTab.id);
}
});
this.watchers.set(releaseName, dispose);
}
isLoading(tabId = dockStore.selectedTabId) {
const values = this.values.getData(tabId);
return !releaseStore.isLoaded || values === undefined;
}
@action
async loadData(tabId: TabId) {
const values = this.values.getData(tabId);
await Promise.all([
!releaseStore.isLoaded && releaseStore.loadAll(),
!values && this.loadValues(tabId)
@@ -83,13 +92,16 @@ export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
this.values.clearData(tabId); // reset
const { releaseName, releaseNamespace } = this.getData(tabId);
const values = await helmReleasesApi.getValues(releaseName, releaseNamespace);
this.values.setData(tabId, values);
}
getTabByRelease(releaseName: string): IDockTab {
const item = [...this.data].find(item => item[1].releaseName === releaseName);
if (item) {
const [tabId] = item;
return dockStore.getTabById(tabId);
}
}
@@ -99,10 +111,12 @@ export const upgradeChartStore = new UpgradeChartStore();
export function createUpgradeChartTab(release: HelmRelease, tabParams: Partial<IDockTab> = {}) {
let tab = upgradeChartStore.getTabByRelease(release.getName());
if (tab) {
dockStore.open();
dockStore.selectTab(tab.id);
}
if (!tab) {
tab = dockStore.createTab({
kind: TabKind.UPGRADE_CHART,
@@ -115,6 +129,7 @@ export function createUpgradeChartTab(release: HelmRelease, tabParams: Partial<I
releaseNamespace: release.getNs()
});
}
return tab;
}
@@ -42,7 +42,9 @@ export class UpgradeChart extends React.Component<Props> {
get release(): HelmRelease {
const tabData = upgradeChartStore.getData(this.tabId);
if (!tabData) return;
return releaseStore.getByName(tabData.releaseName);
}
@@ -55,6 +57,7 @@ export class UpgradeChart extends React.Component<Props> {
this.version = null;
this.versions.clear();
const versions = await helmChartStore.getVersions(this.release.getChart());
this.versions.replace(versions);
this.version = this.versions[0];
}
@@ -69,11 +72,13 @@ export class UpgradeChart extends React.Component<Props> {
const { version, repo } = this.version;
const releaseName = this.release.getName();
const releaseNs = this.release.getNs();
await releaseStore.update(releaseName, releaseNs, {
chart: this.release.getChart(),
values: this.value,
repo, version,
});
return (
<p>
<Trans>Release <b>{releaseName}</b> successfully upgraded to version <b>{version}</b></Trans>
@@ -84,12 +89,14 @@ export class UpgradeChart extends React.Component<Props> {
formatVersionLabel = ({ value }: SelectOption<IChartVersion>) => {
const chartName = this.release.getChart();
const { repo, version } = value;
return `${repo}/${chartName}-${version}`;
};
render() {
const { tabId, release, value, error, onChange, upgrade, versions, version } = this;
const { className } = this.props;
if (!release || upgradeChartStore.isLoading() || !version) {
return <Spinner center/>;
}
@@ -111,6 +118,7 @@ export class UpgradeChart extends React.Component<Props> {
/>
</div>
);
return (
<div className={cssNames("UpgradeChart flex column", className)}>
<InfoPanel