diff --git a/src/components/page-list.jsx b/src/components/page-list.jsx
index a214bf48..9b9ebeea 100644
--- a/src/components/page-list.jsx
+++ b/src/components/page-list.jsx
@@ -5,6 +5,7 @@ import Loading from './loading';
* These props also inherit from React Router's RouteComponent props
* @typedef {Object} PageListProps
* @property {Page[]} pages
+ * @property {(any) => void} onSearch
*/
/**
@@ -15,6 +16,12 @@ import Loading from './loading';
* @param {PageListProps} props
*/
export default class PageList extends React.Component {
+ constructor (props) {
+ super(props);
+ this._didSearch = this._didSearch.bind(this);
+ this._dispatchSearch = debounce(this._dispatchSearch.bind(this), 500);
+ }
+
render () {
if (!this.props.pages) {
return ;
@@ -22,6 +29,13 @@ export default class PageList extends React.Component {
return (
+
+
+
@@ -98,6 +112,28 @@ export default class PageList extends React.Component {
this.props.history.push(`/page/${page.uuid}`);
}
+
+ _didSearch (event) {
+ this._dispatchSearch(event.target.value);
+ }
+
+ _dispatchSearch (url) {
+ if (url) {
+ // If doesn't start with a protocol (or looks like it's going that way),
+ // prefix with an asterisk.
+ if (!/^(\*|\/\/|(h|ht|htt|https?|https?\/|https?\/\/))/.test(url)) {
+ url = url = `*//${url}`;
+ }
+ // If the search is for a domain + TLD, return all paths under it
+ if (/^[\w:*]+(\/\/)?[^/]+$/.test(url)) {
+ url = `${url}*`;
+ }
+ }
+ if (this.props.onSearch) {
+ const query = url ? {url} : null;
+ this.props.onSearch(query);
+ }
+ }
}
function isInAnchor (node) {
@@ -109,3 +145,11 @@ function isInAnchor (node) {
}
return isInAnchor(node.parentNode);
}
+
+function debounce (func, delay) {
+ let timer = null;
+ return function (...args) {
+ clearTimeout(timer);
+ timer = setTimeout(() => func(...args), delay);
+ };
+}
diff --git a/src/components/web-monitoring-ui.jsx b/src/components/web-monitoring-ui.jsx
index 562b96e2..99f0e27d 100644
--- a/src/components/web-monitoring-ui.jsx
+++ b/src/components/web-monitoring-ui.jsx
@@ -37,6 +37,7 @@ export default class WebMonitoringUi extends React.Component {
isLoading: true,
pageFilter: '', // keeps track of which set of pages we are looking at
pages: null,
+ search: null,
showLogin: false,
user: null,
};
@@ -46,6 +47,7 @@ export default class WebMonitoringUi extends React.Component {
this.logOut = this.logOut.bind(this);
this.loadPages = this.loadPages.bind(this);
this.setPageFilter = this.setPageFilter.bind(this);
+ this.search = this.search.bind(this);
}
setPageFilter (filter) {
@@ -71,6 +73,11 @@ export default class WebMonitoringUi extends React.Component {
this.loadPages('pages');
}
+ search (query) {
+ this.setState({search: query});
+ this.loadPages(this.state.pageFilter);
+ }
+
/**
* Load pages depending on whether we want all pages or assigned pages.
* @private
@@ -87,7 +94,7 @@ export default class WebMonitoringUi extends React.Component {
return Promise.reject(new Error('You must be logged in to view pages'));
}
- const query = {include_latest: true};
+ const query = Object.assign({include_latest: true}, this.state.search);
if (pageFilter === 'assignedPages') {
return localApi.getPagesForUser(api.userData.email, null, query);
}
@@ -137,6 +144,7 @@ export default class WebMonitoringUi extends React.Component {
{...routeProps}
pages={pages}
user={this.state.user}
+ onSearch={this.search}
/>;
};
};
diff --git a/src/css/styles.css b/src/css/styles.css
index 4601216f..f20701a7 100644
--- a/src/css/styles.css
+++ b/src/css/styles.css
@@ -129,6 +129,18 @@ body {
background-color: #EEE;
}
+.search-bar {
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 0.5em;
+}
+
+.search-bar input {
+ width: 100%;
+ border: none;
+ font-size: 1.25em;
+ padding: 0.5em 15px;
+}
+
/* ===== Page Detail View ===== */
.page-title {
font-size: 1.5em;