diff --git a/package-lock.json b/package-lock.json index 2b9cff338e9..c4eeafed30a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -158,7 +158,7 @@ "mime": "2.2.0", "p-reduce": "1.0.0", "parse-github-url": "1.0.2", - "url-join": "2.0.2" + "url-join": "2.0.3" }, "dependencies": { "debug": { @@ -14703,7 +14703,7 @@ "p-retry": "1.0.0", "semver": "5.4.1", "update-notifier": "2.3.0", - "url-join": "2.0.2", + "url-join": "2.0.3", "yargs": "10.1.1" }, "dependencies": { @@ -15028,9 +15028,9 @@ } }, "url-join": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.2.tgz", - "integrity": "sha1-wHJ1aWetJLi1nldBVRyqx49QuLc=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.3.tgz", + "integrity": "sha1-Fdsf3ekFZRw6Ihp42l/rj8gJ03I=", "dev": true }, "url-loader": { 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..774c81bd1ed 100644 --- a/src/components/Filter/Filter.test.js +++ b/src/components/Filter/Filter.test.js @@ -1,24 +1,18 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import { - Filter, - FilterTypeSelector, - FilterValueSelector, - FilterCategorySelector, - FilterCategoryValueSelector -} from '../../index'; +import { Filter, FormControl, 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..1822f2a1c17 --- /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 = ({ children, className, ...rest }) => { + const classes = cx('filter-pf-active-label', className); + return

{children}

; +}; + +FilterActiveLabel.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string +}; + +export default FilterActiveLabel; diff --git a/src/components/Filter/FilterItem.js b/src/components/Filter/FilterItem.js new file mode 100644 index 00000000000..d344afc5cd7 --- /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 = ({ children, className, onRemove, filterData, ...rest }) => { + const classes = cx(className); + + return ( +
  • + + {children} + { + e.preventDefault(); + onRemove && onRemove(filterData); + }} + > + + +
  • + ); +}; + +FilterItem.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** additional filter item classes */ + className: PropTypes.string, + /** 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..fe607d50a8f 100644 --- a/src/components/Filter/__mocks__/mockFilterExample.js +++ b/src/components/Filter/__mocks__/mockFilterExample.js @@ -1,11 +1,7 @@ import React from 'react'; -import { Grid, Col, Row, Filter } from '../../../index'; +import { Filter, FormControl, Toolbar } from '../../../index'; +import { bindMethods } from '../../../common/helpers'; -const bindMethods = (context, methods) => { - methods.forEach(method => { - context[method] = context[method].bind(context); - }); -}; export const mockFilterExampleFields = [ { id: 'name', @@ -85,13 +81,15 @@ export class MockFilterExample extends React.Component { 'selectFilterType', 'filterValueSelected', 'filterCategorySelected', - 'categoryValueSelected' + 'categoryValueSelected', + 'removeFilter', + 'clearFilters' ]); this.state = { currentFilterType: mockFilterExampleFields[0], - currentValue: '', - filtersText: '' + activeFilters: [], + currentValue: '' }; } @@ -114,18 +112,28 @@ 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) { const { currentFilterType } = this.state; if (currentFilterType !== filterType) { - this.setState({ currentValue: '', currentFilterType: filterType }); - - if (filterType.filterType === 'complex-select') { - this.setState({ filterCategory: undefined, categoryValue: '' }); - } + this.setState(prevState => { + return { + currentValue: '', + currentFilterType: filterType, + filterCategory: + filterType.filterType === 'complex-select' + ? undefined + : prevState.filterCategory, + categoryValue: + filterType.filterType === 'complex-select' + ? '' + : prevState.categoryValue + }; + }); } } @@ -143,7 +151,7 @@ export class MockFilterExample extends React.Component { filterCategorySelected(category) { const { filterCategory } = this.state; if (filterCategory !== category) { - this.setState({ filterCategory: category, categoryValue: '' }); + this.setState({ filterCategory: category, currentValue: '' }); } } @@ -177,6 +185,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) { @@ -187,6 +212,7 @@ export class MockFilterExample extends React.Component { return ( @@ -209,8 +235,7 @@ export class MockFilterExample extends React.Component { ); } else { return ( - - - - - - {this.renderInput()} - - - - - -
    - - - - - -