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

Fix bugs in ItemListLayouts/KubeObjectListLayouts (#4792)

* Fix virtual list behaviour in item list layout by making table rows observer

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

* Make selecting item in ItemListLayout not scroll to top

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

* Fix scrolling to top when selecting all items in item list layout

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

* Fix refreshing release values when release is unselected

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

* Remove noisy debugging

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

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

* Fix sorting of table rows

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

* Replace avoidable asyncComputed with normal computed

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

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

* Revert unnecessary code style changes

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

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

* Fix render-storm in Events

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

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

* Fix missing key in React array

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

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-02-02 15:46:07 +01:00 committed by GitHub
parent 334ab56f9d
commit e58d9b631a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 70 deletions

View File

@ -10,7 +10,7 @@ import releaseInjectable from "./release.injectable";
const releaseDetailsInjectable = getInjectable({ const releaseDetailsInjectable = getInjectable({
instantiate: (di) => instantiate: (di) =>
asyncComputed(async () => { asyncComputed(async () => {
const release = di.inject(releaseInjectable).value.get(); const release = di.inject(releaseInjectable).get();
return await getRelease(release.name, release.namespace); return await getRelease(release.name, release.namespace);
}), }),

View File

@ -7,7 +7,7 @@ import "./release-details.scss";
import React, { Component } from "react"; import React, { Component } from "react";
import groupBy from "lodash/groupBy"; import groupBy from "lodash/groupBy";
import { computed, makeObservable, observable } from "mobx"; import { computed, IComputedValue, makeObservable, observable } from "mobx";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import kebabCase from "lodash/kebabCase"; import kebabCase from "lodash/kebabCase";
import type { HelmRelease, IReleaseDetails, IReleaseUpdateDetails, IReleaseUpdatePayload } from "../../../../common/k8s-api/endpoints/helm-releases.api"; import type { HelmRelease, IReleaseDetails, IReleaseUpdateDetails, IReleaseUpdatePayload } from "../../../../common/k8s-api/endpoints/helm-releases.api";
@ -39,7 +39,7 @@ interface Props {
} }
interface Dependencies { interface Dependencies {
release: IAsyncComputed<HelmRelease> release: IComputedValue<HelmRelease>
releaseDetails: IAsyncComputed<IReleaseDetails> releaseDetails: IAsyncComputed<IReleaseDetails>
releaseValues: IAsyncComputed<string> releaseValues: IAsyncComputed<string>
updateRelease: (name: string, namespace: string, payload: IReleaseUpdatePayload) => Promise<IReleaseUpdateDetails> updateRelease: (name: string, namespace: string, payload: IReleaseUpdatePayload) => Promise<IReleaseUpdateDetails>
@ -59,7 +59,7 @@ class NonInjectedReleaseDetails extends Component<Props & Dependencies> {
} }
@computed get release() { @computed get release() {
return this.props.release.value.get(); return this.props.release.get();
} }
@computed get details() { @computed get details() {

View File

@ -12,7 +12,13 @@ import userSuppliedValuesAreShownInjectable from "./user-supplied-values-are-sho
const releaseValuesInjectable = getInjectable({ const releaseValuesInjectable = getInjectable({
instantiate: (di) => instantiate: (di) =>
asyncComputed(async () => { asyncComputed(async () => {
const release = di.inject(releaseInjectable).value.get(); const release = di.inject(releaseInjectable).get();
// TODO: Figure out way to get rid of defensive code
if (!release) {
return "";
}
const userSuppliedValuesAreShown = di.inject(userSuppliedValuesAreShownInjectable).value; const userSuppliedValuesAreShown = di.inject(userSuppliedValuesAreShownInjectable).value;
try { try {

View File

@ -6,14 +6,14 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import { matches } from "lodash/fp"; import { matches } from "lodash/fp";
import releasesInjectable from "../releases.injectable"; import releasesInjectable from "../releases.injectable";
import releaseRouteParametersInjectable from "./release-route-parameters.injectable"; import releaseRouteParametersInjectable from "./release-route-parameters.injectable";
import { asyncComputed } from "@ogre-tools/injectable-react"; import { computed } from "mobx";
const releaseInjectable = getInjectable({ const releaseInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const releases = di.inject(releasesInjectable); const releases = di.inject(releasesInjectable);
const releaseRouteParameters = di.inject(releaseRouteParametersInjectable); const releaseRouteParameters = di.inject(releaseRouteParametersInjectable);
return asyncComputed(async () => { return computed(() => {
const { name, namespace } = releaseRouteParameters.get(); const { name, namespace } = releaseRouteParameters.get();
if (!name || !namespace) { if (!name || !namespace) {

View File

@ -124,7 +124,7 @@ export class Events extends React.Component<Props> {
}; };
render() { render() {
const { store, visibleItems } = this; const { store } = this;
const { compact, compactLimit, className, ...layoutProps } = this.props; const { compact, compactLimit, className, ...layoutProps } = this.props;
const events = ( const events = (
@ -137,7 +137,7 @@ export class Events extends React.Component<Props> {
renderHeaderTitle="Events" renderHeaderTitle="Events"
customizeHeader={this.customizeHeader} customizeHeader={this.customizeHeader}
isSelectable={false} isSelectable={false}
items={visibleItems} getItems={() => this.visibleItems}
virtual={!compact} virtual={!compact}
tableProps={{ tableProps={{
sortSyncWithUrl: false, sortSyncWithUrl: false,

View File

@ -7,7 +7,7 @@ import "./item-list-layout.scss";
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import { computed, makeObservable } from "mobx"; import { computed, makeObservable } from "mobx";
import { observer } from "mobx-react"; import { Observer, observer } from "mobx-react";
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog"; import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table"; import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table";
import { boundMethod, cssNames, IClassName, isReactNode, prevDefault, stopPropagation } from "../../utils"; import { boundMethod, cssNames, IClassName, isReactNode, prevDefault, stopPropagation } from "../../utils";
@ -70,6 +70,10 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
@boundMethod @boundMethod
getRow(uid: string) { getRow(uid: string) {
return (
<div key={uid}>
<Observer>
{() => {
const { const {
isSelectable, renderTableHeader, renderTableContents, renderItemMenu, isSelectable, renderTableHeader, renderTableContents, renderItemMenu,
store, hasDetailsView, onDetails, store, hasDetailsView, onDetails,
@ -83,7 +87,6 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
return ( return (
<TableRow <TableRow
key={itemId}
nowrap nowrap
searchItem={item} searchItem={item}
sortItem={item} sortItem={item}
@ -98,13 +101,17 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
onClick={prevDefault(() => store.toggleSelection(item))} onClick={prevDefault(() => store.toggleSelection(item))}
/> />
)} )}
{ {renderTableContents(item).map((content, index) => {
renderTableContents(item).map((content, index) => { const cellProps: TableCellProps = isReactNode(content)
const cellProps: TableCellProps = isReactNode(content) ? { children: content } : content; ? { children: content }
: content;
const headCell = renderTableHeader?.[index]; const headCell = renderTableHeader?.[index];
if (copyClassNameFromHeadCells && headCell) { if (copyClassNameFromHeadCells && headCell) {
cellProps.className = cssNames(cellProps.className, headCell.className); cellProps.className = cssNames(
cellProps.className,
headCell.className,
);
} }
if (!headCell || this.showColumn(headCell)) { if (!headCell || this.showColumn(headCell)) {
@ -112,8 +119,7 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
} }
return null; return null;
}) })}
}
{renderItemMenu && ( {renderItemMenu && (
<TableCell className="menu"> <TableCell className="menu">
<div onClick={stopPropagation}> <div onClick={stopPropagation}>
@ -123,6 +129,10 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
)} )}
</TableRow> </TableRow>
); );
}}
</Observer>
</div>
);
} }
@boundMethod @boundMethod
@ -190,12 +200,16 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
return ( return (
<TableHead showTopLine nowrap> <TableHead showTopLine nowrap>
{isSelectable && ( {isSelectable && (
<Observer>
{() => (
<TableCell <TableCell
checkbox checkbox
isChecked={store.isSelectedAll(enabledItems)} isChecked={store.isSelectedAll(enabledItems)}
onClick={prevDefault(() => store.toggleSelectionAll(enabledItems))} onClick={prevDefault(() => store.toggleSelectionAll(enabledItems))}
/> />
)} )}
</Observer>
)}
{renderTableHeader.map((cellProps, index) => ( {renderTableHeader.map((cellProps, index) => (
this.showColumn(cellProps) && ( this.showColumn(cellProps) && (
<TableCell key={cellProps.id ?? index} {...cellProps} /> <TableCell key={cellProps.id ?? index} {...cellProps} />
@ -213,7 +227,6 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks, store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
detailsItem, className, tableProps = {}, tableId, detailsItem, className, tableProps = {}, tableId,
} = this.props; } = this.props;
const { selectedItems } = store;
const selectedItemId = detailsItem && detailsItem.getId(); const selectedItemId = detailsItem && detailsItem.getId();
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type); const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
@ -234,11 +247,18 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
{this.renderTableHeader()} {this.renderTableHeader()}
{this.renderItems()} {this.renderItems()}
</Table> </Table>
<Observer>
{() => (
<AddRemoveButtons <AddRemoveButtons
onRemove={selectedItems.length ? this.removeItemsDialog : null} onRemove={
removeTooltip={`Remove selected items (${selectedItems.length})`} store.selectedItems.length ? this.removeItemsDialog : null
}
removeTooltip={`Remove selected items (${store.selectedItems.length})`}
{...addRemoveButtons} {...addRemoveButtons}
/> />
)}
</Observer>
</div> </div>
); );
} }

View File

@ -46,7 +46,6 @@ export function hideDetails() {
} }
export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlobals = true) { export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlobals = true) {
console.debug("getDetailsUrl", { selfLink, resetSelected, mergeGlobals });
const params = new URLSearchParams(mergeGlobals ? navigation.searchParams : ""); const params = new URLSearchParams(mergeGlobals ? navigation.searchParams : "");
params.set(kubeDetailsUrlParam.name, selfLink); params.set(kubeDetailsUrlParam.name, selfLink);