Skip to content

Commit

Permalink
Merge pull request #392 from folio-org/yd/checkbox-filter
Browse files Browse the repository at this point in the history
STCOM-440: Add checkbox filter
  • Loading branch information
Yurii Danylenko authored Jan 15, 2019
2 parents a3141f1 + 969371f commit bc1785a
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 0 deletions.
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ export { default as PasswordValidationField } from './lib/PasswordValidationFiel
export { default as ProxyManager } from './lib/ProxyManager';

export { default as SearchAndSort } from './lib/SearchAndSort';

export { default as CheckboxFilter } from './lib/SearchAndSort/components/CheckboxFilter';

export { default as MultiSelectionFilter } from './lib/SearchAndSort/components/MultiSelectionFilter';

export { default as makeQueryFunction } from './lib/SearchAndSort/makeQueryFunction';

export { default as Settings } from './lib/Settings';
Expand Down
63 changes: 63 additions & 0 deletions lib/SearchAndSort/components/CheckboxFilter/CheckboxFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import PropTypes from 'prop-types';

import { Checkbox } from '@folio/stripes-components';

export default class CheckboxFilter extends React.Component {
static propTypes = {
dataOptions: PropTypes.arrayOf(PropTypes.shape({
disabled: PropTypes.bool,
label: PropTypes.node,
readOnly: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})).isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
selectedValues: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
};

static defaultProps = {
selectedValues: [],
}

createOnChangeHandler = (filterValue) => (e) => {
const {
name,
selectedValues,
onChange,
} = this.props;

const newValues = e.target.checked
? [...selectedValues, filterValue]
: selectedValues.filter((value) => value !== filterValue);

onChange({
name,
values: newValues,
});
};

render() {
const {
dataOptions,
selectedValues,
} = this.props;

return (
dataOptions.map(({ value, label, disabled, readOnly }) => {
return (
<Checkbox
{...this.props}
data-test-checkbox-filter-data-option={value}
key={value}
label={label}
disabled={disabled}
readOnly={readOnly}
checked={selectedValues.includes(value)}
onChange={this.createOnChangeHandler(value)}
/>
);
})
);
}
}
1 change: 1 addition & 0 deletions lib/SearchAndSort/components/CheckboxFilter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './CheckboxFilter';
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from 'react';
import { describe, beforeEach, it } from '@bigtest/mocha';
import { expect } from 'chai';
import sinon from 'sinon';

import { mountWithContext } from '@folio/stripes-components/tests/helpers';

import CheckboxFilter from '../CheckboxFilter';
import CheckboxFilterInteractor from './interactor';


describe('CheckboxFilter', () => {
const checkboxFilter = new CheckboxFilterInteractor();
const onChangeHandler = sinon.spy();

const renderComponent = (props) => {
return mountWithContext(
<CheckboxFilter
name="language"
onChange={onChangeHandler}
dataOptions={[
{ value: 'en', label: 'English' },
{ value: 'fr', label: 'French' },
{ value: 'ge', label: 'German' },
{ value: 'it', label: 'Italian' },
]}
{...props}
/>
);
};

beforeEach(async () => {
await renderComponent();

onChangeHandler.resetHistory();
});

it('should render all filter data options', () => {
expect(checkboxFilter.dataOptionsCount).to.equal(4);
});


describe('after click on readonly data option', () => {
beforeEach(async () => {
await renderComponent({
dataOptions: [
{
value: 'en',
label: 'English',
readOnly: true,
},
]
});
await checkboxFilter.dataOptions(0).click();
});

it('should not call onChange callback', () => {
expect(onChangeHandler.called).to.equal(false);
});
});

describe('after click on disabled data option', () => {
beforeEach(async () => {
await renderComponent({
dataOptions: [
{
value: 'en',
label: 'English',
disabled: true,
},
]
});
await checkboxFilter.dataOptions(0).click();
});

it('should not call onChange callback', () => {
expect(onChangeHandler.called).to.equal(false);
});
});

describe('when there are selected data options', () => {
beforeEach(async () => {
await renderComponent({
selectedValues: ['fr', 'ge'],
});
});

it('should render selected data options', () => {
expect(checkboxFilter.dataOptions(1).isSelected).to.equal(true);
expect(checkboxFilter.dataOptions(2).isSelected).to.equal(true);
});

describe('after click on unselected data option', () => {
beforeEach(async () => {
await checkboxFilter.dataOptions(0).click();
});

it('should raise filter name and new selected values to onChange callback', () => {
expect(onChangeHandler.args[0]).to.deep.equal([{
name: 'language',
values: ['fr', 'ge', 'en'],
}]);
});
});

describe('after click on selected option', () => {
beforeEach(async () => {
await checkboxFilter.dataOptions(1).click();
});

it('should raise filter name with the remaining selected values to onChange callback', () => {
expect(onChangeHandler.args[0]).to.deep.equal([{
name: 'language',
values: ['ge'],
}]);
});
});
});
});
19 changes: 19 additions & 0 deletions lib/SearchAndSort/components/CheckboxFilter/tests/interactor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
count,
clickable,
interactor,
collection,
is
} from '@bigtest/interactor';

const DataOption = interactor(class DataOptionInteractor {
click = clickable();
isSelected = is('[checked]');
isDisabled = is('disabled');
isReadOnly = is('readonly')
});

export default interactor(class CheckboxFilterInteractor {
dataOptionsCount = count('[data-test-checkbox-filter-data-option]');
dataOptions = collection('[data-test-checkbox-filter-data-option]', DataOption);
});

0 comments on commit bc1785a

Please sign in to comment.