mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 09:48:18 -05:00
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:
@@ -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),
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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')}
|
||||
>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -6,10 +6,8 @@ export default function getListboxActionProps({
|
||||
listboxSelectionToolbarItems,
|
||||
selections,
|
||||
keyboard,
|
||||
autoConfirm,
|
||||
}) {
|
||||
return {
|
||||
autoConfirm,
|
||||
show: showToolbar && !isDetached,
|
||||
popover: {
|
||||
show: showToolbar && isDetached,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user