Merge pull request #4 from commaai/refactor-for-candbjs

Refactor Signals
main
Christopher Biscardi 2018-01-22 11:36:18 -08:00 committed by GitHub
commit 7c38acbcbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 493 additions and 456 deletions

View File

@ -68,16 +68,6 @@ export default class AddSignals extends Component {
dragSignal: null,
dragCurrentBit: null
};
this.updateSignalStyles = this.updateSignalStyles.bind(this);
this.onSignalHover = this.onSignalHover.bind(this);
this.onBitHover = this.onBitHover.bind(this);
this.onSignalHoverEnd = this.onSignalHoverEnd.bind(this);
this.onTentativeSignalChange = this.onTentativeSignalChange.bind(this);
this.onSignalChange = this.onSignalChange.bind(this);
this.onSignalRemove = this.onSignalRemove.bind(this);
this.onSignalPlotChange = this.onSignalPlotChange.bind(this);
this.resetDragState = this.resetDragState.bind(this);
}
copySignals(signals) {
@ -124,11 +114,11 @@ export default class AddSignals extends Component {
return style;
}
updateSignalStyles() {
updateSignalStyles = () => {
const signalStyles = this.calcSignalStyles(this.state.signals);
this.setState({ signalStyles });
}
};
calcSignalStyles(signals) {
const signalStyles = {};
@ -160,11 +150,11 @@ export default class AddSignals extends Component {
})[0];
}
onSignalHover(signal) {
onSignalHover = signal => {
if (!signal) return;
this.setState({ highlightedSignal: signal.name }, this.updateSignalStyles);
}
};
signalBitIndex(bitIdx, signal) {
// todo does this work for both big and little endian?
@ -175,7 +165,7 @@ export default class AddSignals extends Component {
return bitIdx - startBit;
}
onBitHover(bitIdx, signal) {
onBitHover = (bitIdx, signal) => {
let { dragStartBit, signals, dragSignal } = this.state;
if (dragStartBit !== null) {
@ -279,13 +269,13 @@ export default class AddSignals extends Component {
if (signal) {
this.onSignalHover(signal);
}
}
};
onSignalHoverEnd(signal) {
onSignalHoverEnd = signal => {
if (!signal) return;
this.setState({ highlightedSignal: null }, this.updateSignalStyles);
}
};
nextNewSignalName() {
const existingNames = Object.keys(this.state.signals);
@ -440,10 +430,11 @@ export default class AddSignals extends Component {
}
renderBitMatrix() {
const { message } = this.props;
const rows = [];
let rowCount;
if (this.props.message.frame && this.props.message.frame.size) {
rowCount = Math.floor(this.props.message.frame.size * 8 / 8);
if (message.frame && message.frame.size) {
rowCount = Math.floor(message.frame.size * 8 / 8);
} else {
rowCount = 8;
}
@ -483,11 +474,7 @@ export default class AddSignals extends Component {
);
}
rowBits.push(
<td key={"hex-repr"} className={css(Styles.hex)}>
{this.byteValueHex(i)}
</td>
);
rowBits.push(<td key={"hex-repr"}>{this.byteValueHex(i)}</td>);
rows.push(<tr key={i.toString()}>{rowBits}</tr>);
}
@ -500,23 +487,23 @@ export default class AddSignals extends Component {
);
}
resetDragState() {
resetDragState = () => {
this.setState({
dragStartBit: null,
dragSignal: null,
dragCurrentBit: null
});
}
};
onTentativeSignalChange(signal) {
onTentativeSignalChange = signal => {
// Tentative signal changes are not propagated up
// but their effects are displayed in the bitmatrix
const { signals } = this.state;
signals[signal.name] = signal;
this.setState({ signals });
}
};
onSignalChange(signal, oldSignal) {
onSignalChange = (signal, oldSignal) => {
const { signals } = this.state;
for (let signalName in signals) {
@ -527,13 +514,13 @@ export default class AddSignals extends Component {
signals[signal.name] = signal;
this.setState({ signals }, this.propagateUpSignalChange);
}
};
onSignalRemove(signal) {
onSignalRemove = signal => {
const { signals } = this.state;
delete signals[signal.name];
this.setState({ signals }, this.propagateUpSignalChange);
}
};
propagateUpSignalChange() {
const { signals } = this.state;
@ -544,11 +531,11 @@ export default class AddSignals extends Component {
);
}
onSignalPlotChange(shouldPlot, signalUid) {
onSignalPlotChange = (shouldPlot, signalUid) => {
const { message } = this.props;
this.props.onSignalPlotChange(shouldPlot, message.id, signalUid);
}
};
render() {
return (

View File

@ -18,15 +18,11 @@ export default class SignalLegend extends Component {
plottedSignalUids: PropTypes.array
};
constructor(props) {
super(props);
this.state = {
expandedSignals: []
};
this.toggleExpandSignal = this.toggleExpandSignal.bind(this);
}
state = {
expandedSignals: []
};
toggleExpandSignal(s) {
toggleExpandSignal = s => {
const { expandedSignals } = this.state;
if (!expandedSignals.includes(s.uid)) {
const updatedExpandedSignals = [...expandedSignals, s.uid];
@ -35,7 +31,7 @@ export default class SignalLegend extends Component {
const updatedExpandedSignals = expandedSignals.filter(i => i !== s.uid);
this.setState({ expandedSignals: updatedExpandedSignals });
}
}
};
checkExpandedSignal(s) {
return this.state.expandedSignals.includes(s);

View File

@ -42,19 +42,6 @@
display: block;
}
}
&.is-highlighted {
.signals-legend-entry-color {
opacity: 0.5;
}
}
&-color {
display: block;
height: 100%;
left: 0;
opacity: 0.3;
position: absolute;
width: 1.5%;
}
&-header {
border-bottom: 1px solid transparent;
cursor: pointer;

View File

@ -1,398 +0,0 @@
// SignalLegendEntry.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import Signal from "../models/can/signal";
import DbcUtils from "../utils/dbc";
import { swapKeysAndValues } from "../utils/object";
export default class SignalLegendEntry extends Component {
static propTypes = {
signal: PropTypes.instanceOf(Signal).isRequired,
isHighlighted: PropTypes.bool,
onSignalHover: PropTypes.func,
onSignalHoverEnd: PropTypes.func,
onTentativeSignalChange: PropTypes.func,
onSignalChange: PropTypes.func,
onSignalRemove: PropTypes.func,
onSignalPlotChange: PropTypes.func,
toggleExpandSignal: PropTypes.func,
isPlotted: PropTypes.bool,
isExpanded: PropTypes.bool
};
static unsignedTransformation = field => {
return (value, signal) => {
if (value !== "") {
value = Number(value) || 0;
if (value < 0) {
value = 0;
}
}
signal[field] = value;
return signal;
};
};
static fields = [
{
field: "name",
title: "Name",
type: "string"
},
{
field: "size",
title: "Size",
type: "number",
transform: SignalLegendEntry.unsignedTransformation("size")
},
{
field: "startBit",
title: signal =>
signal.isLittleEndian
? "Least significant bit"
: "Most significant bit",
type: "number",
transform: SignalLegendEntry.unsignedTransformation("startBit")
},
{
field: "isLittleEndian",
title: "Endianness",
type: "option",
options: {
options: ["Little", "Big"],
optionValues: { Little: true, Big: false }
},
transform: (isLittleEndian, signal) => {
if (signal.isLittleEndian !== isLittleEndian) {
const { startBit } = signal;
if (isLittleEndian) {
// big endian -> little endian
const startByte = Math.floor(signal.startBit / 8),
endByte = Math.floor((signal.startBit - signal.size + 1) / 8);
if (startByte === endByte) {
signal.startBit = signal.startBit - signal.size + 1;
} else {
signal.startBit = DbcUtils.matrixBitNumber(startBit);
}
} else {
// little endian -> big endian
const startByte = Math.floor(signal.startBit / 8),
endByte = Math.floor((signal.startBit + signal.size - 1) / 8);
if (startByte === endByte) {
signal.startBit = signal.startBit + signal.size - 1;
} else {
signal.startBit = DbcUtils.bigEndianBitIndex(startBit);
}
}
signal.isLittleEndian = isLittleEndian;
}
return signal;
}
},
{
field: "isSigned",
title: "Sign",
type: "option",
options: {
options: ["Signed", "Unsigned"],
optionValues: { Signed: true, Unsigned: false }
}
},
{
field: "factor",
title: "Factor",
type: "number"
},
{
field: "offset",
title: "Offset",
type: "number"
},
{
field: "unit",
title: "Unit",
type: "string"
},
{
field: "comment",
title: "Comment",
type: "string"
},
{
field: "min",
title: "Minimum value",
type: "number"
},
{
field: "max",
title: "Maximum value",
type: "number"
}
];
static fieldSpecForName = name => {
return SignalLegendEntry.fields.find(field => field.field === name);
};
constructor(props) {
super(props);
this.state = {
isExpanded: false,
signalEdited: Object.assign(Object.create(props.signal), props.signal)
};
this.toggleEditing = this.toggleEditing.bind(this);
this.updateField = this.updateField.bind(this);
this.toggleSignalPlot = this.toggleSignalPlot.bind(this);
}
componentWillReceiveProps(nextProps) {
if (!nextProps.signal.equals(this.props.signal)) {
this.setState({
signalEdited: Object.assign(
Object.create(nextProps.signal),
nextProps.signal
)
});
}
}
renderField(field, title, valueCol, signal) {
let titleStr;
if (typeof title === "function") {
titleStr = title(signal);
} else {
titleStr = title;
}
return (
<div key={field} className="form-field form-field--small">
<label htmlFor={`${signal.name}_${field}`}>{titleStr}</label>
{valueCol}
</div>
);
}
updateField(fieldSpec, value) {
let { signalEdited } = this.state;
const { signal } = this.props;
if (fieldSpec.transform) {
signalEdited = fieldSpec.transform(value, signalEdited);
} else {
signalEdited[fieldSpec.field] = value;
}
// Save entire signal while editing
this.setState({ signalEdited });
const signalCopy = Object.assign(Object.create(signal), signal);
Object.entries(signalEdited).forEach(([field, value]) => {
signalCopy[field] = value;
});
this.props.onSignalChange(signalCopy, signal);
}
renderNumberField(fieldSpec, signal) {
const { field, title } = fieldSpec;
let valueCol;
if (this.props.isExpanded) {
let value = this.state.signalEdited[field];
if (value !== "") {
let num = Number(value);
value = isNaN(num) ? "" : num;
}
valueCol = (
<input
id={`${signal.name}_${field}`}
type="number"
value={value}
onChange={e => {
this.updateField(fieldSpec, e.target.value);
}}
/>
);
} else {
let value = this.props.signal[field];
valueCol = <span>{value}</span>;
}
return this.renderField(field, title, valueCol, signal);
}
renderStringField(fieldSpec, signal) {
const { field, title } = fieldSpec;
let valueCol;
if (this.props.isExpanded) {
valueCol = (
<input
id={`${signal.name}_${field}`}
type="text"
value={this.state.signalEdited[field] || ""}
onChange={e => {
this.updateField(fieldSpec, e.target.value);
}}
/>
);
} else {
valueCol = <span>{this.props.signal[field]}</span>;
}
return this.renderField(field, title, valueCol, signal);
}
renderOptionField(fieldSpec, signal) {
let valueCol;
const { field, title } = fieldSpec;
const { options, optionValues } = fieldSpec.options;
let valueOptions = swapKeysAndValues(optionValues);
if (this.props.isExpanded) {
const optionEles = options.map(opt => (
<option key={opt} value={optionValues[opt]}>
{opt}
</option>
));
valueCol = (
<select
id={`${signal.name}_${field}`}
defaultValue={this.state.signalEdited[field]}
onChange={e => {
this.updateField(fieldSpec, e.target.value === "true");
}}
>
{optionEles}
</select>
);
} else {
valueCol = <span>{valueOptions[this.props.signal[field]]}</span>;
}
return this.renderField(field, title, valueCol, signal);
}
renderFieldNode(field, signal) {
if (field.type === "number") {
return this.renderNumberField(field, signal);
} else if (field.type === "option") {
return this.renderOptionField(field, signal);
} else if (field.type === "string") {
return this.renderStringField(field, signal);
}
}
toggleEditing(e) {
let { signalEdited } = this.state;
const { signal, isExpanded } = this.props;
const signalCopy = Object.assign(Object.create(signal), signal);
if (isExpanded) {
// Finished editing, save changes & reset intermediate
// signalEdited state.
Object.entries(signalEdited).forEach(([field, value]) => {
const fieldSpec = SignalLegendEntry.fieldSpecForName(field);
if (
fieldSpec &&
fieldSpec.type === "number" &&
isNaN(parseInt(value, 10))
) {
value = 0;
}
signalCopy[field] = value;
});
this.props.onSignalChange(signalCopy, signal);
} else {
signalEdited = signalCopy;
}
// Expand and enable signal editing
this.setState({
signalEdited
});
this.props.toggleExpandSignal(signal);
e.stopPropagation();
}
renderSignalForm(signal) {
return (
<div className="signals-legend-entry-form">
{SignalLegendEntry.fields.map(field => {
return (
<div className="signals-legend-entry-form-field" key={field.field}>
{this.renderFieldNode(field, signal)}
</div>
);
})}
<div className="signals-legend-entry-form-remove">
<button
className="button--tiny button--alpha"
onClick={() => this.props.onSignalRemove(signal)}
>
Remove Signal
</button>
</div>
</div>
);
}
toggleSignalPlot(e) {
const { signal, isPlotted } = this.props;
e.preventDefault();
this.props.onSignalPlotChange(!isPlotted, signal.uid);
}
render() {
const { signal } = this.props;
const expandedEntryClass = this.props.isExpanded ? "is-expanded" : null;
const highlightedEntryClass = this.props.isHighlighted
? "is-highlighted"
: null;
const plottedButtonClass = this.props.isPlotted
? "button"
: "button--alpha";
const plottedButtonText = this.props.isPlotted ? "Hide Plot" : "Show Plot";
return (
<div
className={cx(
"signals-legend-entry",
expandedEntryClass,
highlightedEntryClass
)}
onMouseEnter={() => this.props.onSignalHover(signal)}
onMouseLeave={() => this.props.onSignalHoverEnd(signal)}
>
<div
className="signals-legend-entry-color"
style={{ backgroundColor: `rgb(${this.props.color}` }}
/>
<div className="signals-legend-entry-header">
<div
className="signals-legend-entry-header-name"
onClick={this.toggleEditing}
>
<strong>{signal.name}</strong>
</div>
<div
className="signals-legend-entry-header-action"
onClick={this.toggleSignalPlot}
>
<button className={cx("button--tiny", plottedButtonClass)}>
{plottedButtonText}
</button>
</div>
</div>
<div className="signals-legend-entry-body">
{this.props.isExpanded ? this.renderSignalForm(signal) : null}
</div>
</div>
);
}
}

View File

@ -0,0 +1,12 @@
import styled from "react-emotion";
// color bar on the left side of the signals legend
export default styled("div")`
display: block;
height: 100%;
left: 0;
position: absolute;
width: 1.5%;
opacity: ${({ isHighlighted }) => (isHighlighted ? 0.5 : 0.3)};
background-color: rgb(${({ rgb }) => rgb.join(",")});
`;

View File

@ -0,0 +1,113 @@
import DbcUtils from "../../utils/dbc";
const unsignedTransformation = field => {
return (value, signal) => {
if (value !== "") {
value = Number(value) || 0;
if (value < 0) {
value = 0;
}
}
signal[field] = value;
return signal;
};
};
export default [
{
field: "name",
title: "Name",
type: "string"
},
{
field: "size",
title: "Size",
type: "number",
transform: unsignedTransformation("size")
},
{
field: "startBit",
title: signal =>
signal.isLittleEndian ? "Least significant bit" : "Most significant bit",
type: "number",
transform: unsignedTransformation("startBit")
},
{
field: "isLittleEndian",
title: "Endianness",
type: "option",
options: {
options: ["Little", "Big"],
optionValues: { Little: true, Big: false }
},
transform: (isLittleEndian, signal) => {
if (signal.isLittleEndian !== isLittleEndian) {
const { startBit } = signal;
if (isLittleEndian) {
// big endian -> little endian
const startByte = Math.floor(signal.startBit / 8),
endByte = Math.floor((signal.startBit - signal.size + 1) / 8);
if (startByte === endByte) {
signal.startBit = signal.startBit - signal.size + 1;
} else {
signal.startBit = DbcUtils.matrixBitNumber(startBit);
}
} else {
// little endian -> big endian
const startByte = Math.floor(signal.startBit / 8),
endByte = Math.floor((signal.startBit + signal.size - 1) / 8);
if (startByte === endByte) {
signal.startBit = signal.startBit + signal.size - 1;
} else {
signal.startBit = DbcUtils.bigEndianBitIndex(startBit);
}
}
signal.isLittleEndian = isLittleEndian;
}
return signal;
}
},
{
field: "isSigned",
title: "Sign",
type: "option",
options: {
options: ["Signed", "Unsigned"],
optionValues: { Signed: true, Unsigned: false }
}
},
{
field: "factor",
title: "Factor",
type: "number"
},
{
field: "offset",
title: "Offset",
type: "number"
},
{
field: "unit",
title: "Unit",
type: "string"
},
{
field: "comment",
title: "Comment",
type: "string"
},
{
field: "min",
title: "Minimum value",
type: "number"
},
{
field: "max",
title: "Maximum value",
type: "number"
}
];

View File

@ -0,0 +1,13 @@
import React, { Component } from "react";
export default class Field extends Component {
render() {
const { title, htmlFor, children } = this.props;
return (
<div className="form-field form-field--small">
<label htmlFor={htmlFor}>{title}</label>
{children}
</div>
);
}
}

View File

@ -0,0 +1,43 @@
import React from "react";
import Field from "./Field";
export default ({
fieldSpec,
signal,
isExpanded,
signalEdited,
updateField
}) => {
const { field, title } = fieldSpec;
const htmlFor = `${signal.name}_${field}`;
let valueCol;
if (isExpanded) {
let value = signalEdited;
if (value !== "") {
let num = Number(value);
value = isNaN(num) ? "" : num;
}
valueCol = (
<input
id={htmlFor}
type="number"
value={value}
onChange={e => {
updateField(fieldSpec, e.target.value);
}}
/>
);
} else {
let value = signal[field];
valueCol = <span>{value}</span>;
}
return (
<Field
title={typeof title === "function" ? title(signal) : title}
htmlFor={htmlFor}
>
{valueCol}
</Field>
);
};

View File

@ -0,0 +1,48 @@
import React from "react";
import Field from "./Field";
import { swapKeysAndValues } from "../../utils/object";
export default ({
fieldSpec,
signal,
isExpanded,
signalEdited,
updateField
}) => {
let valueCol;
const { field, title } = fieldSpec;
const htmlFor = `${signal.name}_${field}`;
const { options, optionValues } = fieldSpec.options;
let valueOptions = swapKeysAndValues(optionValues);
if (isExpanded) {
const optionEles = options.map(opt => (
<option key={opt} value={optionValues[opt]}>
{opt}
</option>
));
valueCol = (
<select
id={htmlFor}
defaultValue={signalEdited}
onChange={e => {
updateField(fieldSpec, e.target.value === "true");
}}
>
{optionEles}
</select>
);
} else {
valueCol = <span>{valueOptions[signal[field]]}</span>;
}
return (
<Field
title={typeof title === "function" ? title(signal) : title}
htmlFor={htmlFor}
>
{valueCol}
</Field>
);
};

View File

@ -0,0 +1,47 @@
import React from "react";
import FIELDS from "./FIELDS";
import NumberField from "./NumberField";
import StringField from "./StringField";
import OptionField from "./OptionField";
const FieldMap = {
number: NumberField,
option: OptionField,
string: StringField
};
export default ({
signal,
onSignalRemove,
isExpanded,
getSignalEdited,
update
}) => {
return (
<div className="signals-legend-entry-form">
{FIELDS.map(field => {
const Node = FieldMap[field.type];
return (
<div className="signals-legend-entry-form-field" key={field.field}>
<Node
fieldSpec={field}
signal={signal}
isExpanded={isExpanded}
signalEdited={getSignalEdited(field.field)}
updateField={update}
/>
</div>
);
})}
<div className="signals-legend-entry-form-remove">
<button
className="button--tiny button--alpha"
onClick={() => onSignalRemove(signal)}
>
Remove Signal
</button>
</div>
</div>
);
};

View File

@ -0,0 +1,37 @@
import React from "react";
import Field from "./Field";
export default ({
fieldSpec,
signal,
isExpanded,
signalEdited,
updateField
}) => {
const { field, title } = fieldSpec;
const htmlFor = `${signal.name}_${field}`;
let valueCol;
if (isExpanded) {
valueCol = (
<input
id={htmlFor}
type="text"
value={signalEdited || ""}
onChange={e => {
updateField(fieldSpec, e.target.value);
}}
/>
);
} else {
valueCol = <span>{signal[field]}</span>;
}
return (
<Field
title={typeof title === "function" ? title(signal) : title}
htmlFor={htmlFor}
>
{valueCol}
</Field>
);
};

View File

@ -0,0 +1,152 @@
// SignalLegendEntry.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import Signal from "../../models/can/signal";
import SignalForm from "./SignalForm";
import ColorBar from "./ColorBar";
import FIELDS from "./FIELDS";
export default class SignalLegendEntry extends Component {
static propTypes = {
signal: PropTypes.instanceOf(Signal).isRequired,
isHighlighted: PropTypes.bool,
onSignalHover: PropTypes.func,
onSignalHoverEnd: PropTypes.func,
onTentativeSignalChange: PropTypes.func,
onSignalChange: PropTypes.func,
onSignalRemove: PropTypes.func,
onSignalPlotChange: PropTypes.func,
toggleExpandSignal: PropTypes.func,
isPlotted: PropTypes.bool,
isExpanded: PropTypes.bool
};
static fieldSpecForName = name => {
return FIELDS.find(field => field.field === name);
};
constructor(props) {
super(props);
this.state = {
signalEdited: Object.assign(Object.create(props.signal), props.signal)
};
}
componentWillReceiveProps(nextProps) {
if (!nextProps.signal.equals(this.props.signal)) {
this.setState({
signalEdited: Object.assign(
Object.create(nextProps.signal),
nextProps.signal
)
});
}
}
updateField = (fieldSpec, value) => {
let { signalEdited } = this.state;
const { signal } = this.props;
if (fieldSpec.transform) {
signalEdited = fieldSpec.transform(value, signalEdited);
} else {
signalEdited[fieldSpec.field] = value;
}
// Save entire signal while editing
this.setState({ signalEdited });
const signalCopy = Object.assign(Object.create(signal), signal);
Object.entries(signalEdited).forEach(([field, value]) => {
signalCopy[field] = value;
});
this.props.onSignalChange(signalCopy, signal);
};
toggleEditing = e => {
let { signalEdited } = this.state;
const { signal, isExpanded } = this.props;
const signalCopy = Object.assign(Object.create(signal), signal);
if (isExpanded) {
// Finished editing, save changes & reset intermediate
// signalEdited state.
Object.entries(signalEdited).forEach(([field, value]) => {
const fieldSpec = SignalLegendEntry.fieldSpecForName(field);
if (
fieldSpec &&
fieldSpec.type === "number" &&
isNaN(parseInt(value, 10))
) {
value = 0;
}
signalCopy[field] = value;
});
this.props.onSignalChange(signalCopy, signal);
} else {
signalEdited = signalCopy;
}
// Expand and enable signal editing
this.setState({
signalEdited
});
this.props.toggleExpandSignal(signal);
e.stopPropagation();
};
toggleSignalPlot = e => {
const { signal, isPlotted } = this.props;
e.preventDefault();
this.props.onSignalPlotChange(!isPlotted, signal.uid);
};
getSignalEdited = field => {
return this.state.signalEdited[field];
};
render() {
const { signal, isHighlighted, color, isPlotted, isExpanded } = this.props;
const expandedEntryClass = isExpanded ? "is-expanded" : null;
const plottedButtonClass = isPlotted ? "button" : "button--alpha";
return (
<div
className={cx("signals-legend-entry", expandedEntryClass)}
onMouseEnter={() => this.props.onSignalHover(signal)}
onMouseLeave={() => this.props.onSignalHoverEnd(signal)}
>
<ColorBar isHighlighted={isHighlighted} rgb={color} />
<div className="signals-legend-entry-header">
<div
className="signals-legend-entry-header-name"
onClick={this.toggleEditing}
>
<strong>{signal.name}</strong>
</div>
<div
className="signals-legend-entry-header-action"
onClick={this.toggleSignalPlot}
>
<button className={cx("button--tiny", plottedButtonClass)}>
{isPlotted ? "Hide Plot" : "Show Plot"}
</button>
</div>
</div>
<div className="signals-legend-entry-body">
{isExpanded && (
<SignalForm
signal={signal}
onSignalRemove={this.props.onSignalRemove}
isExpanded={isExpanded}
getSignalEdited={this.getSignalEdited}
update={this.updateField}
/>
)}
</div>
</div>
);
}
}

View File

@ -1,4 +1,4 @@
import SignalLegendEntry from "../../components/SignalLegendEntry";
import SignalLegendEntry from ".";
import Signal from "../../models/can/signal";
import React from "react";
import { shallow, mount, render } from "enzyme";