feat(scaffolder): use virtualization with EntityPicker

Signed-off-by: Mikko Korhonen <mikko.korhonen@op.fi>
This commit is contained in:
Mikko Korhonen
2024-06-24 21:57:33 +03:00
parent c9a2e96634
commit 52b6db0922
4 changed files with 59 additions and 34 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
Use virtualization with `EntityPicker` as done earlier with `MultiEntityPicker` to fix performance issues with large data sets. `VirtualizedListbox` extracted into reusable component.
@@ -43,6 +43,7 @@ import {
EntityPickerUiOptions,
EntityPickerFilterQuery,
} from './schema';
import { VirtualizedListbox } from '../VirtualizedListbox';
export { EntityPickerSchema } from './schema';
@@ -205,6 +206,7 @@ export const EntityPicker = (props: EntityPickerProps) => {
entities?.entityRefToPresentation.get(stringifyEntityRef(option))
?.primaryTitle!,
})}
ListboxComponent={VirtualizedListbox}
/>
</FormControl>
);
@@ -35,7 +35,6 @@ import Autocomplete, {
AutocompleteChangeReason,
} from '@material-ui/lab/Autocomplete';
import React, { useCallback, useEffect } from 'react';
import { FixedSizeList, ListChildComponentProps } from 'react-window';
import useAsync from 'react-use/esm/useAsync';
import { FieldValidation } from '@rjsf/utils';
import {
@@ -44,41 +43,10 @@ import {
MultiEntityPickerUiOptions,
MultiEntityPickerFilterQuery,
} from './schema';
import { VirtualizedListbox } from '../VirtualizedListbox';
export { MultiEntityPickerSchema } from './schema';
const renderRow = (props: ListChildComponentProps) => {
const { data, index, style } = props;
return React.cloneElement(data[index], { style });
};
const ListboxComponent = React.forwardRef<
HTMLDivElement,
{ children?: React.ReactNode }
>((props, ref) => {
const itemData = React.Children.toArray(props.children);
const itemCount = itemData.length;
const itemSize = 36;
const itemsToShow = Math.min(10, itemCount);
const height = Math.max(itemSize, itemsToShow * itemSize - 0.5 * itemSize);
return (
<div ref={ref}>
<FixedSizeList
height={height}
itemData={itemData}
itemCount={itemCount}
itemSize={itemSize}
width="100%"
>
{renderRow}
</FixedSizeList>
</div>
);
});
/**
* The underlying component that is rendered in the form for the `MultiEntityPicker`
* field extension.
@@ -210,7 +178,7 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
}}
/>
)}
ListboxComponent={ListboxComponent}
ListboxComponent={VirtualizedListbox}
/>
</FormControl>
);
@@ -0,0 +1,50 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { FixedSizeList, ListChildComponentProps } from 'react-window';
const renderRow = (props: ListChildComponentProps) => {
const { data, index, style } = props;
return React.cloneElement(data[index], { style });
};
export const VirtualizedListbox = React.forwardRef<
HTMLDivElement,
{ children?: React.ReactNode }
>((props, ref) => {
const itemData = React.Children.toArray(props.children);
const itemCount = itemData.length;
const itemSize = 36;
const itemsToShow = Math.min(10, itemCount);
const height = Math.max(itemSize, itemsToShow * itemSize - 0.5 * itemSize);
return (
<div ref={ref}>
<FixedSizeList
height={height}
itemData={itemData}
itemCount={itemCount}
itemSize={itemSize}
width="100%"
>
{renderRow}
</FixedSizeList>
</div>
);
});