${btnsVisible? html`
`: nothing}
diff --git a/src/webcomponents/commons/opencga-browser-filter.js b/src/webcomponents/commons/opencga-browser-filter.js
index 27d17839b..e956dc2dc 100644
--- a/src/webcomponents/commons/opencga-browser-filter.js
+++ b/src/webcomponents/commons/opencga-browser-filter.js
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {LitElement, html, nothing} from "lit";
+import {LitElement, html} from "lit";
import UtilsNew from "../../core/utils-new.js";
import LitUtils from "./utils/lit-utils.js";
import "./filters/catalog-search-autocomplete.js";
@@ -34,7 +34,7 @@ export default class OpencgaBrowserFilter extends LitElement {
constructor() {
super();
- this._init();
+ this.#init();
}
createRenderRoot() {
@@ -67,15 +67,9 @@ export default class OpencgaBrowserFilter extends LitElement {
};
}
- _init() {
+ #init() {
this._prefix = UtilsNew.randomString(8);
- this.annotationFilterConfig = {
- class: "small",
- buttonClass: "btn-sm",
- inputClass: "input-sm"
- };
-
this.query = {};
this.preparedQuery = {};
this.searchButton = true;
@@ -112,12 +106,6 @@ export default class OpencgaBrowserFilter extends LitElement {
};
}
- connectedCallback() {
- super.connectedCallback();
-
- this.preparedQuery = {...this.query}; // propagates here the iva-app query object
- }
-
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties);
@@ -141,17 +129,18 @@ export default class OpencgaBrowserFilter extends LitElement {
queryObserver() {
this.preparedQuery = this.query || {};
- this.requestUpdate();
}
onFilterChange(key, value) {
if (value && value !== "") {
- this.preparedQuery = {...this.preparedQuery, ...{[key]: value}};
+ this.preparedQuery[key] = value;
} else {
delete this.preparedQuery[key];
- this.preparedQuery = {...this.preparedQuery};
}
+ this.preparedQuery = {...this.preparedQuery};
this.notifyQuery(this.preparedQuery);
+ // Note 20241015 Vero: I believe this.requestUpdate() is not needed, but removing it requires further investigation
+ // (see variant-browser-filter.js, onFilterChange())
this.requestUpdate();
}
@@ -163,7 +152,6 @@ export default class OpencgaBrowserFilter extends LitElement {
}
this.preparedQuery = {...this.preparedQuery};
this.notifyQuery(this.preparedQuery);
- this.requestUpdate();
}
notifyQuery(query) {
@@ -289,7 +277,6 @@ export default class OpencgaBrowserFilter extends LitElement {
.opencgaSession="${this.opencgaSession}"
.opencgaClient="${this.opencgaSession.opencgaClient}"
.resource="${this.resource}"
- .config="${this.annotationFilterConfig}"
.selectedVariablesText="${this.preparedQuery.annotation}"
@annotationChange="${this.onAnnotationChange}">
diff --git a/src/webcomponents/opencga/catalog/variableSets/opencga-annotation-filter-modal.js b/src/webcomponents/opencga/catalog/variableSets/opencga-annotation-filter-modal.js
index b0a4160c7..37f5b7500 100644
--- a/src/webcomponents/opencga/catalog/variableSets/opencga-annotation-filter-modal.js
+++ b/src/webcomponents/opencga/catalog/variableSets/opencga-annotation-filter-modal.js
@@ -17,6 +17,8 @@
import {LitElement, html} from "lit";
import {classMap} from "lit/directives/class-map.js";
import UtilsNew from "../../../../core/utils-new.js";
+import ModalUtils from "../../../commons/modal/modal-utils.js";
+import LitUtils from "../../../commons/utils/lit-utils.js";
import "./../../../commons/forms/select-field-filter.js";
export default class OpencgaAnnotationFilterModal extends LitElement {
@@ -25,7 +27,7 @@ export default class OpencgaAnnotationFilterModal extends LitElement {
super();
// Set status and init private properties
- this._init();
+ this.#init();
}
createRenderRoot() {
@@ -43,148 +45,132 @@ export default class OpencgaAnnotationFilterModal extends LitElement {
selectedVariablesText: {
type: String
},
- config: {
- type: Object
- }
};
}
- _init() {
+ #init() {
this._prefix = "oafm-" + UtilsNew.randomString(6) + "_";
this.selectedVariables = {};
this.selectedVariablesText = "";
this.variableMap = {};
}
- connectedCallback() {
- super.connectedCallback();
- this._config = {...this.getDefaultConfig(), ...this.config};
- }
-
- updated(changedProperties) {
+ update(changedProperties) {
if (changedProperties.has("opencgaSession")) {
this.opencgaSessionObserver();
}
if (changedProperties.has("selectedVariablesText")) {
this.selectedVariablesTextObserver();
}
+
+ super.update(changedProperties);
+ }
+
+ opencgaSessionObserver() {
+ this.variableSets = [];
+ if (typeof this.opencgaSession.study.variableSets !== "undefined") {
+ this._updateVariableSets(this.opencgaSession.study);
+ } else {
+ this.opencgaClient.studies()
+ .info(this.opencgaSession.study.id, {include: "variableSets"})
+ .then(response => {
+ this._updateVariableSets(response.getResult(0));
+ })
+ .catch(() => {
+ // this.dispatchEvent(new CustomEvent("variablesetselected", {detail: {id: null}}));
+ console.error("Could not obtain the variable sets of the study " + this.opencgaSession.study);
+ });
+ }
}
/**
- * It builds this.selectedVariables from the serialized string this.selectedVariablesText
+ * It builds the variable this.selectedVariables from the serialized string this.selectedVariablesText
*/
- async selectedVariablesTextObserver() {
+ selectedVariablesTextObserver() {
this.selectedVariables = {};
+
if (this.selectedVariablesText) {
const variables = this.selectedVariablesText.split(";");
- this.requestUpdate();
- await this.updateComplete;
+
for (const v of variables) {
- const [, variableSetId, variableId, operator, value] = [...v.matchAll(/(\w+):(\w+\.?\w+)(<=?|>=?|=)(\w+)/g)][0];
- this.selectedVariables[variableSetId] = {...this.selectedVariables[variableSetId] ?? {}, [variableId]: {operator, value}};
+ const match = [...v.matchAll(/(\w+):(\w+\.?\w+)(<=?|>=?|=)(\w+(?:\s+\w+)*)/g)][0];
+ if (!match) {
+ // TODO 20241017: Handle potential match failures
+ console.log(`Annotation variable ${v} failed at matching regular expression`);
+ } else {
+ const [, variableSetId, variableId, operator, value] = match;
+ // Update the variable this.selectedVariables
+ this.selectedVariables[variableSetId] = {
+ ...this.selectedVariables[variableSetId] ?? {},
+ [variableId]: {operator, value},
+ };
+ }
}
-
}
- this.selectedVariables = {...this.selectedVariables};
- this.requestUpdate();
-
}
/**
* It serializes this.selectedVariables in a single string and fire the event
*/
- // fire in case of selectedVariables change
selectedVariablesSerializer() {
const selected = [];
+
for (const [variableSetId, variables] of Object.entries(this.selectedVariables)) {
- // value is not defined iff an operator (<=, >=, ...) has been selected before setting the value. In that case we filter out that entry.
- const singleVariableSetvariables = Object.entries(variables)
+ // Value is not defined iff an operator (<=, >=, ...) has been selected before setting the value. In that case we filter out that entry.
+ const singleVariableSetVariables = Object.entries(variables)
.filter(([, {value}]) => Boolean(value))
.map(([variableId, {operator, value}]) => `${variableSetId}:${variableId}${operator}${value}`)
.join(";");
- selected.push(singleVariableSetvariables);
- }
- const event = new CustomEvent("annotationChange", {
- detail: {
- value: selected.join(";")
- }
- });
- this.dispatchEvent(event);
- }
-
- opencgaSessionObserver() {
-
- this.variableSets = [];
-
- /* if (typeof this.opencgaSession.study === "undefined") {
- this.dispatchEvent(new CustomEvent("variablesetselected", {detail: {id: null}}));
- return;
- }*/
-
- if (typeof this.opencgaSession.study.variableSets !== "undefined") {
- this._updateVariableSets(this.opencgaSession.study);
- } else {
- const _this = this;
-
- this.opencgaClient.studies().info(this.opencgaSession.study.id, {include: "variableSets"})
- .then(response => {
- this._updateVariableSets(response.getResult(0));
- })
- .catch(function () {
- // this.dispatchEvent(new CustomEvent("variablesetselected", {detail: {id: null}}));
- console.error("Could not obtain the variable sets of the study " + _this.opencgaSession.study);
- });
+ selected.push(singleVariableSetVariables);
}
+ LitUtils.dispatchCustomEvent(this, "annotationChange", selected.join(";"));
}
- async _updateVariableSets(study) {
+ _updateVariableSets(study) {
+ // CAUTION: MAP_BOOLEAN MISSING
const sort = ["TEXT", "STRING", "NUMERIC", "INTEGER", "DOUBLE", "CATEGORICAL", "BOOLEAN", "OBJECT", "MAP_STRING", "MAP_DOUBLE", "MAP_INTEGER"];
- if (typeof study.variableSets === "undefined") {
- this.variableSets = [];
- } else {
- const _variableSets = [];
- for (const variableSet of study.variableSets) {
+ this.variableSets = [];
+ if (typeof study.variableSets !== "undefined") {
+ this.variableSets = study.variableSets.filter(variableSet => {
if (UtilsNew.isEmpty(this.resource) || variableSet.entities.includes(this.resource)) {
- variableSet.variables.sort((a, b) => {
- return sort.indexOf(a.type) - sort.indexOf(b.type);
- });
- _variableSets.push({
+ variableSet.variables.sort((a, b) => sort.indexOf(a.type) - sort.indexOf(b.type));
+ return {
name: variableSet.name || variableSet.id,
...variableSet
- });
+ };
}
- }
- this.variableSets = _variableSets;
+ });
}
- this.requestUpdate();
}
changeOperator(e) {
const {variableId, variableSetId} = e.target.dataset;
const operator = e.target.value;
- // TODO remove this line and use the this.selectedVariables[variableSetId][variableId].operator in addNumericFilter
- $(`.annotation-modal input[type=text][data-variable-id="${variableId}"][data-variable-set-id="${variableSetId}"]`).attr("data-operator", e.target.value);
- if (this.selectedVariables[variableSetId]?.[variableId]) {
- this.selectedVariables[variableSetId][variableId] = {...this.selectedVariables[variableSetId][variableId], operator};
- } else {
- // the value hasn't been set yet
- this.selectedVariables[variableSetId] = {...this.selectedVariables[variableSetId] ?? {}, [variableId]: {operator}};
- }
+
+ this.selectedVariables[variableSetId] = {
+ ...this.selectedVariables[variableSetId] ?? {},
+ [variableId]: {
+ ...this.selectedVariables[variableSetId]?.[variableId], // Spread existing variableId if present
+ operator,
+ },
+ };
+
this.selectedVariables = {...this.selectedVariables};
this.selectedVariablesSerializer();
-
}
addNumericFilter(e) {
- const {variableId, variableSetId, operator = ""} = e.target.dataset; // numericOperator is defined only for INTEGER and DOUBLE types
+ const {variableId, variableSetId} = e.target.dataset; // numericOperator is defined only for INTEGER and DOUBLE types
const value = e.target.value.trim();
+ const operator = this.selectedVariables[variableSetId]?.[variableId]?.operator || ">";
+
if (value) {
- /* if (this.selectedVariables[variableSetId][variableId].operator) {
- // TODO continue
- }*/
- this.selectedVariables[variableSetId] = {...this.selectedVariables[variableSetId] ?? {}, [variableId]: {value, operator}};
+ this.selectedVariables[variableSetId] = {
+ ...this.selectedVariables[variableSetId] ?? {},
+ [variableId]: {value, operator}
+ };
} else {
delete this.selectedVariables[variableSetId][variableId];
}
@@ -197,7 +183,24 @@ export default class OpencgaAnnotationFilterModal extends LitElement {
const value = e.target.value.trim();
if (value) {
- this.selectedVariables[variableSetId] = {...this.selectedVariables[variableSetId] ?? {}, [variableId]: {value, operator: "="}};
+ this.selectedVariables[variableSetId] = {
+ ...this.selectedVariables[variableSetId] ?? {},
+ [variableId]: {value, operator: "="}
+ };
+ } else {
+ delete this.selectedVariables[variableSetId][variableId];
+ }
+ this.selectedVariables = {...this.selectedVariables};
+ this.selectedVariablesSerializer();
+ }
+
+ addCategoricalFilter(variableSetId, variableId, key) {
+ const value = key.trim();
+ if (value) {
+ this.selectedVariables[variableSetId] = {
+ ...this.selectedVariables[variableSetId] ?? {},
+ [variableId]: {value, operator: "="}
+ };
} else {
delete this.selectedVariables[variableSetId][variableId];
}
@@ -208,149 +211,176 @@ export default class OpencgaAnnotationFilterModal extends LitElement {
addBooleanFilter(e) {
const {variableId, variableSetId, value} = e.target.dataset;
console.log(variableId, variableSetId, value);
- this.selectedVariables[variableSetId] = {...this.selectedVariables[variableSetId] ?? {}, [variableId]: {value, operator: "="}};
+ this.selectedVariables[variableSetId] = {
+ ...this.selectedVariables[variableSetId] ?? {},
+ [variableId]: {value, operator: "="}
+ };
this.selectedVariables = {...this.selectedVariables};
this.selectedVariablesSerializer();
}
- changeMap(variableSetId, variableId, key) {
- console.log(variableSetId, variableId, key);
+ changeMap(e, variableSetId, variableId, key) {
if (key) {
this.variableMap[variableSetId] = {
...this.variableMap[variableSetId],
- [variableId]: key.split(",")
+ [variableId]: key.split(","),
};
} else {
delete this.variableMap[variableSetId][variableId];
}
+ if (this.selectedVariables?.[variableSetId]) {
+ Object.keys(this.selectedVariables[variableSetId])
+ .forEach(key => {
+ const [keyVariableId, keyOption] = key.split(".");
+ if (keyOption && !this.variableMap[variableSetId][keyVariableId]?.includes(keyOption)) {
+ delete this.selectedVariables[variableSetId][key];
+ }
+ });
+ }
this.variableMap = {...this.variableMap};
+ this.selectedVariables = {...this.selectedVariables};
+ this.selectedVariablesSerializer();
this.requestUpdate();
}
renderVariable(variable, variableSet) {
- // console.log("going to render", variable, "of", variableSet.id)
let content = "";
switch (variable.type) {
case "OBJECT":
content = html`
${variable?.variableSet?.length ? html`
-
-
- ${variable.id}
-
- ${variable.variableSet.map(v => this.renderVariable(v, variableSet))}
-
- ` : html`
-
-
-
-
- ${variable.id}
-
-
-
- `}
-