@@ -81,6 +83,8 @@ export const KubernetesDashboard = React.createClass({
@@ -51,11 +59,13 @@ export const KubernetesPage = React.createClass({
},
});
-const mapStateToProps = (state) => ({
- inPresentationMode: state.appUI.presentationMode,
+const mapStateToProps = ({app: {ephemeral: {inPresentationMode}, persisted: {autoRefresh}}}) => ({
+ inPresentationMode,
+ autoRefresh,
})
const mapDispatchToProps = (dispatch) => ({
+ handleChooseAutoRefresh: bindActionCreators(setAutoRefresh, dispatch),
handleClickPresentationButton: presentationButtonDispatcher(dispatch),
})
diff --git a/ui/src/localStorage.js b/ui/src/localStorage.js
index 5cd29aa071..4b8381503c 100644
--- a/ui/src/localStorage.js
+++ b/ui/src/localStorage.js
@@ -9,9 +9,12 @@ export const loadLocalStorage = () => {
}
};
-export const saveToLocalStorage = ({queryConfigs, timeRange, dataExplorer}) => {
+export const saveToLocalStorage = ({app: {persisted}, queryConfigs, timeRange, dataExplorer}) => {
try {
+ const appPersisted = Object.assign({}, {app: {persisted}})
+
window.localStorage.setItem('state', JSON.stringify({
+ ...appPersisted,
queryConfigs,
timeRange,
dataExplorer,
diff --git a/ui/src/shared/actions/app.js b/ui/src/shared/actions/app.js
new file mode 100644
index 0000000000..f8939f408f
--- /dev/null
+++ b/ui/src/shared/actions/app.js
@@ -0,0 +1,22 @@
+import {PRESENTATION_MODE_ANIMATION_DELAY} from '../constants'
+
+// ephemeral state reducers
+export const enablePresentationMode = () => ({
+ type: 'ENABLE_PRESENTATION_MODE',
+})
+
+export const disablePresentationMode = () => ({
+ type: 'DISABLE_PRESENTATION_MODE',
+})
+
+export const delayEnablePresentationMode = () => (dispatch) => {
+ setTimeout(() => dispatch(enablePresentationMode()), PRESENTATION_MODE_ANIMATION_DELAY)
+}
+
+// persistent state reducers
+export const setAutoRefresh = (milliseconds) => ({
+ type: 'SET_AUTOREFRESH',
+ payload: {
+ milliseconds,
+ },
+})
diff --git a/ui/src/shared/actions/ui.js b/ui/src/shared/actions/ui.js
deleted file mode 100644
index 740566beb9..0000000000
--- a/ui/src/shared/actions/ui.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import {PRESENTATION_MODE_ANIMATION_DELAY} from '../constants'
-
-export function enablePresentationMode() {
- return {
- type: 'ENABLE_PRESENTATION_MODE',
- }
-}
-
-export function disablePresentationMode() {
- return {
- type: 'DISABLE_PRESENTATION_MODE',
- }
-}
-
-export function delayEnablePresentationMode() {
- return (dispatch) => {
- setTimeout(() => dispatch(enablePresentationMode()), PRESENTATION_MODE_ANIMATION_DELAY)
- }
-}
diff --git a/ui/src/shared/components/AutoRefresh.js b/ui/src/shared/components/AutoRefresh.js
index fd79cd620a..ecf560beff 100644
--- a/ui/src/shared/components/AutoRefresh.js
+++ b/ui/src/shared/components/AutoRefresh.js
@@ -6,25 +6,34 @@ function _fetchTimeSeries(source, db, rp, query) {
return proxy({source, db, rp, query});
}
+const {
+ element,
+ number,
+ arrayOf,
+ shape,
+ oneOfType,
+ string,
+} = PropTypes
+
export default function AutoRefresh(ComposedComponent) {
const wrapper = React.createClass({
displayName: `AutoRefresh_${ComposedComponent.displayName}`,
propTypes: {
- children: PropTypes.element,
- autoRefresh: PropTypes.number,
- queries: PropTypes.arrayOf(PropTypes.shape({
- host: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
- text: PropTypes.string,
+ children: element,
+ autoRefresh: number.isRequired,
+ queries: arrayOf(shape({
+ host: oneOfType([string, arrayOf(string)]),
+ text: string,
}).isRequired).isRequired,
},
getInitialState() {
return {timeSeries: []};
},
componentDidMount() {
- const {queries} = this.props;
+ const {queries, autoRefresh} = this.props;
this.executeQueries(queries);
- if (this.props.autoRefresh) {
- this.intervalID = setInterval(() => this.executeQueries(queries), this.props.autoRefresh);
+ if (autoRefresh) {
+ this.intervalID = setInterval(() => this.executeQueries(queries), autoRefresh);
}
},
componentWillReceiveProps(nextProps) {
diff --git a/ui/src/shared/components/AutoRefreshDropdown.js b/ui/src/shared/components/AutoRefreshDropdown.js
new file mode 100644
index 0000000000..d8bafe2fda
--- /dev/null
+++ b/ui/src/shared/components/AutoRefreshDropdown.js
@@ -0,0 +1,72 @@
+import React, {PropTypes} from 'react';
+import classnames from 'classnames';
+import OnClickOutside from 'shared/components/OnClickOutside';
+
+import autoRefreshItems from 'hson!../data/autoRefreshes.hson';
+
+const {
+ number,
+ func,
+} = PropTypes
+
+const AutoRefreshDropdown = React.createClass({
+ autobind: false,
+
+ propTypes: {
+ selected: number.isRequired,
+ onChoose: func.isRequired,
+ },
+
+ getInitialState() {
+ return {
+ isOpen: false,
+ };
+ },
+
+ findAutoRefreshItem(milliseconds) {
+ return autoRefreshItems.find((values) => values.milliseconds === milliseconds)
+ },
+
+ handleClickOutside() {
+ this.setState({isOpen: false});
+ },
+
+ handleSelection(milliseconds) {
+ this.props.onChoose(milliseconds);
+ this.setState({isOpen: false});
+ },
+
+ toggleMenu() {
+ this.setState({isOpen: !this.state.isOpen});
+ },
+
+ render() {
+ const self = this;
+ const {selected} = self.props;
+ const {isOpen} = self.state;
+
+ return (
+
+
self.toggleMenu()}>
+
+ {this.findAutoRefreshItem(selected).inputValue}
+
+
+
+
+ );
+ },
+});
+
+export default OnClickOutside(AutoRefreshDropdown);
diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js
index 178b842d50..2c749449a6 100644
--- a/ui/src/shared/components/Dropdown.js
+++ b/ui/src/shared/components/Dropdown.js
@@ -1,14 +1,23 @@
import React, {PropTypes} from 'react';
+import classnames from 'classnames';
import OnClickOutside from 'shared/components/OnClickOutside';
+const {
+ arrayOf,
+ shape,
+ string,
+ func,
+} = PropTypes
+
const Dropdown = React.createClass({
propTypes: {
- items: PropTypes.arrayOf(PropTypes.shape({
- text: PropTypes.string.isRequired,
+ items: arrayOf(shape({
+ text: string.isRequired,
})).isRequired,
- onChoose: PropTypes.func.isRequired,
- selected: PropTypes.string.isRequired,
- className: PropTypes.string,
+ onChoose: func.isRequired,
+ selected: string.isRequired,
+ iconName: string,
+ className: string,
},
getInitialState() {
return {
@@ -39,11 +48,12 @@ const Dropdown = React.createClass({
},
render() {
const self = this;
- const {items, selected, className, actions} = self.props;
+ const {items, selected, className, iconName, actions} = self.props;
return (
+ {iconName ? : null}
{selected}
diff --git a/ui/src/shared/components/LayoutRenderer.js b/ui/src/shared/components/LayoutRenderer.js
index e1c2df5e77..60eace1466 100644
--- a/ui/src/shared/components/LayoutRenderer.js
+++ b/ui/src/shared/components/LayoutRenderer.js
@@ -18,6 +18,7 @@ const {
export const LayoutRenderer = React.createClass({
propTypes: {
+ autoRefresh: number.isRequired,
timeRange: shape({
defaultGroupBy: string.isRequired,
queryValue: string.isRequired,
@@ -46,7 +47,6 @@ export const LayoutRenderer = React.createClass({
name: string.isRequired,
}).isRequired
),
- autoRefreshMs: number.isRequired,
host: string,
source: string,
onPositionChange: func,
@@ -84,7 +84,7 @@ export const LayoutRenderer = React.createClass({
},
generateVisualizations() {
- const {autoRefreshMs, source, cells} = this.props;
+ const {autoRefresh, source, cells} = this.props;
return cells.map((cell) => {
const qs = cell.queries.map((q) => {
@@ -100,7 +100,7 @@ export const LayoutRenderer = React.createClass({
{cell.name || `Graph`}
-
+
);
@@ -117,7 +117,7 @@ export const LayoutRenderer = React.createClass({
diff --git a/ui/src/shared/components/TimeRangeDropdown.js b/ui/src/shared/components/TimeRangeDropdown.js
index dbdf5155cd..076226896c 100644
--- a/ui/src/shared/components/TimeRangeDropdown.js
+++ b/ui/src/shared/components/TimeRangeDropdown.js
@@ -1,5 +1,5 @@
import React from 'react';
-import cN from 'classnames';
+import classnames from 'classnames';
import OnClickOutside from 'shared/components/OnClickOutside';
import timeRanges from 'hson!../data/timeRanges.hson';
@@ -48,7 +48,7 @@ const TimeRangeDropdown = React.createClass({
{selected}
-
+
- Time Range
{timeRanges.map((item) => {
return (
diff --git a/ui/src/shared/constants/index.js b/ui/src/shared/constants/index.js
index 6b0d7cb600..f6f5bb7b68 100644
--- a/ui/src/shared/constants/index.js
+++ b/ui/src/shared/constants/index.js
@@ -470,3 +470,5 @@ export const STROKE_WIDTH = {
export const PRESENTATION_MODE_ANIMATION_DELAY = 0 // In milliseconds.
export const PRESENTATION_MODE_NOTIFICATION_DELAY = 2000 // In milliseconds.
+
+export const AUTOREFRESH_DEFAULT = 15000 // in milliseconds
diff --git a/ui/src/shared/data/autoRefreshes.hson b/ui/src/shared/data/autoRefreshes.hson
new file mode 100644
index 0000000000..09b4237ff4
--- /dev/null
+++ b/ui/src/shared/data/autoRefreshes.hson
@@ -0,0 +1,7 @@
+[
+ {milliseconds: 5000, inputValue: 'Every 5 seconds', menuOption: 'Every 5 seconds'},
+ {milliseconds: 10000, inputValue: 'Every 10 seconds', menuOption: 'Every 10 seconds'},
+ {milliseconds: 15000, inputValue: 'Every 15 seconds', menuOption: 'Every 15 seconds'},
+ {milliseconds: 30000, inputValue: 'Every 30 seconds', menuOption: 'Every 30 seconds'},
+ {milliseconds: 60000, inputValue: 'Every 60 seconds', menuOption: 'Every 60 seconds'}
+]
diff --git a/ui/src/shared/dispatchers/index.js b/ui/src/shared/dispatchers/index.js
index fcf02767f9..a7771a9ff9 100644
--- a/ui/src/shared/dispatchers/index.js
+++ b/ui/src/shared/dispatchers/index.js
@@ -1,4 +1,4 @@
-import {delayEnablePresentationMode} from 'shared/actions/ui'
+import {delayEnablePresentationMode} from 'shared/actions/app'
import {publishNotification, delayDismissNotification} from 'shared/actions/notifications'
import {PRESENTATION_MODE_NOTIFICATION_DELAY} from 'shared/constants'
diff --git a/ui/src/shared/reducers/app.js b/ui/src/shared/reducers/app.js
new file mode 100644
index 0000000000..adcb4c2428
--- /dev/null
+++ b/ui/src/shared/reducers/app.js
@@ -0,0 +1,57 @@
+import {combineReducers} from 'redux';
+
+import {AUTOREFRESH_DEFAULT} from 'src/shared/constants'
+
+const initialState = {
+ ephemeral: {
+ inPresentationMode: false,
+ },
+ persisted: {
+ autoRefresh: AUTOREFRESH_DEFAULT,
+ },
+}
+
+const {
+ ephemeral: initialEphemeralState,
+ persisted: initialPersistedState,
+} = initialState
+
+const ephemeralReducer = (state = initialEphemeralState, action) => {
+ switch (action.type) {
+ case 'ENABLE_PRESENTATION_MODE': {
+ return {
+ ...state,
+ inPresentationMode: true,
+ }
+ }
+
+ case 'DISABLE_PRESENTATION_MODE': {
+ return {
+ ...state,
+ inPresentationMode: false,
+ }
+ }
+
+ default:
+ return state
+ }
+}
+
+const persistedReducer = (state = initialPersistedState, action) => {
+ switch (action.type) {
+ case 'SET_AUTOREFRESH': {
+ return {
+ ...state,
+ autoRefresh: action.payload.milliseconds,
+ }
+ }
+
+ default:
+ return state
+ }
+}
+
+export default combineReducers({
+ ephemeral: ephemeralReducer,
+ persisted: persistedReducer,
+})
diff --git a/ui/src/shared/reducers/index.js b/ui/src/shared/reducers/index.js
index 47ed0c62f4..bf191ecd78 100644
--- a/ui/src/shared/reducers/index.js
+++ b/ui/src/shared/reducers/index.js
@@ -1,13 +1,13 @@
-import appUI from './ui';
import me from './me';
+import app from './app';
import auth from './auth';
import notifications from './notifications';
import sources from './sources';
-export {
- appUI,
+export default {
me,
+ app,
auth,
notifications,
sources,
-};
+}
diff --git a/ui/src/shared/reducers/ui.js b/ui/src/shared/reducers/ui.js
deleted file mode 100644
index 77a2f77a8a..0000000000
--- a/ui/src/shared/reducers/ui.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const initialState = {
- presentationMode: false,
-};
-
-export default function ui(state = initialState, action) {
- switch (action.type) {
- case 'ENABLE_PRESENTATION_MODE': {
- return {
- ...state,
- presentationMode: true,
- }
- }
-
- case 'DISABLE_PRESENTATION_MODE': {
- return {
- ...state,
- presentationMode: false,
- }
- }
- }
-
- return state
-}
diff --git a/ui/src/side_nav/containers/SideNavApp.js b/ui/src/side_nav/containers/SideNavApp.js
index a93bffdf72..5c25415718 100644
--- a/ui/src/side_nav/containers/SideNavApp.js
+++ b/ui/src/side_nav/containers/SideNavApp.js
@@ -34,11 +34,9 @@ const SideNavApp = React.createClass({
},
});
-function mapStateToProps(state) {
- return {
- me: state.me,
- inPresentationMode: state.appUI.presentationMode,
- };
-}
+const mapStateToProps = ({me, app: {ephemeral: {inPresentationMode}}}) => ({
+ me,
+ inPresentationMode,
+})
export default connect(mapStateToProps)(SideNavApp);
diff --git a/ui/src/store/configureStore.js b/ui/src/store/configureStore.js
index 44b03fa66c..5aa6b77eee 100644
--- a/ui/src/store/configureStore.js
+++ b/ui/src/store/configureStore.js
@@ -3,8 +3,8 @@ import {combineReducers} from 'redux';
import thunkMiddleware from 'redux-thunk';
import makeQueryExecuter from 'src/shared/middleware/queryExecuter';
import resizeLayout from 'src/shared/middleware/resizeLayout';
-import * as dataExplorerReducers from 'src/data_explorer/reducers';
-import * as sharedReducers from 'src/shared/reducers';
+import sharedReducers from 'src/shared/reducers';
+import dataExplorerReducers from 'src/data_explorer/reducers';
import rulesReducer from 'src/kapacitor/reducers/rules';
import dashboardUI from 'src/dashboards/reducers/ui';
import persistStateEnhancer from './persistStateEnhancer';