fix(listbox): detach toolbar on resize (#1232)

* fix: detach toolbar on resize

* fix: add logical or
This commit is contained in:
Daniel Sjöstrand
2023-04-24 12:34:21 +02:00
committed by GitHub
parent a3fafd2eed
commit 4df9595fb2
4 changed files with 36 additions and 21 deletions

View File

@@ -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>
)}

View File

@@ -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;
});
});

View File

@@ -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);
});
});

View File

@@ -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;
}