mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2026-05-26 04:00:15 -04:00
fix(listbox): detach toolbar on resize (#1232)
* fix: detach toolbar on resize * fix: add logical or
This commit is contained in:
@@ -22,6 +22,7 @@ import createSelectionState from './hooks/selections/selectionState';
|
||||
import { CELL_PADDING_LEFT, ICON_WIDTH, ICON_PADDING, BUTTON_ICON_WIDTH } from './constants';
|
||||
import useTempKeyboard from './components/useTempKeyboard';
|
||||
import ListBoxError from './components/ListBoxError';
|
||||
import useRect from '../../hooks/useRect';
|
||||
|
||||
const PREFIX = 'ListBoxInline';
|
||||
const classes = {
|
||||
@@ -110,6 +111,7 @@ function ListBoxInline({ options, layout }) {
|
||||
theme.listBox = addListboxTheme(themeApi);
|
||||
|
||||
const containerRef = useRef();
|
||||
const [containerRectRef, containerRect] = useRect();
|
||||
const [searchContainer, searchContainerRef] = useRefWithCallback();
|
||||
|
||||
const [showToolbar, setShowToolbar] = useState(false);
|
||||
@@ -125,6 +127,7 @@ function ListBoxInline({ options, layout }) {
|
||||
const isModalMode = useCallback(() => isModal({ app, appSelections }), [app, appSelections]);
|
||||
const isInvalid = layout?.qListObject.qDimensionInfo.qError;
|
||||
const errorText = isInvalid && constraints.active ? 'Visualization.Invalid.Dimension' : 'Visualization.Incomplete';
|
||||
const [isToolbarDetached, setIsToolbarDetached] = useState(false);
|
||||
|
||||
const { handleKeyDown, handleOnMouseEnter, handleOnMouseLeave, globalKeyDown } = useMemo(
|
||||
() =>
|
||||
@@ -207,13 +210,28 @@ function ListBoxInline({ options, layout }) {
|
||||
}
|
||||
}, [searchContainer && searchContainer.current, showSearch, search, focusSearch]);
|
||||
|
||||
const { wildCardSearch, searchEnabled, layoutOptions = {} } = layout ?? {};
|
||||
const showSearchIcon = searchEnabled !== false && search === 'toggle';
|
||||
const isLocked = layout?.qListObject?.qDimensionInfo?.qLocked === true;
|
||||
const showSearchOrLockIcon = isLocked || showSearchIcon;
|
||||
const isDrillDown = layout?.qListObject?.qDimensionInfo?.qGrouping === 'H';
|
||||
const showIcons = showSearchOrLockIcon || isDrillDown;
|
||||
const iconsWidth = (showSearchOrLockIcon ? BUTTON_ICON_WIDTH : 0) + (isDrillDown ? ICON_WIDTH + ICON_PADDING : 0); // Drill-down icon needs padding right so there is space between the icon and the title
|
||||
|
||||
useEffect(() => {
|
||||
if (!titleRef.current || !containerRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDetached = showToolbarDetached({ containerRect, titleRef, iconsWidth });
|
||||
setIsToolbarDetached(isDetached);
|
||||
}, [titleRef.current, containerRect]);
|
||||
|
||||
if (!model || !layout || !translator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isLocked = layout.qListObject.qDimensionInfo.qLocked === true;
|
||||
const isRtl = direction === 'rtl';
|
||||
const isDrillDown = layout.qListObject.qDimensionInfo.qGrouping === 'H';
|
||||
const listboxSelectionToolbarItems = createListboxSelectionToolbar({
|
||||
layout,
|
||||
model,
|
||||
@@ -221,7 +239,6 @@ function ListBoxInline({ options, layout }) {
|
||||
selectionState,
|
||||
});
|
||||
|
||||
const { wildCardSearch, searchEnabled, layoutOptions = {} } = layout;
|
||||
const showTitle = true;
|
||||
const showSearchToggle = search === 'toggle' && showSearch;
|
||||
const searchVisible = (search === true || showSearchToggle) && !selectDisabled() && searchEnabled !== false;
|
||||
@@ -258,13 +275,8 @@ function ListBoxInline({ options, layout }) {
|
||||
});
|
||||
|
||||
const shouldAutoFocus = searchVisible && search === 'toggle';
|
||||
const showSearchIcon = searchEnabled !== false && search === 'toggle';
|
||||
const showSearchOrLockIcon = isLocked || showSearchIcon;
|
||||
const showIcons = showSearchOrLockIcon || isDrillDown;
|
||||
const iconsWidth = (showSearchOrLockIcon ? BUTTON_ICON_WIDTH : 0) + (isDrillDown ? ICON_WIDTH + ICON_PADDING : 0); // Drill-down icon needs padding right so there is space between the icon and the title
|
||||
const headerPaddingLeft = CELL_PADDING_LEFT - (showSearchOrLockIcon ? ICON_PADDING : 0);
|
||||
const headerPaddingRight = isRtl ? CELL_PADDING_LEFT - (showIcons ? ICON_PADDING : 0) : 0;
|
||||
const isDetached = showToolbarDetached({ containerRef, titleRef, iconsWidth });
|
||||
|
||||
// Add a container padding for grid mode to harmonize with the grid item margins (should sum to 8px).
|
||||
const isGridMode = layoutOptions?.dataLayout === 'grid';
|
||||
@@ -310,7 +322,10 @@ function ListBoxInline({ options, layout }) {
|
||||
onKeyDown={handleKeyDown}
|
||||
onMouseEnter={handleOnMouseEnter}
|
||||
onMouseLeave={handleOnMouseLeave}
|
||||
ref={containerRef}
|
||||
ref={(el) => {
|
||||
containerRef.current = el;
|
||||
containerRectRef(el);
|
||||
}}
|
||||
>
|
||||
{showToolbarWithTitle && (
|
||||
<Grid
|
||||
@@ -350,7 +365,7 @@ function ListBoxInline({ options, layout }) {
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<ActionsToolbar direction={direction} {...getActionToolbarProps(isDetached)} />
|
||||
<ActionsToolbar direction={direction} {...getActionToolbarProps(isToolbarDetached)} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@@ -137,11 +137,11 @@ describe('<ListboxInline />', () => {
|
||||
|
||||
useCallback
|
||||
.mockImplementationOnce((effectFunc, watchArr) => {
|
||||
expect(watchArr[1].key).toBe('model');
|
||||
expect(watchArr).toHaveLength(0);
|
||||
return effectFunc;
|
||||
})
|
||||
.mockImplementationOnce((effectFunc, watchArr) => {
|
||||
expect(watchArr[1].key).toBe('model');
|
||||
expect(watchArr).toHaveLength(0);
|
||||
return effectFunc;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,20 +4,20 @@ const iconsWidth = 28;
|
||||
|
||||
describe('show listbox toolbar detached', () => {
|
||||
it('should return true if there is not enough space for toolbar', () => {
|
||||
const containerRef = { current: { clientWidth: 100 } };
|
||||
const containerRect = { width: 100 };
|
||||
const titleRef = { current: { clientWidth: 60, scrollWidth: 80, offsetWidth: 81 } };
|
||||
expect(showToolbarDetached({ containerRef, titleRef, iconsWidth })).toStrictEqual(true);
|
||||
expect(showToolbarDetached({ containerRect, titleRef, iconsWidth })).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('should return true if title is truncated', () => {
|
||||
const containerRef = { current: { clientWidth: 300 } };
|
||||
const containerRect = { width: 300 };
|
||||
const titleRef = { current: { clientWidth: 60, scrollWidth: 200, offsetWidth: 199 } };
|
||||
expect(showToolbarDetached({ containerRef, titleRef, iconsWidth })).toStrictEqual(true);
|
||||
expect(showToolbarDetached({ containerRect, titleRef, iconsWidth })).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('should return false if there is enough space for title and toolbar', () => {
|
||||
const containerRef = { current: { clientWidth: 300 } };
|
||||
const containerRect = { width: 300 };
|
||||
const titleRef = { current: { clientWidth: 60, scrollWidth: 200, offsetWidth: 201 } };
|
||||
expect(showToolbarDetached({ containerRef, titleRef, iconsWidth })).toStrictEqual(false);
|
||||
expect(showToolbarDetached({ containerRect, titleRef, iconsWidth })).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export default function showToolbarDetached({ containerRef, titleRef, iconsWidth }) {
|
||||
const containerWidth = containerRef?.current?.clientWidth ?? 0;
|
||||
export default function showToolbarDetached({ containerRect, titleRef, iconsWidth }) {
|
||||
const containerWidth = containerRect.width;
|
||||
const padding = 16;
|
||||
const contentWidth = (titleRef?.current?.clientWidth ?? 0) + iconsWidth + padding;
|
||||
const actionToolbarWidth = 128;
|
||||
const notSufficientSpace = containerWidth < contentWidth + actionToolbarWidth;
|
||||
const isTruncated = titleRef?.current?.scrollWidth > titleRef?.current?.offsetWidth;
|
||||
const isDetached = !!(notSufficientSpace | isTruncated);
|
||||
const isDetached = !!(notSufficientSpace || isTruncated);
|
||||
|
||||
return isDetached;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user