diff --git a/public/js/__tests__/components/feature/FeatureList-test.js b/public/js/__tests__/components/feature/FeatureList-test.js new file mode 100644 index 0000000000..b86230a24b --- /dev/null +++ b/public/js/__tests__/components/feature/FeatureList-test.js @@ -0,0 +1,55 @@ +jest.dontMock("../../../components/feature/FeatureList"); +jest.dontMock("../../../components/feature/Feature"); + +var React = require("react/addons"); +var TestUtils = React.addons.TestUtils; +var FeatureList = require("../../../components/feature/FeatureList"); + +describe("FeatureList", function () { + var Component, features; + + beforeEach(function() { + features = [ + { name: "featureX", strategy: "other" }, + { name: "group.featureY", strategy: "default" } + ]; + Component = TestUtils .renderIntoDocument(); + }); + + afterEach(function() { + React.unmountComponentAtNode(document.body); + }); + + it("should render all features", function() { + var features = Component.getDOMNode().querySelectorAll(".feature"); + expect(features.length).toEqual(2); + }); + + it("should filter list of features", function() { + var filterNode = Component.refs.filter.getDOMNode(); + TestUtils.Simulate.change(filterNode, {target: {value: "group"}}); + + var features = Component.getDOMNode().querySelectorAll(".feature"); + expect(features.length).toEqual(1); + }); + + it("should filter list of features ignoring case", function() { + var filterNode = Component.refs.filter.getDOMNode(); + TestUtils.Simulate.change(filterNode, {target: {value: "GROUP"}}); + + var features = Component.getDOMNode().querySelectorAll(".feature"); + expect(features.length).toEqual(1); + expect(features[0].textContent).toMatch("group"); + }); + + it("should filter list of features by strategy name", function() { + var searchString = "other"; + var filterNode = Component.refs.filter.getDOMNode(); + TestUtils.Simulate.change(filterNode, {target: {value: searchString}}); + + var features = Component.getDOMNode().querySelectorAll(".feature"); + expect(features.length).toEqual(1); + expect(features[0].textContent).toMatch(searchString); + }); + +}); \ No newline at end of file diff --git a/public/js/components/feature/Feature.jsx b/public/js/components/feature/Feature.jsx index 30b1913930..bad6ccd442 100644 --- a/public/js/components/feature/Feature.jsx +++ b/public/js/components/feature/Feature.jsx @@ -53,7 +53,7 @@ var Feature = React.createClass({ render: function() { return ( - + @@ -66,7 +66,7 @@ var Feature = React.createClass({ - + {this.props.feature.strategy} diff --git a/public/js/components/feature/FeatureList.jsx b/public/js/components/feature/FeatureList.jsx index 6831c38121..f62a9583bf 100644 --- a/public/js/components/feature/FeatureList.jsx +++ b/public/js/components/feature/FeatureList.jsx @@ -1,9 +1,47 @@ var React = require('react'); var Feature = require('./Feature'); +var noop = function() {}; + var FeatureList = React.createClass({ + propTypes: { + features: React.PropTypes.array.isRequired + }, + + getDefaultProps: function() { + return { + onFeatureChanged: noop, + onFeatureArchive: noop + }; + }, + + getInitialState: function() { + return { + filter: undefined + }; + }, + + onFilterChange: function(e) { + e.preventDefault(); + this.setState({filter: e.target.value.trim()}); + }, + + filteredFeatures: function() { + if(this.state.filter) { + var regex = new RegExp(this.state.filter, "i"); + + return this.props.features.filter(function(item) { + return regex.test(item.name) || regex.test(item.strategy); + }.bind(this)); + + } else { + return this.props.features; + } + + }, + render: function() { - var featureNodes = this.props.features.map(function(feature) { + var featureNodes = this.filteredFeatures().map(function(feature) { return ( - - - - - - - - - - {featureNodes} -
NameStrategy
- +
+ + + + + + + + + + + + + + + {featureNodes} +
NameStrategy
+ +
+
); } });