Skip to content

Commit

Permalink
Merge pull request #888 from silx-kit/debounce-fetch
Browse files Browse the repository at this point in the history
Debounce dim mapping state to reduce fetching rate when slicing
  • Loading branch information
loichuder authored Dec 2, 2021
2 parents 130cef2 + 0813e45 commit 7a79bf1
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 21 deletions.
7 changes: 5 additions & 2 deletions packages/app/src/__tests__/CorePack.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mockValues } from '@h5web/shared';
import { screen } from '@testing-library/react';
import { screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import {
Expand Down Expand Up @@ -123,7 +123,10 @@ test('visualize 1D slice of a 3D dataset with and without autoscale', async () =

// Check that entire dataset is fetched
d0Slider.focus();
userEvent.keyboard('{ArrowUp}');
act(() => {
userEvent.keyboard('{ArrowUp}');
jest.advanceTimersByTime(100); // account for debouncing of `dimMapping` state
});
expect(await screen.findByRole('figure')).toBeVisible();
d0Slider.blur(); // remove focus to avoid state update after unmount

Expand Down
45 changes: 31 additions & 14 deletions packages/app/src/__tests__/DimensionMapper.test.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,80 @@
import { screen, within } from '@testing-library/react';
import { act, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { renderApp, selectExplorerNode, selectVisTab } from '../test-utils';
import { Vis } from '../vis-packs/core/visualizations';

test('display mapping for X axis when visualizing 2D dataset as Line', async () => {
jest.useFakeTimers('modern');
await renderApp();
await selectExplorerNode('nD_datasets/twoD');
await selectVisTab(Vis.Line);

// Ensure that the dimension mapper is only visible for X (and not for Y)
const xRadioGroup = screen.getByLabelText('Dimension as x axis');
expect(xRadioGroup).toBeVisible();
const xDimsButtons = within(xRadioGroup).getAllByRole('radio');
expect(xDimsButtons).toHaveLength(2);
const yRadioGroup = screen.queryByLabelText('Dimension as y axis');
expect(xRadioGroup).toBeVisible();
expect(yRadioGroup).not.toBeInTheDocument();

// Ensure that the default mapping is [0, 'x']
const xDimsButtons = within(xRadioGroup).getAllByRole('radio');
expect(xDimsButtons).toHaveLength(2);
expect(xDimsButtons[0]).not.toBeChecked();
expect(xDimsButtons[1]).toBeChecked();

const d0Slider = screen.getByRole('slider');
expect(d0Slider).toHaveAttribute('aria-valueNow', '0');

// Ensure that the swap from [0, 'x'] to ['x', 0] works
userEvent.click(xDimsButtons[0]);
act(() => {
// Ensure that the swap from [0, 'x'] to ['x', 0] works
userEvent.click(xDimsButtons[0]);
jest.advanceTimersByTime(100); // account for debouncing of `dimMapping` state
});

expect(xDimsButtons[0]).toBeChecked();
expect(xDimsButtons[1]).not.toBeChecked();

const d1Slider = screen.getByRole('slider');
expect(d1Slider).toHaveAttribute('aria-valueNow', '0');

jest.runOnlyPendingTimers();
jest.useRealTimers();
});

test('display mappings for X and Y axes when visualizing 2D dataset as Heatmap', async () => {
jest.useFakeTimers('modern');
await renderApp();
await selectExplorerNode('nD_datasets/twoD');
await selectVisTab(Vis.Heatmap);

// Ensure that the dimension mapper is visible for X and Y
const xRadioGroup = screen.getByLabelText('Dimension as x axis');
expect(xRadioGroup).toBeVisible();
const yRadioGroup = screen.getByLabelText('Dimension as y axis');
expect(xRadioGroup).toBeVisible();
expect(yRadioGroup).toBeVisible();

// Ensure that the default mapping is ['y', 'x']
const xD0Button = within(xRadioGroup).getByRole('radio', { name: 'D0' });
const xD1Button = within(xRadioGroup).getByRole('radio', { name: 'D1' });
expect(xD1Button).toBeChecked();
const yD0Button = within(yRadioGroup).getByRole('radio', { name: 'D0' });
const yD1Button = within(yRadioGroup).getByRole('radio', { name: 'D1' });

// Ensure that the default mapping is ['y', 'x']
expect(xD1Button).toBeChecked();
expect(yD0Button).toBeChecked();

// Ensure that the swap from ['y', 'x'] to ['x', 'y'] works
const xD0Button = within(xRadioGroup).getByRole('radio', { name: 'D0' });
userEvent.click(xD0Button);
act(() => {
// Ensure that the swap from ['y', 'x'] to ['x', 'y'] works
userEvent.click(xD0Button);
jest.advanceTimersByTime(100); // account for debouncing of `dimMapping` state
});

expect(xD0Button).toBeChecked();
expect(xD1Button).not.toBeChecked();

const yD1Button = within(yRadioGroup).getByRole('radio', { name: 'D1' });
expect(yD0Button).not.toBeChecked();
expect(yD1Button).toBeChecked();

jest.runOnlyPendingTimers();
jest.useRealTimers();
});

test('display one dimension slider and mappings for X and Y axes when visualizing 3D dataset as Matrix', async () => {
Expand Down
13 changes: 8 additions & 5 deletions packages/app/src/dimension-mapper/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useState } from 'react';
import { useDebouncedState } from '@react-hookz/web';

import type { DimensionMapping } from './models';

export function useDimMappingState(dims: number[], axesCount: number) {
return useState<DimensionMapping>([
...Array.from({ length: dims.length - axesCount }, () => 0),
...['y' as const, 'x' as const].slice(-axesCount),
]);
return useDebouncedState<DimensionMapping>(
[
...Array.from({ length: dims.length - axesCount }, () => 0),
...['y' as const, 'x' as const].slice(-axesCount),
],
100
);
}

0 comments on commit 7a79bf1

Please sign in to comment.