feat: auto confirm selections only on click outside listbox (#1355)

* feat: implement auto confirm

* refactor: auto confirm only on click outside

* refactor: do not render at all if autoConfirm

* refactor: smaller min height and use constants

* test: fix selector

* fix: use correct attribute key

* test: use another testid

* fix: increase minHeight for small grid listboxes

* fix: prevent cut listboxes
This commit is contained in:
Johan Lahti
2023-10-03 09:34:08 +02:00
committed by GitHub
parent f6f4db55ac
commit 9d564d1516
9 changed files with 51 additions and 36 deletions

View File

@@ -79,7 +79,7 @@ function ActionsToolbar({
focusHandler = null,
actionsRefMock = null, // for testing
direction = 'ltr',
autoConfirm,
autoConfirm = false,
}) {
const defaultSelectionActions = useDefaultSelectionActions(selections);
@@ -131,12 +131,7 @@ function ActionsToolbar({
}, []);
if (autoConfirm) {
const canConfirm = selections.show;
if (canConfirm) {
const confirm = defaultSelectionActions.find((o) => o.key === 'confirm') || { action: () => {} };
confirm.action();
}
return null;
return undefined;
}
let moreEnabled = more.enabled;
@@ -224,13 +219,15 @@ function ActionsToolbar({
transformOrigin={popoverTransformOrigin}
hideBackdrop
style={popoverStyle}
PaperProps={{
id: 'njs-action-toolbar-popover',
'data-testid': 'njs-action-toolbar-popover',
className: ActionToolbarElement.className,
style: {
pointerEvents: 'auto',
padding: theme.spacing(0.5, 0.25),
slotProps={{
paper: {
id: 'njs-action-toolbar-popover',
'data-testid': 'njs-action-toolbar-popover',
className: ActionToolbarElement.className,
style: {
pointerEvents: 'auto',
padding: theme.spacing(0.5, 0.25),
},
},
}}
>

View File

@@ -19,7 +19,15 @@ import useAppSelections from '../../hooks/useAppSelections';
import showToolbarDetached from './interactions/listbox-show-toolbar-detached';
import getListboxActionProps from './interactions/listbox-get-action-props';
import createSelectionState from './hooks/selections/selectionState';
import { CELL_PADDING_LEFT, ICON_WIDTH, ICON_PADDING, BUTTON_ICON_WIDTH, HEADER_PADDING_RIGHT } from './constants';
import {
CELL_PADDING_LEFT,
ICON_WIDTH,
ICON_PADDING,
BUTTON_ICON_WIDTH,
HEADER_PADDING_RIGHT,
DENSE_ROW_HEIGHT,
SCROLL_BAR_WIDTH,
} from './constants';
import useTempKeyboard from './components/useTempKeyboard';
import ListBoxError from './components/ListBoxError';
import useRect from '../../hooks/useRect';
@@ -258,10 +266,6 @@ function ListBoxInline({ options, layout }) {
const showSearchToggle = search === 'toggle' && showSearch;
const searchVisible = (search === true || showSearchToggle) && !selectDisabled() && searchEnabled !== false;
const dense = layoutOptions.dense ?? false;
const searchHeight = dense ? 27 : 40;
const extraheight = dense ? 39 : 49;
const searchAddHeight = searchVisible ? searchHeight : 0;
const minHeight = showToolbarWithTitle ? 49 + searchAddHeight + extraheight : 0;
const headerHeight = 32;
const onShowSearch = () => {
@@ -278,8 +282,8 @@ function ListBoxInline({ options, layout }) {
}
};
const getActionToolbarProps = (isDetached) =>
getListboxActionProps({
const getActionToolbarProps = (isDetached) => ({
...getListboxActionProps({
isDetached: isPopover ? false : isDetached,
showToolbar,
containerRef,
@@ -287,8 +291,9 @@ function ListBoxInline({ options, layout }) {
listboxSelectionToolbarItems,
selections,
keyboard,
autoConfirm,
});
}),
autoConfirm,
});
const shouldAutoFocus = searchVisible && search === 'toggle';
@@ -336,7 +341,7 @@ function ListBoxInline({ options, layout }) {
direction="column"
gap={0}
containerPadding={containerPadding}
style={{ height: '100%', minHeight: `${minHeight}px`, flexFlow: 'column nowrap' }}
style={{ height: '100%', flexFlow: 'column nowrap' }}
onKeyDown={handleKeyDown}
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
@@ -393,7 +398,7 @@ function ListBoxInline({ options, layout }) {
item
container
direction="column"
style={{ height: '100%', minHeight: '50px' }}
style={{ height: '100%', minHeight: DENSE_ROW_HEIGHT + SCROLL_BAR_WIDTH }}
role="region"
aria-label={translator.get('Listbox.ResultFilterLabel')}
>

View File

@@ -1,6 +1,15 @@
import calculateColumnMode from './column-mode';
import calculateRowMode from './row-mode';
import { ITEM_MAX_WIDTH, ITEM_MIN_WIDTH, SCROLL_BAR_WIDTH, CHECKBOX_WIDTH, REMOVE_TICK_LIMIT } from '../../constants';
import {
ITEM_MAX_WIDTH,
ITEM_MIN_WIDTH,
SCROLL_BAR_WIDTH,
CHECKBOX_WIDTH,
REMOVE_TICK_LIMIT,
GRID_ROW_HEIGHT,
LIST_ROW_HEIGHT,
DENSE_ROW_HEIGHT,
} from '../../constants';
import useTextWidth from '../../hooks/useTextWidth';
import getMeasureText from '../measure-text';
@@ -35,9 +44,8 @@ export default function useListSizes({ layout, width, height, listCount, count,
const isGridMode = dataLayout === 'grid';
const itemPadding = 4;
const denseItemHeight = 20;
const normalItemHeight = isGridMode ? 32 : 29;
let itemHeight = dense ? denseItemHeight : normalItemHeight;
const normalItemHeight = isGridMode ? GRID_ROW_HEIGHT : LIST_ROW_HEIGHT;
let itemHeight = dense ? DENSE_ROW_HEIGHT : normalItemHeight;
if (isGridMode) {
// Emulate a margin between items using padding, since the list library

View File

@@ -12,3 +12,6 @@ export const frequencyTextNone = '-';
export const barPadPx = 4;
export const barBorderWidthPx = 1;
export const barWithCheckboxLeftPadPx = 29;
export const GRID_ROW_HEIGHT = 32;
export const LIST_ROW_HEIGHT = 29;
export const DENSE_ROW_HEIGHT = 20; // same for both list and grid in dense mode

View File

@@ -91,7 +91,7 @@ const listdef = {
*/
wildCardSearch: false,
/**
* Automatically confirm selections without showing the selections toolbar.
* Automatically confirm selections when clicking outside a listbox, without showing the selections toolbar.
* @type {boolean=}
* @default
*/

View File

@@ -6,10 +6,8 @@ export default function getListboxActionProps({
listboxSelectionToolbarItems,
selections,
keyboard,
autoConfirm,
}) {
return {
autoConfirm,
show: showToolbar && !isDetached,
popover: {
show: showToolbar && isDetached,

View File

@@ -13,7 +13,7 @@
"ListboxProperties": {
"entries": {
"autoConfirm": {
"description": "Automatically confirm selections without showing the selections toolbar.",
"description": "Automatically confirm selections when clicking outside a listbox, without showing the selections toolbar.",
"optional": true,
"defaultValue": false,
"type": "boolean"

View File

@@ -53,6 +53,6 @@ Theme to use when rendering visualization.
Specify an array of available themes in the instance config. Provide the `id` of theme to use as the parameter value.
### `langauge`
### `language`
Language to use when rendering visualization.

View File

@@ -1,4 +1,5 @@
import { test, expect } from '@playwright/test';
import { act } from '@testing-library/react';
import fs from 'fs';
import path from 'path';
@@ -11,7 +12,7 @@ const paths = path.join(__dirname, '__fixtures__');
test.describe('listbox mashup rendering test', () => {
const object = '[data-type="listbox"]';
const listboxSelector = `${object} .listbox-container`;
const toolbarPopoverSelector = '.njs-action-toolbar-popover';
const toolbarPopoverSelector = '[data-testid="njs-action-toolbar-popover"]';
let page;
let destroyServer;
@@ -89,7 +90,10 @@ test.describe('listbox mashup rendering test', () => {
await page.goto(`${url}/listbox/listbox.html?scenario=longTitle`, { waitUntil: 'networkidle' });
const selector = await page.waitForSelector(listboxSelector, { visible: true });
await page.click(listboxSelector);
act(async () => {
await page.click(listboxSelector);
});
await page.waitForSelector(toolbarPopoverSelector);
// Wait for animation
await new Promise((resolve) => {