Add tests for VirtualizedListBox

Signed-off-by: danny-may <dannyjmay97@gmail.com>
This commit is contained in:
danny-may
2024-08-13 10:54:46 +01:00
parent 115d684716
commit 47ed51b3ad
3 changed files with 293 additions and 7 deletions
@@ -0,0 +1,277 @@
/*
* 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 { VirtualizedListbox } from './VirtualizedListbox';
import { renderInTestApp } from '@backstage/test-utils';
describe('<VirtualizedListbox />', () => {
it('Should forward additional props to the outer div', async () => {
const { baseElement } = await renderInTestApp(
<VirtualizedListbox
className="MuiAutocomplete-root MuiAutocomplete-hasClearIcon MuiAutocomplete-hasPopupIcon"
aria-expanded="true"
role="combobox"
/>,
);
expect(baseElement.children[0]).toMatchInlineSnapshot(`
<div>
<div>
<div
aria-expanded="true"
class="MuiAutocomplete-root MuiAutocomplete-hasClearIcon MuiAutocomplete-hasPopupIcon"
role="combobox"
style="position: relative; height: 18px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
style="height: 0px; width: 100%;"
/>
</div>
</div>
</div>
`);
});
it('Should render with no items', async () => {
const { baseElement } = await renderInTestApp(<VirtualizedListbox />);
expect(baseElement.children[0]).toMatchInlineSnapshot(`
<div>
<div>
<div
style="position: relative; height: 18px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
style="height: 0px; width: 100%;"
/>
</div>
</div>
</div>
`);
});
it('Should render with one item', async () => {
const { baseElement } = await renderInTestApp(
<VirtualizedListbox>
<span>Item 1</span>
</VirtualizedListbox>,
);
expect(baseElement.children[0]).toMatchInlineSnapshot(`
<div>
<div>
<div
style="position: relative; height: 54px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
style="height: 36px; width: 100%;"
>
<span
style="position: absolute; left: 0px; top: 0px; height: 36px; width: 100%;"
>
Item 1
</span>
</div>
</div>
</div>
</div>
`);
});
it('Should render with 10 items', async () => {
const { baseElement } = await renderInTestApp(
<VirtualizedListbox>
{[...new Array(10)].map((_, i) => (
<span key={i}>Item {i}</span>
))}
</VirtualizedListbox>,
);
expect(baseElement.children[0]).toMatchInlineSnapshot(`
<div>
<div>
<div
style="position: relative; height: 378px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
style="height: 360px; width: 100%;"
>
<span
style="position: absolute; left: 0px; top: 0px; height: 36px; width: 100%;"
>
Item
0
</span>
<span
style="position: absolute; left: 0px; top: 36px; height: 36px; width: 100%;"
>
Item
1
</span>
<span
style="position: absolute; left: 0px; top: 72px; height: 36px; width: 100%;"
>
Item
2
</span>
<span
style="position: absolute; left: 0px; top: 108px; height: 36px; width: 100%;"
>
Item
3
</span>
<span
style="position: absolute; left: 0px; top: 144px; height: 36px; width: 100%;"
>
Item
4
</span>
<span
style="position: absolute; left: 0px; top: 180px; height: 36px; width: 100%;"
>
Item
5
</span>
<span
style="position: absolute; left: 0px; top: 216px; height: 36px; width: 100%;"
>
Item
6
</span>
<span
style="position: absolute; left: 0px; top: 252px; height: 36px; width: 100%;"
>
Item
7
</span>
<span
style="position: absolute; left: 0px; top: 288px; height: 36px; width: 100%;"
>
Item
8
</span>
<span
style="position: absolute; left: 0px; top: 324px; height: 36px; width: 100%;"
>
Item
9
</span>
</div>
</div>
</div>
</div>
`);
});
it('Should render up to 10.5 items (+ 2 buffer) even when there are many more', async () => {
const { baseElement } = await renderInTestApp(
<VirtualizedListbox>
{[...new Array(100)].map((_, i) => (
<span key={i}>Item {i}</span>
))}
</VirtualizedListbox>,
);
expect(baseElement.children[0]).toMatchInlineSnapshot(`
<div>
<div>
<div
style="position: relative; height: 378px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
style="height: 3600px; width: 100%;"
>
<span
style="position: absolute; left: 0px; top: 0px; height: 36px; width: 100%;"
>
Item
0
</span>
<span
style="position: absolute; left: 0px; top: 36px; height: 36px; width: 100%;"
>
Item
1
</span>
<span
style="position: absolute; left: 0px; top: 72px; height: 36px; width: 100%;"
>
Item
2
</span>
<span
style="position: absolute; left: 0px; top: 108px; height: 36px; width: 100%;"
>
Item
3
</span>
<span
style="position: absolute; left: 0px; top: 144px; height: 36px; width: 100%;"
>
Item
4
</span>
<span
style="position: absolute; left: 0px; top: 180px; height: 36px; width: 100%;"
>
Item
5
</span>
<span
style="position: absolute; left: 0px; top: 216px; height: 36px; width: 100%;"
>
Item
6
</span>
<span
style="position: absolute; left: 0px; top: 252px; height: 36px; width: 100%;"
>
Item
7
</span>
<span
style="position: absolute; left: 0px; top: 288px; height: 36px; width: 100%;"
>
Item
8
</span>
<span
style="position: absolute; left: 0px; top: 324px; height: 36px; width: 100%;"
>
Item
9
</span>
<span
style="position: absolute; left: 0px; top: 360px; height: 36px; width: 100%;"
>
Item
10
</span>
<span
style="position: absolute; left: 0px; top: 396px; height: 36px; width: 100%;"
>
Item
11
</span>
<span
style="position: absolute; left: 0px; top: 432px; height: 36px; width: 100%;"
>
Item
12
</span>
</div>
</div>
</div>
</div>
`);
});
});
@@ -17,22 +17,26 @@
import React from 'react';
import { FixedSizeList, ListChildComponentProps } from 'react-window';
type HTMLDivProps = React.HTMLAttributes<HTMLDivElement>;
const renderRow = (props: ListChildComponentProps) => {
const { data, index, style } = props;
return React.cloneElement(data[index], { style });
};
// Context needed to keep Autocomplete working correctly : https://v4.mui.com/components/autocomplete/#Virtualize.tsx
const OuterElementContext = React.createContext({});
const OuterElementContext = React.createContext<HTMLDivProps>({});
const OuterElementType = React.forwardRef<HTMLDivElement>((props, ref) => {
const outerProps = React.useContext(OuterElementContext);
return <div ref={ref} {...props} {...outerProps} />;
});
const OuterElementType = React.forwardRef<HTMLDivElement, HTMLDivProps>(
(props, ref) => {
const outerProps = React.useContext(OuterElementContext);
return <div ref={ref} {...props} {...outerProps} />;
},
);
export const VirtualizedListbox = React.forwardRef<
HTMLDivElement,
{ children?: React.ReactNode }
HTMLDivProps
>((props, ref) => {
const { children, ...other } = props;
const itemData = React.Children.toArray(children);
@@ -40,7 +44,7 @@ export const VirtualizedListbox = React.forwardRef<
const itemSize = 36;
const itemsToShow = Math.min(10, itemCount);
const itemsToShow = Math.min(10, itemCount) + 0.5;
const height = itemsToShow * itemSize;
return (