├── .babelrc ├── .eslintrc ├── .gitignore ├── .jest ├── jest.setup.js └── register-context.js ├── .storybook ├── Storyshots.test.js ├── addons.js ├── config.js └── webpack.config.js ├── .travis.script.sh ├── .travis.yml ├── LICENSE ├── README.md ├── __mocks__ ├── react-select │ ├── index.js │ └── lib │ │ ├── Async.js │ │ └── default.js └── styleMock.js ├── demo-site ├── README.md ├── _config.yml ├── _includes │ ├── head.html │ └── required_static.html ├── demo.data.json ├── demos.jsx ├── demos.sass ├── index.html ├── list-demo.jsx ├── package.json ├── simple-demo.jsx ├── table-demo.jsx ├── webpack.config.js └── yarn.lock ├── jest.config.js ├── package.json ├── src ├── ObjectList.demo.stories.js ├── ObjectList.js ├── ObjectList.stories.js ├── __snapshots__ │ ├── ObjectList.demo.stories.storyshot │ └── ObjectList.stories.storyshot ├── __tests__ │ └── ObjectList.test.js ├── actions-filters │ ├── ActionsFiltersContainer.js │ ├── ActionsFiltersContainer.stories.js │ ├── Favourites.js │ ├── Favourites.stories.js │ ├── FavouritesItem.js │ ├── FiltersContainer.js │ ├── FiltersContainer.stories.js │ ├── OptionalField.js │ ├── OptionalFields.js │ ├── OptionalFields.stories.js │ ├── SelectAllAction.js │ ├── SelectFilters.js │ ├── SelectFilters.stories.js │ ├── __snapshots__ │ │ ├── ActionsFiltersContainer.stories.storyshot │ │ ├── Favourites.stories.storyshot │ │ ├── FiltersContainer.stories.storyshot │ │ ├── OptionalFields.stories.storyshot │ │ └── SelectFilters.stories.storyshot │ └── __tests__ │ │ ├── Favourites.test.js │ │ ├── FavouritesItem.test.js │ │ ├── FiltersContainer.test.js │ │ ├── OptionalField.test.js │ │ ├── OptionalFields.test.js │ │ ├── SelectAllAction.test.js │ │ ├── SelectFilters.test.js │ │ └── __snapshots__ │ │ ├── Favourites.test.js.snap │ │ ├── FiltersContainer.test.js.snap │ │ ├── OptionalField.test.js.snap │ │ ├── OptionalFields.test.js.snap │ │ ├── SelectAllAction.test.js.snap │ │ └── SelectFilters.test.js.snap ├── data-renderers │ ├── HeaderField.js │ ├── List.js │ ├── List.stories.js │ ├── ListCard.js │ ├── Overlay.js │ ├── Table.js │ ├── TableHeader.js │ ├── WidthHandle.js │ ├── __snapshots__ │ │ ├── List.stories.storyshot │ │ └── table.stories.storyshot │ ├── __tests__ │ │ ├── HeaderField.test.js │ │ ├── List.test.js │ │ ├── Overlay.test.js │ │ ├── Table.test.js │ │ ├── TableHeader.test.js │ │ ├── WidthHandle.test.js │ │ └── __snapshots__ │ │ │ ├── HeaderField.test.js.snap │ │ │ ├── List.test.js.snap │ │ │ ├── Overlay.test.js.snap │ │ │ ├── Table.test.js.snap │ │ │ ├── TableHeader.test.js.snap │ │ │ └── WidthHandle.test.js.snap │ ├── index.js │ ├── table.stories.js │ └── utils │ │ ├── __tests__ │ │ └── functions.test.js │ │ ├── functions.js │ │ └── index.js ├── demo.data.json ├── filters │ ├── ChoiceFilter.js │ ├── CurrencyFilter.js │ ├── DateFilter.js │ ├── DayFilter.js │ ├── MultiChoiceFilter.js │ ├── NumberSliderFilter.js │ ├── RemoteChoiceFilter.js │ ├── RemoteMultiChoiceFilter.js │ ├── SearchFilter.js │ ├── TextContainsFilter.js │ ├── __snapshots__ │ │ └── filters.stories.storyshot │ ├── filters.stories.js │ ├── index.js │ ├── types │ │ ├── Boolean.js │ │ ├── Choice.js │ │ ├── Currency.js │ │ ├── Date.js │ │ ├── Day.js │ │ ├── Month.js │ │ ├── NumberSlider.js │ │ ├── Search.js │ │ ├── __tests__ │ │ │ ├── Boolean.test.js │ │ │ ├── Choice.test.js │ │ │ ├── Currency.test.js │ │ │ ├── Date.test.js │ │ │ ├── Day.test.js │ │ │ ├── Month.test.js │ │ │ ├── NumberSlider.test.js │ │ │ ├── Search.test.js │ │ │ └── __snapshots__ │ │ │ │ ├── Boolean.test.js.snap │ │ │ │ ├── Choice.test.js.snap │ │ │ │ ├── Currency.test.js.snap │ │ │ │ ├── Date.test.js.snap │ │ │ │ ├── Day.test.js.snap │ │ │ │ ├── Month.test.js.snap │ │ │ │ ├── NumberSlider.test.js.snap │ │ │ │ └── Search.test.js.snap │ │ └── index.js │ └── utils │ │ ├── FilterComparison.js │ │ ├── FilterLabel.js │ │ ├── RemoveFilter.js │ │ ├── __mocks__ │ │ └── FilterComparison.js │ │ ├── __tests__ │ │ ├── FilterComparison.test.js │ │ ├── FilterLabel.test.js │ │ ├── RemoveFilter.test.js │ │ ├── __snapshots__ │ │ │ ├── FilterComparison.test.js.snap │ │ │ ├── FilterLabel.test.js.snap │ │ │ ├── RemoveFilter.test.js.snap │ │ │ └── makeFilter.test.js.snap │ │ └── makeFilter.test.js │ │ ├── index.js │ │ ├── makeFilter.js │ │ └── utils.js ├── icons │ ├── FontAwesome.js │ └── index.js ├── index.js ├── pagination │ ├── Page.js │ ├── Pagination.js │ ├── Pagination.stories.js │ ├── __snapshots__ │ │ └── Pagination.stories.storyshot │ ├── __tests__ │ │ ├── Page.test.js │ │ ├── Pagination.test.js │ │ └── __snapshots__ │ │ │ ├── Page.test.js.snap │ │ │ └── Pagination.test.js.snap │ └── index.js ├── readme.md ├── resources │ ├── colours.sass │ ├── dark-table.sass │ ├── input-range.sass │ └── main.sass ├── types │ ├── AllSelector.js │ ├── BooleanType.js │ ├── Currency.js │ ├── DatePart.js │ ├── DateTime.js │ ├── Empty.js │ ├── Links.js │ ├── RelativeDate.js │ ├── RelativeDateTime.js │ ├── Selector.js │ ├── TextAttr.js │ ├── __tests__ │ │ ├── AllSelector.test.js │ │ ├── BooleanType.test.js │ │ ├── Currency.test.js │ │ ├── DatePart.test.js │ │ ├── DateTime.test.js │ │ ├── Empty.test.js │ │ ├── Links.test.js │ │ ├── RelativeDate.test.js │ │ ├── Selector.test.js │ │ ├── TextAttr.test.js │ │ ├── __snapshots__ │ │ │ ├── AllSelector.test.js.snap │ │ │ ├── BooleanType.test.js.snap │ │ │ ├── Currency.test.js.snap │ │ │ ├── DateTime.test.js.snap │ │ │ ├── Empty.test.js.snap │ │ │ ├── Links.test.js.snap │ │ │ ├── RelativeDate.test.js.snap │ │ │ ├── Selector.test.js.snap │ │ │ └── TextAttr.test.js.snap │ │ └── utils.test.js │ ├── index.js │ └── utils.js └── utils │ ├── ErrorMessage.js │ ├── Select.js │ ├── Select.stories.js │ ├── __snapshots__ │ ├── Select.stories.storyshot │ └── loading.stories.storyshot │ ├── __tests__ │ ├── ErrorMessage.test.js │ ├── __snapshots__ │ │ └── ErrorMessage.test.js.snap │ ├── functions.test.js │ └── proptypes.test.js │ ├── constants.js │ ├── functions.js │ ├── index.js │ └── proptypes.js ├── utils └── tests.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react","env","stage-0"], 3 | "env": { 4 | "test": { 5 | "plugins": ["require-context-hook"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "babel" 4 | ], 5 | "parser": "babel-eslint", 6 | "rules": { 7 | "comma-dangle": ["error", "always-multiline"], 8 | "space-before-function-paren": ["error", { 9 | "anonymous": "never", 10 | "named": "never", 11 | "asyncArrow": "always" 12 | }], 13 | "jsx-quotes": ["error", "prefer-double"], 14 | "no-var": "error", 15 | "prefer-const": "warn", 16 | 17 | "babel/no-invalid-this": "warn", 18 | "babel/semi": ["error", "never"], 19 | "react/prop-types": "warn", 20 | "react/no-unused-state": "warn", 21 | "react/no-access-state-in-setstate": "warn", 22 | "object-curly-spacing": "off" 23 | }, 24 | "overrides": [ 25 | { 26 | "files": [ "*.test.js", "*.stories.js" ], 27 | "rules": { 28 | "react/prop-types": "off" 29 | } 30 | } 31 | ], 32 | "extends": ["standard", "standard-react"], 33 | "env": { 34 | "browser": true, 35 | "jest": true, 36 | "jasmine": true 37 | }, 38 | "globals": { 39 | "mapboxgl": true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | demo-site/_site/ 4 | /cells.js 5 | /filters.js 6 | /main.js 7 | /renderers.js 8 | /utils.js 9 | /icons.js 10 | coverage/ 11 | .vscode/ 12 | .DS_Store 13 | **~ 14 | 15 | *.log 16 | 17 | demo-site/.jekyll-cache 18 | -------------------------------------------------------------------------------- /.jest/jest.setup.js: -------------------------------------------------------------------------------- 1 | import 'raf/polyfill' 2 | import {configure} from 'enzyme' 3 | import moment from 'moment' 4 | import Adapter from 'enzyme-adapter-react-16' 5 | 6 | configure({ adapter: new Adapter() }) 7 | Date.now = jest.fn(() => new Date(Date.UTC(2017, 6, 15)).valueOf()) 8 | moment.prototype.local = function() { return this.utcOffset('+10:00') } 9 | 10 | jest.mock('react-select', () => require('__mocks__/react-select')) 11 | -------------------------------------------------------------------------------- /.jest/register-context.js: -------------------------------------------------------------------------------- 1 | import registerRequireContextHook from 'babel-plugin-require-context-hook/register' 2 | registerRequireContextHook() 3 | -------------------------------------------------------------------------------- /.storybook/Storyshots.test.js: -------------------------------------------------------------------------------- 1 | import initStoryshots, {multiSnapshotWithOptions} from '@storybook/addon-storyshots' 2 | 3 | jest.mock('@storybook/addon-info', () => ({ 4 | withInfo: () => jest.fn(story => story), 5 | })) 6 | 7 | jest.mock('react-select', () => require('__mocks__/react-select')) 8 | 9 | initStoryshots({ 10 | test: multiSnapshotWithOptions({ 11 | createNodeMock: elem => { 12 | if (elem.type === 'input' && elem.props.type === 'checkbox') { 13 | const ref = document.createElement('input') 14 | ref.setAttribute('type', 'checkbox') 15 | elem.checkbox = ref 16 | return elem 17 | } 18 | } 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register' 2 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import { configure } from '@storybook/react'; 3 | import '../src/resources/main.sass' 4 | import '../node_modules/@fortawesome/fontawesome-free/css/all.css' 5 | 6 | const appStories = require.context('../src/', true, /stories\.js$/) 7 | 8 | const requireAll = (requireContext) => requireContext.keys().map(requireContext) 9 | 10 | function loadStories() { 11 | requireAll(appStories) 12 | } 13 | 14 | 15 | configure(loadStories, module) 16 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.(scss|sass|css)$/, 8 | use: [{ 9 | loader: 'style-loader' // creates style nodes from JS strings 10 | }, { 11 | loader: 'css-loader' // translates CSS into CommonJS 12 | }, { 13 | loader: 'resolve-url-loader' // resolve relative urls inside the css files 14 | },{ 15 | loader: 'sass-loader?sourceMap' // compiles Sass to CSS 16 | }], 17 | include: [ 18 | path.resolve(__dirname, '../src'), 19 | path.resolve(__dirname, '../node_modules/@fortawesome'), 20 | ], 21 | }, { 22 | test: /\.(woff2?|eot|ttf|svg|otf)(\?.+)?$/i, 23 | use: [ 24 | { 25 | loader: 'url-loader', 26 | options: { 27 | limit: 10000, 28 | name: '[name].[ext]', 29 | }, 30 | }, 31 | ], 32 | } 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.travis.script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set exit on exception 3 | set -e 4 | 5 | if [ "${TASK}" = "jest" ]; then 6 | yarn jest --coverage 7 | fi 8 | 9 | if [ "${TASK}" = "eslint" ]; then 10 | eslint src/ 11 | fi 12 | 13 | if [ "${TASK}" = "build" ]; then 14 | yarn build 15 | fi 16 | 17 | if [ "${TASK}" = "nojsx" ]; then 18 | if find src -type f -name "*.jsx" | grep ".jsx"; then 19 | echo "Extension *.jsx is not encouraged on this project. Please use *.js" 20 | exit 1 21 | else 22 | echo "All good" 23 | fi 24 | fi 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 10.16.3 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | env: 10 | matrix: 11 | - TASK=nojsx 12 | - TASK=jest NODE_VERSION=v8.15.0 YARN_VERSION=1.13.0 13 | - TASK=eslint NODE_VERSION=v8.15.0 14 | - TASK=build 15 | 16 | install: 17 | - if [ $TASK == jest ]; 18 | then npm i -g yarn@1.3.2; 19 | yarn install; 20 | 21 | elif [ $TASK = eslint ]; then 22 | npm i eslint 23 | eslint-plugin-react-hooks 24 | eslint-plugin-promise 25 | eslint-plugin-standard 26 | eslint-config-standard 27 | eslint-config-standard-react 28 | eslint-config-standard-jsx 29 | eslint-plugin-react 30 | eslint-plugin-babel 31 | babel-eslint 32 | eslint-plugin-import 33 | eslint-plugin-node; 34 | 35 | elif [ $TASK == build ]; then npm i -g yarn@1.3.2; yarn install; fi 36 | 37 | script: ./.travis.script.sh 38 | 39 | after_script: 40 | - if [ $TASK == jest ]; then yarn codecov; fi 41 | 42 | 43 | notifications: 44 | email: false 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Uptick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-object-list 2 | 3 | [![npm version](https://badge.fury.io/js/react-object-list.svg)](http://badge.fury.io/js/react-object-list) 4 | ![Downloads](http://img.shields.io/npm/dm/react-object-list.svg?style=flat) 5 | [![Build Status](https://travis-ci.org/uptick/react-object-list.svg?branch=master)](https://travis-ci.org/uptick/react-object-list) 6 | [![codecov](https://codecov.io/gh/uptick/react-object-list/branch/master/graph/badge.svg)](https://codecov.io/gh/uptick/react-object-list) 7 | 8 | Component used to display an array of object based data in a sortable, filterable, paginated, list based view, powered by React. 9 | 10 | Custom components can be passed in if they are designed to handle the same props as the default components. This functionality is available for the DataRenderer, Pagination and ErrorMessage and your component can simply be passed in through the props with one of the above keys. 11 | 12 | ## Live Demo 13 | 14 | Check out the live demo here: http://uptick.github.io/react-object-list/ 15 | 16 | ## Installation 17 | 18 | Install the package: 19 | 20 | ``` 21 | yarn add react-object-list 22 | ``` 23 | Ensure you have all peer dependencies installed 24 | ``` 25 | yarn add classnames moment prop-types react-month-picker react-select 26 | ``` 27 | 28 | 29 | Include icons from FontAwesome 4: 30 | 31 | ```javascript 32 | import React from 'react' 33 | import ReactDOM from 'react-dom' 34 | 35 | import ObjectList from 'react-object-list' 36 | import {FontAwesome} from 'react-object-list/icons' 37 | 38 | var mount = document.querySelectorAll('div.browser-mount'); 39 | ReactDOM.render( 40 | , 43 | mount[0] 44 | ); 45 | ``` 46 | 47 | or your own icons by specifying as so: 48 | ```javascript 49 | , 52 | Favourites: , 53 | RemoveFavourite: , 54 | RemoveFilter: , 55 | DropdownOpen: , 56 | DropdownClose: , 57 | SortAsc: , 58 | SortDesc: , 59 | Unsorted: , 60 | Loading: , 61 | CheckboxChecked: , 62 | CheckboxUnchecked: , 63 | }} 64 | /> 65 | ``` 66 | 67 | Unspecified icons will not show (excl. RemoveFavourite, SortAsc, SortDesc, CheckboxChecked, CheckboxUnchecked, RemoveFilter). 68 | -------------------------------------------------------------------------------- /__mocks__/react-select/index.js: -------------------------------------------------------------------------------- 1 | import Select from './lib/default' 2 | export default Select 3 | export const components = {Option: 'Option'} 4 | -------------------------------------------------------------------------------- /__mocks__/react-select/lib/Async.js: -------------------------------------------------------------------------------- 1 | const Async = 'Select.Async' 2 | export default Async 3 | -------------------------------------------------------------------------------- /__mocks__/react-select/lib/default.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Select = props => React.createElement('Select', props, props.children) 5 | /* eslint-disable-next-line */ 6 | Select.propTypes = {value: PropTypes.any, children: PropTypes.any} 7 | 8 | export default Select 9 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | -------------------------------------------------------------------------------- /demo-site/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | rm yarn.lock && rm -rf node_modules 3 | yarn 4 | yarn build 5 | jekyll build 6 | jekyll serve 7 | ``` 8 | -------------------------------------------------------------------------------- /demo-site/_config.yml: -------------------------------------------------------------------------------- 1 | layouts_dir: 'node_modules/uptick-demo-site/dist' 2 | 3 | package_name: React Object List 4 | package_github_url: https://github.com/uptick/react-object-list 5 | package_npm_url: https://www.npmjs.com/package/react-object-list 6 | -------------------------------------------------------------------------------- /demo-site/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo-site/_includes/required_static.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo-site/demos.jsx: -------------------------------------------------------------------------------- 1 | import { init } from 'uptick-demo-site' 2 | 3 | import SimpleDemo from './simple-demo.jsx' 4 | import Table from './table-demo.jsx' 5 | import List from './list-demo.jsx' 6 | import './demos.sass' 7 | 8 | init() 9 | -------------------------------------------------------------------------------- /demo-site/demos.sass: -------------------------------------------------------------------------------- 1 | @import 'node_modules/uptick-demo-site/dist/uptick-demo-site' 2 | @import 'node_modules/react-object-list/styles/main' 3 | @import 'node_modules/font-awesome/css/font-awesome.min' 4 | -------------------------------------------------------------------------------- /demo-site/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | --- 4 |
5 | 8 |

To participate in the demonstration you will need:

9 |
    10 |
  • Javascript enabled
  • 11 |
  • A modern browser
  • 12 |
13 |

All examples given are written with babel loaders to support es6 (stage-0) and JSX syntax.

14 |

The demo data used in the examples can be found here: demo.data.json

15 | 16 |

Simple Example

17 |

This is the simplest implementation of React-Object-List, displaying data in a table.

18 |
19 | Loading ... 23 | 24 |

Table render

25 |

This example demonstrates:

26 |
    27 |
  • Filters
  • 28 |
  • Optional Columns
  • 29 |
  • Pagination
  • 30 |
31 |
32 | Loading ... 36 | 37 |

List render

38 |

This example demonstrates:

39 |
    40 |
  • List rendering
  • 41 |
42 |
43 | Loading ... 47 |
48 | -------------------------------------------------------------------------------- /demo-site/list-demo.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDom from 'react-dom' 3 | import ObjectList from 'react-object-list' 4 | import { List } from 'react-object-list/renderers' 5 | 6 | const mockData = require('./demo.data.json').slice(0, 3) 7 | 8 | ReactDom.render( 9 | , 25 | document.querySelector('div.demo-mount-list') 26 | ) 27 | -------------------------------------------------------------------------------- /demo-site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-object-list-demo-site", 3 | "version": "1.0.0", 4 | "description": "Demo site for react-object-list package", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack", 9 | "build:watch": "webpack --watch", 10 | "watch": "parallel --ungroup ::: \"yarn build:watch\" \"jekyll serve\"" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "react-object-list" 15 | }, 16 | "author": "Uptick Pty Ltd", 17 | "license": "MIT", 18 | "dependencies": { 19 | "babel-regenerator-runtime": "^6.5.0", 20 | "classnames": "^2.2.5", 21 | "font-awesome": "^4.7.0", 22 | "moment": "^2.21.0", 23 | "prop-types": "^15.6.1", 24 | "react": "^16.2.0", 25 | "react-day-picker": "^7.1.4", 26 | "react-dom": "^16.2.0", 27 | "react-month-picker": "^1.3.5", 28 | "react-object-list": "^0.1.1", 29 | "react-select": "^2.0.0-beta.6", 30 | "uptick-demo-site": "latest" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.26.0", 34 | "babel-core": "^6.26.0", 35 | "babel-loader": "^7.1.4", 36 | "babel-preset-es2015": "^6.24.1", 37 | "babel-preset-react": "^6.24.1", 38 | "babel-preset-stage-0": "^6.24.1", 39 | "css-loader": "^0.28.11", 40 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 41 | "node-sass": "^4.7.2", 42 | "resolve-url-loader": "^2.3.0", 43 | "sass-loader": "^6.0.7", 44 | "url-loader": "^1.0.1", 45 | "webpack": "^4.1.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo-site/simple-demo.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDom from 'react-dom' 3 | import ObjectList from 'react-object-list' 4 | 5 | const mockData = require('./demo.data.json').slice(0, 5) 6 | 7 | ReactDom.render( 8 | , 23 | document.querySelector('div.demo-mount-simple') 24 | ) 25 | -------------------------------------------------------------------------------- /demo-site/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | const extractSass = new ExtractTextPlugin({ 5 | filename: 'demos.css', 6 | }) 7 | 8 | module.exports = { 9 | entry: ['babel-regenerator-runtime', './demos.jsx'], 10 | mode: 'production', 11 | output: { 12 | path: path.resolve(__dirname, 'dist'), 13 | filename: 'demos.js', 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.jsx?$/, 19 | exclude: /node_modules/, 20 | use: { 21 | loader: 'babel-loader', 22 | options: { 23 | babelrc: false, 24 | presets: [ 25 | 'react', 26 | 'es2015', 27 | 'stage-0', 28 | ], 29 | }, 30 | }, 31 | }, { 32 | test: /\.(scss|sass)$/, 33 | exclude: /node_modules/, 34 | use: extractSass.extract({ 35 | use: [{ 36 | loader: 'css-loader', // translates CSS into CommonJS 37 | options: { 38 | minimize: true, 39 | }, 40 | }, { 41 | loader: 'resolve-url-loader', // resolve relative urls inside the css files 42 | }, { 43 | loader: 'sass-loader?sourceMap', // compiles Sass to CSS 44 | }], 45 | }), 46 | }, { 47 | test: /\.(woff2?|eot|ttf|svg|otf)(\?.+)?$/i, 48 | use: [ 49 | { 50 | loader: 'url-loader', 51 | options: { 52 | limit: 10000, 53 | name: '[name].[ext]', 54 | }, 55 | }, 56 | ], 57 | }, 58 | ], 59 | }, 60 | plugins: [ 61 | extractSass, 62 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), 63 | ], 64 | resolve: { 65 | modules: [ 66 | path.resolve('node_modules'), 67 | ], 68 | alias: { 69 | mireco: path.resolve('..'), 70 | }, 71 | }, 72 | } 73 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupFiles: ['./.jest/jest.setup.js', './.jest/register-context.js'], 3 | snapshotSerializers: ['enzyme-to-json/serializer'], 4 | modulePaths: [ 5 | '.', 6 | ], 7 | moduleNameMapper: { 8 | '\\.(css|less|sass)$': '/__mocks__/styleMock.js', 9 | }, 10 | collectCoverageFrom: [ 11 | 'src/**/*.js', 12 | '!src/**/*.stories.js', 13 | ], 14 | testURL: 'http://localhost/', 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-object-list", 3 | "version": "0.2.10", 4 | "description": "React component to display an array of object data in a filterable view", 5 | "repository": { 6 | "url": "https://github.com/uptick/react-object-list", 7 | "type": "git" 8 | }, 9 | "bugs": "https://github.com/uptick/react-object-list/issues", 10 | "files": [ 11 | "main.js", 12 | "styles", 13 | "dist", 14 | "filters.js", 15 | "cells.js", 16 | "renderers.js", 17 | "utils.js", 18 | "icons.js", 19 | "src/resources/colours.sass", 20 | "src/resources/dark-table.sass", 21 | "src/resources/input-range.sass", 22 | "src/resources/main.sass" 23 | ], 24 | "main": "main.js", 25 | "scripts": { 26 | "publish-demo": "git branch -D gh-pages; git push origin --delete gh-pages; git checkout -b gh-pages; cd demo-site; yarn; npm run build; cd ..; git add .; git add -f demo-site/dist; git add -f demo-site/node_modules/uptick-demo-site/dist; git commit -m \"Demo site build\"; git push origin gh-pages; git checkout master; git push origin `git subtree split --prefix demo-site gh-pages`:gh-pages --force;", 27 | "build:js": "webpack --mode production", 28 | "build:css": "mkdir -p dist && npx node-sass src/resources/main.sass > dist/react-object-list.css", 29 | "build": "yarn build:js && yarn build:css", 30 | "dev": "webpack --mode development", 31 | "watch": "webpack --mode development --watch", 32 | "test": "jest", 33 | "storybook": "start-storybook -p 9001 -c .storybook", 34 | "prepare": "yarn build" 35 | }, 36 | "author": "Uptick Pty Ltd (http://uptickhq.com)", 37 | "license": "MIT", 38 | "devDependencies": { 39 | "@fortawesome/fontawesome-free": "^5.4.1", 40 | "@storybook/addon-actions": "^4.0.0-alpha.8", 41 | "@storybook/addon-info": "^4.0.0-alpha.8", 42 | "@storybook/addon-knobs": "^4.0.0-alpha.8", 43 | "@storybook/addon-storyshots": "^4.0.0-alpha.8", 44 | "@storybook/addons": "^4.0.0-alpha.8", 45 | "@storybook/react": "^4.0.0-alpha.8", 46 | "babel-core": "^6.26.0", 47 | "babel-eslint": "^10.0.3", 48 | "babel-loader": "^7.1.4", 49 | "babel-plugin-require-context-hook": "^1.0.0", 50 | "babel-polyfill": "^6.26.0", 51 | "babel-preset-env": "^1.6.1", 52 | "babel-preset-react": "^6.24.1", 53 | "babel-preset-stage-0": "^6.24.1", 54 | "classnames": "^2.2.5", 55 | "codecov": "^3.0.0", 56 | "css-loader": "^0.28.10", 57 | "enzyme": "^3.3.0", 58 | "enzyme-adapter-react-16": "^1.1.1", 59 | "enzyme-to-json": "^3.3.1", 60 | "eslint": "^6.0.0", 61 | "eslint-config-standard": "^12.0.0", 62 | "eslint-config-standard-react": "^7.0.2", 63 | "eslint-plugin-babel": "^5.1.0", 64 | "eslint-plugin-import": "^2.14.0", 65 | "eslint-plugin-node": "^7.0.1", 66 | "eslint-plugin-promise": "^4.0.0", 67 | "eslint-plugin-react": "^7.11.1", 68 | "eslint-plugin-standard": "^4.0.0", 69 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 70 | "file-loader": "^1.1.11", 71 | "jest": "^22.4.2", 72 | "moment": "^2.21.0", 73 | "node-sass": "^4.12.0", 74 | "peer-deps-externals-webpack-plugin": "^1.0.2", 75 | "prop-types": "^15.6.1", 76 | "raf": "^3.4.0", 77 | "react": "^16.2.0", 78 | "react-dom": "^16.2.0", 79 | "react-month-picker": "^1.3.5", 80 | "react-select": "^2.4.2", 81 | "react-test-renderer": "^16.2.0", 82 | "resolve-url-loader": "^2.3.0", 83 | "sass-loader": "^6.0.7", 84 | "style-loader": "^0.20.2", 85 | "url-loader": "^1.0.1", 86 | "webpack": "^4.41.2", 87 | "webpack-cli": "3.1.2" 88 | }, 89 | "peerDependencies": { 90 | "classnames": "^2.2.5", 91 | "moment": "^2.21.0", 92 | "prop-types": "^15.6.1", 93 | "react": "15.x - 16.x", 94 | "react-dom": "15.x - 16.x", 95 | "react-month-picker": "^1.3.5", 96 | "react-select": "^2.0.0-beta.6" 97 | }, 98 | "dependencies": { 99 | "date-fns": "^2.4.1", 100 | "mireco": "^0.0.20" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/__tests__/ObjectList.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | 4 | import ObjectList from '../' 5 | 6 | describe('ObjectList', () => { 7 | describe('state', () => { 8 | it('sets itemSingleName/itemPluralName to "item/items" if unset', () => { 9 | const instance = shallow().instance() 10 | expect(instance.state.itemSingleName).toBe('item') 11 | expect(instance.state.itemPluralName).toBe('items') 12 | }) 13 | 14 | it('sets itemSingleName/itemPluralName to "blah/blahs" if only itemSingleName set', () => { 15 | const instance = shallow().instance() 16 | expect(instance.state.itemSingleName).toBe('blah') 17 | expect(instance.state.itemPluralName).toBe('blahs') 18 | }) 19 | 20 | it('sets itemSingleName/itemPluralName to "blah/blahies" if both names set', () => { 21 | const instance = shallow().instance() 22 | expect(instance.state.itemSingleName).toBe('blah') 23 | expect(instance.state.itemPluralName).toBe('blahies') 24 | }) 25 | }) 26 | 27 | describe('Functions', () => { 28 | const props = { 29 | selectItems: jest.fn(), 30 | } 31 | 32 | it('handles selectAll correctly', () => { 33 | spyOn(props, 'selectItems') 34 | const instance = shallow().instance() 35 | instance.selectAll() 36 | expect(props.selectItems).toHaveBeenCalledWith('all') 37 | }) 38 | 39 | it('handles deselectAll correctly', () => { 40 | spyOn(props, 'selectItems') 41 | const instance = shallow().instance() 42 | instance.deselectAll() 43 | expect(props.selectItems).toHaveBeenCalledWith(null) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /src/actions-filters/Favourites.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { withInfo } from '@storybook/addon-info' 4 | import { action } from '@storybook/addon-actions' 5 | 6 | import Favourites from './Favourites' 7 | 8 | const defaultFilterValues = { 9 | filters: {}, 10 | activeSort: '-id', 11 | optionalFields: {}, 12 | activeFilters: {}, 13 | } 14 | 15 | const defaultProps = { 16 | favourites: [ 17 | { 18 | name: 'Favourite 1', 19 | ...defaultFilterValues, 20 | }, 21 | { 22 | name: 'Favourite 2', 23 | ...defaultFilterValues, 24 | optionalFields: {name: true}, 25 | }, 26 | ], 27 | handleDeleteFavourite: action('deleteFavourite'), 28 | handleAddFavourite: action('addFavourite'), 29 | } 30 | 31 | storiesOf('object-list/Favourites', module) 32 | .addDecorator((story, context) => withInfo( 33 | 'Favourites dropdown selector' 34 | )(story)(context)) 35 | .add('default view', () => ( 36 |
37 | 38 |
39 | )).add('interactive', () => { 40 | class FavouritesWrapper extends React.Component { 41 | state = {favourites: defaultProps.favourites} 42 | addFavourite = (favName) => this.setState(({favourites}) => 43 | ({favourites: favourites.filter(({name}) => name !== favName).concat({name: favName, ...defaultFilterValues})})) 44 | deleteFavourite = (favName) => this.setState(({favourites}) => 45 | ({favourites: favourites.filter(({name}) => name !== favName)})) 46 | render() { 47 | return ( 48 | ) 54 | } 55 | } 56 | return ( 57 |
58 | 59 |
60 | ) 61 | }) 62 | -------------------------------------------------------------------------------- /src/actions-filters/FavouritesItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import ClassNames from 'classnames' 4 | 5 | /** 6 | * Class representing one item in a favourites dropdown found in the api-list 7 | */ 8 | export default class FavouritesItem extends React.Component { 9 | static propTypes = { 10 | /** callback function that sets this favourite filter as active in the api-list */ 11 | loadFavourite: PropTypes.func, 12 | /** filters associated with a particular favourite e.g. { "status": "&status=ACTIVE%2CSETUP"} */ 13 | filters: PropTypes.object, 14 | /** the name of the favourite */ 15 | name: PropTypes.string, 16 | /** the kind of data being displayed by the api-list eg. "properties:property_list" */ 17 | handleDelete: PropTypes.func, 18 | /** whether or not the particular filter is selected */ 19 | isSelected: PropTypes.bool, 20 | /** icon to render to remove a favourite */ 21 | RemoveFavouriteIcon: PropTypes.element, 22 | } 23 | 24 | static defaultProps = { 25 | RemoveFavouriteIcon: Delete, 26 | } 27 | 28 | /** 29 | * This function calls the callback function to set the current 30 | * favourite to active 31 | * 32 | * @param {MouseEvent} event - the click event 33 | */ 34 | handleClick = event => { 35 | event.preventDefault() 36 | const {loadFavourite, name, filters: {activeFilters, optionalFields, activeSort}} = this.props 37 | loadFavourite(name, activeFilters, optionalFields, activeSort) 38 | } 39 | 40 | handleDelete = event => { 41 | event.preventDefault() 42 | this.props.handleDelete(this.props.name) 43 | } 44 | 45 | render() { 46 | const {RemoveFavouriteIcon, isSelected, name} = this.props 47 | return ( 48 |
53 | 54 | {name} 55 | 56 | {RemoveFavouriteIcon && React.cloneElement(RemoveFavouriteIcon, {onClick: this.handleDelete})} 57 |
58 | ) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/actions-filters/FiltersContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import {FILTER_BASE_TYPE} from '../utils/proptypes' 4 | 5 | class FiltersContainer extends Component { 6 | static propTypes = { 7 | /** array of potential filters that can be displayed inside the object-list */ 8 | filters: PropTypes.arrayOf(PropTypes.shape({ 9 | ...FILTER_BASE_TYPE, 10 | active: PropTypes.bool, 11 | })), 12 | /** callback containing {filterKey, comparison, value} for the individual filter */ 13 | updateFilter: PropTypes.func, 14 | /** callback to remove a filter from the active list of filters */ 15 | removeFilter: PropTypes.func, 16 | /** icons to use */ 17 | icons: PropTypes.object, 18 | /** Object of custom react-select styles */ 19 | selectStyles: PropTypes.object, 20 | } 21 | static defaultProps = { 22 | filters: [], 23 | activeFilters: [], 24 | icons: {}, 25 | selectStyles: {}, 26 | } 27 | 28 | renderFilter = (filter, i) => { 29 | const { Renderer, filterKey, loadOptions, value, ...props } = filter 30 | const { removeFilter, updateFilter, icons, selectStyles } = this.props 31 | return ( 32 | 43 | ) 44 | } 45 | 46 | render() { 47 | return ( 48 |
49 | {this.props.filters.filter(filter => filter.active).map(this.renderFilter)} 50 |
51 | ) 52 | } 53 | } 54 | 55 | export default FiltersContainer 56 | -------------------------------------------------------------------------------- /src/actions-filters/FiltersContainer.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { withInfo } from '@storybook/addon-info' 4 | import { action } from '@storybook/addon-actions' 5 | 6 | import FiltersContainer from './FiltersContainer' 7 | import * as importedFilters from '../filters' 8 | 9 | const filters = Object.entries(importedFilters).map(([name, filter]) => { 10 | const props = {} 11 | switch (name) { 12 | case 'RemoteChoiceFilter': 13 | case 'RemoteMultiChoiceFilter': 14 | props.loadOptions = action('loadOptions') 15 | break 16 | case 'DayFilter': 17 | props.name = null 18 | break 19 | } 20 | 21 | return { 22 | Renderer: filter, 23 | filterKey: name, 24 | active: true, 25 | name, 26 | ...props, 27 | } 28 | }) 29 | 30 | storiesOf('object-list/FiltersContainer', module) 31 | .addDecorator((story, context) => withInfo( 32 | 'Container to render all the filters underneath eachother' 33 | )(story)(context)) 34 | .add('default view', () => ( 35 | 40 | )) 41 | -------------------------------------------------------------------------------- /src/actions-filters/OptionalField.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | /** 5 | * Represents a single item in the "Visible Fields" drop-down 6 | */ 7 | export default class OptionalField extends React.Component { 8 | static propTypes = { 9 | /** callback function to enable the optional field and save it to user preferences */ 10 | onChange: PropTypes.func.isRequired, 11 | /** the api key used for this particular field */ 12 | fieldKey: PropTypes.string, 13 | /** whether or not the field is enabled (displayed) */ 14 | enabled: PropTypes.bool, 15 | /** the display text for the field */ 16 | name: PropTypes.string, 17 | /** Icon to display when a field is enabled */ 18 | CheckboxCheckedIcon: PropTypes.element, 19 | /** Icon to display when a field is not enabled */ 20 | CheckboxUnCheckedIcon: PropTypes.element, 21 | } 22 | 23 | static defaultProps = { 24 | enabled: false, 25 | CheckboxCheckedIcon: , 26 | CheckboxUnCheckedIcon: , 27 | } 28 | 29 | /** 30 | * Toggles an optional field to be displayed by the objectlist 31 | * 32 | * @event {MouseEvent} event - event that fires when clicking on an OptionalField component 33 | */ 34 | toggle = (event) => { 35 | event.preventDefault() 36 | this.props.onChange(this.props.fieldKey) 37 | } 38 | 39 | render() { 40 | const {enabled, CheckboxCheckedIcon, CheckboxUnCheckedIcon, name} = this.props 41 | const checked = enabled ? CheckboxCheckedIcon : CheckboxUnCheckedIcon 42 | return ( 43 | 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/actions-filters/OptionalFields.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { withInfo } from '@storybook/addon-info' 4 | import { action } from '@storybook/addon-actions' 5 | 6 | import OptionalFields from './OptionalFields' 7 | 8 | const defaultProps = { 9 | extraColumns: ['a', 'b'], 10 | optionalFields: [ 11 | {dataKey: 'a', header: 'Apple'}, 12 | {dataKey: 'c', header: 'Cat'}, 13 | {dataKey: 'd', header: 'Dragon'}, 14 | {dataKey: 'e', header: 'Elephant'}, 15 | {dataKey: 'f', header: 'Frappuccino'}, 16 | {dataKey: 'g', header: 'Grizzly Bear'}, 17 | {dataKey: 'h', header: 'Heaps Good Ay'}, 18 | {dataKey: 'i', header: 'I\'m just waitin\' for a mate'}, 19 | ], 20 | updateColumns: action('updateColumns'), 21 | } 22 | 23 | storiesOf('object-list/OptionalFields', module) 24 | .addDecorator((story, context) => withInfo( 25 | 'Optional fields dropdown selector' 26 | )(story)(context)) 27 | .add('default view', () => ( 28 |
29 | 30 | 31 | 32 |
33 | )) 34 | -------------------------------------------------------------------------------- /src/actions-filters/SelectAllAction.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | class SelectAllAction extends Component { 5 | static propTypes = { 6 | /** the total number of items in the dataset */ 7 | count: PropTypes.number, 8 | /** the amount of items displayed on the current page */ 9 | itemCount: PropTypes.number, 10 | /** the name used to describe multiples of the items displayed */ 11 | itemPluralName: PropTypes.string, 12 | /** the number of items currently selected */ 13 | numSelected: PropTypes.number, 14 | /** callback function to handle selecting all items in the dataset. Set to null to not show it. */ 15 | selectAll: PropTypes.func, 16 | /** callback function to handle deselecting all items */ 17 | deselectAll: PropTypes.func, 18 | } 19 | 20 | handleSelectAllClick = (event) => { 21 | event.preventDefault() 22 | this.props.selectAll() 23 | } 24 | 25 | handleDeselectAllClick = event => { 26 | event.preventDefault() 27 | this.props.deselectAll() 28 | } 29 | 30 | render() { 31 | const {selectAll, count, numSelected, itemPluralName, itemCount, deselectAll} = this.props 32 | let selectAllLink 33 | if (selectAll && count > 0 && numSelected < count && numSelected >= itemCount) { 34 | selectAllLink = ( 35 | 40 | Select all {count.toLocaleString()} {itemPluralName && itemPluralName.toLowerCase()} 41 | 42 | ) 43 | } 44 | let deselectLink 45 | if (deselectAll && numSelected > 0) { 46 | deselectLink = ( 47 | 52 | Clear selection 53 | 54 | ) 55 | } 56 | if (selectAllLink || deselectLink) { 57 | return ( 58 |
59 | {selectAllLink}{deselectLink} 60 |
61 | ) 62 | } else { 63 | return null 64 | } 65 | } 66 | } 67 | 68 | export default SelectAllAction 69 | -------------------------------------------------------------------------------- /src/actions-filters/SelectFilters.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import PropTypes from 'prop-types' 3 | import Select from '../utils/Select' 4 | import {FILTER_BASE_TYPE} from '../utils/proptypes' 5 | import {sortByName} from '../utils' 6 | 7 | class SelectFilters extends Component { 8 | static propTypes = { 9 | /** array of potential filters that can be displayed inside the object-list */ 10 | filters: PropTypes.arrayOf(PropTypes.shape({ 11 | ...FILTER_BASE_TYPE, 12 | active: PropTypes.bool, 13 | })), 14 | /** callback to add a filter to the list of active filters */ 15 | addFilter: PropTypes.func, 16 | /** Object of custom react-select styles */ 17 | selectStyles: PropTypes.object, 18 | } 19 | static defaultProps = { 20 | filters: [], 21 | selectStyles: {}, 22 | } 23 | render() { 24 | const {filters, addFilter, selectStyles} = this.props 25 | if (filters.length === 0) { return null } 26 | 27 | let quickFilters = filters.filter(filter => 28 | !filter.active && !filter.alwaysVisible 29 | ) 30 | quickFilters = quickFilters.sort(sortByName) 31 | 32 | // There's something pretty odd happening with react-select v2 that 33 | // causes any option that itself contains an `options` field to use 34 | // that subfield instead of itself. WTF. 35 | // TODO: Check if this issue has been resolved in a later version. 36 | quickFilters = quickFilters.map(({options, ...rest}) => rest) 37 | 38 | return ( 39 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | `; 77 | 78 | exports[`Storyshots object-list/Favourites interactive 1`] = ` 79 |
86 |
89 | 95 |
98 |
102 | 103 | Favourite 1 104 | 105 | 108 | Delete 109 | 110 |
111 |
115 | 116 | Favourite 2 117 | 118 | 121 | Delete 122 | 123 |
124 |
127 |
130 |
133 | 140 | 146 |
147 |
148 |
149 |
150 |
151 | `; 152 | -------------------------------------------------------------------------------- /src/actions-filters/__snapshots__/OptionalFields.stories.storyshot: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Storyshots object-list/OptionalFields default view 1`] = ` 4 |
12 | 19 |
22 | 27 |
30 | 37 | 44 | 51 | 58 | 65 | 72 | 79 | 86 |
87 |
88 |
89 |
90 | `; 91 | -------------------------------------------------------------------------------- /src/actions-filters/__snapshots__/SelectFilters.stories.storyshot: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Storyshots object-list/SelectFilters default view 1`] = ` 4 | 56 | 62 | 63 |
64 | 65 | 66 | `; 67 | -------------------------------------------------------------------------------- /src/actions-filters/__tests__/__snapshots__/FiltersContainer.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` Snapshot renders correctly 1`] = ` 4 |
7 | 8 | a 9 | 10 |
11 | `; 12 | -------------------------------------------------------------------------------- /src/actions-filters/__tests__/__snapshots__/OptionalField.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`OptionalField Snapshot renders default 1`] = ` 4 | 11 | `; 12 | 13 | exports[`OptionalField Snapshot renders enabled 1`] = ` 14 | 21 | `; 22 | -------------------------------------------------------------------------------- /src/actions-filters/__tests__/__snapshots__/OptionalFields.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` Snapshot renders correctly 1`] = ` 4 |
7 | 12 |
15 | 24 | 33 |
34 |
35 | `; 36 | 37 | exports[` Snapshot renders correctly 2`] = ` 38 |
41 | 46 |
49 | 58 | 67 |
68 |
69 | `; 70 | -------------------------------------------------------------------------------- /src/actions-filters/__tests__/__snapshots__/SelectAllAction.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` Snapshot does not render with no props 1`] = `null`; 4 | 5 | exports[` Snapshot shows both select all and deselect 1`] = ` 6 | 26 | `; 27 | 28 | exports[` Snapshot shows deselect all when all the items are selected 1`] = ` 29 | 40 | `; 41 | 42 | exports[` Snapshot shows deselect all when some of the items are selected 1`] = ` 43 | 54 | `; 55 | 56 | exports[` Snapshot shows global select all when selected all on page 1`] = ` 57 | 78 | `; 79 | -------------------------------------------------------------------------------- /src/actions-filters/__tests__/__snapshots__/SelectFilters.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` Snapshot renders correctly 1`] = ` 4 | 40 | .00 41 | 42 | ) 43 | } 44 | } 45 | 46 | export default Currency 47 | -------------------------------------------------------------------------------- /src/filters/types/Date.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { format } from 'date-fns' 4 | 5 | import Select from '../../utils/Select' 6 | import { Date as DateInput } from 'mireco' 7 | import { ISO_8601_DATE_FORMAT } from 'mireco/constants' 8 | 9 | /** 10 | * Filter input used to pass date values either fixed 11 | * or relative to the current date 12 | */ 13 | export default class DateComponent extends React.Component { 14 | static propTypes = { 15 | /** Function to be called when value changes */ 16 | onChange: PropTypes.func, 17 | /** Current filter value */ 18 | value: PropTypes.oneOfType([ 19 | PropTypes.string, 20 | PropTypes.shape({value: PropTypes.string, label: PropTypes.string}), 21 | ]), 22 | /** Selected comparison */ 23 | comparison: PropTypes.string.isRequired, 24 | /** Comparison used for fixed date */ 25 | fixedComparison: PropTypes.shape({ 26 | /** Value passed back */ 27 | value: PropTypes.any, 28 | /** Value shown to user */ 29 | label: PropTypes.string, 30 | }), 31 | /** Valid options for relative dates */ 32 | relativeDateOptions: PropTypes.arrayOf(PropTypes.shape({ 33 | /** Value passed back */ 34 | value: PropTypes.string, 35 | /** Value shown to user */ 36 | label: PropTypes.string, 37 | })), 38 | /** Object of custom react-select styles */ 39 | selectStyles: PropTypes.object, 40 | } 41 | 42 | static defaultProps = { 43 | relativeDateOptions: [ 44 | {value: 'today', label: 'Today'}, 45 | {value: 'week_start', label: 'Beginning of this week'}, 46 | {value: 'month_start', label: 'Beginning of this month'}, 47 | {value: 'year_start', label: 'Beginning of this year'}, 48 | ], 49 | selectStyles: {}, 50 | } 51 | 52 | /** 53 | * Handles relative date value changes. 54 | * Calls onChange with new value if valid 55 | * @param {Object} newValue Relative date option to change to 56 | */ 57 | handleDateRelativeChange = (newValue) => { 58 | if (Array.isArray(newValue)) { return } 59 | this.props.onChange(newValue) 60 | } 61 | 62 | render() { 63 | const { value, relativeDateOptions, comparison, fixedComparison, selectStyles } = this.props 64 | let date 65 | if (value === null) { 66 | date = null 67 | } else if (value instanceof Date && value.isValid()) { 68 | date = format(value, ISO_8601_DATE_FORMAT) 69 | } else if (typeof value === 'string') { 70 | date = value 71 | } 72 | let dateChoice 73 | if (comparison === fixedComparison.value) { 74 | dateChoice = ( 75 | 82 | ) 83 | } else { 84 | dateChoice = ( 85 | 68 | ) 69 | } 70 | } 71 | 72 | export default Search 73 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/Boolean.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { snapshotTest } from 'utils/tests' 3 | import { shallow } from 'enzyme' 4 | import Boolean from '../Boolean' 5 | 6 | jest.mock('../Choice', () => 'Choice') 7 | 8 | describe('Boolean', () => { 9 | const baseProps = { 10 | onChange: jest.fn(), 11 | value: 'True', 12 | } 13 | describe('Snapshots', () => { 14 | it('renders true', () => { 15 | snapshotTest() 16 | }) 17 | it('renders false', () => { 18 | snapshotTest() 19 | }) 20 | it('has custom labels', () => { 21 | snapshotTest() 22 | }) 23 | }) 24 | describe('Functions', () => { 25 | let instance 26 | beforeEach(() => { 27 | spyOn(baseProps, 'onChange') 28 | instance = shallow().instance() 29 | }) 30 | it('handles value change', () => { 31 | const mockValue = {value: 'False', key: '1'} 32 | instance.onValueChange(mockValue) 33 | expect(baseProps.onChange).lastCalledWith('False') 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/Currency.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import { snapshotTest } from 'utils/tests' 4 | import Currency from '../Currency' 5 | 6 | describe('Currency', () => { 7 | const baseProps = { 8 | onChange: jest.fn(), 9 | value: '25.08', 10 | } 11 | describe('Snapshots', () => { 12 | it('renders default', () => { 13 | snapshotTest() 14 | }) 15 | it('renders with custom currency symbol', () => { 16 | snapshotTest() 17 | }) 18 | }) 19 | describe('Functions', () => { 20 | it('handles value changing', () => { 21 | spyOn(baseProps, 'onChange') 22 | const instance = shallow().instance() 23 | instance.refs = { 24 | input: { 25 | value: '145.98', 26 | }, 27 | } 28 | instance.handleValueChange() 29 | expect(baseProps.onChange).toHaveBeenCalledWith('145.98') 30 | }) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/Date.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import { snapshotTest } from 'utils/tests' 4 | import DateComponent from '../Date' 5 | 6 | describe('Date', () => { 7 | const baseProps = { 8 | onChange: jest.fn(), 9 | fixedComparison: {value: 'fixed', label: 'Exact'}, 10 | comparison: 'fixed', 11 | } 12 | describe('Snapshots', () => { 13 | it('renders default', () => { 14 | snapshotTest() 15 | }) 16 | it('renders with relative comparison', () => { 17 | snapshotTest() 18 | }) 19 | it('has custom input format', () => { 20 | snapshotTest() 21 | }) 22 | it('has custom relative date options', () => { 23 | snapshotTest() 28 | }) 29 | it('has null value', () => { 30 | snapshotTest() 31 | }) 32 | }) 33 | describe('Functions', () => { 34 | describe('handles fixed date changing', () => { 35 | let instance 36 | beforeEach(() => { 37 | spyOn(baseProps, 'onChange') 38 | instance = shallow().instance() 39 | }) 40 | it('handles valid date', () => { 41 | const newValue = Date() 42 | instance.props.onChange(newValue) 43 | expect(baseProps.onChange).toHaveBeenCalledWith(newValue) 44 | }) 45 | }) 46 | describe('handles relative date changing', () => { 47 | let instance 48 | beforeEach(() => { 49 | spyOn(baseProps, 'onChange') 50 | instance = shallow().instance() 51 | }) 52 | it('handles array passed', () => { 53 | const newValue = ['now'] 54 | instance.handleDateRelativeChange(newValue) 55 | expect(baseProps.onChange).not.toHaveBeenCalled() 56 | }) 57 | it('handles single value', () => { 58 | const newValue = 'now' 59 | instance.handleDateRelativeChange(newValue) 60 | expect(baseProps.onChange).toHaveBeenCalledWith(newValue) 61 | }) 62 | }) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/Day.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Moment from 'moment' 3 | import { shallow } from 'enzyme' 4 | import { snapshotTest } from 'utils/tests' 5 | import Day from '../Day' 6 | 7 | describe('Day', () => { 8 | const baseProps = { 9 | onChange: jest.fn(), 10 | value: Moment(), 11 | } 12 | describe('Snapshots', () => { 13 | it('renders default', () => { 14 | snapshotTest() 15 | }) 16 | }) 17 | describe('Functions', () => { 18 | let instance 19 | let mockEvent 20 | beforeEach(() => { 21 | spyOn(baseProps, 'onChange') 22 | instance = shallow().instance() 23 | mockEvent = { 24 | preventDefault: jasmine.createSpy(), 25 | } 26 | }) 27 | it('handles previous click', () => { 28 | instance.handlePreviousClick(mockEvent) 29 | expect(mockEvent.preventDefault).toHaveBeenCalled() 30 | const expectedValue = instance.props.value.clone().subtract(1, 'days') 31 | expect(baseProps.onChange).toHaveBeenCalledWith(expectedValue) 32 | }) 33 | it('handles next click', () => { 34 | instance.handleNextClick(mockEvent) 35 | expect(mockEvent.preventDefault).toHaveBeenCalled() 36 | const expectedValue = instance.props.value.clone().add(1, 'days') 37 | expect(baseProps.onChange).toHaveBeenCalledWith(expectedValue) 38 | }) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/Month.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Moment from 'moment' 3 | import { shallow } from 'enzyme' 4 | import { snapshotTest } from 'utils/tests' 5 | import Month from '../Month' 6 | 7 | jest.mock('react-month-picker', () => 'MonthPicker') 8 | 9 | describe('Month', () => { 10 | const baseProps = { 11 | onChange: jest.fn(), 12 | value: Moment(), 13 | minYear: 2000, 14 | maxYear: 2050, 15 | } 16 | describe('Snapshots', () => { 17 | it('renders default', () => { 18 | snapshotTest() 19 | }) 20 | it('has format', () => { 21 | snapshotTest() 22 | }) 23 | it('has minYear', () => { 24 | snapshotTest() 25 | }) 26 | it('has maxYear', () => { 27 | snapshotTest() 28 | }) 29 | it('has null value', () => { 30 | snapshotTest() 31 | }) 32 | it('has undefined value', () => { 33 | snapshotTest() 34 | }) 35 | }) 36 | describe('Lifecycle', () => { 37 | it('componentWillReceiveProps', () => { 38 | const newProps = { 39 | value: Moment('2018-02-01'), 40 | } 41 | const wrapper = shallow() 42 | wrapper.setProps(newProps) 43 | expect(wrapper.instance().state.textValue).toEqual(newProps.value.format(Month.defaultProps.format)) 44 | }) 45 | }) 46 | describe('Functions', () => { 47 | let instance 48 | beforeEach(() => { 49 | spyOn(baseProps, 'onChange') 50 | instance = shallow().instance() 51 | }) 52 | describe('handleTextChange', () => { 53 | it('handles valid month', () => { 54 | instance.refs = { 55 | textInput: { 56 | value: 'Jan 2016', 57 | }, 58 | } 59 | const monthValue = Moment(instance.refs.textInput.value, instance.props.format, true) // strict=true 60 | instance.handleTextChange() 61 | expect(instance.state.textValue).toBe(instance.refs.textInput.value) 62 | expect(baseProps.onChange).toHaveBeenCalledWith(monthValue) 63 | }) 64 | it('handles invalid month', () => { 65 | instance.refs = { 66 | textInput: { 67 | value: 'Jemima puddle duck', 68 | }, 69 | } 70 | instance.handleTextChange() 71 | expect(instance.state.textValue).toBe(instance.refs.textInput.value) 72 | expect(baseProps.onChange).toHaveBeenCalledWith(null) 73 | }) 74 | }) 75 | it('handles click', () => { 76 | instance = shallow().instance() 77 | instance.refs = { 78 | monthPicker: { 79 | show: jest.fn(), 80 | }, 81 | } 82 | spyOn(instance.refs.monthPicker, 'show') 83 | instance.handleClick() 84 | expect(instance.refs.monthPicker.show).toHaveBeenCalled() 85 | }) 86 | it('handles value change', () => { 87 | const mockYear = Math.floor(Math.random() * 50) + 2000 88 | const mockMonth = Math.floor(Math.random() * 12) 89 | const date = Moment({year: mockYear, month: mockMonth}) 90 | instance.handleValueChange(mockYear, mockMonth + 1) 91 | expect(instance.state.textValue).toBe(date.format(instance.props.format)) 92 | const arg = baseProps.onChange.calls.mostRecent().args 93 | expect(arg.toString()).toEqual(date.toString()) 94 | }) 95 | }) 96 | }) 97 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/NumberSlider.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import { snapshotTest } from 'utils/tests' 4 | import NumberSlider from '../NumberSlider' 5 | 6 | describe('NumberSlider', () => { 7 | const baseProps = { 8 | onChange: jest.fn(), 9 | value: 50, 10 | precision: 1, 11 | } 12 | describe('Snapshots', () => { 13 | it('renders default', () => { 14 | snapshotTest() 15 | }) 16 | it('custom min', () => { 17 | snapshotTest() 18 | }) 19 | it('custom max', () => { 20 | snapshotTest() 21 | }) 22 | }) 23 | describe('Lifecycle', () => { 24 | it('componentWillReceiveProps', () => { 25 | const newProps = { 26 | value: 3128907234589789, 27 | } 28 | const wrapper = shallow() 29 | wrapper.setProps(newProps) 30 | expect(wrapper.instance().state.currentValue).toEqual(newProps.value) 31 | }) 32 | }) 33 | describe('Functions', () => { 34 | let instance 35 | beforeEach(() => { 36 | spyOn(baseProps, 'onChange') 37 | instance = shallow().instance() 38 | }) 39 | it('handles slider value change', () => { 40 | const mockValue = Math.floor(Math.random() * 100) + 1 41 | const mockEvent = { 42 | target: { 43 | value: mockValue, 44 | }, 45 | } 46 | instance.handleSliderValueChange(mockEvent) 47 | expect(instance.state.currentValue).toBe(mockValue) 48 | expect(baseProps.onChange).not.toHaveBeenCalled() 49 | }) 50 | it('handles value change', () => { 51 | const mockValue = Math.floor(Math.random() * 100) + 1 52 | const mockEvent = { 53 | target: { 54 | value: mockValue, 55 | }, 56 | } 57 | instance.handleValueChange(mockEvent) 58 | expect(instance.state.currentValue).toBe(mockValue) 59 | expect(baseProps.onChange).toHaveBeenCalledWith(mockValue.toFixed(1)) 60 | }) 61 | it('handles slider value finalise', () => { 62 | const mockValue = Math.floor(Math.random() * 100) + 1 63 | const mockEvent = { 64 | target: { 65 | value: mockValue, 66 | }, 67 | } 68 | instance.handleSliderValueFinalise(mockEvent) 69 | expect(instance.state.currentValue).toBe(mockValue) 70 | expect(baseProps.onChange).toHaveBeenCalledWith(mockValue.toFixed(1)) 71 | }) 72 | it('handles logarithmic translation', () => { 73 | const mockValue = Math.floor(Math.random() * 100) + 1 74 | instance = shallow().instance() 75 | const actualValue = instance.getValueFromSlider(mockValue) 76 | expect(instance.getValueForSlider(actualValue)).toBeCloseTo(mockValue, 10) 77 | }) 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/Search.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import { snapshotTest } from 'utils/tests' 4 | import Search from '../Search' 5 | 6 | jest.useFakeTimers() 7 | 8 | describe('Search', () => { 9 | const baseProps = { 10 | onChange: jest.fn(), 11 | value: 'Kittens', 12 | updateDelay: 1234, 13 | } 14 | describe('Snapshots', () => { 15 | it('renders default', () => { 16 | snapshotTest() 17 | }) 18 | it('has new value', () => { 19 | snapshotTest(, {currentValue: 21}) 20 | }) 21 | }) 22 | describe('Lifecycle', () => { 23 | it('componentWillReceiveProps', () => { 24 | const newProps = { 25 | value: 'Hello my fluffies 🐈🐈', 26 | } 27 | const wrapper = shallow() 28 | wrapper.setProps(newProps) 29 | expect(wrapper.instance().state.currentValue).toEqual(newProps.value) 30 | }) 31 | }) 32 | describe('Functions', () => { 33 | let instance 34 | beforeEach(() => { 35 | spyOn(baseProps, 'onChange') 36 | instance = shallow().instance() 37 | }) 38 | it('handles value change', () => { 39 | const mockValue = 'Some search term' 40 | const mockEvent = { 41 | target: { 42 | value: mockValue, 43 | }, 44 | } 45 | spyOn(instance, 'scheduleUpdate') 46 | instance.handleValueChange(mockEvent) 47 | expect(instance.state.currentValue).toBe(mockValue) 48 | expect(instance.scheduleUpdate).toHaveBeenCalled() 49 | expect(baseProps.onChange).not.toHaveBeenCalled() 50 | }) 51 | it('schedules update', () => { 52 | instance.setState({currentValue: 'Bob', updateScheduled: null}) 53 | instance.scheduleUpdate() 54 | expect(setTimeout).toHaveBeenCalledTimes(2) 55 | expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), baseProps.updateDelay) 56 | expect(typeof instance.state.updateScheduled).toBe('number') 57 | expect(instance.state.updateScheduled % 1).toBe(0) 58 | instance.scheduleUpdate() 59 | expect(clearTimeout).toHaveBeenCalledTimes(1) 60 | instance.setState({currentValue: 'Not Bob'}) 61 | jest.runOnlyPendingTimers() 62 | expect(baseProps.onChange).toHaveBeenCalledWith('Not Bob') 63 | }) 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/__snapshots__/Boolean.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Boolean Snapshots has custom labels 1`] = ` 4 | 26 | `; 27 | 28 | exports[`Boolean Snapshots renders false 1`] = ` 29 | 51 | `; 52 | 53 | exports[`Boolean Snapshots renders true 1`] = ` 54 | 76 | `; 77 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/__snapshots__/Currency.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Currency Snapshots renders default 1`] = ` 4 |
7 | 10 | $ 11 | 12 | 18 | 21 | .00 22 | 23 |
24 | `; 25 | 26 | exports[`Currency Snapshots renders with custom currency symbol 1`] = ` 27 |
30 | 33 | % 34 | 35 | 41 | 44 | .00 45 | 46 |
47 | `; 48 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/__snapshots__/Day.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Day Snapshots renders default 1`] = ` 4 |
7 | 13 |

16 | Saturday July 15th 2017 17 |

18 | 24 |
25 | `; 26 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/__snapshots__/NumberSlider.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`NumberSlider Snapshots custom max 1`] = ` 4 |
7 | 16 | 26 |
27 | `; 28 | 29 | exports[`NumberSlider Snapshots custom min 1`] = ` 30 |
33 | 42 | 52 |
53 | `; 54 | 55 | exports[`NumberSlider Snapshots renders default 1`] = ` 56 |
59 | 68 | 78 |
79 | `; 80 | -------------------------------------------------------------------------------- /src/filters/types/__tests__/__snapshots__/Search.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Search Snapshots has new value 1`] = ` 4 | 11 | `; 12 | 13 | exports[`Search Snapshots renders default 1`] = ` 14 | 21 | `; 22 | -------------------------------------------------------------------------------- /src/filters/types/index.js: -------------------------------------------------------------------------------- 1 | export Boolean from './Boolean' 2 | export Choice from './Choice' 3 | export Currency from './Currency' 4 | export Date from './Date' 5 | export Day from './Day' 6 | export Month from './Month' 7 | export NumberSlider from './NumberSlider' 8 | export Search from './Search' 9 | -------------------------------------------------------------------------------- /src/filters/utils/FilterComparison.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Select from '../../utils/Select' 4 | 5 | class FilterComparison extends React.Component { 6 | static propTypes = { 7 | options: PropTypes.arrayOf(PropTypes.shape({ 8 | label: PropTypes.string, 9 | value: Select.propTypes.value, 10 | })), 11 | value: Select.propTypes.value, 12 | onChange: PropTypes.func.isRequired, 13 | /** Object of custom react-select styles */ 14 | selectStyles: PropTypes.object, 15 | } 16 | 17 | static defaultProps = { 18 | selectStyles: {}, 19 | } 20 | 21 | handleChange = newValue => this.props.onChange(newValue.value) 22 | 23 | render() { 24 | const {options, value, selectStyles} = this.props 25 | return ( 26 |
27 | 34 |
35 | `; 36 | -------------------------------------------------------------------------------- /src/filters/utils/__tests__/__snapshots__/FilterLabel.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`FilterLabel Snapshots renders default 1`] = ` 4 | 10 | `; 11 | -------------------------------------------------------------------------------- /src/filters/utils/__tests__/__snapshots__/RemoveFilter.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RemoveFilter Snapshots renders default 1`] = ` 4 | 11 | `; 12 | -------------------------------------------------------------------------------- /src/filters/utils/__tests__/__snapshots__/makeFilter.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`makeFilter Snapshots has a name 1`] = ` 4 |
7 | 10 |
11 | 44 tests 12 |
13 | 17 |
18 | `; 19 | 20 | exports[`makeFilter Snapshots renders default 1`] = ` 21 |
24 |
25 | 44 tests 26 |
27 | 31 |
32 | `; 33 | 34 | exports[`makeFilter Snapshots renders with comparison null 1`] = ` 35 |
38 | 60 |
61 | 44 tests 62 |
63 | 67 |
68 | `; 69 | 70 | exports[`makeFilter Snapshots renders with comparison options 1`] = ` 71 |
74 | 96 |
97 | 44 tests 98 |
99 | 103 |
104 | `; 105 | 106 | exports[`makeFilter Snapshots renders with permanent true 1`] = ` 107 |
110 |
111 | 44 tests 112 |
113 |
114 | `; 115 | -------------------------------------------------------------------------------- /src/filters/utils/index.js: -------------------------------------------------------------------------------- 1 | export FilterComparison from './FilterComparison' 2 | export FilterLabel from './FilterLabel' 3 | export RemoveFilter from './RemoveFilter' 4 | export makeFilter from './makeFilter' 5 | export * from './utils' 6 | -------------------------------------------------------------------------------- /src/filters/utils/utils.js: -------------------------------------------------------------------------------- 1 | const possibleQueryStringLookupExpressions = ['exact', 'lt', 'gt', 'gte', 'lte'] 2 | 3 | export { 4 | possibleQueryStringLookupExpressions, 5 | } 6 | -------------------------------------------------------------------------------- /src/icons/FontAwesome.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const FontAwesomeIcons = (majorVersion = 5) => { 4 | switch (majorVersion) { 5 | case 5: 6 | return { 7 | OptionalFields: