Add support for search and custom actions
Summary: Introduced search bar and support for custom buttons therein. Reviewed By: nikoant Differential Revision: D26338666 fbshipit-source-id: e53cd3c4381e11f5f90c05c92e39a6c8ac2eca65
This commit is contained in:
committed by
Facebook GitHub Bot
parent
44bb5b1beb
commit
fb7c09c972
@@ -15,11 +15,14 @@ import {TableHead} from './TableHead';
|
||||
import {Percentage} from '../utils/widthUtils';
|
||||
import {DataSourceRenderer} from './DataSourceRenderer';
|
||||
import {useDataTableManager, TableManager} from './useDataTableManager';
|
||||
import {TableSearch} from './TableSearch';
|
||||
|
||||
interface DataTableProps<T = any> {
|
||||
columns: DataTableColumn<T>[];
|
||||
dataSource: DataSource<T, any, any>;
|
||||
autoScroll?: boolean;
|
||||
extraActions?: React.ReactElement;
|
||||
// custom onSearch(text, row) option?
|
||||
tableManagerRef?: RefObject<TableManager>;
|
||||
_testHeight?: number; // exposed for unit testing only
|
||||
}
|
||||
@@ -58,6 +61,11 @@ export function DataTable<T extends object>(props: DataTableProps<T>) {
|
||||
|
||||
return (
|
||||
<Layout.Top>
|
||||
<Layout.Container>
|
||||
<TableSearch
|
||||
onSearch={tableManager.setSearchValue}
|
||||
extraActions={props.extraActions}
|
||||
/>
|
||||
<TableHead
|
||||
columns={tableManager.columns}
|
||||
visibleColumns={tableManager.visibleColumns}
|
||||
@@ -67,6 +75,7 @@ export function DataTable<T extends object>(props: DataTableProps<T>) {
|
||||
sorting={tableManager.sorting}
|
||||
onColumnSort={tableManager.sortColumn}
|
||||
/>
|
||||
</Layout.Container>
|
||||
<DataSourceRenderer<any, RenderContext>
|
||||
dataSource={props.dataSource}
|
||||
autoScroll={props.autoScroll}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Percentage,
|
||||
Width,
|
||||
} from '../utils/widthUtils';
|
||||
import {useRef} from 'react';
|
||||
import {memo, useRef} from 'react';
|
||||
import {Interactive, InteractiveProps} from '../Interactive';
|
||||
import styled from '@emotion/styled';
|
||||
import React from 'react';
|
||||
@@ -181,7 +181,7 @@ function TableHeadColumn({
|
||||
);
|
||||
}
|
||||
|
||||
export function TableHead({
|
||||
export const TableHead = memo(function TableHead({
|
||||
columns,
|
||||
visibleColumns,
|
||||
...props
|
||||
@@ -243,7 +243,7 @@ export function TableHead({
|
||||
</Dropdown>
|
||||
</TableHeadContainer>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const SettingsButton = styled(Button)({
|
||||
padding: 4,
|
||||
@@ -251,4 +251,5 @@ const SettingsButton = styled(Button)({
|
||||
right: 0,
|
||||
top: 0,
|
||||
backgroundColor: theme.backgroundWash,
|
||||
borderRadius: 0,
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import styled from '@emotion/styled';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import type {RenderContext} from './DataTable';
|
||||
import {Width} from '../utils/widthUtils';
|
||||
import {pad} from 'lodash';
|
||||
|
||||
// heuristic for row estimation, should match any future styling updates
|
||||
export const DEFAULT_ROW_HEIGHT = 24;
|
||||
@@ -91,6 +92,13 @@ export const TableRow = memo(function TableRow(props: Props) {
|
||||
value = value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (value instanceof Date) {
|
||||
value =
|
||||
value.toTimeString().split(' ')[0] +
|
||||
'.' +
|
||||
pad('' + value.getMilliseconds(), 3);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableBodyColumnContainer
|
||||
className="ant-table-cell"
|
||||
|
||||
33
desktop/flipper-plugin/src/ui/datatable/TableSearch.tsx
Normal file
33
desktop/flipper-plugin/src/ui/datatable/TableSearch.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {Input} from 'antd';
|
||||
import React, {memo} from 'react';
|
||||
import {Layout} from '../Layout';
|
||||
import {theme} from '../theme';
|
||||
|
||||
export const TableSearch = memo(function TableSearch({
|
||||
onSearch,
|
||||
extraActions,
|
||||
}: {
|
||||
onSearch(value: string): void;
|
||||
extraActions?: React.ReactElement;
|
||||
}) {
|
||||
return (
|
||||
<Layout.Horizontal
|
||||
gap
|
||||
style={{
|
||||
backgroundColor: theme.backgroundWash,
|
||||
padding: theme.space.small,
|
||||
}}>
|
||||
<Input.Search allowClear placeholder="Search..." onSearch={onSearch} />
|
||||
{extraActions}
|
||||
</Layout.Horizontal>
|
||||
);
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import {DataTable, DataTableColumn} from '../DataTable';
|
||||
import {render, act} from '@testing-library/react';
|
||||
import {createDataSource} from '../../../state/datasource/DataSource';
|
||||
import {TableManager} from '../useDataTableManager';
|
||||
import {Button} from 'antd';
|
||||
|
||||
type Todo = {
|
||||
title: string;
|
||||
@@ -222,3 +223,57 @@ test('sorting', async () => {
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
test('search', async () => {
|
||||
const ds = createTestDataSource();
|
||||
ds.clear();
|
||||
ds.append({
|
||||
title: 'item abc',
|
||||
done: false,
|
||||
});
|
||||
ds.append({
|
||||
title: 'item x',
|
||||
done: false,
|
||||
});
|
||||
ds.append({
|
||||
title: 'item b',
|
||||
done: false,
|
||||
});
|
||||
const ref = createRef<TableManager>();
|
||||
const rendering = render(
|
||||
<DataTable
|
||||
dataSource={ds}
|
||||
columns={columns}
|
||||
tableManagerRef={ref}
|
||||
extraActions={<Button>Test Button</Button>}
|
||||
_testHeight={400}
|
||||
/>,
|
||||
);
|
||||
{
|
||||
// button is visible
|
||||
rendering.getByText('Test Button');
|
||||
const elem = await rendering.findAllByText(/item/);
|
||||
expect(elem.length).toBe(3);
|
||||
expect(elem.map((e) => e.textContent)).toEqual([
|
||||
'item abc',
|
||||
'item x',
|
||||
'item b',
|
||||
]);
|
||||
}
|
||||
{
|
||||
// filter
|
||||
act(() => {
|
||||
ref.current?.setSearchValue('b');
|
||||
});
|
||||
const elem = await rendering.findAllByText(/item/);
|
||||
expect(elem.map((e) => e.textContent)).toEqual(['item abc', 'item b']);
|
||||
}
|
||||
{
|
||||
// reset
|
||||
act(() => {
|
||||
ref.current?.reset();
|
||||
});
|
||||
const elem = await rendering.findAllByText(/item/);
|
||||
expect(elem.length).toBe(3);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import {DataTableColumn} from 'flipper-plugin/src/ui/datatable/DataTable';
|
||||
import {Percentage} from '../utils/widthUtils';
|
||||
import produce from 'immer';
|
||||
import {useCallback, useMemo, useState} from 'react';
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {DataSource} from '../../state/datasource/DataSource';
|
||||
|
||||
export type OnColumnResize = (id: string, size: number | Percentage) => void;
|
||||
@@ -33,11 +33,30 @@ export function useDataTableManager<T extends object>(
|
||||
computeInitialColumns(defaultColumns),
|
||||
);
|
||||
const [sorting, setSorting] = useState<Sorting | undefined>(undefined);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const visibleColumns = useMemo(
|
||||
() => columns.filter((column) => column.visible),
|
||||
[columns],
|
||||
);
|
||||
|
||||
// filter is computed by useMemo to support adding column filters etc here in the future
|
||||
const currentFilter = useMemo(
|
||||
function computeFilter() {
|
||||
if (searchValue === '') {
|
||||
// unset
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const searchString = searchValue.toLowerCase();
|
||||
return function dataTableFilter(item: object) {
|
||||
return Object.values(item).some((v) =>
|
||||
String(v).toLowerCase().includes(searchString),
|
||||
);
|
||||
};
|
||||
},
|
||||
[searchValue],
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setEffectiveColumns(computeInitialColumns(defaultColumns));
|
||||
setSorting(undefined);
|
||||
@@ -88,6 +107,13 @@ export function useDataTableManager<T extends object>(
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
function applyFilter() {
|
||||
dataSource.setFilter(currentFilter);
|
||||
},
|
||||
[currentFilter, dataSource],
|
||||
);
|
||||
|
||||
return {
|
||||
/** The default columns, but normalized */
|
||||
columns,
|
||||
@@ -103,6 +129,8 @@ export function useDataTableManager<T extends object>(
|
||||
sortColumn,
|
||||
/** Show / hide the given column */
|
||||
toggleColumnVisibility,
|
||||
/** Active search value */
|
||||
setSearchValue,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user