-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Supports Enterprise DH-14630 This PR contains a number of custom hooks + utils to support windowed viewports for the ACL Editor. resolves #1221
- Loading branch information
Showing
17 changed files
with
1,006 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
export { default as TableInput } from './TableInput'; | ||
export { default as useInitializeViewportData } from './useInitializeViewportData'; | ||
export { default as useTable } from './useTable'; | ||
export { default as useTableColumn } from './useTableColumn'; | ||
export { default as useTableListener } from './useTableListener'; | ||
export { default as useSelectDistinctTable } from './useSelectDistinctTable'; | ||
export { default as useSetPaddedViewportCallback } from './useSetPaddedViewportCallback'; | ||
export { default as useViewportData } from './useViewportData'; |
45 changes: 45 additions & 0 deletions
45
packages/jsapi-components/src/useInitializeViewportData.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { act, renderHook } from '@testing-library/react-hooks'; | ||
import { Table } from '@deephaven/jsapi-shim'; | ||
import { KeyedItem } from '@deephaven/jsapi-utils'; | ||
import { TestUtils } from '@deephaven/utils'; | ||
import useInitializeViewportData from './useInitializeViewportData'; | ||
|
||
const tableA = TestUtils.createMockProxy<Table>({ size: 4 }); | ||
const expectedInitialA: KeyedItem<unknown>[] = [ | ||
{ key: '0' }, | ||
{ key: '1' }, | ||
{ key: '2' }, | ||
{ key: '3' }, | ||
]; | ||
|
||
const tableB = TestUtils.createMockProxy<Table>({ size: 2 }); | ||
const expectedInitialB = [{ key: '0' }, { key: '1' }]; | ||
|
||
it('should initialize a ListData object based on Table size', () => { | ||
const { result } = renderHook(() => useInitializeViewportData(tableA)); | ||
|
||
expect(result.current.items).toEqual(expectedInitialA); | ||
}); | ||
|
||
it('should re-initialize a ListData object if Table reference changes', () => { | ||
const { result, rerender } = renderHook( | ||
({ table }) => useInitializeViewportData(table), | ||
{ | ||
initialProps: { table: tableA }, | ||
} | ||
); | ||
|
||
// Update an item | ||
const updatedItem = { key: '0', item: 'mock.item' }; | ||
act(() => { | ||
result.current.update(updatedItem.key, updatedItem); | ||
}); | ||
|
||
const expectedAfterUpdate = [updatedItem, ...expectedInitialA.slice(1)]; | ||
expect(result.current.items).toEqual(expectedAfterUpdate); | ||
|
||
// Re-render with a new table instance | ||
rerender({ table: tableB }); | ||
|
||
expect(result.current.items).toEqual(expectedInitialB); | ||
}); |
46 changes: 46 additions & 0 deletions
46
packages/jsapi-components/src/useInitializeViewportData.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { useEffect } from 'react'; | ||
import { ListData, useListData } from '@react-stately/data'; | ||
import { Table, TreeTable } from '@deephaven/jsapi-shim'; | ||
import { | ||
KeyedItem, | ||
generateEmptyKeyedItems, | ||
getSize, | ||
} from '@deephaven/jsapi-utils'; | ||
|
||
/** | ||
* Initializes a ListData instance that can be used for windowed views of a | ||
* Table. The list must always contain a KeyedItem for every record in the table, | ||
* so it is pre-populated with empty items that can be updated with real data as | ||
* the window changes. | ||
* | ||
* IMPORTANT: this will create an empty KeyedItem object for every row in the | ||
* source table. This is intended for "human" sized tables such as those used in | ||
* admin panels. This is not suitable for "machine" scale with millions+ rows. | ||
* @param table The table that will be used to determine the list size. | ||
* @returns | ||
*/ | ||
export default function useInitializeViewportData<T>( | ||
table: Table | TreeTable | null | ||
): ListData<KeyedItem<T>> { | ||
const viewportData = useListData<KeyedItem<T>>({}); | ||
|
||
// We only want this to fire 1x once the table exists. Note that `useListData` | ||
// has no way to respond to a reference change of the `table` instance so we | ||
// have to manually delete any previous keyed items from the list. | ||
useEffect(() => { | ||
if (!table) { | ||
return; | ||
} | ||
|
||
if (viewportData.items.length) { | ||
viewportData.remove(...viewportData.items.map(({ key }) => key)); | ||
} | ||
|
||
viewportData.insert(0, ...generateEmptyKeyedItems<T>(getSize(table))); | ||
|
||
// Intentionally excluding viewportData since it changes on every render. | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [table]); | ||
|
||
return viewportData; | ||
} |
54 changes: 54 additions & 0 deletions
54
packages/jsapi-components/src/useSelectDistinctTable.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Table } from '@deephaven/jsapi-shim'; | ||
import { TestUtils } from '@deephaven/utils'; | ||
import { renderHook } from '@testing-library/react-hooks'; | ||
import useSelectDistinctTable from './useSelectDistinctTable'; | ||
|
||
let table: Table; | ||
let derivedTable: Table; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
|
||
table = TestUtils.createMockProxy<Table>(); | ||
derivedTable = TestUtils.createMockProxy<Table>(); | ||
|
||
(table.selectDistinct as jest.Mock).mockResolvedValue(derivedTable); | ||
}); | ||
|
||
it('should create and subscribe to a `selectDistinct` derivation of a given table', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => | ||
useSelectDistinctTable(table) | ||
); | ||
|
||
expect(result.current.distinctTable).toBeNull(); | ||
|
||
await waitForNextUpdate(); | ||
|
||
expect(result.current.distinctTable).toBe(derivedTable); | ||
}); | ||
|
||
it('should safely ignore null table', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => | ||
useSelectDistinctTable(null) | ||
); | ||
|
||
expect(result.current.distinctTable).toBeNull(); | ||
|
||
await waitForNextUpdate(); | ||
|
||
expect(result.current.distinctTable).toBeNull(); | ||
}); | ||
|
||
it('should unsubscribe on unmount', async () => { | ||
const { unmount, waitForNextUpdate } = renderHook(() => | ||
useSelectDistinctTable(table) | ||
); | ||
|
||
await waitForNextUpdate(); | ||
|
||
expect(derivedTable.close).not.toHaveBeenCalled(); | ||
|
||
unmount(); | ||
|
||
expect(derivedTable.close).toHaveBeenCalled(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { useCallback, useEffect } from 'react'; | ||
import { Table, TreeTable } from '@deephaven/jsapi-shim'; | ||
import { usePromiseFactory } from '@deephaven/react-hooks'; | ||
|
||
/** | ||
* Creates and subscribes to a `selectDistinct` derived table and unsubscribes | ||
* on unmount. | ||
* @param table The table to call `selectDistinct` on. | ||
* @param columnNames The list of column names to pass to `selectDistinct`. | ||
*/ | ||
export default function useSelectDistinctTable( | ||
table: Table | TreeTable | null, | ||
...columnNames: string[] | ||
) { | ||
const selectDistinct = useCallback( | ||
async () => table?.selectDistinct(table.findColumns(columnNames)) ?? null, | ||
// Disabling the exhaustive checks due to the spreading of `columnNames` | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
[table, ...columnNames] | ||
); | ||
|
||
const { data: distinctTable, error, isError, isLoading } = usePromiseFactory( | ||
selectDistinct, | ||
[] | ||
); | ||
|
||
useEffect( | ||
() => () => { | ||
distinctTable?.close(); | ||
}, | ||
[distinctTable] | ||
); | ||
|
||
return { distinctTable, error, isError, isLoading }; | ||
} |
32 changes: 32 additions & 0 deletions
32
packages/jsapi-components/src/useSetPaddedViewportCallback.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { renderHook } from '@testing-library/react-hooks'; | ||
import { Table } from '@deephaven/jsapi-shim'; | ||
import { TestUtils } from '@deephaven/utils'; | ||
import useSetPaddedViewportCallback from './useSetPaddedViewportCallback'; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should create a callback that sets a padded viewport', () => { | ||
const table = TestUtils.createMockProxy<Table>({ size: 100 }); | ||
const viewportSize = 10; | ||
const viewportPadding = 4; | ||
|
||
const { result } = renderHook(() => | ||
useSetPaddedViewportCallback(table, viewportSize, viewportPadding) | ||
); | ||
|
||
// Call our `setPaddedViewport` callback. | ||
const firstRow = 30; | ||
result.current(firstRow); | ||
|
||
const expected = { | ||
firstRow: firstRow - viewportPadding, | ||
lastRow: firstRow + viewportSize + viewportPadding - 1, | ||
}; | ||
|
||
expect(table.setViewport).toHaveBeenCalledWith( | ||
expected.firstRow, | ||
expected.lastRow | ||
); | ||
}); |
32 changes: 32 additions & 0 deletions
32
packages/jsapi-components/src/useSetPaddedViewportCallback.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { useCallback } from 'react'; | ||
import { Table, TreeTable } from '@deephaven/jsapi-shim'; | ||
import { getSize, padFirstAndLastRow } from '@deephaven/jsapi-utils'; | ||
|
||
/** | ||
* Creates a callback function that will set a Table viewport. The callback has | ||
* a closure over the Table, a desired viewport size, and additional padding. | ||
* These will be combined with a first row index passed to the callback to | ||
* calculate the final viewport. | ||
* @param table Table to call `setViewport` on. | ||
* @param viewportSize The desired viewport size. | ||
* @param viewportPadding Padding to add before and after the viewport. | ||
* @returns A callback function for setting the viewport. | ||
*/ | ||
export default function useSetPaddedViewportCallback( | ||
table: Table | TreeTable | null, | ||
viewportSize: number, | ||
viewportPadding: number | ||
) { | ||
return useCallback( | ||
function setPaddedViewport(firstRow: number) { | ||
const [first, last] = padFirstAndLastRow( | ||
firstRow, | ||
viewportSize, | ||
viewportPadding, | ||
getSize(table) | ||
); | ||
table?.setViewport(first, last); | ||
}, | ||
[table, viewportPadding, viewportSize] | ||
); | ||
} |
Oops, something went wrong.