diff --git a/e2e/tests/taxonomy.test.js b/e2e/tests/taxonomy.test.js
new file mode 100644
index 000000000..dabd1553d
--- /dev/null
+++ b/e2e/tests/taxonomy.test.js
@@ -0,0 +1,293 @@
+/* global Feature, Scenario, locate, DataTable, Data */
+
+const { serialize, selectText } = require("./helpers");
+
+const assert = require("assert");
+
+Feature("Taxonomy");
+
+const cases = {
+ taxonomy: {
+ config: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ text: `To have faith is to trust yourself to the water`,
+ annotations: [
+ { label: 'PER', rangeStart: 0, rangeEnd: 2, text: 'To', test: {
+ clickTaxonomy: ['Extraterrestial', 'Archaea'],
+ assertTrue: [
+ 'Archaea',
+ ],
+ assertFalse: [
+ 'Extraterrestial',
+ ],
+ } },
+ { label: 'PER', rangeStart: 3, rangeEnd: 7, text: 'have', test: {
+ clickTaxonomy: ['Archaea'],
+ assertTrue: [
+ 'Archaea',
+ 'Extraterrestial',
+ ],
+ assertFalse: [],
+ } },
+ ],
+ isPerRegion: false,
+ FF: {
+ ff_front_1170_outliner_030222_short: false,
+ },
+ },
+ taxonomyPerRegion: {
+ config: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ text: `To have faith is to trust yourself to the water`,
+ annotations: [
+ { label: 'PER', rangeStart: 0, rangeEnd: 2, text: 'To', test: {
+ clickTaxonomy: ['Extraterrestial', 'Archaea'],
+ assertTrue: [
+ 'Archaea',
+ ],
+ assertFalse: [
+ 'Extraterrestial',
+ ],
+ } },
+ { label: 'PER', rangeStart: 3, rangeEnd: 7, text: 'have', test: {
+ clickTaxonomy: ['Archaea'],
+ assertTrue: [
+ 'Archaea',
+ 'Extraterrestial',
+ ],
+ assertFalse: [],
+ } },
+ ],
+ isPerRegion: true,
+ FF: {
+ ff_front_1170_outliner_030222_short: false,
+ },
+ },
+ taxonomyWithShowLabels: {
+ config: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ text: `To have faith is to trust yourself to the water`,
+ annotations: [
+ { label: 'PER', rangeStart: 0, rangeEnd: 2, text: 'To', test: {
+ clickTaxonomy: ['Human'],
+ assertTrue: [
+ 'Eukarya / Extraterrestial',
+ 'Eukarya / Human',
+ ],
+ assertFalse: [
+ 'Bacteria',
+ ],
+ } },
+ ],
+ isPerRegion: true,
+ FF: {
+ ff_front_1170_outliner_030222_short: false,
+ },
+ },
+ taxonomyOutliner: {
+ config: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ text: `To have faith is to trust yourself to the water`,
+ annotations: [
+ { label: 'PER', rangeStart: 0, rangeEnd: 2, text: 'To', test: {
+ clickTaxonomy: ['Extraterrestial'],
+ assertTrue: [
+ ],
+ assertFalse: [
+ 'Extraterrestial',
+ ],
+ } },
+ { label: 'PER', rangeStart: 3, rangeEnd: 7, text: 'have', test: {
+ clickTaxonomy: ['Archaea'],
+ assertTrue: [
+ 'Archaea',
+ 'Extraterrestial',
+ ],
+ assertFalse: [],
+ } },
+ ],
+ isPerRegion: true,
+ FF: {
+ ff_front_1170_outliner_030222_short: true,
+ },
+ },
+ taxonomyWithShowLabelsWithOuliner: {
+ config: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ text: `To have faith is to trust yourself to the water`,
+ annotations: [
+ { label: 'PER', rangeStart: 0, rangeEnd: 2, text: 'To', test: {
+ clickTaxonomy: [['Human']],
+ assertTrue: [
+ 'Eukarya / Extraterrestial',
+ 'Eukarya / Human',
+ ],
+ assertFalse: [
+ 'Bacteria',
+ ],
+ } },
+ ],
+ isPerRegion: true,
+ FF: {
+ ff_front_1170_outliner_030222_short: true,
+ },
+ },
+};
+
+const taxonomyTable = new DataTable(["taxonomyName"]);
+
+for (const taxonomyName of Object.keys(cases)) {
+ taxonomyTable.add([taxonomyName]);
+}
+
+Data(taxonomyTable).Scenario("Check Taxonomy", async ({ I, LabelStudio, current }) => {
+ const { taxonomyName } = current;
+ const Taxonomy = cases[taxonomyName];
+ const { annotations, config, text, isPerRegion, FF } = Taxonomy;
+ const outlinerSelector = ".lsf-outliner-item__title";
+ const sideBarRegionSelector = "li";
+ const taxonomyLabelSelector = ".lsf-taxonomy__label";
+
+ I.amOnPage("/");
+
+ LabelStudio.setFeatureFlags({ ff_dev_2007_rework_choices_280322_short: true, ...FF });
+ LabelStudio.init({ config, data: { text } });
+
+ const isOutliner = FF.ff_front_1170_outliner_030222_short;
+
+ annotations.forEach(annotation => {
+ let regionEl;
+
+ if (isPerRegion) {
+ I.click(locate(".lsf-label__text").withText(annotation.label));
+ I.executeScript(selectText, {
+ selector: ".lsf-htx-richtext",
+ rangeStart: annotation.rangeStart,
+ rangeEnd: annotation.rangeEnd,
+ });
+
+ regionEl = isOutliner ? locate(outlinerSelector).withText(annotation.label) : locate(sideBarRegionSelector).withText(annotation.text);
+
+ I.seeElement(regionEl);
+ I.click(regionEl);
+ }
+
+ I.click(locate("span").withText("Click to add..."));
+ I.click(locate(".collapser.collapsed"));
+ annotation.test.clickTaxonomy.forEach(t => I.click(locate("label").withText(t)));
+
+
+ /* reseting clicks */
+ I.click(locate(".collapser.open"));
+ if (isPerRegion) {
+ I.click(regionEl);
+ I.click(locate(".lsf-label__text").withText(annotation.label));
+ } else {
+ I.click(locate("span").withText("Click to add..."));
+ }
+ });
+
+ const results = await I.executeScript(serialize);
+
+ results.filter(result => result.value.labels).forEach((result, index) => {
+ const annotation = annotations[index];
+ const expected = {
+ end: annotation.rangeEnd,
+ labels: [annotation.label],
+ start: annotation.rangeStart,
+ text: annotation.text,
+ };
+
+ assert.deepEqual(result.value, expected);
+
+ if (isPerRegion) {
+ const regionEl = isOutliner ? locate(outlinerSelector).withText(annotation.label) : locate(sideBarRegionSelector).withText(annotation.text);
+
+ I.click(regionEl);
+ }
+
+ annotation.test.assertTrue.forEach(label => I.seeElement(locate(taxonomyLabelSelector).withText(label)));
+ annotation.test.assertFalse.forEach(label => I.dontSeeElement(locate(taxonomyLabelSelector).withText(label)));
+ });
+
+});
\ No newline at end of file
diff --git a/src/components/Taxonomy/Taxonomy.module.scss b/src/components/Taxonomy/Taxonomy.module.scss
index b769dde6d..30d7b92ff 100644
--- a/src/components/Taxonomy/Taxonomy.module.scss
+++ b/src/components/Taxonomy/Taxonomy.module.scss
@@ -15,6 +15,38 @@
white-space: nowrap;
cursor: pointer;
}
+ &__selected {
+ display: flex;
+ flex-wrap: wrap;
+ min-height: 28px; // 24px button + 4px margin
+
+ div {
+ margin: 0 2px 4px 0;
+ background: hsl(0, 0%, 95%);
+ padding: 0; // all the right space should be a clickable button, so no padding
+ padding-left: 8px;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ }
+
+ input[type="button"] {
+ border: none;
+ background: none;
+ cursor: pointer;
+ width: 24px;
+ height: 24px;
+ padding: 0;
+ line-height: 1;
+ font-weight: 500;
+ font-size: 20px;
+ color: #09f;
+
+ &:hover {
+ color: red;
+ }
+ }
+ }
}
.taxonomy > span > svg {
@@ -24,39 +56,6 @@
transform: none;
}
-.taxonomy__selected {
- display: flex;
- flex-wrap: wrap;
- min-height: 28px; // 24px button + 4px margin
-
- div {
- margin: 0 2px 4px 0;
- background: hsl(0, 0%, 95%);
- padding: 0; // all the right space should be a clickable button, so no padding
- padding-left: 8px;
- border-radius: 4px;
- display: flex;
- align-items: center;
- }
-
- input[type="button"] {
- border: none;
- background: none;
- cursor: pointer;
- width: 24px;
- height: 24px;
- padding: 0;
- line-height: 1;
- font-weight: 500;
- font-size: 20px;
- color: #09f;
-
- &:hover {
- color: red;
- }
- }
-}
-
.taxonomy__dropdown {
position: absolute;
z-index: 10;
diff --git a/src/components/Taxonomy/Taxonomy.tsx b/src/components/Taxonomy/Taxonomy.tsx
index 15186bafe..5288a649f 100644
--- a/src/components/Taxonomy/Taxonomy.tsx
+++ b/src/components/Taxonomy/Taxonomy.tsx
@@ -5,6 +5,7 @@ import { useToggle } from "../../hooks/useToggle";
import { isArraysEqual } from "../../utils/utilities";
import { LsChevron } from "../../assets/icons";
import TreeStructure from "../TreeStructure/TreeStructure";
+import { Elem } from "../../utils/bem";
import styles from "./Taxonomy.module.scss";
@@ -129,12 +130,12 @@ const SelectedList = ({ isReadonly, flatItems } : { isReadonly:boolean, flatItem
return (
{selectedLabels.map((path, index) => (
-
+
{showFullPath ? path.join(pathSeparator) : path[path.length - 1]}
{!isReadonly &&
setSelected(selected[index], false)} value="×" />
}
-
+
))}
);
@@ -217,7 +218,11 @@ const Item: React.FC = ({ style, item, dimensionCallback, maxWidth }:
toggle(id)}>
-
+
{
- if (isFF(FF_DEV_2100_A) && tag?.type === "choices" && tag.preselectedValues?.length) {
+ let couldHavePreselectedValues = false;
+
+ if (isFF(FF_DEV_2100_A) && tag?.type === "choices") {
//
- self.createResult({}, { choices: tag.preselectedValues }, tag, tag.toname);
+ couldHavePreselectedValues = true;
+
+ } else if (tag?.type === "taxonomy") {
+ couldHavePreselectedValues = true;
+ }
+
+ if (couldHavePreselectedValues && tag.preselectedValues?.length) {
+ console.log("setDefaultValues", tag.valueType, tag.preselectedValues, self.selected, self.preselectedValues);
+ self.createResult({}, { [tag.valueType]: tag.preselectedValues }, tag, tag.toname);
}
});
},
@@ -765,6 +775,14 @@ export const Annotation = types
};
const area = self.areas.put(areaRaw);
+ const childrenWithPreselectedValues = self.toNames.get(object?.name ?? object)?.toJSON?.()?.filter(item => (item.preselectedValues?.length ?? 0) > 0);
+ const preRegionChildWithPreselectedValues = childrenWithPreselectedValues.filter(item => item.perregion);
+ const annotationChildWithPreselectedValues = childrenWithPreselectedValues.filter(item => !item.perregion);
+
+ preRegionChildWithPreselectedValues?.forEach(item => area.setDefaultValue(item));
+ annotationChildWithPreselectedValues?.forEach(item => item.selected = item.preselectedValues);
+
+ console.log("annotationChildWithPreselectedValues", annotationChildWithPreselectedValues, self);
if (!area.classification) getEnv(self).events.invoke('entityCreate', area);
if (!skipAfrerCreate) self.afterCreateResult(area, control);
@@ -1063,6 +1081,7 @@ export const Annotation = types
},
prepareValue(value, type) {
+ console.log("prepareValue", value, type);
switch (type) {
case "text":
case "hypertext":
diff --git a/src/tags/control/Taxonomy.js b/src/tags/control/Taxonomy.js
index cbb31fc75..615d7e20f 100644
--- a/src/tags/control/Taxonomy.js
+++ b/src/tags/control/Taxonomy.js
@@ -14,6 +14,8 @@ import VisibilityMixin from "../../mixins/Visibility";
import ControlBase from "./Base";
import DynamicChildrenMixin from "../../mixins/DynamicChildrenMixin";
import { FF_DEV_2007_DEV_2008, isFF } from "../../utils/feature-flags";
+import Tree from "../../core/Tree";
+import { Block } from "../../utils/bem";
/**
* Use the Taxonomy tag to create one or more hierarchical classifications, storing both choice selections and their ancestors in the results. Use for nested classification tasks with the Choice tag.
@@ -160,6 +162,13 @@ const Model = types
return fromConfig;
},
+ get preselectedValues() {
+ const items = Tree.filterChildrenOfType(self, ['ChoiceModel']);
+ const selectedItems = items.filter(c => c.selected);
+
+ return selectedItems.map(c => c.resultValue);
+ },
+
get defaultChildType() {
return "choice";
},
@@ -224,7 +233,7 @@ const TaxonomyModel = types.compose("TaxonomyModel",
const HtxTaxonomy = observer(({ item }) => {
const style = { marginTop: "1em", marginBottom: "1em" };
- const visibleStyle = item.perRegionVisible() || item.isVisible ? {} : { display: "none" };
+ const visibleStyle = item.perRegionVisible() && item.isVisible ? {} : { display: "none" };
const options = {
showFullPath: item.showfullpath,
leafsOnly: item.leafsonly,
@@ -236,7 +245,7 @@ const HtxTaxonomy = observer(({ item }) => {
};
return (
-
+
{
options={options}
isReadonly={item.annotation.readonly}
/>
-
+
);
});