diff --git a/packages/selenium-ide/src/neo/__test__/containers/Console/index.spec.js b/packages/selenium-ide/src/neo/__test__/containers/Console/index.spec.js index 743f80fcc..5b898a022 100644 --- a/packages/selenium-ide/src/neo/__test__/containers/Console/index.spec.js +++ b/packages/selenium-ide/src/neo/__test__/containers/Console/index.spec.js @@ -49,4 +49,13 @@ describe("", () => { const unreadLogNotification = container.querySelector(".log.unread"); expect(unreadLogNotification).toBeInTheDOM(); }); + it("should display an unread log notification when viewing the variables tab)", async () => { + const { container } = renderIntoDocument(); + const variablesTab = container.querySelector(".variables"); + fireEvent.click(variablesTab); + await waitForElement(() => container.querySelector(".value-list")); + Logger.log("blah"); + const unreadLogNotification = container.querySelector(".log.unread"); + expect(unreadLogNotification).toBeInTheDOM(); + }); }); diff --git a/packages/selenium-ide/src/neo/components/ActionButtons/Clear/index.jsx b/packages/selenium-ide/src/neo/components/ActionButtons/Clear/index.jsx index 299acca18..952bfa92e 100644 --- a/packages/selenium-ide/src/neo/components/ActionButtons/Clear/index.jsx +++ b/packages/selenium-ide/src/neo/components/ActionButtons/Clear/index.jsx @@ -22,7 +22,7 @@ import classNames from "classnames"; export default class ClearButton extends React.Component { render() { return ( - // eslint-disable-line react/prop-types + // eslint-disable-line react/prop-types ); } } diff --git a/packages/selenium-ide/src/neo/components/ActionButtons/Delete/index.jsx b/packages/selenium-ide/src/neo/components/ActionButtons/Delete/index.jsx new file mode 100644 index 000000000..f12ac680c --- /dev/null +++ b/packages/selenium-ide/src/neo/components/ActionButtons/Delete/index.jsx @@ -0,0 +1,28 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import React from "react"; +import ActionButton from "../ActionButton"; +import classNames from "classnames"; + +export default class DeleteButton extends React.Component { + render() { + return ( + // eslint-disable-line react/prop-types + ); + } +} diff --git a/packages/selenium-ide/src/neo/components/Variable/index.jsx b/packages/selenium-ide/src/neo/components/Variable/index.jsx new file mode 100644 index 000000000..d53ec7799 --- /dev/null +++ b/packages/selenium-ide/src/neo/components/Variable/index.jsx @@ -0,0 +1,88 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import { observer } from "mobx-react"; +import DeleteButton from "../ActionButtons/Delete"; +import Input from "../FormInput"; +import "./style.css"; + +@observer +export default class Variable extends React.Component { + constructor(props){ + super(props); + this.handleChanged = this.handleChanged.bind(this); + this.keyChanged = this.keyChanged.bind(this); + this.valueChanged = this.valueChanged.bind(this); + this.delete = this.delete.bind(this); + this.edit = this.edit.bind(this); + this.state = { key: this.props.keyVar || "", value: this.props.value || undefined }; + } + + handleChanged() { + this.edit(); + } + delete() { + this.props.delete(this.props.keyVar); + } + edit(){ + const validKey = this.state.key; + if (validKey && this.state.value != undefined) { + this.delete(); + this.props.add(validKey, this.state.value); + } + } + keyChanged(key) { + this.setState({ key: key }); + } + valueChanged(value) { + this.setState({ value: value }); + } + render() { + return ( +
  • + + + { this.props.isPristine ? null : } +
  • + ); + } + static propTypes = { + keyVar: PropTypes.string, + value: PropTypes.string, + delete: PropTypes.func, + add: PropTypes.func, + isPristine: PropTypes.bool, + readOnly: PropTypes.bool + }; +} diff --git a/packages/selenium-ide/src/neo/components/Variable/style.css b/packages/selenium-ide/src/neo/components/Variable/style.css new file mode 100644 index 000000000..2fb5e1504 --- /dev/null +++ b/packages/selenium-ide/src/neo/components/Variable/style.css @@ -0,0 +1,39 @@ +.variable { + font-size: 12px; + word-break: break-all; + display: flex; +} + +.variable .form-input { + float: left; + width: 48%; + text-align: left; + border: 1px transparent solid; +} + +.variable:not(.value-header):hover { + background-color: #f3f3f3; +} + +.variable .deleteBtn { + width: 4%; + opacity: 0; + height: 100%; +} + +.variable:hover .deleteBtn { + opacity: 1; +} + +.variable .form-input input, +.variable .form-input input:disabled { + background-color: transparent; + border: 1px transparent solid; + margin: 0; +} + +.variable .form-input input:focus { + border: 1px #4ea1ff solid; + outline: 0; + background-color: white; +} diff --git a/packages/selenium-ide/src/neo/components/VariableList/index.jsx b/packages/selenium-ide/src/neo/components/VariableList/index.jsx new file mode 100644 index 000000000..fff164b02 --- /dev/null +++ b/packages/selenium-ide/src/neo/components/VariableList/index.jsx @@ -0,0 +1,81 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import React from "react"; +import PropTypes from "prop-types"; +import Variable from "../Variable"; +import { observer } from "mobx-react"; +import "./style.css"; + +@observer +export default class VariableList extends React.Component { + constructor(props){ + super(props); + this.deleteVariable = this.deleteVariable.bind(this); + this.addVariable = this.addVariable.bind(this); + this._OnSubmit = this._OnSubmit.bind(this); + } + deleteVariable(key){ + this.props.variables.deleteVariable(key); + } + addVariable(key, value){ + this.props.variables.addVariable(key, value); + } + _OnSubmit(e) { + e.preventDefault(); + // This set focus at deleteBtn for calling onBlur event that is to save value. + e.target[e.target.length - 1].focus(); + } + render() { + const variables = this.props.variables; + const pristineID = Math.random(); + return ( +
    +
      +
    • + Name + Value +
      +
    • + {variables.getEntries.map((storedMap) => ( + + )).concat( + )} + +
    +
    + ); + } + static propTypes = { + variables: PropTypes.object, + readOnly: PropTypes.bool + }; +} diff --git a/packages/selenium-ide/src/neo/components/VariableList/style.css b/packages/selenium-ide/src/neo/components/VariableList/style.css new file mode 100644 index 000000000..5d7df52ef --- /dev/null +++ b/packages/selenium-ide/src/neo/components/VariableList/style.css @@ -0,0 +1,28 @@ +.value-list { + text-align: left; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; +} + +.variable .name, +.variable .value { + float: left; + padding: 4px 5px; + width: 48%; + text-align: left; + border: 1px transparent solid; +} + +.value-list li:last-child { + margin-bottom: 3px; +} + +.value-list input[type="submit"] { + position: absolute; + width: 0; + height: 0; + padding: 0; + border: none; +} diff --git a/packages/selenium-ide/src/neo/containers/Console/index.jsx b/packages/selenium-ide/src/neo/containers/Console/index.jsx index fa714a3cf..f04fbc8ee 100644 --- a/packages/selenium-ide/src/neo/containers/Console/index.jsx +++ b/packages/selenium-ide/src/neo/containers/Console/index.jsx @@ -17,26 +17,27 @@ import React from "react"; import PropTypes from "prop-types"; +import { observer } from "mobx-react"; +import { observe } from "mobx"; import TabBar from "../../components/TabBar"; import LogList from "../../components/LogList"; +import VariableList from "../../components/VariableList"; import ClearButton from "../../components/ActionButtons/Clear"; import { output } from "../../stores/view/Logs"; +import PlaybackState from "../../stores/view/PlaybackState"; import PlaybackLogger from "../../side-effects/playback-logging"; -import "./style.css"; import CommandReference from "../../components/CommandReference"; +import variables from "../../stores/view/Variables"; import UiState from "../../stores/view/UiState"; -import { observer } from "mobx-react"; -import { observe } from "mobx"; import { Commands } from "../../models/Command"; +import "./style.css"; @observer export default class Console extends React.Component { constructor(props) { super(props); - this.state = { - tab: "Log", - logsUnread: false - }; + this.state = { tab: "Log", logsUnread: false }; + this.tabClicked = this.tabClicked.bind(this); this.playbackLogger = new PlaybackLogger(); this.loggerDisposer = observe(output.logs, () => { this.setState({ logsUnread: this.state.tab === "Log" ? false : true }); @@ -58,24 +59,32 @@ export default class Console extends React.Component { logsUnread: tab === "Log" ? false : this.state.logsUnread }); } - tabClicked(){ - this.props.restoreSize(); + //create different object which stores name and read status (e.g., unread boolean) + tabClicked(tab) { + this.setState({ + tab + }); + if(this.props.restoreSize) this.props.restoreSize(); } scroll(to) { this.viewport.scrollTo(0, to); } render() { const command = UiState.selectedCommand ? Commands.list.get(UiState.selectedCommand.command) : undefined; - const tabs = [{ name: "Log", unread: this.state.logsUnread }, { name: "Reference", unread: false }]; + const tabs = [{ name: "Log", unread: this.state.logsUnread }, { name: "Reference", unread: false }, { name: "Variables", unread: false }]; + const readOnly = (PlaybackState.isPlaying && !PlaybackState.paused); return ( diff --git a/packages/selenium-ide/src/neo/stores/view/Variables.js b/packages/selenium-ide/src/neo/stores/view/Variables.js index b80ae83c1..7817f3da6 100644 --- a/packages/selenium-ide/src/neo/stores/view/Variables.js +++ b/packages/selenium-ide/src/neo/stores/view/Variables.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { action, observable } from "mobx"; +import { action, observable, computed } from "mobx"; class Variables { @observable storedVars = new Map(); @@ -36,6 +36,10 @@ class Variables { @action.bound clearVariables() { this.storedVars.clear(); } + + @computed get getEntries() { + return this.storedVars.entries().sort(); + } } if (!window._variables) window._variables = new Variables();