Skip to content

Commit

Permalink
refactor: limit selection actions in direct query (#1246)
Browse files Browse the repository at this point in the history
* refactor: compensate for checkboxes in item width

* test: search highlight rendering test

* refactor: disable some select actions in dq mode

* fix: rm test

* refactor: add flags fallback to context

* refactor: send flags through options instead of
  context

* refactor: rm flags completely
  • Loading branch information
johanlahti committed Apr 27, 2023
1 parent 6dbd394 commit ca46370
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 24 deletions.
4 changes: 4 additions & 0 deletions apis/nucleus/src/components/listbox/ListBoxInline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CELL_PADDING_LEFT, ICON_WIDTH, ICON_PADDING, BUTTON_ICON_WIDTH } from '
import useTempKeyboard from './components/useTempKeyboard';
import ListBoxError from './components/ListBoxError';
import useRect from '../../hooks/useRect';
import isDirectQueryEnabled from './utils/is-direct-query';

const PREFIX = 'ListBoxInline';
const classes = {
Expand Down Expand Up @@ -113,6 +114,8 @@ function ListBoxInline({ options, layout }) {
const { translator, keyboardNavigation, themeApi, constraints } = useContext(InstanceContext);
theme.listBox = addListboxTheme(themeApi);

const isDirectQuery = isDirectQueryEnabled({ appLayout: app?.layout });

const containerRef = useRef();
const [containerRectRef, containerRect] = useRect();
const [searchContainer, searchContainerRef] = useRefWithCallback();
Expand Down Expand Up @@ -240,6 +243,7 @@ function ListBoxInline({ options, layout }) {
model,
translator,
selectionState,
isDirectQuery,
});

const showTitle = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as ListBoxModule from '../ListBox';
import * as ListBoxSearchModule from '../components/ListBoxSearch';
import * as listboxSelectionToolbarModule from '../interactions/listbox-selection-toolbar';
import * as addListboxTheme from '../assets/addListboxTheme';
import * as isDirectQueryEnabled from '../utils/is-direct-query';

const virtualizedModule = require('react-virtualized-auto-sizer');
const listboxKeyboardNavigationModule = require('../interactions/keyboard-navigation/keyboard-nav-container');
Expand Down Expand Up @@ -91,6 +92,7 @@ describe('<ListboxInline />', () => {
jest.spyOn(useLayoutModule, 'default').mockImplementation(() => [layout]);
jest.spyOn(listboxKeyboardNavigationModule, 'default').mockImplementation(getListboxInlineKeyboardNavigation);
jest.spyOn(addListboxTheme, 'default').mockImplementation(() => {});
jest.spyOn(isDirectQueryEnabled, 'default').mockImplementation(() => false);

ActionsToolbarModule.default = ActionsToolbar;
ListBoxModule.default = <div className="theListBox" />;
Expand Down Expand Up @@ -192,6 +194,7 @@ describe('<ListboxInline />', () => {
expect(selections.on.mock.calls[0][0]).toBe('activated');
expect(selections.on.mock.calls[1][0]).toBe('deactivated');
expect(selections.removeListener).not.toHaveBeenCalled();
expect(isDirectQueryEnabled.default).toHaveBeenCalled();
});

test('should render properly with search toggle option', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import createListboxSelectionToolbar from '../listbox-selection-toolbar';

describe('getScrollIndex', () => {
let layout;
let model;
let translator;
let selectionState;
let isDirectQuery;

afterEach(() => {
jest.resetAllMocks();
});
afterAll(() => {
jest.restoreAllMocks();
});

beforeEach(() => {
isDirectQuery = false;
layout = {
qListObject: {
qDimensionInfo: {
qStateCounts: {
qOption: 2,
qAlternative: 2,
qExcluded: 2,
qDeselected: 2,
},
qIsOneAndOnlyOne: false,
},
},
};
model = {
selectListObjectAll: jest.fn(),
selectListObjectPossible: jest.fn(),
selectListObjectAlternative: jest.fn(),
selectListObjectExcluded: jest.fn(),
};
translator = {
get: jest.fn((t) => t),
};
selectionState = {
clearItemStates: jest.fn(),
};
});

const create = (overrides = {}) =>
createListboxSelectionToolbar({
layout,
model,
translator,
selectionState,
isDirectQuery,
...overrides,
});

it('should create all actions', () => {
const actions = create();
expect(actions).toHaveLength(4);

const [all, possible, alternative, excluded] = actions;

expect(all).toMatchObject({ key: 'selectAll', label: 'Selection.SelectAll', type: 'menu-icon-button' });
expect(possible).toMatchObject({
key: 'selectPossible',
label: 'Selection.SelectPossible',
type: 'menu-icon-button',
});
expect(alternative).toMatchObject({
key: 'selectAlternative',
label: 'Selection.SelectAlternative',
type: 'menu-icon-button',
});
expect(excluded).toMatchObject({
key: 'selectExcluded',
label: 'Selection.SelectExcluded',
type: 'menu-icon-button',
});

expect(all.enabled()).toEqual(true);
expect(possible.enabled()).toEqual(true);
expect(alternative.enabled()).toEqual(true);
expect(excluded.enabled()).toEqual(true);
});

it('should not create any actions if single select', () => {
layout.qListObject.qDimensionInfo.qIsOneAndOnlyOne = true;
const actions = create({ layout });
expect(actions).toEqual([]);
});

it('should only create two actions in direct query mode', () => {
const actions = create({ isDirectQuery: true });
expect(actions).toHaveLength(2);

const [all, possible] = actions;

expect(all).toMatchObject({ key: 'selectAll', label: 'Selection.SelectAll', type: 'menu-icon-button' });
expect(possible).toMatchObject({
key: 'selectPossible',
label: 'Selection.SelectPossible',
type: 'menu-icon-button',
});
});

describe('test each action', () => {
let all;
let possible;
let alternative;
let excluded;

beforeEach(() => {
const actions = create();
[all, possible, alternative, excluded] = actions;

expect(selectionState.clearItemStates).not.toHaveBeenCalled();
});

afterEach(() => {
expect(selectionState.clearItemStates).toHaveBeenCalledTimes(1);
});

it('select all', () => {
all.action();
expect(model.selectListObjectAll).toHaveBeenCalledTimes(1);
expect(model.selectListObjectAll).toBeCalledWith('/qListObjectDef');
});
it('select possible', () => {
possible.action();
expect(model.selectListObjectPossible).toHaveBeenCalledTimes(1);
expect(model.selectListObjectPossible).toBeCalledWith('/qListObjectDef');
});
it('select alternative', () => {
alternative.action();
expect(model.selectListObjectAlternative).toHaveBeenCalledTimes(1);
expect(model.selectListObjectAlternative).toBeCalledWith('/qListObjectDef');
});
it('select excluded', () => {
excluded.action();
expect(model.selectListObjectExcluded).toHaveBeenCalledTimes(1);
expect(model.selectListObjectExcluded).toBeCalledWith('/qListObjectDef');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { selectAlternative } from '@nebula.js/ui/icons/select-alternative';
import { selectPossible } from '@nebula.js/ui/icons/select-possible';
import { selectExcluded } from '@nebula.js/ui/icons/select-excluded';

export default ({ layout, model, translator, selectionState }) => {
export default ({ layout, model, translator, selectionState, isDirectQuery = false }) => {
if (layout.qListObject.qDimensionInfo.qIsOneAndOnlyOne) {
return [];
}
Expand Down Expand Up @@ -41,27 +41,31 @@ export default ({ layout, model, translator, selectionState }) => {
model.selectListObjectPossible('/qListObjectDef');
},
},
{
key: 'selectAlternative',
type: 'menu-icon-button',
label: translator.get('Selection.SelectAlternative'),
getSvgIconShape: selectAlternative,
enabled: canSelectAlternative,
action: () => {
selectionState.clearItemStates(false);
model.selectListObjectAlternative('/qListObjectDef');
},
},
{
key: 'selectExcluded',
type: 'menu-icon-button',
label: translator.get('Selection.SelectExcluded'),
getSvgIconShape: selectExcluded,
enabled: canSelectExcluded,
action: () => {
selectionState.clearItemStates(false);
model.selectListObjectExcluded('/qListObjectDef');
},
},
];
isDirectQuery
? false
: {
key: 'selectAlternative',
type: 'menu-icon-button',
label: translator.get('Selection.SelectAlternative'),
getSvgIconShape: selectAlternative,
enabled: canSelectAlternative,
action: () => {
selectionState.clearItemStates(false);
model.selectListObjectAlternative('/qListObjectDef');
},
},
isDirectQuery
? false
: {
key: 'selectExcluded',
type: 'menu-icon-button',
label: translator.get('Selection.SelectExcluded'),
getSvgIconShape: selectExcluded,
enabled: canSelectExcluded,
action: () => {
selectionState.clearItemStates(false);
model.selectListObjectExcluded('/qListObjectDef');
},
},
].filter(Boolean);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { default: isDirectQueryEnabled } = require('../is-direct-query');

describe('isDirectQuery', () => {
const appLayout = { qIsDirectQueryMode: false };

test('should return false since app is not a DQ app', async () => {
const dq = isDirectQueryEnabled({ appLayout });
expect(dq).toEqual(false);
});

test('should return true since it is a DQ app', async () => {
appLayout.qIsDirectQueryMode = true;
const dq = isDirectQueryEnabled({ appLayout });
expect(dq).toEqual(true);
});
});
4 changes: 4 additions & 0 deletions apis/nucleus/src/components/listbox/utils/is-direct-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default function isDirectQueryEnabled({ appLayout }) {
const isDQ = !!appLayout?.qIsDirectQueryMode;
return isDQ;
}

0 comments on commit ca46370

Please sign in to comment.