diff --git a/client/.eslintrc b/client/.eslintrc index d7e58c4701..456aca91d7 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -6,6 +6,7 @@ "node": true }, "rules": { + "no-debugger" : 1, "comma-dangle": 0, "global-require": 0, "import/no-extraneous-dependencies": [ diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js index daa825787b..9f9f5f35e0 100644 --- a/client/app/scripts/actions/app-actions.js +++ b/client/app/scripts/actions/app-actions.js @@ -11,6 +11,7 @@ import { doControlRequest, getAllNodes, getNodesDelta, getNodeDetails, getTopologies, deletePipe } from '../utils/web-api-utils'; import { getActiveTopologyOptions, getCurrentTopologyUrl } from '../utils/topology-utils'; +import { storageSet } from '../utils/storage-utils'; const log = debug('scope:app-actions'); @@ -665,3 +666,18 @@ export function route(urlState) { ); }; } + +export function resetLocalViewState() { + return (dispatch) => { + dispatch({type: ActionTypes.RESET_LOCAL_VIEW_STATE}); + storageSet('scopeViewState', ''); + window.location.href = window.location.href.split('#')[0]; + }; +} + +export function toggleTroubleshootingMenu(ev) { + if (ev) { ev.preventDefault(); ev.stopPropagation(); } + return { + type: ActionTypes.TOGGLE_TROUBLESHOOTING_MENU + }; +} diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index 696508212d..3361c7285f 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -6,6 +6,7 @@ import Logo from './logo'; import Footer from './footer'; import Sidebar from './sidebar'; import HelpPanel from './help-panel'; +import TroubleshootingMenu from './troubleshooting-menu'; import Search from './search'; import Status from './status'; import Topologies from './topologies'; @@ -99,7 +100,7 @@ class App extends React.Component { render() { const { gridMode, showingDetails, showingHelp, showingMetricsSelector, - showingNetworkSelector } = this.props; + showingNetworkSelector, showingTroubleshootingMenu } = this.props; const isIframe = window !== window.top; return ( @@ -108,6 +109,8 @@ class App extends React.Component { {showingHelp && } + {showingTroubleshootingMenu && } + {showingDetails &&
}
@@ -146,6 +149,7 @@ function mapStateToProps(state) { searchQuery: state.get('searchQuery'), showingDetails: state.get('nodeDetails').size > 0, showingHelp: state.get('showingHelp'), + showingTroubleshootingMenu: state.get('showingTroubleshootingMenu'), showingMetricsSelector: state.get('availableCanvasMetrics').count() > 0, showingNetworkSelector: state.get('availableNetworks').count() > 0, showingTerminal: state.get('controlPipes').size > 0, diff --git a/client/app/scripts/components/footer.js b/client/app/scripts/components/footer.js index b1a178704c..3633ece006 100644 --- a/client/app/scripts/components/footer.js +++ b/client/app/scripts/components/footer.js @@ -6,7 +6,7 @@ import Plugins from './plugins'; import { getUpdateBufferSize } from '../utils/update-buffer-utils'; import { contrastModeUrl, isContrastMode } from '../utils/contrast-utils'; import { clickDownloadGraph, clickForceRelayout, clickPauseUpdate, - clickResumeUpdate, toggleHelp } from '../actions/app-actions'; + clickResumeUpdate, toggleHelp, toggleTroubleshootingMenu } from '../actions/app-actions'; import { basePathSlash } from '../utils/web-api-utils'; class Footer extends React.Component { @@ -76,21 +76,14 @@ class Footer extends React.Component { title={forceRelayoutTitle}> - - - - - - + onClick={this.props.toggleTroubleshootingMenu} + className="footer-icon" title="Open troubleshooting menu" + href="" + > @@ -119,6 +112,7 @@ export default connect( clickForceRelayout, clickPauseUpdate, clickResumeUpdate, - toggleHelp + toggleHelp, + toggleTroubleshootingMenu } )(Footer); diff --git a/client/app/scripts/components/help-panel.js b/client/app/scripts/components/help-panel.js index aad9b3b7f0..ef442a1b43 100644 --- a/client/app/scripts/components/help-panel.js +++ b/client/app/scripts/components/help-panel.js @@ -162,7 +162,11 @@ function HelpPanel({currentTopologyName, searchableFields, onClickClose}) { {renderFieldsPanel(currentTopologyName, searchableFields)}
- +
@@ -178,4 +182,6 @@ function mapStateToProps(state) { } -export default connect(mapStateToProps, { onClickClose: hideHelp })(HelpPanel); +export default connect(mapStateToProps, { + onClickClose: hideHelp +})(HelpPanel); diff --git a/client/app/scripts/components/troubleshooting-menu.js b/client/app/scripts/components/troubleshooting-menu.js new file mode 100644 index 0000000000..9da47f9cab --- /dev/null +++ b/client/app/scripts/components/troubleshooting-menu.js @@ -0,0 +1,93 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { + toggleTroubleshootingMenu, + resetLocalViewState, + clickDownloadGraph +} from '../actions/app-actions'; + +class DebugMenu extends React.Component { + constructor(props, context) { + super(props, context); + + this.handleClickReset = this.handleClickReset.bind(this); + } + + handleClickReset(ev) { + ev.preventDefault(); + this.props.resetLocalViewState(); + } + + render() { + return ( +
+
+ +
+
+ ); + } +} + +export default connect(null, { + toggleTroubleshootingMenu, + resetLocalViewState, + clickDownloadGraph +})(DebugMenu); diff --git a/client/app/scripts/constants/action-types.js b/client/app/scripts/constants/action-types.js index cb617363c3..c56f06286b 100644 --- a/client/app/scripts/constants/action-types.js +++ b/client/app/scripts/constants/action-types.js @@ -45,12 +45,13 @@ const ACTION_TYPES = [ 'RECEIVE_TOPOLOGIES', 'RECEIVE_API_DETAILS', 'RECEIVE_ERROR', + 'RESET_LOCAL_VIEW_STATE', 'ROUTE_TOPOLOGY', 'SELECT_METRIC', 'SHOW_HELP', 'SET_EXPORTING_GRAPH', - 'SELECT_NETWORK', + 'TOGGLE_TROUBLESHOOTING_MENU', 'PIN_NETWORK', 'UNPIN_NETWORK', 'SHOW_NETWORKS', diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js index 235f578ff2..31a1532a97 100644 --- a/client/app/scripts/reducers/root.js +++ b/client/app/scripts/reducers/root.js @@ -58,6 +58,7 @@ export const initialState = makeMap({ selectedNetwork: null, selectedNodeId: null, showingHelp: false, + showingTroubleshootingMenu: false, showingNetworks: false, topologies: makeList(), topologiesLoaded: false, @@ -190,6 +191,10 @@ export function rootReducer(state = initialState, action) { if (state.get('showingHelp')) { state = state.set('showingHelp', false); } + + if (state.get('showingTroubleshootingMenu')) { + state = state.set('showingTroubleshootingMenu', false); + } return closeAllNodeDetails(state); } @@ -705,6 +710,10 @@ export function rootReducer(state = initialState, action) { return action.fn(state); } + case ActionTypes.TOGGLE_TROUBLESHOOTING_MENU: { + return state.set('showingTroubleshootingMenu', !state.get('showingTroubleshootingMenu')); + } + default: { return state; } diff --git a/client/app/styles/main.less b/client/app/styles/main.less index 60a64d166e..e3e8d75cb9 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -624,11 +624,13 @@ h2 { position: relative; } + &-tools { position: absolute; top: 6px; right: 8px; + > span { .btn-opacity; padding: 4px 5px; @@ -1586,12 +1588,12 @@ h2 { border-color: rgba(131, 131, 172, 0.6); } } - } + } &-main { - padding: 12px 36px 36px 36px; display: flex; + padding: 12px 36px 36px 36px; flex-direction: row; align-items: stretch; @@ -1834,3 +1836,39 @@ h2 { } } } + +.troubleshooting-menu { + display: flex; + position: relative; + + &-wrapper { + height: 100%; + width: 100%; + align-items: center; + display: flex; + justify-content: center; + position: absolute; + } + + &-content { + position: relative; + background-color: @white; + padding: 20px; + .shadow-2; + z-index: 2048; + } + + &-item { + height: 40px; + } + + .fa { + width: 20px; + text-align: center; + margin-right: 10px; + } + + .fa-close { + width: 25px; + } +}