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 (
-
-
-
- |
- Name |
- Strategy |
- |
-
-
- {featureNodes}
-
-
+
);
}
});