Commit a5f2952e authored by Lyza Danger Gardner's avatar Lyza Danger Gardner Committed by Lyza Gardner

Add user filtering to Notebook

parent ef4575c5
import { createElement } from 'preact';
import { useStoreProxy } from '../store/use-store';
import { useUserFilterOptions } from './hooks/use-filter-options';
import FilterSelect from './filter-select';
/**
* @typedef {import('../store/modules/filters').FilterOption} FilterOption
*/
/**
* Filters for the Notebook
*/
function NotebookFilters() {
const store = useStoreProxy();
const userFilter = store.getFilter('user');
const userFilterOptions = useUserFilterOptions();
return (
<FilterSelect
defaultOption={{ value: '', display: 'Everybody' }}
icon="profile"
onSelect={userFilter => store.setFilter('user', userFilter)}
options={userFilterOptions}
selectedOption={userFilter}
title="Filter by user"
/>
);
}
NotebookFilters.propTypes = {};
export default NotebookFilters;
......@@ -6,6 +6,7 @@ import { withServices } from '../util/service-context';
import useRootThread from './hooks/use-root-thread';
import { useStoreProxy } from '../store/use-store';
import NotebookFilters from './notebook-filters';
import NotebookResultCount from './notebook-result-count';
import ThreadList from './thread-list';
......@@ -44,6 +45,9 @@ function NotebookView({ loadAnnotationsService }) {
<header className="notebook-view__heading">
<h1>{groupName}</h1>
</header>
<div className="notebook-view__filters">
<NotebookFilters />
</div>
<div className="notebook-view__results">
<NotebookResultCount />
</div>
......
import { mount } from 'enzyme';
import { createElement } from 'preact';
import NotebookFilters from '../notebook-filters';
import { $imports } from '../notebook-filters';
import mockImportedComponents from '../../../test-util/mock-imported-components';
describe('NotebookFilters', () => {
let fakeStore;
let fakeUseUserFilterOptions;
const createComponent = () => {
return mount(<NotebookFilters />);
};
beforeEach(() => {
fakeUseUserFilterOptions = sinon.stub().returns([]);
fakeStore = {
getFilter: sinon.stub().returns(undefined),
setFilter: sinon.stub(),
};
$imports.$mock(mockImportedComponents());
$imports.$mock({
'./hooks/use-filter-options': {
useUserFilterOptions: fakeUseUserFilterOptions,
},
'../store/use-store': { useStoreProxy: () => fakeStore },
});
});
afterEach(() => {
$imports.$restore();
});
it('should render a user filter with options', () => {
fakeUseUserFilterOptions.returns([
{ display: 'One User', value: 'oneuser' },
]);
const wrapper = createComponent();
const props = wrapper.find('FilterSelect').props();
assert.deepEqual(props.options[0], {
display: 'One User',
value: 'oneuser',
});
assert.deepEqual(props.defaultOption, { value: '', display: 'Everybody' });
assert.equal(props.icon, 'profile');
assert.equal(props.title, 'Filter by user');
assert.equal(props.options.length, 1);
assert.isUndefined(props.selectedOption);
});
it('should render the filter with a selected option if a user filter is applied', () => {
fakeUseUserFilterOptions.returns([
{ display: 'One User', value: 'oneuser' },
]);
fakeStore.getFilter
.withArgs('user')
.returns({ display: 'One User', value: 'oneuser' });
const wrapper = createComponent();
assert.deepEqual(wrapper.find('FilterSelect').props().selectedOption, {
display: 'One User',
value: 'oneuser',
});
});
it('should set a user filter when a user is selected', () => {
fakeUseUserFilterOptions.returns([
{ display: 'One User', value: 'oneuser' },
]);
const wrapper = createComponent();
wrapper
.find('FilterSelect')
.props()
.onSelect({ display: 'One User', value: 'oneuser' });
assert.calledOnce(fakeStore.setFilter);
assert.calledWith(
fakeStore.setFilter,
'user',
sinon.match({ display: 'One User', value: 'oneuser' })
);
});
});
......@@ -68,5 +68,9 @@ describe('NotebookView', () => {
const wrapper = createComponent();
assert.isTrue(wrapper.find('NotebookResultCount').exists());
});
it('renders filters', () => {
const wrapper = createComponent();
assert.isTrue(wrapper.find('NotebookFilters').exists());
});
});
......@@ -10,6 +10,7 @@
'results'
'items';
/* TODO: find a better place for these heading styles */
h1 {
display: inline;
font-size: var.$font-size--heading;
......@@ -28,6 +29,10 @@
font-weight: bold;
}
&__filters {
grid-area: filters;
}
&__results {
grid-area: results;
}
......@@ -38,13 +43,16 @@
@include responsive.tablet-and-up {
grid-template-areas:
'heading results'
'heading heading'
'filters results'
'items items';
&__filters {
justify-self: start;
}
&__results {
// Right-aligned when sharing a row with the heading
text-align: right;
align-self: end;
justify-self: end;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment