Skip to content

Commit

Permalink
test: spice up SelectionToolbarItem (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
stoffeastrom committed Dec 6, 2019
1 parent 75d066d commit 3292d43
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 34 deletions.
9 changes: 7 additions & 2 deletions apis/nucleus/src/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import { Grid, Typography } from '@material-ui/core';

import STToolbar from './SelectionToolbar';
import SelectionToolbarWithDefault from './SelectionToolbar';

const Header = ({ layout, sn }) => {
const showTitle = layout && layout.showTitles && !!layout.title;
Expand All @@ -26,7 +26,12 @@ const Header = ({ layout, sn }) => {
</Grid>
<Grid item style={{ whiteSpace: 'nowrap', minHeight: '32px' }}>
{showInSelectionActions && (
<STToolbar inline api={sn.component.selections} xItems={sn.selectionToolbar.items} />
<SelectionToolbarWithDefault
inline
layout={layout}
api={sn.component.selections}
xItems={sn.selectionToolbar.items}
/>
)}
</Grid>
</Grid>
Expand Down
12 changes: 4 additions & 8 deletions apis/nucleus/src/components/SelectionToolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import { clearSelections } from '@nebula.js/ui/icons/clear-selections';
import LocaleContext from '../contexts/LocaleContext';
import Item from './SelectionToolbarItem';

const SelectionToolbar = React.forwardRef(({ items }, ref) => {
const SelectionToolbar = React.forwardRef(({ layout, items }, ref) => {
return (
<>
{items.map((e, ix) => (
<Item key={e.key} item={e} ref={ix === 0 ? ref : null} />
<Item key={e.key} layout={layout} item={e} ref={ix === 0 ? ref : null} />
))}
</>
);
});

const SelectionToolbarWithDefault = ({ api, xItems = [], onCancel = () => {}, onConfirm = () => {} }) => {
const SelectionToolbarWithDefault = ({ layout, api, xItems = [], onCancel = () => {}, onConfirm = () => {} }) => {
const translator = useContext(LocaleContext);

const items = [
Expand All @@ -27,7 +27,6 @@ const SelectionToolbarWithDefault = ({ api, xItems = [], onCancel = () => {}, on
label: translator.get('Selection.Clear'),
icon: 'clear-selections',
enabled: () => api.canClear(),
disabled: !api.canClear(),
action: () => api.clear(),
getSvgIconShape: clearSelections,
},
Expand All @@ -37,7 +36,6 @@ const SelectionToolbarWithDefault = ({ api, xItems = [], onCancel = () => {}, on
label: translator.get('Selection.Cancel'),
icon: 'close',
enabled: () => api.canCancel(),
disabled: !api.canCancel(),
action: () => {
api.cancel();
onCancel();
Expand All @@ -50,16 +48,14 @@ const SelectionToolbarWithDefault = ({ api, xItems = [], onCancel = () => {}, on
label: translator.get('Selection.Confirm'),
icon: 'tick',
enabled: () => api.canConfirm(),
disabled: !api.canConfirm(),
action: () => {
api.confirm();
onConfirm();
},
getSvgIconShape: tick,
},
];

return <SelectionToolbar items={items} />;
return <SelectionToolbar layout={layout} items={items} />;
};

export default SelectionToolbarWithDefault;
Expand Down
39 changes: 17 additions & 22 deletions apis/nucleus/src/components/SelectionToolbarItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,30 @@ const useStyles = makeStyles(theme => ({
},
}));

const Item = props => {
const initialValue =
typeof props.item.disabled !== 'undefined' ? props.item.disabled : props.item.enabled && !props.item.enabled();
const [disabled, setDisabled] = useState(initialValue);
if (disabled !== props.item.disabled) {
setDisabled(props.item.disabled);
}
const Item = ({ layout, item }) => {
const getDisabled = () => (typeof item.enabled === 'function' ? !item.enabled() : false);
const [disabled, setDisabled] = useState(getDisabled());

const onChanged = () => {
setDisabled(getDisabled());
};
// Handle changed from action-hero
useEffect(() => {
let onChange;
if (props.item && props.item.action && props.item.on) {
onChange = () => {
setDisabled(props.item.enabled && !props.item.enabled());
if (item && item.action && item.on) {
item.on('changed', onChanged);
return () => {
item.removeListener('changed', onChanged);
};

props.item.on('changed', onChange);
}

return () => {
if (onChange && props.item.removeListener) {
props.item.removeListener('changed', onChange);
}

onChange = null;
};
return undefined;
}, []);

// Handle layout changed
useEffect(() => {
onChanged();
}, [layout]);

const { icon } = useStyles();
const { item } = props;
const hasSvgIconShape = typeof item.getSvgIconShape === 'function';

if (item.type === 'menu-icon-button') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import { create, act } from 'react-test-renderer';
import { IconButton, Button, MenuItem } from '@material-ui/core';

const SvgIcon = () => 'svgicon';

const [{ default: SelectionToolbarItem }] = aw.mock(
[
[require.resolve('@nebula.js/ui/theme'), () => ({ makeStyles: () => () => ({ icon: 'icon' }) })],
[require.resolve('@nebula.js/ui/icons/SvgIcon'), () => SvgIcon],
],
['../SelectionToolbarItem']
);

describe('<SelectionToolbarItem />', () => {
let sandbox;
let renderer;
let render;
beforeEach(() => {
sandbox = sinon.createSandbox();
render = async (item, layout) => {
await act(async () => {
renderer = create(<SelectionToolbarItem item={item} layout={layout} />);
});
};
});
afterEach(() => {
sandbox.restore();
renderer.unmount();
});
it('should render default', async () => {
const action = sandbox.spy();
await render({ action });
const types = renderer.root.findAllByType(IconButton);
expect(types).to.have.length(1);
types[0].props.onClick();
expect(action.callCount).to.equal(1);
});
it('should listen on changed', async () => {
const action = sandbox.spy();
const getSvgIconShape = sandbox.spy();
const on = sandbox.spy();
const removeListener = sandbox.spy();
const enabled = sandbox.stub().returns(true);
await render({ on, removeListener, action, enabled, getSvgIconShape });
const types = renderer.root.findAllByType(IconButton);
expect(types).to.have.length(1);
expect(types[0].props.disabled).to.equal(false);
enabled.returns(false);
on.callArg(1);
expect(types[0].props.disabled).to.equal(true);
});
it('should render button', async () => {
const action = sandbox.spy();
await render({ type: 'button', label: 'foo', color: 'purple', action });
const types = renderer.root.findAllByType(Button);
expect(types).to.have.length(1);
expect(types[0].props).to.containSubset({
title: 'foo',
variant: 'contained',
style: {
backgroundColor: 'purple',
},
});
types[0].props.onClick();
expect(action.callCount).to.equal(1);
});
it('should render button with svg icon', async () => {
const getSvgIconShape = sandbox.spy();
await render({ type: 'button', label: 'foo', color: 'purple', getSvgIconShape });
const types = renderer.root.findAllByType(Button);
expect(types[0].props.children).to.equal('svgicon');
});
it('should render menu icon button', async () => {
const action = sandbox.spy();
await render({ type: 'menu-icon-button', action });
const types = renderer.root.findAllByType(MenuItem);
expect(types).to.have.length(1);
types[0].props.onClick();
expect(action.callCount).to.equal(1);
});
});
5 changes: 3 additions & 2 deletions apis/nucleus/src/components/listbox/ListBoxPopover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function ListBoxPopover({ alignTo, show, close, app, fieldName, s
type: 'icon-button',
label: translator.get('Selection.Menu'),
getSvgIconShape: more,
disabled: isLocked,
enabled: () => isLocked,
action: () => setShowSelectionsMenu(!showSelectionsMenu),
};

Expand Down Expand Up @@ -141,6 +141,7 @@ export default function ListBoxPopover({ alignTo, show, close, app, fieldName, s
<Grid item xs />
<Grid item>
<SelectionToolbarWithDefault
layout={layout}
api={sel}
xItems={[moreItem]}
onConfirm={popoverClose}
Expand Down Expand Up @@ -172,7 +173,7 @@ export default function ListBoxPopover({ alignTo, show, close, app, fieldName, s
}}
>
<MenuList>
<SelectionToolbar items={listboxSelectionToolbarItems} />
<SelectionToolbar layout={layout} items={listboxSelectionToolbarItems} />
</MenuList>
</Popover>
)}
Expand Down

0 comments on commit 3292d43

Please sign in to comment.