Skip to content
This repository has been archived by the owner on Jan 15, 2021. It is now read-only.

Commit

Permalink
feat(Picky): Added render prop for select all
Browse files Browse the repository at this point in the history
Can now custom render select all.

fix #27
  • Loading branch information
Aidurber committed Jan 10, 2018
1 parent c6c7494 commit a4c83c9
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 30 deletions.
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Picky ☜

Yet another React select list with virtualised option.

[![Build Status](https://travis-ci.org/Aidurber/react-picky.svg?branch=master)](https://travis-ci.org/Aidurber/react-picky)
[![codecov](https://codecov.io/gh/Aidurber/react-picky/branch/master/graph/badge.svg)](https://codecov.io/gh/Aidurber/react-picky)
[![license](https://img.shields.io/github/license/aidurber/react-picky.svg)]()
[![npm version](https://badge.fury.io/js/react-picky.svg)](https://badge.fury.io/js/react-picky)

Yet another React select list.

## Motivation

When dealing with medium+ length select lists, especially multi-select lists. The common approach is to use tags e.g.
Expand Down Expand Up @@ -116,7 +116,8 @@ Picky.propTypes = {
virtual: PropTypes.bool,
manySelectedPlaceholder: PropTypes.string,
allSelectedPlaceholder: PropTypes.string,
selectAllText: PropTypes.string
selectAllText: PropTypes.string,
renderSelectAll: PropTypes.func
};
```

Expand Down Expand Up @@ -146,9 +147,12 @@ Picky.propTypes = {
* `manySelectedPlaceholder` - Default "%s selected" where %s is the number of items selected. This gets used when the number if items selected is more than the `numberDisplayed` prop and when all options are not selected.
* `allSelectedPlaceholder` - Default "%s selected" where %s is the number of items selected. This gets used when all options are selected.
* `selectAllText` - Default "Select all", use this to override "Select all" text from top of dropdown when included with `includeSelectAll`.
* `renderSelectAll` - Used for rendering a custom select all

## Custom rendering

### Items

You can render out custom items for the dropdown.

**Example**
Expand Down Expand Up @@ -207,6 +211,44 @@ style, isSelected, item, labelKey, valueKey, selectValue, multiple
* If your rendered item affects the height of the item in anyway. Supply `itemHeight` to Picky.
* If you wish to show a radio button or a checkbox, be sure to add `readOnly` prop to the input.

### Select All

```javascript
<Picky
// ...
renderSelectAll={({
filtered,
tabIndex,
allSelected,
toggleSelectAll,
multiple
}) => {
// Don't show if single select or items have been filtered.
if (multiple && !filtered) {
return (
<div
tabIndex={tabIndex}
role="option"
className={allSelected ? 'option selected' : 'option'}
onClick={toggleSelectAll}
onKeyPress={toggleSelectAll}
>
<h1>SELECT ALL</h1>
</div>
);
}
}}
/>
```

Gets called with the following properties:

* `filtered`: boolean - true if items have been filtered.
* `allSelected`: boolean true if all items are selected
* `toggleSelectAll`: function selects or deselects all items
* `tabIndex`: number used for specifying tab index
* `multiple`: boolean true if multiselect

# Internals

The component uses [React Virtualized](https://github.com/bvaughn/react-virtualized) for rendering out the items. This is a for a performance gain. You can have 1,000,000 items in the dropdown with no performance drop! It's such a great little library. This is why we have a dropdown height.
28 changes: 19 additions & 9 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ var Picky$1 = function (_React$PureComponent) {
fixedWidth: true
});
_this.toggleDropDown = _this.toggleDropDown.bind(_this);
_this.selectAll = _this.selectAll.bind(_this);
_this.toggleSelectAll = _this.toggleSelectAll.bind(_this);
_this.onFilterChange = _this.onFilterChange.bind(_this);
_this.selectValue = _this.selectValue.bind(_this);
_this.allSelected = _this.allSelected.bind(_this);
Expand Down Expand Up @@ -345,8 +345,8 @@ var Picky$1 = function (_React$PureComponent) {
*/

}, {
key: 'selectAll',
value: function selectAll() {
key: 'toggleSelectAll',
value: function toggleSelectAll() {
var _this3 = this;

this.setState({
Expand Down Expand Up @@ -623,7 +623,8 @@ var Picky$1 = function (_React$PureComponent) {
valueKey = _props2.valueKey,
labelKey = _props2.labelKey,
tabIndex = _props2.tabIndex,
dropdownHeight = _props2.dropdownHeight;
dropdownHeight = _props2.dropdownHeight,
renderSelectAll = _props2.renderSelectAll;
var open = this.state.open;

var ariaOwns = '';
Expand Down Expand Up @@ -674,13 +675,21 @@ var Picky$1 = function (_React$PureComponent) {
'div',
{
className: 'picky__dropdown',
'data-test': 'dropdown',
id: this.state.id + '-list',
style: dropdownStyle
},
includeFilter && React__default.createElement(Filter, {
onFilterChange: filterDebounce > 0 ? debounce(this.onFilterChange, filterDebounce) : this.onFilterChange
}),
includeSelectAll && multiple && !this.state.filtered && React__default.createElement(
renderSelectAll && renderSelectAll({
filtered: this.state.filtered,
allSelected: this.state.allSelected,
toggleSelectAll: this.toggleSelectAll,
tabIndex: tabIndex,
multiple: multiple
}),
!renderSelectAll && includeSelectAll && multiple && !this.state.filtered && React__default.createElement(
'div',
{
tabIndex: tabIndex,
Expand All @@ -689,13 +698,13 @@ var Picky$1 = function (_React$PureComponent) {
'data-selectall': 'true',
'aria-selected': this.state.allSelected,
className: this.state.allSelected ? 'option selected' : 'option',
onClick: this.selectAll,
onKeyPress: this.selectAll
onClick: this.toggleSelectAll,
onKeyPress: this.toggleSelectAll
},
React__default.createElement('input', {
type: 'checkbox',
readOnly: true,
onClick: this.selectAll,
onClick: this.toggleSelectAll,
tabIndex: -1,
checked: this.state.allSelected,
'aria-label': 'select all'
Expand Down Expand Up @@ -751,7 +760,8 @@ Picky$1.propTypes = {
virtual: PropTypes.bool,
manySelectedPlaceholder: PropTypes.string,
allSelectedPlaceholder: PropTypes.string,
selectAllText: PropTypes.string
selectAllText: PropTypes.string,
renderSelectAll: PropTypes.func
};

module.exports = Picky$1;
48 changes: 45 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Picky ☜

Yet another React select list with virtualised option.

[![Build Status](https://travis-ci.org/Aidurber/react-picky.svg?branch=master)](https://travis-ci.org/Aidurber/react-picky)
[![codecov](https://codecov.io/gh/Aidurber/react-picky/branch/master/graph/badge.svg)](https://codecov.io/gh/Aidurber/react-picky)
[![license](https://img.shields.io/github/license/aidurber/react-picky.svg)]()
[![npm version](https://badge.fury.io/js/react-picky.svg)](https://badge.fury.io/js/react-picky)

Yet another React select list.

## Motivation

When dealing with medium+ length select lists, especially multi-select lists. The common approach is to use tags e.g.
Expand Down Expand Up @@ -116,7 +116,8 @@ Picky.propTypes = {
virtual: PropTypes.bool,
manySelectedPlaceholder: PropTypes.string,
allSelectedPlaceholder: PropTypes.string,
selectAllText: PropTypes.string
selectAllText: PropTypes.string,
renderSelectAll: PropTypes.func
};
```

Expand Down Expand Up @@ -146,9 +147,12 @@ Picky.propTypes = {
* `manySelectedPlaceholder` - Default "%s selected" where %s is the number of items selected. This gets used when the number if items selected is more than the `numberDisplayed` prop and when all options are not selected.
* `allSelectedPlaceholder` - Default "%s selected" where %s is the number of items selected. This gets used when all options are selected.
* `selectAllText` - Default "Select all", use this to override "Select all" text from top of dropdown when included with `includeSelectAll`.
* `renderSelectAll` - Used for rendering a custom select all

## Custom rendering

### Items

You can render out custom items for the dropdown.

**Example**
Expand Down Expand Up @@ -207,6 +211,44 @@ style, isSelected, item, labelKey, valueKey, selectValue, multiple
* If your rendered item affects the height of the item in anyway. Supply `itemHeight` to Picky.
* If you wish to show a radio button or a checkbox, be sure to add `readOnly` prop to the input.

### Select All

```javascript
<Picky
// ...
renderSelectAll={({
filtered,
tabIndex,
allSelected,
toggleSelectAll,
multiple
}) => {
// Don't show if single select or items have been filtered.
if (multiple && !filtered) {
return (
<div
tabIndex={tabIndex}
role="option"
className={allSelected ? 'option selected' : 'option'}
onClick={toggleSelectAll}
onKeyPress={toggleSelectAll}
>
<h1>SELECT ALL</h1>
</div>
);
}
}}
/>
```

Gets called with the following properties:

* `filtered`: boolean - true if items have been filtered.
* `allSelected`: boolean true if all items are selected
* `toggleSelectAll`: function selects or deselects all items
* `tabIndex`: number used for specifying tab index
* `multiple`: boolean true if multiselect

# Internals

The component uses [React Virtualized](https://github.com/bvaughn/react-virtualized) for rendering out the items. This is a for a performance gain. You can have 1,000,000 items in the dropdown with no performance drop! It's such a great little library. This is why we have a dropdown height.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
"license": "MIT",
"scripts": {
"commit": "git-cz",
"build": "rollup -c",
"build": "rollup -c && npm run copyreadme",
"dev": "rollup -c -w",
"pretest": "npm run lint && npm run build",
"lint": "eslint src/**/*.js",
"test": "cross-env NODE_ENV=test jest && codecov",
"test:watch": "cross-env NODE_ENV=test jest --watchAll",
"semantic-release": "semantic-release",
"precommit": "npm run test"
"precommit": "npm run test",
"copyreadme": "cpx ./README.md docs"
},
"dependencies": {
"lodash.debounce": "^4.0.8",
Expand All @@ -37,6 +38,7 @@
"babel-preset-react": "^6.24.1",
"babel-preset-stage-1": "^6.24.1",
"codecov": "^3.0.0",
"cpx": "^1.5.0",
"cross-env": "^5.1.3",
"cz-conventional-changelog": "^2.1.0",
"enzyme": "^3.2.0",
Expand Down
29 changes: 20 additions & 9 deletions src/Picky.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Picky extends React.PureComponent {
fixedWidth: true
});
this.toggleDropDown = this.toggleDropDown.bind(this);
this.selectAll = this.selectAll.bind(this);
this.toggleSelectAll = this.toggleSelectAll.bind(this);
this.onFilterChange = this.onFilterChange.bind(this);
this.selectValue = this.selectValue.bind(this);
this.allSelected = this.allSelected.bind(this);
Expand Down Expand Up @@ -119,7 +119,7 @@ class Picky extends React.PureComponent {
*
* @memberof Picky
*/
selectAll() {
toggleSelectAll() {
this.setState(
{
selectedValue: !this.state.allSelected ? this.props.options : [],
Expand Down Expand Up @@ -388,7 +388,8 @@ class Picky extends React.PureComponent {
valueKey,
labelKey,
tabIndex,
dropdownHeight
dropdownHeight,
renderSelectAll
} = this.props;
const { open } = this.state;
let ariaOwns = '';
Expand Down Expand Up @@ -435,6 +436,7 @@ class Picky extends React.PureComponent {
{open && (
<div
className="picky__dropdown"
data-test="dropdown"
id={this.state.id + '-list'}
style={dropdownStyle}
>
Expand All @@ -447,8 +449,16 @@ class Picky extends React.PureComponent {
}
/>
)}

{includeSelectAll &&
{renderSelectAll &&
renderSelectAll({
filtered: this.state.filtered,
allSelected: this.state.allSelected,
toggleSelectAll: this.toggleSelectAll,
tabIndex,
multiple
})}
{!renderSelectAll &&
includeSelectAll &&
multiple &&
!this.state.filtered && (
<div
Expand All @@ -460,13 +470,13 @@ class Picky extends React.PureComponent {
className={
this.state.allSelected ? 'option selected' : 'option'
}
onClick={this.selectAll}
onKeyPress={this.selectAll}
onClick={this.toggleSelectAll}
onKeyPress={this.toggleSelectAll}
>
<input
type="checkbox"
readOnly
onClick={this.selectAll}
onClick={this.toggleSelectAll}
tabIndex={-1}
checked={this.state.allSelected}
aria-label="select all"
Expand Down Expand Up @@ -525,7 +535,8 @@ Picky.propTypes = {
virtual: PropTypes.bool,
manySelectedPlaceholder: PropTypes.string,
allSelectedPlaceholder: PropTypes.string,
selectAllText: PropTypes.string
selectAllText: PropTypes.string,
renderSelectAll: PropTypes.func
};

export default Picky;
41 changes: 41 additions & 0 deletions tests/Picky.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -579,4 +579,45 @@ describe('Picky', () => {
const wrapper = mount(<Picky options={[1, 2, 3]} itemHeight={60} />);
expect(wrapper.instance().cellMeasurerCache.defaultHeight).toEqual(60);
});

it('should not render custom selectall when renderSelectAll prop is not supplied', () => {
const wrapper = mount(
<Picky
options={[]}
virtual={false}
open={true}
includeSelectAll={true}
multiple={true}
/>
);
const dropdown = wrapper.find(sel('dropdown')).first();
expect(dropdown.find(sel('select-all-text'))).toHaveLength(1);
});
it('should not render custom selectall when renderSelectAll prop is not supplied', () => {
const renderSelectAllMock = jest.fn();
const wrapper = mount(
<Picky
options={[1, 2, 3, 4]}
virtual={false}
open={true}
includeSelectAll={true}
multiple={true}
renderSelectAll={renderSelectAllMock}
/>
);
const calledWithProps = renderSelectAllMock.mock.calls[0][0];
expect(calledWithProps.filtered).toEqual(false);
expect(calledWithProps.multiple).toEqual(true);
expect(calledWithProps.allSelected).toEqual(false);
expect(calledWithProps.tabIndex).toEqual(0);

// expect(renderSelectAllMock).toHaveBeenCalledWith({
// filtered: false,
// includeSelectAll: true,
// multiple: true,
// allSelected: false,
// toggleSelectAll: () => {},
// tabIndex: 0
// });
});
});
Loading

0 comments on commit a4c83c9

Please sign in to comment.