Skip to content

Commit

Permalink
Merge branch 'main' into chore-update-sheet-landing-readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mournfulCoroner authored Apr 18, 2024
2 parents 595fbda + 9ab7d7c commit 59b7f05
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 14 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [6.11.0](https://github.com/gravity-ui/uikit/compare/v6.10.2...v6.11.0) (2024-04-17)


### Features

* **Button:** refactor to flex and center icons ([#1452](https://github.com/gravity-ui/uikit/issues/1452)) ([31c22e8](https://github.com/gravity-ui/uikit/commit/31c22e8418f67cb2df3354350f19e44471918aa4))


### Bug Fixes

* add load more functionallity to virtualized list ([#1490](https://github.com/gravity-ui/uikit/issues/1490)) ([#1513](https://github.com/gravity-ui/uikit/issues/1513)) ([8de1653](https://github.com/gravity-ui/uikit/commit/8de16536b7541835d58cc651a3ab4248f0e1ebf0))
* **Breadcrumbs:** unset more item alignment ([#1505](https://github.com/gravity-ui/uikit/issues/1505)) ([7d9797a](https://github.com/gravity-ui/uikit/commit/7d9797ad0b1389b2e732ebeafcae312f750ae9d8))
* **Dialog:** correct width's vars order ([#1510](https://github.com/gravity-ui/uikit/issues/1510)) ([f54da2d](https://github.com/gravity-ui/uikit/commit/f54da2de711e738a2089f26833be4dc2fa01ec6b))
* **Label:** isolate inner z-indexes ([#1519](https://github.com/gravity-ui/uikit/issues/1519)) ([200c052](https://github.com/gravity-ui/uikit/commit/200c0522dfeedd637f0656fa27c23a2cb9f844bf))
* **layout:** ability to override breakpoint during theme ([#1512](https://github.com/gravity-ui/uikit/issues/1512)) ([bad4fa9](https://github.com/gravity-ui/uikit/commit/bad4fa9e8336cd4b06a9875aef948fc8caaaf293))
* **ListItemView:** fix indentation in depth more than 10 ([#1517](https://github.com/gravity-ui/uikit/issues/1517)) ([2cde017](https://github.com/gravity-ui/uikit/commit/2cde017be22a58b331a4aecff40ca3e83cb23e26))
* **ListItemView:** some changes after feedback ([#1516](https://github.com/gravity-ui/uikit/issues/1516)) ([b274498](https://github.com/gravity-ui/uikit/commit/b2744989b4398ae7755e8ea9ae6fed0516bf8618))
* prevent deselection of required table column items ([#1508](https://github.com/gravity-ui/uikit/issues/1508)) ([a69050c](https://github.com/gravity-ui/uikit/commit/a69050cd7213dacce7868a0259359d59e52a1743))
* return `--g-color-private-white-20-solid` css variable ([#1511](https://github.com/gravity-ui/uikit/issues/1511)) ([4a366be](https://github.com/gravity-ui/uikit/commit/4a366be252609c1369262a2e76101e82b6d87a67))
* **theme:** add option for controlling :root color-scheme ([#1468](https://github.com/gravity-ui/uikit/issues/1468)) ([f6237e1](https://github.com/gravity-ui/uikit/commit/f6237e1356248668693f1cc34c7515a8e6a52e41))

## [6.10.2](https://github.com/gravity-ui/uikit/compare/v6.10.1...v6.10.2) (2024-04-10)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gravity-ui/uikit",
"version": "6.10.2",
"version": "6.11.0",
"description": "Gravity UI base styling and components",
"license": "MIT",
"repository": {
Expand Down
14 changes: 6 additions & 8 deletions src/components/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ import React from 'react';

import isEqual from 'lodash/isEqual';
import isObject from 'lodash/isObject';
import {DragDropContext, Draggable, Droppable} from 'react-beautiful-dnd';
import type {
DraggableProvided,
DraggableRubric,
DraggableStateSnapshot,
DropResult,
DroppableProvided,
} from 'react-beautiful-dnd';
import AutoSizer from 'react-virtualized-auto-sizer';
import {DragDropContext, Draggable, Droppable} from 'react-beautiful-dnd';
import type {Size} from 'react-virtualized-auto-sizer';
import {VariableSizeList} from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
import type {VariableSizeListProps} from 'react-window';
import {VariableSizeList} from 'react-window';

import {SelectLoadingIndicator} from '../Select/components/SelectList/SelectLoadingIndicator';
import {TextInput} from '../controls';
import {MobileContext} from '../mobile';
import {useDirection} from '../theme';
import {block} from '../utils/cn';
import {getUniqId} from '../utils/common';

import {ListLoadingIndicator} from './ListLoadingIndicator';
import {ListItem, SimpleContainer, defaultRenderItem} from './components';
import {listNavigationIgnoredKeys} from './constants';
import type {ListItemData, ListItemProps, ListProps} from './types';
Expand Down Expand Up @@ -260,9 +260,7 @@ export class List<T = unknown> extends React.Component<ListProps<T>, ListState<T
const {onLoadMore} = this.props;

if (isObject(item) && 'value' in item && item.value === this.loadingItem.value) {
return (
<SelectLoadingIndicator onIntersect={itemIndex === 0 ? undefined : onLoadMore} />
);
return <ListLoadingIndicator onIntersect={itemIndex === 0 ? undefined : onLoadMore} />;
}

return this.props.renderItem
Expand Down Expand Up @@ -421,7 +419,7 @@ export class List<T = unknown> extends React.Component<ListProps<T>, ListState<T

private renderVirtualizedContainer() {
// Otherwise, react-window will not update the list items
const items = [...this.getItems()];
const items = [...this.getItemsWithLoading()];

if (this.props.sortable) {
return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/List/ListLoadingIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {Loader} from '../Loader';
import {block} from '../utils/cn';

const b = block('list');
export const SelectLoadingIndicator = (props: {onIntersect?: () => void}) => {
export const ListLoadingIndicator = (props: {onIntersect?: () => void}) => {
const ref = React.useRef<HTMLDivElement | null>(null);

useIntersection({element: ref.current, onIntersect: props?.onIntersect});

return (
<div ref={ref} className={b('loading-indicator')}>
<Loader />
<Loader qa={'list-loader'} />
</div>
);
};
68 changes: 68 additions & 0 deletions src/components/List/__tests__/List.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';

import _ from 'react-virtualized-auto-sizer';

import {setupIntersectionObserverMock} from '../../../../test-utils/setupIntersectionObserverMock';
import {cleanup, render, screen} from '../../../../test-utils/utils';
import {List} from '../List';
import type {ListProps} from '../types';

function setup(props: Partial<ListProps<string>> = {}) {
const baseProps: ListProps<string> = {
items: ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'],
itemsHeight: 150,
itemHeight: 28,
filterable: false,
};

return render(
<div>
<List<string> {...baseProps} {...props} />
</div>,
);
}

const mockOnLoadMorFn = jest.fn();

beforeAll(() => {
setupIntersectionObserverMock();
});

afterEach(() => {
cleanup();
jest.clearAllMocks();
});

describe('base List', () => {
it('should render loading indicator', () => {
setup({virtualized: false, onLoadMore: mockOnLoadMorFn, loading: true});

const loader = screen.getByTestId('list-loader');
expect(loader).toBeInTheDocument();
});
it('should call onLoadMore callback when loading indicator is visible', () => {
setup({virtualized: false, onLoadMore: mockOnLoadMorFn, loading: true});

const loader = screen.getByTestId('list-loader');

expect(loader).toBeVisible();
expect(mockOnLoadMorFn).toHaveBeenCalled();
});
});

describe('virtualized List', () => {
it('should render loading indicator', () => {
setup({virtualized: true, onLoadMore: mockOnLoadMorFn, loading: true});

const loader = screen.getByTestId('list-loader');
expect(loader).toBeInTheDocument();
});

it('should call onLoadMore callback when loading indicator is visible', () => {
setup({virtualized: true, onLoadMore: mockOnLoadMorFn, loading: true});

const loader = screen.getByTestId('list-loader');
expect(loader).toBeVisible();
expect(mockOnLoadMorFn).toHaveBeenCalled();
});
});
2 changes: 1 addition & 1 deletion src/components/utils/warn.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const didWarn = new Map<string, boolean>();

export function warnOnce(msg: string) {
if (!msg || didWarn.has(msg)) {
if (!msg || didWarn.has(msg) || process.env.NODE_ENV === 'production') {
return;
}

Expand Down
10 changes: 10 additions & 0 deletions test-utils/setup-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,13 @@ global.ResizeObserver = class implements ResizeObserver {
observe(_target: Element, _options?: ResizeObserverOptions) {}
unobserve(_target: Element) {}
};

// mock AutoSizer to properly test functionality related to virtualization
// 400 x 400 is a random size and might be changed if needed
jest.mock(
'react-virtualized-auto-sizer',
() =>
//@ts-ignore
({children}) =>
children({height: 400, width: 400}),
);
50 changes: 50 additions & 0 deletions test-utils/setupIntersectionObserverMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export function setupIntersectionObserverMock({
root = null,
rootMargin = '',
thresholds = [],
disconnect = () => null,
observe = () => null,
takeRecords = () => [
{
boundingClientRect: {} as DOMRectReadOnly,
intersectionRatio: 1,
intersectionRect: {} as DOMRectReadOnly,
isIntersecting: true,
rootBounds: null,
target: {} as Element,
time: 1,
},
],
unobserve = () => null,
} = {}): void {
class MockIntersectionObserver implements IntersectionObserver {
readonly root: Element | null = root;
readonly rootMargin: string = rootMargin;
readonly thresholds: ReadonlyArray<number> = thresholds;
disconnect: () => void = disconnect;
observe: (target: Element) => void = observe;
takeRecords: () => IntersectionObserverEntry[] = takeRecords;
unobserve: (target: Element) => void = unobserve;

constructor(
callback: (
entries: IntersectionObserverEntry[],
observer: IntersectionObserver,
) => void,
) {
callback(takeRecords(), this);
}
}

Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver,
});

Object.defineProperty(global, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver,
});
}

0 comments on commit 59b7f05

Please sign in to comment.