Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"homepage": "https://github.com/patternfly/patternfly-react#readme",
"dependencies": {
"classnames": "^2.2.5",
"patternfly": "^3.31.0",
"patternfly": "^3.35.0",
"react-bootstrap": "^0.31.5",
"react-c3js": "^0.1.20",
"react-fontawesome": "^1.6.1",
Expand Down
23 changes: 23 additions & 0 deletions src/components/Filter/Filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import cx from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';

const Filter = ({ children, className, ...rest }) => {
const classes = cx('filter-pf form-group', className);
return (
<div className={classes} {...rest}>
<div className="filter-pf-fields">
<div className="input-group">{children}</div>
</div>
</div>
);
};

Filter.propTypes = {
/** Children nodes */
children: PropTypes.node,
/** Additional css classes */
className: PropTypes.string
};

export default Filter;
47 changes: 47 additions & 0 deletions src/components/Filter/Filter.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { defaultTemplate } from '../../../storybook/decorators/storyTemplates';
import { withInfo } from '@storybook/addon-info/dist/index';
import {
Filter,
FilterTypeSelector,
FilterValueSelector,
FilterCategorySelector,
FilterCategoryValueSelector
} from '../../index';

import {
MockFilterExample,
mockFilterExampleSource
} from './__mocks__/mockFilterExample';

const stories = storiesOf('Filter', module);

stories.addDecorator(
defaultTemplate({
title: 'Filter',
documentationLink:
'http://www.patternfly.org/pattern-library/forms-and-controls/filter/'
})
);

stories.add(
'Filter',
withInfo({
source: false,
propTables: [
Filter,
FilterTypeSelector,
FilterValueSelector,
FilterCategorySelector,
FilterCategoryValueSelector
],
propTablesExclude: [MockFilterExample],
text: (
<div>
<h1>Story Source</h1>
<pre>{mockFilterExampleSource}</pre>
</div>
)
})(() => <MockFilterExample />)
);
77 changes: 77 additions & 0 deletions src/components/Filter/Filter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import renderer from 'react-test-renderer';
import {
Filter,
FilterTypeSelector,
FilterValueSelector,
FilterCategorySelector,
FilterCategoryValueSelector
} from '../../index';
import { mockFilterExampleFields } from './__mocks__/mockFilterExample';

test('Filter input renders properly', () => {
const component = renderer.create(
<Filter>
<FilterTypeSelector
filterTypes={mockFilterExampleFields}
currentFilterType={mockFilterExampleFields[0]}
/>
<input
className="form-control"
type={mockFilterExampleFields[0].filterType}
value=""
placeholder="Filter by Name"
/>
</Filter>
);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test('Filter select renders properly', () => {
const component = renderer.create(
<Filter>
<FilterTypeSelector
filterTypes={mockFilterExampleFields}
currentFilterType={mockFilterExampleFields[2]}
/>
<FilterValueSelector
filterValues={mockFilterExampleFields[2].filterValues}
currentValue={mockFilterExampleFields[2].filterValues[4]}
/>
</Filter>
);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test('Filter categories renders properly', () => {
const component = renderer.create(
<Filter>
<FilterTypeSelector
filterTypes={mockFilterExampleFields}
currentFilterType={mockFilterExampleFields[3]}
/>
<FilterCategorySelector
filterCategories={mockFilterExampleFields[3].filterCategories}
currentCategory={mockFilterExampleFields[3].filterCategories[0]}
placeholder={mockFilterExampleFields[3].placeholder}
>
<FilterCategoryValueSelector
categoryValues={
mockFilterExampleFields[3].filterCategories[0].filterValues
}
currentValue={
mockFilterExampleFields[3].filterCategories[0].filterValues[0]
}
placeholder={mockFilterExampleFields[3].filterCategoriesPlaceholder}
/>
</FilterCategorySelector>
</Filter>
);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
93 changes: 93 additions & 0 deletions src/components/Filter/FilterCategorySelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React from 'react';
import PropTypes from 'prop-types';
import { DropdownButton } from '../Button';
import { MenuItem } from '../MenuItem';
import cx from 'classnames';

const FilterCategorySelector = ({
children,
className,
id,
filterCategories,
currentCategory,
placeholder,
onFilterCategorySelected,
...rest
}) => {
let classes = cx('filter-pf-category-select', className);

if (placeholder || (filterCategories && filterCategories.length > 1)) {
let title;
if (currentCategory) {
title = currentCategory.title || currentCategory;
} else {
title = placeholder || filterCategories[0].title || filterCategories[0];
}

let menuId = 'filterCategoryMenu';
menuId += id ? `_${id}` : '';

return (
<div className={classes} {...rest}>
<div className="filter-pf-select">
<DropdownButton
title={title}
id={menuId}
className="filter-pf-select-dropdown"
>
{placeholder && (
<MenuItem
title={placeholder}
key="Placeholder"
onSelect={() =>
onFilterCategorySelected && onFilterCategorySelected()
}
>
{placeholder}
</MenuItem>
)}
{filterCategories &&
filterCategories.map((item, index) => {
let classes = {
selected: item === currentCategory
};
return (
<MenuItem
className={classes}
key={item.id || index}
onSelect={() =>
onFilterCategorySelected && onFilterCategorySelected(item)
}
>
{item.title || item}
</MenuItem>
);
})}
</DropdownButton>
</div>
{children}
</div>
);
} else {
return null;
}
};

FilterCategorySelector.propTypes = {
/** Children nodes */
children: PropTypes.node,
/** Additional css classes */
className: PropTypes.string,
/** ID for the component, necessary for accessibility if there are multiple filters on a page */
id: PropTypes.string,
/** Array of filter categories, each can be a string or an object with a 'title' field */
filterCategories: PropTypes.array.isRequired,
/** Current selected category */
currentCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
/** Placeholder text when no category is selected */
placeholder: PropTypes.string,
/** function(field, value) - Callback to call when a category is added */
onFilterCategorySelected: PropTypes.func
};

export default FilterCategorySelector;
87 changes: 87 additions & 0 deletions src/components/Filter/FilterCategoryValueSelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
import PropTypes from 'prop-types';
import { DropdownButton } from '../Button';
import { MenuItem } from '../MenuItem';
import cx from 'classnames';

const FilterCategoryValueSelector = ({
className,
id,
categoryValues,
currentValue,
placeholder,
onCategoryValueSelected,
...rest
}) => {
let classes = cx('filter-pf-select', className);

if (placeholder || (categoryValues && categoryValues.length > 1)) {
let title;
if (currentValue) {
title = currentValue.title || currentValue;
} else {
title = placeholder || categoryValues[0].title || categoryValues[0];
}

let menuId = 'filterCategoryMenu';
menuId += id ? `_${id}` : '';

return (
<div className={classes} {...rest}>
<DropdownButton
className="filter-pf-category-select-value filter-pf-select-dropdown"
title={title}
id={menuId}
>
{placeholder && (
<MenuItem
title={placeholder}
key="Placeholder"
onSelect={() =>
onCategoryValueSelected && onCategoryValueSelected()
}
>
{placeholder}
</MenuItem>
)}
{categoryValues &&
categoryValues.map((item, index) => {
let classes = {
selected: item === currentValue
};
return (
<MenuItem
className={classes}
key={item.id || index}
onSelect={() =>
onCategoryValueSelected && onCategoryValueSelected(item)
}
>
{item.title || item}
</MenuItem>
);
})}
</DropdownButton>
</div>
);
} else {
return null;
}
};

FilterCategoryValueSelector.propTypes = {
/** Additional css classes */
className: PropTypes.string,
/** ID for the filter component, necessary for accessibility if there are multiple filters on a page */
id: PropTypes.string,
/** Array of valid values for the category to select from, each can be a string or an object with a 'title' field */
categoryValues: PropTypes.array,
/** Currently selected category value */
currentValue: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
/** Placeholder text when no category value is selected */
placeholder: PropTypes.string,
/** function(field, value) - Callback to call when a category value is selected */
onCategoryValueSelected: PropTypes.func
};

export default FilterCategoryValueSelector;
Loading