Add and persist activePanel state

pull/841/head
Andrew Watkins 2017-02-06 11:32:23 -08:00
parent 01821ec5e3
commit 0fc2faa2a8
11 changed files with 62 additions and 41 deletions

View File

@ -4,8 +4,8 @@ export function createPanel() {
return { return {
type: 'CREATE_PANEL', type: 'CREATE_PANEL',
payload: { payload: {
panelId: uuid.v4(), // for the default Panel panelID: uuid.v4(), // for the default Panel
queryId: uuid.v4(), // for the default Query queryID: uuid.v4(), // for the default Query
}, },
}; };
} }
@ -159,3 +159,12 @@ export function updateRawQuery(queryID, text) {
}, },
}; };
} }
export function activatePanel(panelID) {
return {
type: 'ACTIVATE_PANEL',
payload: {
panelID,
},
};
}

View File

@ -9,6 +9,7 @@ const PanelBuilder = React.createClass({
propTypes: { propTypes: {
width: string, width: string,
actions: PropTypes.shape({ actions: PropTypes.shape({
activatePanel: func.isRequired,
createPanel: func.isRequired, createPanel: func.isRequired,
deleteQuery: func.isRequired, deleteQuery: func.isRequired,
addQuery: func.isRequired, addQuery: func.isRequired,
@ -23,25 +24,23 @@ const PanelBuilder = React.createClass({
toggleTagAcceptance: func.isRequired, toggleTagAcceptance: func.isRequired,
deletePanel: func.isRequired, deletePanel: func.isRequired,
}).isRequired, }).isRequired,
setActivePanel: func.isRequired,
setActiveQuery: func.isRequired, setActiveQuery: func.isRequired,
activePanelID: string, activePanelID: string,
activeQueryID: string, activeQueryID: string,
}, },
handleCreateExploer() { handleCreateExplorer() {
this.props.actions.createPanel(); this.props.actions.createPanel();
}, },
render() { render() {
const {width, actions, setActivePanel, setActiveQuery, activePanelID, activeQueryID} = this.props; const {width, actions, setActiveQuery, activePanelID, activeQueryID} = this.props;
return ( return (
<div className="panel-builder" style={{width}}> <div className="panel-builder" style={{width}}>
<div className="btn btn-block btn-primary" onClick={this.handleCreateExploer}><span className="icon graphline"></span>&nbsp;&nbsp;Create Graph</div> <div className="btn btn-block btn-primary" onClick={this.handleCreateExplorer}><span className="icon graphline"></span>&nbsp;&nbsp;Create Graph</div>
<PanelList <PanelList
actions={actions} actions={actions}
setActivePanel={setActivePanel}
setActiveQuery={setActiveQuery} setActiveQuery={setActiveQuery}
activePanelID={activePanelID} activePanelID={activePanelID}
activeQueryID={activeQueryID} activeQueryID={activeQueryID}

View File

@ -13,20 +13,20 @@ const PanelList = React.createClass({
}).isRequired, }).isRequired,
panels: shape({}).isRequired, panels: shape({}).isRequired,
queryConfigs: PropTypes.shape({}), queryConfigs: PropTypes.shape({}),
actions: shape({}).isRequired, actions: shape({
setActivePanel: func.isRequired, activatePanel: func.isRequired,
deleteQuery: func.isRequired,
addQuery: func.isRequired,
}).isRequired,
setActiveQuery: func.isRequired, setActiveQuery: func.isRequired,
activePanelID: string, activePanelID: string,
activeQueryID: string, activeQueryID: string,
}, },
handleTogglePanel(panel) { handleTogglePanel(panel) {
// If the panel being toggled is currently active, it means we should const panelID = panel.id === this.props.activePanelID ? null : panel.id;
// close everything by setting `activePanelID` to null. this.props.actions.activatePanel(panelID);
const activePanelID = panel.id === this.props.activePanelID ?
null : panel.id;
this.props.setActivePanel(activePanelID);
// Reset the activeQueryID when toggling Exporations // Reset the activeQueryID when toggling Exporations
this.props.setActiveQuery(null); this.props.setActiveQuery(null);
}, },

View File

@ -27,6 +27,7 @@ const DataExplorer = React.createClass({
upper: string, upper: string,
lower: string, lower: string,
}).isRequired, }).isRequired,
activePanel: string,
setTimeRange: func.isRequired, setTimeRange: func.isRequired,
}, },
@ -45,21 +46,16 @@ const DataExplorer = React.createClass({
getInitialState() { getInitialState() {
return { return {
activePanelID: null,
activeQueryID: null, activeQueryID: null,
}; };
}, },
handleSetActivePanel(id) {
this.setState({activePanelID: id});
},
handleSetActiveQuery(id) { handleSetActiveQuery(id) {
this.setState({activeQueryID: id}); this.setState({activeQueryID: id});
}, },
render() { render() {
const {timeRange, setTimeRange} = this.props; const {timeRange, setTimeRange, activePanel} = this.props;
return ( return (
<div className="data-explorer"> <div className="data-explorer">
@ -70,14 +66,13 @@ const DataExplorer = React.createClass({
<ResizeContainer> <ResizeContainer>
<PanelBuilder <PanelBuilder
timeRange={timeRange} timeRange={timeRange}
activePanelID={this.state.activePanelID} activePanelID={activePanel}
activeQueryID={this.state.activeQueryID} activeQueryID={this.state.activeQueryID}
setActiveQuery={this.handleSetActiveQuery} setActiveQuery={this.handleSetActiveQuery}
setActivePanel={this.handleSetActivePanel}
/> />
<Visualizations <Visualizations
timeRange={timeRange} timeRange={timeRange}
activePanelID={this.state.activePanelID} activePanelID={activePanel}
activeQueryID={this.state.activeQueryID} activeQueryID={this.state.activeQueryID}
/> />
</ResizeContainer> </ResizeContainer>
@ -87,8 +82,11 @@ const DataExplorer = React.createClass({
}); });
function mapStateToProps(state) { function mapStateToProps(state) {
const {timeRange, dataExplorerUI} = state;
return { return {
timeRange: state.timeRange, timeRange,
activePanel: dataExplorerUI.activePanel,
}; };
} }

View File

@ -0,0 +1,11 @@
export default function dataExplorerUI(state = {}, action) {
switch (action.type) {
case 'ACTIVATE_PANEL':
case 'CREATE_PANEL': {
const {panelID} = action.payload;
return {...state, activePanel: panelID};
}
}
return state;
}

View File

@ -1,9 +1,11 @@
import queryConfigs from './queryConfigs'; import queryConfigs from './queryConfigs';
import panels from './panels'; import panels from './panels';
import timeRange from './timeRange'; import timeRange from './timeRange';
import dataExplorerUI from './dataExplorerUI';
export { export {
queryConfigs, queryConfigs,
panels, panels,
timeRange, timeRange,
dataExplorerUI,
}; };

View File

@ -3,15 +3,11 @@ import update from 'react-addons-update';
export default function panels(state = {}, action) { export default function panels(state = {}, action) {
switch (action.type) { switch (action.type) {
case 'CREATE_PANEL': { case 'CREATE_PANEL': {
const {panelId, queryId} = action.payload; const {panelID, queryID} = action.payload;
const panel = { return {
id: panelId, ...state,
queryIds: [queryId], [panelID]: {id: panelID, queryIds: [queryID]},
}; };
return update(state, {
[panelId]: {$set: panel},
});
} }
case 'RENAME_PANEL': { case 'RENAME_PANEL': {
@ -50,5 +46,6 @@ export default function panels(state = {}, action) {
}); });
} }
} }
return state; return state;
} }

View File

@ -49,9 +49,9 @@ export default function queryConfigs(state = {}, action) {
case 'CREATE_PANEL': case 'CREATE_PANEL':
case 'ADD_KAPACITOR_QUERY': case 'ADD_KAPACITOR_QUERY':
case 'ADD_QUERY': { case 'ADD_QUERY': {
const {queryId, options} = action.payload; const {queryID, options} = action.payload;
const nextState = Object.assign({}, state, { const nextState = Object.assign({}, state, {
[queryId]: Object.assign({}, defaultQueryConfig(queryId), options), [queryID]: Object.assign({}, defaultQueryConfig(queryID), options),
}); });
return nextState; return nextState;

View File

@ -19,9 +19,14 @@ export const loadLocalStorage = () => {
} }
}; };
export const saveToLocalStorage = ({panels, queryConfigs, timeRange}) => { export const saveToLocalStorage = ({panels, queryConfigs, timeRange, dataExplorerUI}) => {
try { try {
window.localStorage.setItem('state', JSON.stringify({panels, queryConfigs, timeRange})); window.localStorage.setItem('state', JSON.stringify({
panels,
queryConfigs,
timeRange,
dataExplorerUI,
}));
} catch (err) { } catch (err) {
console.error('Unable to save data explorer: ', JSON.parse(err)); // eslint-disable-line no-console console.error('Unable to save data explorer: ', JSON.parse(err)); // eslint-disable-line no-console
} }

View File

@ -2,14 +2,14 @@ import {createStore, applyMiddleware, compose} from 'redux';
import {combineReducers} from 'redux'; import {combineReducers} from 'redux';
import thunkMiddleware from 'redux-thunk'; import thunkMiddleware from 'redux-thunk';
import makeQueryExecuter from 'src/shared/middleware/queryExecuter'; import makeQueryExecuter from 'src/shared/middleware/queryExecuter';
import * as chronografReducers from 'src/data_explorer/reducers'; import * as dataExplorerReducers from 'src/data_explorer/reducers';
import * as sharedReducers from 'src/shared/reducers'; import * as sharedReducers from 'src/shared/reducers';
import rulesReducer from 'src/kapacitor/reducers/rules'; import rulesReducer from 'src/kapacitor/reducers/rules';
import persistStateEnhancer from './persistStateEnhancer'; import persistStateEnhancer from './persistStateEnhancer';
const rootReducer = combineReducers({ const rootReducer = combineReducers({
...sharedReducers, ...sharedReducers,
...chronografReducers, ...dataExplorerReducers,
rules: rulesReducer, rules: rulesReducer,
}); });

View File

@ -13,11 +13,11 @@ import {saveToLocalStorage} from '../localStorage';
export default function persistState() { export default function persistState() {
return (next) => (reducer, initialState, enhancer) => { return (next) => (reducer, initialState, enhancer) => {
const store = next(reducer, initialState, enhancer); const store = next(reducer, initialState, enhancer);
const debounceMs = 1000; const throttleMs = 1000;
store.subscribe(_.debounce(() => { store.subscribe(_.throttle(() => {
saveToLocalStorage({...store.getState()}); saveToLocalStorage({...store.getState()});
}, debounceMs)); }, throttleMs));
return store; return store;
}; };