From 59915596d8f9db4e7a1da2e97755e5d30f13b324 Mon Sep 17 00:00:00 2001 From: hlomzik Date: Fri, 22 May 2020 07:17:55 +0700 Subject: [PATCH 1/4] Fix doubled text after deserialisation --- src/mixins/Tool.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mixins/Tool.js b/src/mixins/Tool.js index 17d892cd4..0ccaa14c0 100644 --- a/src/mixins/Tool.js +++ b/src/mixins/Tool.js @@ -104,6 +104,10 @@ const ToolMixin = types const fm = self.completion.names.get(obj.from_name); fm.fromStateJSON(obj); + // workaround to prevent perregion textarea from duplicating + // during deserialisation + if (fm.perregion && fromModel.type === "textarea") return; + if (!fm.perregion && fromModel.type.indexOf("labels") === -1) return; const { stateTypes, controlTagTypes } = self.tagTypes; From 071b0087c953a055ddeeb7b4d5937c1b03d8ded0 Mon Sep 17 00:00:00 2001 From: hlomzik Date: Tue, 26 May 2020 07:24:25 +0700 Subject: [PATCH 2/4] Improve TextArea perRegion style, code and tests Use '...' in appropriate situations (via css) Don't serialize empty TextArea regions Add `data-testid` for easy testing --- src/components/Entity/Entity.js | 13 +-- src/components/Entity/Entity.module.scss | 15 ++++ src/regions/RectRegion.js | 10 +-- src/regions/TextAreaRegion.js | 86 +++++++------------ .../TextAreaRegion/TextAreaRegion.module.scss | 34 ++++++++ src/tags/control/TextArea.js | 1 + 6 files changed, 95 insertions(+), 64 deletions(-) create mode 100644 src/regions/TextAreaRegion/TextAreaRegion.module.scss diff --git a/src/components/Entity/Entity.js b/src/components/Entity/Entity.js index a58e9d3d7..61bf9ad1c 100644 --- a/src/components/Entity/Entity.js +++ b/src/components/Entity/Entity.js @@ -17,7 +17,7 @@ import { NodeMinimal } from "../Node/Node"; import Hint from "../Hint/Hint"; import styles from "./Entity.module.scss"; -const { Text } = Typography; +const { Paragraph, Text } = Typography; const templateElement = element => { return ( @@ -41,13 +41,16 @@ const RenderStates = observer(({ node }) => { if (getType(s).name.indexOf("Labels") !== -1) { return templateElement(s); } else if (getType(s).name === "RatingModel") { - return Rating: {s.getSelectedString()}; + return Rating: {s.getSelectedString()}; } else if (getType(s).name === "TextAreaModel") { const text = s.regions.map(r => r._value).join("\n"); return ( - - Text: {text.substring(0, 26)}... - + + Text: + + {text} + + ); } diff --git a/src/components/Entity/Entity.module.scss b/src/components/Entity/Entity.module.scss index 29b0ec208..71548c89e 100644 --- a/src/components/Entity/Entity.module.scss +++ b/src/components/Entity/Entity.module.scss @@ -23,3 +23,18 @@ .statesblk > span { display: block; } + +.statesblk > div { + margin-bottom: 0; +} + +.row { + display: flex; + white-space: pre-wrap; +} + +.long { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/src/regions/RectRegion.js b/src/regions/RectRegion.js index 713ed5274..20e253f75 100644 --- a/src/regions/RectRegion.js +++ b/src/regions/RectRegion.js @@ -186,7 +186,10 @@ const Model = types }, serialize(control, object) { - let res = { + const value = control.serializableValue; + if (!value) return null; + + return { original_width: object.naturalWidth, original_height: object.naturalHeight, value: { @@ -195,12 +198,9 @@ const Model = types width: (self.width * (self.scaleX || 1) * 100) / object.stageWidth, // * (self.scaleX || 1) height: (self.height * (self.scaleY || 1) * 100) / object.stageHeight, rotation: self.rotation, + ...value, }, }; - - res.value = Object.assign(res.value, control.serializableValue); - - return res; }, })); diff --git a/src/regions/TextAreaRegion.js b/src/regions/TextAreaRegion.js index c9a310f33..3bc24419f 100644 --- a/src/regions/TextAreaRegion.js +++ b/src/regions/TextAreaRegion.js @@ -1,7 +1,6 @@ import React from "react"; import { observer, inject } from "mobx-react"; import { types, getParent, getParentOfType, getRoot } from "mobx-state-tree"; -import { Alert, Typography, Button } from "antd"; import { DeleteOutlined } from "@ant-design/icons"; @@ -13,7 +12,7 @@ import Registry from "../core/Registry"; import { TextAreaModel } from "../tags/control/TextArea"; import { guidGenerator } from "../core/Helpers"; -const { Paragraph } = Typography; +import styles from "./TextAreaRegion/TextAreaRegion.module.scss"; const Model = types .model("TextAreaRegionModel", { @@ -44,34 +43,21 @@ const TextAreaRegionModel = types.compose( ); const HtxTextAreaRegionView = ({ store, item }) => { - let markStyle = { - cursor: store.completionStore.selected.relationMode ? Constants.RELATION_MODE_CURSOR : Constants.POINTER_CURSOR, - display: "block", - marginBottom: "0.5em", - - backgroundColor: "#f6ffed", - border: "1px solid #b7eb8f", + const classes = [styles.mark]; + const params = {}; + const { parent } = item; + const { relationMode } = item.completion; - borderRadius: "5px", - padding: "0.4em", - paddingLeft: "1em", - paddingRight: "1em", - }; + if (relationMode) { + classes.push(styles.relation); + } if (item.selected) { - markStyle = { - ...markStyle, - border: "1px solid red", - }; + classes.push(styles.selected); } else if (item.highlighted) { - markStyle = { - ...markStyle, - border: Constants.HIGHLIGHTED_CSS_BORDER, - }; + classes.push(styles.highlighted); } - const params = {}; - const { parent } = item; if (parent.editable) { params["editable"] = { onChange: str => { @@ -79,7 +65,7 @@ const HtxTextAreaRegionView = ({ store, item }) => { // here we update the parent object's state if (parent.perregion) { - const reg = parent.completion.highlightedNode; + const reg = item.completion.highlightedNode; reg && reg.updateSingleState(parent); // self.regions = []; @@ -89,17 +75,17 @@ const HtxTextAreaRegionView = ({ store, item }) => { } let divAttrs = {}; - if (!item.parent.perregion) { + if (!parent.perregion) { divAttrs = { onClick: item.onClickRegion, onMouseOver: () => { - if (store.completionStore.selected.relationMode) { + if (relationMode) { item.setHighlight(true); } }, onMouseOut: () => { /* range.setHighlight(false); */ - if (store.completionStore.selected.relationMode) { + if (relationMode) { item.setHighlight(false); } }, @@ -107,32 +93,24 @@ const HtxTextAreaRegionView = ({ store, item }) => { } return ( -
-
- - {item._value} - -
-
- {item.parent.perregion && ( -
- { - const reg = item.completion.highlightedNode; - item.completion.deleteRegion(item); - - reg && reg.updateSingleState(item.parent); - - ev.preventDefault(); - return false; - }} - > - - -
- )} -
+
+

+ {item._value} +

+ {parent.perregion && ( + { + const reg = item.completion.highlightedNode; + item.completion.deleteRegion(item); + + reg && reg.updateSingleState(parent); + + ev.preventDefault(); + return false; + }} + /> + )}
); }; diff --git a/src/regions/TextAreaRegion/TextAreaRegion.module.scss b/src/regions/TextAreaRegion/TextAreaRegion.module.scss new file mode 100644 index 000000000..0e7fa8236 --- /dev/null +++ b/src/regions/TextAreaRegion/TextAreaRegion.module.scss @@ -0,0 +1,34 @@ +.mark { + cursor: pointer; + + background-color: #f6ffed; + border: 1px solid #b7eb8f; + + border-radius: 5px; + margin: 0; + padding: 0.4em 1em; + + &.selected { + border: 1px solid red; + } + + &.highlighted { + border: 1px dashed #00aeff; + } + + &.relation { + cursor: crosshair; + } +} + +.delete { + color: #1890ff; + padding-top: 0.5em; + padding-left: 1em; + line-height: 1.8em; +} + +.row { + display: flex; + margin-bottom: 0.5em; +} diff --git a/src/tags/control/TextArea.js b/src/tags/control/TextArea.js index fb754799b..c01dc7021 100644 --- a/src/tags/control/TextArea.js +++ b/src/tags/control/TextArea.js @@ -82,6 +82,7 @@ const Model = types }, get serializableValue() { + if (!self.regions.length) return null; return { text: self.selectedValues() }; }, From 4cccebc1c15b73d6d5cc2661556625364c9dff58 Mon Sep 17 00:00:00 2001 From: hlomzik Date: Tue, 26 May 2020 07:31:54 +0700 Subject: [PATCH 3/4] Add e2e test for perRegion TextArea --- e2e/tests/helpers.js | 2 + e2e/tests/image.test.js | 114 +++++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/e2e/tests/helpers.js b/e2e/tests/helpers.js index 2a331ea13..8c0aa612d 100644 --- a/e2e/tests/helpers.js +++ b/e2e/tests/helpers.js @@ -8,6 +8,8 @@ * @param {function} done */ const initLabelStudio = async ({ config, data, completions = [{ result: [] }], predictions = [] }, done) => { + if (window.Konva && window.Konva.stages.length) window.Konva.stages.forEach(stage => stage.destroy()); + const interfaces = [ "panel", "update", diff --git a/e2e/tests/image.test.js b/e2e/tests/image.test.js index a70beae53..aefc58673 100644 --- a/e2e/tests/image.test.js +++ b/e2e/tests/image.test.js @@ -1,6 +1,8 @@ /* global Feature, Scenario, locate */ -const { initLabelStudio, clickRect } = require("./helpers"); +const { initLabelStudio, clickRect, serialize } = require("./helpers"); + +const assert = require("assert"); Feature("Test Image object"); @@ -13,26 +15,43 @@ const config = ` `; +const perRegionConfig = ` + + + + + + +