From df42d7565140d9bcd6d5f0274493d2dabe646242 Mon Sep 17 00:00:00 2001 From: Thomas Nordquist Date: Thu, 11 Jul 2019 13:54:53 +0200 Subject: [PATCH] Refactor --- .../components/Sidebar/Publish/Publish.tsx | 158 ++++++++------ .../components/Sidebar/Publish/TopicInput.tsx | 39 ++-- app/src/components/Sidebar/Sidebar.tsx | 4 +- .../Sidebar/ValueRenderer/ActionButtons.tsx | 79 +++++++ .../DeleteSelectedTopicButton.tsx | 39 ++++ .../Sidebar/ValueRenderer/MessageHistory.tsx | 7 +- .../Sidebar/ValueRenderer/ValuePanel.tsx | 201 +++++------------- .../Sidebar/ValueRenderer/ValueRenderer.tsx | 7 +- src/spec/scenarios/copyValueToClipboard.ts | 6 +- 9 files changed, 294 insertions(+), 246 deletions(-) create mode 100644 app/src/components/Sidebar/ValueRenderer/ActionButtons.tsx create mode 100644 app/src/components/Sidebar/ValueRenderer/DeleteSelectedTopicButton.tsx diff --git a/app/src/components/Sidebar/Publish/Publish.tsx b/app/src/components/Sidebar/Publish/Publish.tsx index cfc698f..02a2db4 100644 --- a/app/src/components/Sidebar/Publish/Publish.tsx +++ b/app/src/components/Sidebar/Publish/Publish.tsx @@ -3,7 +3,8 @@ import FormatAlignLeft from '@material-ui/icons/FormatAlignLeft' import Message from './Model/Message' import Navigation from '@material-ui/icons/Navigation' import PublishHistory from './PublishHistory' -import React, { useCallback, useState, useMemo } from 'react' +import React, { useCallback, useMemo, useState } from 'react' +import RetainSwitch from './RetainSwitch' import TopicInput from './TopicInput' import { AppState } from '../../../reducers' import { bindActionCreators } from 'redux' @@ -12,7 +13,6 @@ import { connect } from 'react-redux' import { EditorModeSelect } from './EditorModeSelect' import { globalActions, publishActions } from '../../../actions' import { KeyCodes } from '../../../utils/KeyCodes' -import RetainSwitch from './RetainSwitch' interface Props { connectionId?: string @@ -41,14 +41,8 @@ function useHistory(): [Array, (topic: string, payload?: string) => voi } function Publish(props: Props) { - console.log(props.connectionId) - const updatePayload = props.actions.setPayload const [history, amendToHistory] = useHistory() - const updateMode = useCallback((e: React.ChangeEvent<{}>, value: string) => { - props.actions.setEditorMode(value) - }, []) - const publish = useCallback(() => { if (!props.connectionId) { return @@ -63,65 +57,6 @@ function Publish(props: Props) { } }, [props, props.connectionId, props.topic, props.payload, amendToHistory]) - const handleClickPublish = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation() - publish() - }, - [publish] - ) - - const PublishButton = () => { - return ( - - ) - } - - const formatJson = () => { - if (props.payload) { - try { - const str = JSON.stringify(JSON.parse(props.payload), undefined, ' ') - updatePayload(str) - } catch (error) { - props.globalActions.showError(`Format error: ${error.message}`) - } - } - } - - const renderFormatJson = () => { - if (props.editorMode !== 'json') { - return null - } - - return ( - - - - - - ) - } - - function EditorMode() { - return ( -
-
- - {renderFormatJson()} -
- -
-
-
- ) - } - const handleSubmit = useCallback( (e: React.KeyboardEvent) => { if (e.keyCode === KeyCodes.enter && (e.metaKey || e.ctrlKey)) { @@ -138,14 +73,97 @@ function Publish(props: Props) {
- - + +
), - [props.payload, props.editorMode, history, handleSubmit, updatePayload] + [props.payload, props.editorMode, history, handleSubmit, publish] + ) +} + +function EditorMode(props: { + payload?: string + editorMode: string + actions: typeof publishActions + globalActions: typeof globalActions + publish: () => void +}) { + const updatePayload = props.actions.setPayload + + const updateMode = useCallback((e: React.ChangeEvent<{}>, value: string) => { + props.actions.setEditorMode(value) + }, []) + + const formatJson = useCallback(() => { + if (props.payload) { + try { + const str = JSON.stringify(JSON.parse(props.payload), undefined, ' ') + updatePayload(str) + } catch (error) { + props.globalActions.showError(`Format error: ${error.message}`) + } + } + }, [props.payload]) + + const renderFormatJson = useCallback(() => { + if (props.editorMode !== 'json') { + return null + } + + return ( + + + + + + ) + }, [formatJson, props.editorMode, props.publish]) + + return useMemo( + () => ( +
+
+ + {renderFormatJson()} +
+ +
+
+
+ ), + [props.editorMode, renderFormatJson] + ) +} + +const PublishButton = (props: { publish: () => void }) => { + const handleClickPublish = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + props.publish() + }, + [props.publish] + ) + + return useMemo( + () => ( + + ), + [handleClickPublish] ) } diff --git a/app/src/components/Sidebar/Publish/TopicInput.tsx b/app/src/components/Sidebar/Publish/TopicInput.tsx index 1ac1eb2..d71cc13 100644 --- a/app/src/components/Sidebar/Publish/TopicInput.tsx +++ b/app/src/components/Sidebar/Publish/TopicInput.tsx @@ -1,5 +1,5 @@ import ClearAdornment from '../../helper/ClearAdornment' -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import { FormControl, Input, InputLabel } from '@material-ui/core' import { publishActions } from '../../../actions' import { bindActionCreators } from 'redux' @@ -7,7 +7,6 @@ import { AppState } from '../../../reducers' import { connect } from 'react-redux' function TopicInput(props: { actions: typeof publishActions; topic?: string }) { - console.log(props.topic) const updateTopic = useCallback((e: React.ChangeEvent) => { props.actions.setTopic(e.target.value) }, []) @@ -23,22 +22,26 @@ function TopicInput(props: { actions: typeof publishActions; topic?: string }) { }, []) const topicStr = props.topic || '' - return ( -
- - Topic - } - endAdornment={} - onBlur={onTopicBlur} - onChange={updateTopic} - multiline={true} - placeholder="example/topic" - /> - -
+ + return useMemo( + () => ( +
+ + Topic + } + endAdornment={} + onBlur={onTopicBlur} + onChange={updateTopic} + multiline={false} + placeholder="example/topic" + /> + +
+ ), + [topicStr] ) } diff --git a/app/src/components/Sidebar/Sidebar.tsx b/app/src/components/Sidebar/Sidebar.tsx index 85f9056..8e1928e 100644 --- a/app/src/components/Sidebar/Sidebar.tsx +++ b/app/src/components/Sidebar/Sidebar.tsx @@ -54,9 +54,7 @@ function Sidebar(props: Props) { Publish - Loading...}> - - + Stats diff --git a/app/src/components/Sidebar/ValueRenderer/ActionButtons.tsx b/app/src/components/Sidebar/ValueRenderer/ActionButtons.tsx new file mode 100644 index 0000000..152f134 --- /dev/null +++ b/app/src/components/Sidebar/ValueRenderer/ActionButtons.tsx @@ -0,0 +1,79 @@ +import React, { useCallback } from 'react' +import Code from '@material-ui/icons/Code' +import Reorder from '@material-ui/icons/Reorder' +import ToggleButton from '@material-ui/lab/ToggleButton' +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup' +import { settingsActions } from '../../../actions' +import { Tooltip, withStyles, Theme } from '@material-ui/core' +import { bindActionCreators } from 'redux' +import { AppState } from '../../../reducers' +import { connect } from 'react-redux' +import { ValueRendererDisplayMode } from '../../../reducers/Settings' + +function ActionButtons(props: { + actions: { settings: typeof settingsActions } + valueRendererDisplayMode: ValueRendererDisplayMode + classes: any +}) { + const handleValue = useCallback( + (mouseEvent: React.MouseEvent, value: any) => { + if (value === null) { + return + } + props.actions.settings.setValueDisplayMode(value) + }, + [props.actions.settings.setValueDisplayMode] + ) + + return ( + + + + + + + + + + + + + + + + + ) +} + +const styles = (theme: Theme) => ({ + toggleButton: { + height: '36px', + }, + toggleButtonIcon: { + verticalAlign: 'middle', + }, +}) + +const mapDispatchToProps = (dispatch: any) => { + return { + actions: { + settings: bindActionCreators(settingsActions, dispatch), + }, + } +} + +const mapStateToProps = (state: AppState) => { + return { + valueRendererDisplayMode: state.settings.get('valueRendererDisplayMode'), + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(ActionButtons)) diff --git a/app/src/components/Sidebar/ValueRenderer/DeleteSelectedTopicButton.tsx b/app/src/components/Sidebar/ValueRenderer/DeleteSelectedTopicButton.tsx new file mode 100644 index 0000000..bd6ac84 --- /dev/null +++ b/app/src/components/Sidebar/ValueRenderer/DeleteSelectedTopicButton.tsx @@ -0,0 +1,39 @@ +import Clear from '@material-ui/icons/Clear' +import React, { useMemo } from 'react' +import { bindActionCreators } from 'redux' +import { Button, Tooltip } from '@material-ui/core' +import { connect } from 'react-redux' +import { sidebarActions } from '../../../actions' + +const DeleteSelectedTopicButton = (props: { + actions: { + sidebar: typeof sidebarActions + } +}) => + useMemo( + () => ( + + + + ), + [props.actions.sidebar.clearRetainedTopic] + ) + +const mapDispatchToProps = (dispatch: any) => { + return { + actions: { sidebar: bindActionCreators(sidebarActions, dispatch) }, + } +} + +export default connect( + undefined, + mapDispatchToProps +)(DeleteSelectedTopicButton) diff --git a/app/src/components/Sidebar/ValueRenderer/MessageHistory.tsx b/app/src/components/Sidebar/ValueRenderer/MessageHistory.tsx index d1c6a40..a63575a 100644 --- a/app/src/components/Sidebar/ValueRenderer/MessageHistory.tsx +++ b/app/src/components/Sidebar/ValueRenderer/MessageHistory.tsx @@ -27,16 +27,17 @@ interface Props { interface State { displayMessage?: q.Message anchorEl?: HTMLElement + lastUpdate: number } -class MessageHistory extends React.Component { +class MessageHistory extends React.PureComponent { private updateNode = throttle(() => { - this.setState(this.state) + this.setState({ lastUpdate: Date.now() }) }, 300) constructor(props: any) { super(props) - this.state = {} + this.state = { lastUpdate: 0 } } private addNodeToCharts = (event: React.MouseEvent) => { diff --git a/app/src/components/Sidebar/ValueRenderer/ValuePanel.tsx b/app/src/components/Sidebar/ValueRenderer/ValuePanel.tsx index eb832d3..a268618 100644 --- a/app/src/components/Sidebar/ValueRenderer/ValuePanel.tsx +++ b/app/src/components/Sidebar/ValueRenderer/ValuePanel.tsx @@ -1,196 +1,117 @@ import * as q from '../../../../../backend/src/Model' -import * as React from 'react' -import Clear from '@material-ui/icons/Clear' -import Code from '@material-ui/icons/Code' +import ActionButtons from './ActionButtons' import Copy from '../../helper/Copy' import DateFormatter from '../../helper/DateFormatter' -import ExpandMore from '@material-ui/icons/ExpandMore' import MessageHistory from './MessageHistory' -import Reorder from '@material-ui/icons/Reorder' -import ToggleButton from '@material-ui/lab/ToggleButton' -import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup' +import Panel from '../Panel' +import React, { useCallback } from 'react' import ValueRenderer from './ValueRenderer' import { AppState } from '../../../reducers' import { Base64Message } from '../../../../../backend/src/Model/Base64Message' import { bindActionCreators } from 'redux' +import { Theme, Typography, withStyles } from '@material-ui/core' import { connect } from 'react-redux' -import { settingsActions, sidebarActions } from '../../../actions' -import { ValueRendererDisplayMode } from '../../../reducers/Settings' - -import { - Button, - ExpansionPanel, - ExpansionPanelDetails, - ExpansionPanelSummary, - Tooltip, - Typography, - StyleRulesCallback, - withStyles, - Theme, -} from '@material-ui/core' +import { sidebarActions } from '../../../actions' +import DeleteSelectedTopicButton from './DeleteSelectedTopicButton' interface Props { node?: q.TreeNode - valueRendererDisplayMode: ValueRendererDisplayMode sidebarActions: typeof sidebarActions - settingsActions: typeof settingsActions classes: any lastUpdate: number compareMessage?: q.Message } -interface State {} +function RenderedValue(props: { node?: q.TreeNode; compareMessage?: q.Message }) { + const { node, compareMessage } = props -class ValuePanel extends React.PureComponent { - constructor(props: Props) { - super(props) - this.state = {} + if (!node || !node.message) { + return null } - private renderValue() { - const node = this.props.node - if (!node || !node.message) { + return +} + +function ValuePanel(props: Props) { + const { node, compareMessage } = props + + function renderViewOptions() { + if (!props.node || !props.node.message || !props.node.mqttMessage) { return null } - return - } - - private renderViewOptions() { - if (!this.props.node || !this.props.node.message || !this.props.node.mqttMessage) { - return null - } - - const retainedButton = ( - - - - ) - return (
- {this.renderActionButtons()} -
{this.props.node.mqttMessage.retain ? retainedButton : null}
- {this.messageMetaInfo()} + + + +
+ {props.node.mqttMessage.retain ? : null} +
+ {messageMetaInfo()}
) } - private messageMetaInfo() { - if (!this.props.node || !this.props.node.message || !this.props.node.mqttMessage) { + function messageMetaInfo() { + if (!props.node || !props.node.message || !props.node.mqttMessage) { return null } return ( - QoS: {this.props.node.mqttMessage.qos} + QoS: {props.node.mqttMessage.qos} - + ) } - private renderActionButtons() { - const handleValue = (mouseEvent: React.MouseEvent, value: any) => { - if (value === null) { - return + const handleMessageHistorySelect = useCallback( + (message: q.Message) => { + if (message !== compareMessage) { + props.sidebarActions.setCompareMessage(message) + } else { + props.sidebarActions.setCompareMessage(undefined) } - this.props.settingsActions.setValueDisplayMode(value) - } + }, + [compareMessage] + ) - return ( - - - - - - - - - - - - - - - - - ) - } + const copyValue = + node && node.message && node.message.value ? ( + + ) : null - private panelStyle() { - return { - detailsStyle: { padding: '0px 16px 8px 8px', display: 'block' }, - summaryStyle: { minHeight: '0' }, - } - } - - private handleMessageHistorySelect = (message: q.Message) => { - if (message !== this.props.compareMessage) { - this.props.sidebarActions.setCompareMessage(message) - } else { - this.props.sidebarActions.setCompareMessage(undefined) - } - } - - public render() { - const { node, classes } = this.props - const { detailsStyle, summaryStyle } = this.panelStyle() - - const copyValue = - node && node.message && node.message.value ? ( - - ) : null - - return ( - - } style={summaryStyle}> - Value {copyValue} - - - {this.renderViewOptions()} -
- Loading...
}>{this.renderValue()} - -
- -
-
-
- ) - } + return ( + + Value {copyValue} + + {renderViewOptions()} +
+ Loading...
}> + + + +
+ +
+
+
+ ) } const mapDispatchToProps = (dispatch: any) => { return { sidebarActions: bindActionCreators(sidebarActions, dispatch), - settingsActions: bindActionCreators(settingsActions, dispatch), } } const mapStateToProps = (state: AppState) => { return { - valueRendererDisplayMode: state.settings.get('valueRendererDisplayMode'), node: state.tree.get('selectedTopic'), compareMessage: state.sidebar.get('compareMessage'), } @@ -201,12 +122,6 @@ const styles = (theme: Theme) => ({ fontSize: theme.typography.pxToRem(15), fontWeight: theme.typography.fontWeightRegular, }, - toggleButton: { - height: '36px', - }, - toggleButtonIcon: { - verticalAlign: 'middle', - }, }) export default connect( diff --git a/app/src/components/Sidebar/ValueRenderer/ValueRenderer.tsx b/app/src/components/Sidebar/ValueRenderer/ValueRenderer.tsx index a113003..8a65db4 100644 --- a/app/src/components/Sidebar/ValueRenderer/ValueRenderer.tsx +++ b/app/src/components/Sidebar/ValueRenderer/ValueRenderer.tsx @@ -56,12 +56,7 @@ class ValueRenderer extends React.Component { } public render() { - return ( -
- - {this.renderValue()} -
- ) + return
{this.renderValue()}
} public renderValue() { diff --git a/src/spec/scenarios/copyValueToClipboard.ts b/src/spec/scenarios/copyValueToClipboard.ts index a696a58..f2f4f49 100644 --- a/src/spec/scenarios/copyValueToClipboard.ts +++ b/src/spec/scenarios/copyValueToClipboard.ts @@ -1,7 +1,7 @@ -import { Browser, Element } from 'webdriverio' -import { clickOn, expandTopic, sleep, writeText } from '../util' +import { Browser } from 'webdriverio' +import { clickOn } from '../util' export async function copyValueToClipboard(browser: Browser) { - const copyButton = await browser.$('//p[contains(text(), "Value")]//button') + const copyButton = await browser.$('//span[contains(text(), "Value")]//button') await clickOn(copyButton, browser, 1) }