From 888b768551c58356dd27b7ad5276b1c61683ada6 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Date: Thu, 4 Jan 2018 14:30:08 -0500 Subject: [PATCH 1/5] feat(Toolbar): Add Toolbar and its components --- src/components/Filter/Filter.js | 16 +- src/components/Filter/Filter.stories.js | 10 +- src/components/Filter/Filter.test.js | 59 +- src/components/Filter/FilterActiveLabel.js | 17 + src/components/Filter/FilterItem.js | 38 + src/components/Filter/FilterList.js | 20 + .../Filter/__mocks__/mockFilterExample.js | 200 ++++-- .../Filter/__snapshots__/Filter.test.js.snap | 194 +++++ src/components/Filter/index.js | 11 +- src/components/Sort/Sort.js | 21 + src/components/Sort/Sort.stories.js | 57 ++ src/components/Sort/Sort.test.js | 86 +++ src/components/Sort/SortDirectionSelector.js | 38 + src/components/Sort/SortTypeSelector.js | 60 ++ .../Sort/__mocks__/mockSortExample.js | 214 ++++++ .../Sort/__snapshots__/Sort.test.js.snap | 417 +++++++++++ src/components/Sort/index.js | 8 + src/components/Toolbar/Toolbar.js | 46 ++ src/components/Toolbar/Toolbar.stories.js | 48 ++ src/components/Toolbar/Toolbar.test.js | 82 +++ src/components/Toolbar/ToolbarConstants.js | 20 + src/components/Toolbar/ToolbarFind.js | 158 +++++ src/components/Toolbar/ToolbarResults.js | 25 + src/components/Toolbar/ToolbarRightContent.js | 17 + src/components/Toolbar/ToolbarViewSelector.js | 17 + .../Toolbar/__mocks__/mockToolbarExample.js | 671 ++++++++++++++++++ .../__snapshots__/Toolbar.test.js.snap | 496 +++++++++++++ src/components/Toolbar/index.js | 18 + src/index.js | 2 + 29 files changed, 2976 insertions(+), 90 deletions(-) create mode 100644 src/components/Filter/FilterActiveLabel.js create mode 100644 src/components/Filter/FilterItem.js create mode 100644 src/components/Filter/FilterList.js create mode 100644 src/components/Sort/Sort.js create mode 100644 src/components/Sort/Sort.stories.js create mode 100644 src/components/Sort/Sort.test.js create mode 100644 src/components/Sort/SortDirectionSelector.js create mode 100644 src/components/Sort/SortTypeSelector.js create mode 100644 src/components/Sort/__mocks__/mockSortExample.js create mode 100644 src/components/Sort/__snapshots__/Sort.test.js.snap create mode 100644 src/components/Sort/index.js create mode 100644 src/components/Toolbar/Toolbar.js create mode 100644 src/components/Toolbar/Toolbar.stories.js create mode 100644 src/components/Toolbar/Toolbar.test.js create mode 100644 src/components/Toolbar/ToolbarConstants.js create mode 100644 src/components/Toolbar/ToolbarFind.js create mode 100644 src/components/Toolbar/ToolbarResults.js create mode 100644 src/components/Toolbar/ToolbarRightContent.js create mode 100644 src/components/Toolbar/ToolbarViewSelector.js create mode 100644 src/components/Toolbar/__mocks__/mockToolbarExample.js create mode 100644 src/components/Toolbar/__snapshots__/Toolbar.test.js.snap create mode 100644 src/components/Toolbar/index.js diff --git a/src/components/Filter/Filter.js b/src/components/Filter/Filter.js index ba9b048c018..d1e97833025 100644 --- a/src/components/Filter/Filter.js +++ b/src/components/Filter/Filter.js @@ -1,9 +1,19 @@ import cx from 'classnames'; import React from 'react'; import PropTypes from 'prop-types'; +import { getContext } from 'recompose'; +import { toolbarContextTypes } from '../Toolbar/ToolbarConstants'; + +// Disabled eslint due to `isDescendantOfToolbar` being a context property we don't want passed by consumers +const Filter = ({ children, className, isDescendantOfToolbar, ...rest }) => { // eslint-disable-line + const classes = cx( + { + 'filter-pf form-group': true, + 'toolbar-pf-filter': isDescendantOfToolbar + }, + className + ); -const Filter = ({ children, className, ...rest }) => { - const classes = cx('filter-pf form-group', className); return (
@@ -20,4 +30,4 @@ Filter.propTypes = { className: PropTypes.string }; -export default Filter; +export default getContext(toolbarContextTypes)(Filter); diff --git a/src/components/Filter/Filter.stories.js b/src/components/Filter/Filter.stories.js index 28454480ad1..e41f79525de 100644 --- a/src/components/Filter/Filter.stories.js +++ b/src/components/Filter/Filter.stories.js @@ -8,7 +8,10 @@ import { FilterTypeSelector, FilterValueSelector, FilterCategorySelector, - FilterCategoryValueSelector + FilterCategoryValueSelector, + FilterActiveLabel, + FilterList, + FilterItem } from '../../index'; import { @@ -34,7 +37,10 @@ stories.add( FilterTypeSelector, FilterValueSelector, FilterCategorySelector, - FilterCategoryValueSelector + FilterCategoryValueSelector, + FilterActiveLabel, + FilterList, + FilterItem ], propTablesExclude: [MockFilterExample], text: ( diff --git a/src/components/Filter/Filter.test.js b/src/components/Filter/Filter.test.js index bc5e3d52154..3bd30ef6780 100644 --- a/src/components/Filter/Filter.test.js +++ b/src/components/Filter/Filter.test.js @@ -1,18 +1,12 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import { - Filter, - FilterTypeSelector, - FilterValueSelector, - FilterCategorySelector, - FilterCategoryValueSelector -} from '../../index'; +import { Filter, Toolbar } from '../../index'; import { mockFilterExampleFields } from './__mocks__/mockFilterExample'; test('Filter input renders properly', () => { const component = renderer.create( - @@ -32,11 +26,11 @@ test('Filter input renders properly', () => { test('Filter select renders properly', () => { const component = renderer.create( - - @@ -50,16 +44,16 @@ test('Filter select renders properly', () => { test('Filter categories renders properly', () => { const component = renderer.create( - - - { } placeholder={mockFilterExampleFields[3].filterCategoriesPlaceholder} /> - + ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); }); + +test('Filter renders properly in a Toolbar', () => { + const component = renderer.create( + + + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Filter active components render properly', () => { + const component = renderer.create( + + + + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/Filter/FilterActiveLabel.js b/src/components/Filter/FilterActiveLabel.js new file mode 100644 index 00000000000..0596c8a0eb6 --- /dev/null +++ b/src/components/Filter/FilterActiveLabel.js @@ -0,0 +1,17 @@ +import cx from 'classnames'; +import React from 'react'; +import PropTypes from 'prop-types'; + +const FilterActiveLabel = ({ className, title, ...rest }) => { + const classes = cx('filter-pf-active-label', className); + return

{title}

; +}; + +FilterActiveLabel.propTypes = { + /** Additional css classes */ + className: PropTypes.string, + /** Title for the list (ie. 'Active Filters:') */ + title: PropTypes.string.isRequired +}; + +export default FilterActiveLabel; diff --git a/src/components/Filter/FilterItem.js b/src/components/Filter/FilterItem.js new file mode 100644 index 00000000000..8ce006da7f3 --- /dev/null +++ b/src/components/Filter/FilterItem.js @@ -0,0 +1,38 @@ +import cx from 'classnames'; +import React from 'react'; +import PropTypes from 'prop-types'; + +const FilterItem = ({ className, label, onRemove, filterData, ...rest }) => { + const classes = cx(className); + + return ( +
  • + + {label} + { + e.preventDefault(); + onRemove && onRemove(filterData); + }} + > + + +
  • + ); +}; + +FilterItem.propTypes = { + /** additional filter item classes */ + className: PropTypes.string, + /** Label to show for the filter item */ + label: PropTypes.string.isRequired, + /** callback when filter is removed */ + onRemove: PropTypes.func, + /** Data to pass to onRemove function */ + filterData: PropTypes.object +}; + +export default FilterItem; diff --git a/src/components/Filter/FilterList.js b/src/components/Filter/FilterList.js new file mode 100644 index 00000000000..d2a9cab1188 --- /dev/null +++ b/src/components/Filter/FilterList.js @@ -0,0 +1,20 @@ +import cx from 'classnames'; +import React from 'react'; +import PropTypes from 'prop-types'; + +const FilterList = ({ children, className, ...rest }) => { + if (!children) { + return null; + } + const classes = cx('list-inline', className); + return
      {children}
    ; +}; + +FilterList.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string +}; + +export default FilterList; diff --git a/src/components/Filter/__mocks__/mockFilterExample.js b/src/components/Filter/__mocks__/mockFilterExample.js index 66d8ef4953a..f8a371fdc0c 100644 --- a/src/components/Filter/__mocks__/mockFilterExample.js +++ b/src/components/Filter/__mocks__/mockFilterExample.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Grid, Col, Row, Filter } from '../../../index'; +import { Filter, Toolbar } from '../../../index'; const bindMethods = (context, methods) => { methods.forEach(method => { @@ -85,13 +85,15 @@ export class MockFilterExample extends React.Component { 'selectFilterType', 'filterValueSelected', 'filterCategorySelected', - 'categoryValueSelected' + 'categoryValueSelected', + 'removeFilter', + 'clearFilters' ]); this.state = { currentFilterType: mockFilterExampleFields[0], - currentValue: '', - filtersText: '' + activeFilters: [], + currentValue: '' }; } @@ -114,8 +116,9 @@ export class MockFilterExample extends React.Component { } else { filterText += value; } - filterText += '\n'; - this.setState({ filtersText: this.state.filtersText + filterText }); + + let activeFilters = [...this.state.activeFilters, { label: filterText }]; + this.setState({ activeFilters: activeFilters }); }; selectFilterType(filterType) { @@ -177,6 +180,23 @@ export class MockFilterExample extends React.Component { } } + removeFilter(filter) { + const { activeFilters } = this.state; + + let index = activeFilters.indexOf(filter); + if (index > -1) { + let updated = [ + ...activeFilters.slice(0, index), + ...activeFilters.slice(index + 1) + ]; + this.setState({ activeFilters: updated }); + } + } + + clearFilters() { + this.setState({ activeFilters: [] }); + } + renderInput() { const { currentFilterType, currentValue, filterCategory } = this.state; if (!currentFilterType) { @@ -222,45 +242,55 @@ export class MockFilterExample extends React.Component { } render() { - const { currentFilterType } = this.state; + const { currentFilterType, activeFilters } = this.state; return ( - - - - - - {this.renderInput()} - - - - - -
    - - - - - -