This is an automated email from the ASF dual-hosted git repository.

zehnder pushed a commit to branch 
3430-filtering-for-numerical-dimension-properties-in-data-explorer
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to 
refs/heads/3430-filtering-for-numerical-dimension-properties-in-data-explorer 
by this push:
     new f40022c7e8 fix(#3430): Refactor filter selection panel row and filter 
operator according to data type
f40022c7e8 is described below

commit f40022c7e8860ce098d0f844f836f5eb108d95cd
Author: Philipp Zehnder <[email protected]>
AuthorDate: Wed Jan 22 15:54:20 2025 +0100

    fix(#3430): Refactor filter selection panel row and filter operator 
according to data type
---
 .../param/model/WhereClauseParams.java             |   8 +-
 ui/angular.json                                    |   3 +-
 ui/cypress/support/utils/datalake/DataLakeUtils.ts |  45 ++++++-
 .../datalake/widgetDataConfiguration.smoke.spec.ts |   6 +
 ...on-panel-row-operation-selection.component.html |  46 +++++++
 ...tion-panel-row-operation-selection.component.ts |  36 ++++++
 ...ion-panel-row-property-selection.component.html |  31 +++++
 ...ction-panel-row-property-selection.component.ts |  43 +++++++
 ...nel-row-value-input-autocomplete.component.html |  42 ++++++
 ...panel-row-value-input-autocomplete.component.ts |  39 ++++++
 ...-selection-panel-row-value-input.component.html |  29 +++++
 ...er-selection-panel-row-value-input.component.ts |  35 +++++
 .../filter-selection-panel-row.component.html      |  58 +++++++++
 .../filter-selection-panel-row.component.ts        |  51 ++++++++
 .../filter-selection-panel.component.html          | 143 ++-------------------
 .../filter-selection-panel.component.ts            |   9 +-
 ui/src/app/data-explorer/data-explorer.module.ts   |  10 ++
 17 files changed, 494 insertions(+), 140 deletions(-)

diff --git 
a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
 
b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
index 3381983268..6bf5589d99 100644
--- 
a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
+++ 
b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
@@ -98,7 +98,9 @@ public class WhereClauseParams implements IQueryStatement {
   }
 
   private Object returnCondition(String inputCondition) {
-    if (NumberUtils.isParsable(inputCondition)) {
+    if (isQuotedString(inputCondition)) {
+      return inputCondition.substring(1, inputCondition.length() - 1);
+    } else if (NumberUtils.isParsable(inputCondition)) {
       return Double.parseDouble(inputCondition);
     } else if (isBoolean(inputCondition)) {
       return Boolean.parseBoolean(inputCondition);
@@ -107,6 +109,10 @@ public class WhereClauseParams implements IQueryStatement {
     }
   }
 
+  private boolean isQuotedString(String input) {
+    return input.startsWith("\"") && input.endsWith("\"");
+  }
+
   private boolean isBoolean(String input) {
     return "true".equalsIgnoreCase(input) || "false".equalsIgnoreCase(input);
   }
diff --git a/ui/angular.json b/ui/angular.json
index 17b7a423cc..384b9b88b4 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -189,7 +189,8 @@
   },
   "schematics": {
     "@schematics/angular:component": {
-      "style": "scss"
+      "style": "scss",
+      "standalone": false
     }
   },
   "cli": {
diff --git a/ui/cypress/support/utils/datalake/DataLakeUtils.ts 
b/ui/cypress/support/utils/datalake/DataLakeUtils.ts
index dc45a8cc42..e9b87103d5 100644
--- a/ui/cypress/support/utils/datalake/DataLakeUtils.ts
+++ b/ui/cypress/support/utils/datalake/DataLakeUtils.ts
@@ -181,7 +181,9 @@ export class DataLakeUtils {
     }
 
     public static saveDataViewConfiguration() {
-        cy.dataCy('save-data-view-btn', { timeout: 10000 }).click();
+        cy.dataCy('save-data-view-btn', { timeout: 10000 }).click({
+            force: true,
+        });
     }
 
     public static saveDashboardConfiguration() {
@@ -278,6 +280,43 @@ export class DataLakeUtils {
         }
     }
 
+    /**
+     * This method validates that the defined filter options are available in 
the UI
+     * @param expectedFilterOptions
+     */
+    public static validateFilterOptions(
+        expectedFilterOptions: ('=' | '<' | '<=' | '>=' | '>' | '!=')[],
+    ) {
+        cy.dataCy('design-panel-data-settings-filter-operator')
+            .click()
+            .dataCy('operator-', {}, true)
+            .should('have.length', expectedFilterOptions.length);
+
+        expectedFilterOptions.forEach(option => {
+            const escapedOption = option.replace(/([=<>!])/g, '\\$1');
+            cy.dataCy('operator-' + escapedOption).should('be.visible');
+        });
+
+        cy.dataCy('design-panel-data-settings-filter-operator').click({
+            force: true,
+        });
+    }
+
+    public static validateAutoCompleteOptions(options: string[]) {
+        cy.dataCy('design-panel-data-settings-filter-value')
+            .click({ force: true })
+            .dataCy('autocomplete-value-', {}, true)
+            .should('have.length', options.length);
+
+        options.forEach(option => {
+            cy.dataCy('autocomplete-value-' + option).should('be.visible');
+        });
+
+        cy.dataCy('design-panel-data-settings-filter-value').click({
+            force: true,
+        });
+    }
+
     /**
      * In the data set panel select all property fields
      */
@@ -309,7 +348,9 @@ export class DataLakeUtils {
     }
 
     public static dataConfigRemoveFilter() {
-        cy.dataCy('design-panel-data-settings-remove-filter').first().click();
+        cy.dataCy('design-panel-data-settings-remove-filter')
+            .first()
+            .click({ force: true });
     }
 
     public static clickGroupBy(propertyName: string) {
diff --git a/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts 
b/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
index 1a21da1559..3b9eb2f858 100644
--- a/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
+++ b/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
@@ -45,6 +45,7 @@ describe('Test Table View in Data Explorer', () => {
         let filterConfig = new DataLakeFilterConfig('randomnumber', '22', '=');
         DataLakeUtils.dataConfigAddFilter(filterConfig);
         DataLakeWidgetTableUtils.checkAmountOfRows(2);
+        DataLakeUtils.validateFilterOptions(['=', '<', '<=', '>=', '>', '!=']);
         DataLakeUtils.dataConfigRemoveFilter();
         DataLakeWidgetTableUtils.checkAmountOfRows(10);
 
@@ -52,6 +53,7 @@ describe('Test Table View in Data Explorer', () => {
         filterConfig = new DataLakeFilterConfig('randomnumber', '50', '>');
         DataLakeUtils.dataConfigAddFilter(filterConfig);
         DataLakeWidgetTableUtils.checkAmountOfRows(5);
+        DataLakeUtils.validateFilterOptions(['=', '<', '<=', '>=', '>', '!=']);
         DataLakeUtils.dataConfigRemoveFilter();
 
         // Test number smaller then
@@ -64,6 +66,8 @@ describe('Test Table View in Data Explorer', () => {
         filterConfig = new DataLakeFilterConfig('randombool', 'true', '=');
         DataLakeUtils.dataConfigAddFilter(filterConfig);
         DataLakeWidgetTableUtils.checkAmountOfRows(6);
+        DataLakeUtils.validateFilterOptions(['=', '!=']);
+        DataLakeUtils.validateAutoCompleteOptions(['true', 'false']);
         DataLakeUtils.dataConfigRemoveFilter();
 
         // Test string & if filter is persisted correctly
@@ -72,6 +76,8 @@ describe('Test Table View in Data Explorer', () => {
         DataLakeUtils.dataConfigAddFilter(filterConfig);
         DataLakeUtils.checkIfFilterIsSet(1);
         DataLakeWidgetTableUtils.checkAmountOfRows(4);
+        DataLakeUtils.validateFilterOptions(['=', '!=']);
+        DataLakeUtils.validateAutoCompleteOptions(['a', 'b', 'c']);
         DataLakeUtils.saveAndReEditWidget('NewWidget');
         DataLakeUtils.checkIfFilterIsSet(1);
         DataLakeWidgetTableUtils.checkAmountOfRows(4);
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.compo
 [...]
new file mode 100644
index 0000000000..3af11dd781
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.html
@@ -0,0 +1,46 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF 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.
+  ~
+  -->
+<mat-form-field color="accent" class="w-80-px mr-5" appearance="outline">
+    <mat-select
+        [(value)]="filter.operator"
+        panelClass="form-field-small min-w-100"
+        (selectionChange)="updateParentComponent()"
+        data-cy="design-panel-data-settings-filter-operator"
+    >
+        <mat-option [value]="'='" data-cy="operator-=">
+            <span class="pipeline-name">=</span>
+        </mat-option>
+        @if (filter.field && filter.field['numeric']) {
+            <mat-option [value]="'<'" data-cy="operator-<">
+                <span class="pipeline-name"><</span>
+            </mat-option>
+            <mat-option [value]="'<='" data-cy="operator-<=">
+                <span class="pipeline-name"><=</span>
+            </mat-option>
+            <mat-option [value]="'>='" data-cy="operator->=">
+                <span class="pipeline-name">>=</span>
+            </mat-option>
+            <mat-option [value]="'>'" data-cy="operator->">
+                <span class="pipeline-name">></span>
+            </mat-option>
+        }
+        <mat-option [value]="'!='" data-cy="operator-!=">
+            <span class="pipeline-name">!=</span>
+        </mat-option>
+    </mat-select>
+</mat-form-field>
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.ts
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.ts
new file mode 100644
index 0000000000..1ba9c766a1
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.ts
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 { Component, EventEmitter, Input, Output } from '@angular/core';
+import { SelectedFilter } from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-filter-selection-panel-row-operation-selection',
+    templateUrl:
+        './filter-selection-panel-row-operation-selection.component.html',
+})
+export class FilterSelectionPanelRowOperationSelectionComponent {
+    @Input()
+    public filter: SelectedFilter;
+
+    @Output()
+    public update = new EventEmitter<void>();
+
+    updateParentComponent() {
+        this.update.emit();
+    }
+}
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component.html
new file mode 100644
index 0000000000..23c7ba5c26
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component.html
@@ -0,0 +1,31 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF 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.
+  ~
+  -->
+<mat-form-field color="accent" class="w-140-px mr-5" appearance="outline">
+    <mat-label>Field</mat-label>
+    <mat-select
+        [(value)]="filter.field"
+        (selectionChange)="updateParentComponent()"
+        [compareWith]="compare"
+        panelClass="form-field-small min-w-200"
+        data-cy="design-panel-data-settings-filter-field"
+    >
+        @for (field of possibleFields; track field) {
+            <mat-option [value]="field">{{ field.runtimeName }} </mat-option>
+        }
+    </mat-select>
+</mat-form-field>
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component.ts
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component.ts
new file mode 100644
index 0000000000..f1f874cfb3
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component.ts
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 { Component, EventEmitter, Input, Output } from '@angular/core';
+import { FieldConfig, SelectedFilter } from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-filter-selection-panel-row-property-selection',
+    templateUrl:
+        './filter-selection-panel-row-property-selection.component.html',
+})
+export class FilterSelectionPanelRowPropertySelectionComponent {
+    @Input()
+    public filter: SelectedFilter;
+
+    @Input()
+    public possibleFields: FieldConfig[];
+
+    @Output()
+    public update = new EventEmitter<void>();
+
+    updateParentComponent() {
+        this.update.emit();
+    }
+
+    compare(available: FieldConfig, selected: FieldConfig) {
+        return available.runtimeName === selected.runtimeName;
+    }
+}
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-input-autocomplete.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-inpu
 [...]
new file mode 100644
index 0000000000..9ff3259aca
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-input-autocomplete.component.html
@@ -0,0 +1,42 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF 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.
+  ~
+  -->
+<mat-form-field color="accent" appearance="outline" class="w-140-px mr-5">
+    <input
+        type="text"
+        placeholder="Value"
+        aria-label="Number"
+        matInput
+        [(ngModel)]="filter.value"
+        (change)="updateParentComponent()"
+        data-cy="design-panel-data-settings-filter-value"
+        [matAutocomplete]="auto"
+    />
+    <mat-autocomplete
+        #auto="matAutocomplete"
+        (optionSelected)="updateParentComponent()"
+    >
+        @for (value of tagValues.get(filter.field.runtimeName); track value) {
+            <mat-option
+                [attr.data-cy]="'autocomplete-value-' + value"
+                [value]="value"
+            >
+                {{ value }}
+            </mat-option>
+        }
+    </mat-autocomplete>
+</mat-form-field>
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-input-autocomplete.component.ts
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-input-
 [...]
new file mode 100644
index 0000000000..e02ca4b998
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-input-autocomplete.component.ts
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 { Component, EventEmitter, Input, Output } from '@angular/core';
+import { SelectedFilter } from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-filter-selection-panel-row-value-input-autocomplete',
+    templateUrl:
+        './filter-selection-panel-row-value-input-autocomplete.component.html',
+})
+export class FilterSelectionPanelRowValueInputAutocompleteComponent {
+    @Input()
+    public filter: SelectedFilter;
+
+    @Input()
+    public tagValues: Map<string, string[]>;
+
+    @Output()
+    public update = new EventEmitter<void>();
+
+    updateParentComponent() {
+        this.update.emit();
+    }
+}
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component.html
new file mode 100644
index 0000000000..38614ea5b1
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component.html
@@ -0,0 +1,29 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF 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.
+  ~
+  -->
+<mat-form-field color="accent" appearance="outline" class="w-140-px mr-5">
+    <mat-label>Value</mat-label>
+    <input
+        type="text"
+        placeholder="Value"
+        aria-label="Number"
+        matInput
+        [(ngModel)]="filter.value"
+        (change)="updateParentComponent()"
+        data-cy="design-panel-data-settings-filter-value"
+    />
+</mat-form-field>
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component.ts
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component.ts
new file mode 100644
index 0000000000..4e52a4bf97
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component.ts
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 { Component, EventEmitter, Input, Output } from '@angular/core';
+import { SelectedFilter } from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-filter-selection-panel-row-value-input',
+    templateUrl: './filter-selection-panel-row-value-input.component.html',
+})
+export class FilterSelectionPanelRowValueInputComponent {
+    @Input()
+    public filter: SelectedFilter;
+
+    @Output()
+    public update = new EventEmitter<void>();
+
+    updateParentComponent() {
+        this.update.emit();
+    }
+}
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.html
new file mode 100644
index 0000000000..5d617b6dd4
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.html
@@ -0,0 +1,58 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF 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.
+  ~
+  -->
+
+<div fxFlex="100" fxLayout="column" class="form-field-small">
+    <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
+        <sp-filter-selection-panel-row-property-selection
+            [filter]="filter"
+            [possibleFields]="possibleFields"
+            (update)="updateParentComponent()"
+        >
+        </sp-filter-selection-panel-row-property-selection>
+
+        <sp-filter-selection-panel-row-operation-selection
+            [filter]="filter"
+            (update)="updateParentComponent()"
+        >
+        </sp-filter-selection-panel-row-operation-selection>
+
+        @if (!filter.field || !tagValues.has(filter.field.runtimeName)) {
+            <sp-filter-selection-panel-row-value-input
+                [filter]="filter"
+                (update)="updateParentComponent()"
+            >
+            </sp-filter-selection-panel-row-value-input>
+        } @else {
+            <sp-filter-selection-panel-row-value-input-autocomplete
+                [filter]="filter"
+                [tagValues]="tagValues"
+                (update)="updateParentComponent()"
+            >
+            </sp-filter-selection-panel-row-value-input-autocomplete>
+        }
+
+        <button
+            mat-icon-button
+            color="accent"
+            (click)="remove()"
+            data-cy="design-panel-data-settings-remove-filter"
+        >
+            <i class="material-icons">remove</i>
+        </button>
+    </div>
+</div>
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.ts
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.ts
new file mode 100644
index 0000000000..9e7cc75810
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.ts
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 { Component, Input, Output, EventEmitter } from '@angular/core';
+import { FieldConfig, SelectedFilter } from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-filter-selection-panel-row',
+    templateUrl: './filter-selection-panel-row.component.html',
+})
+export class FilterSelectionPanelRowComponent {
+    @Input()
+    public filter: SelectedFilter;
+
+    @Input()
+    public possibleFields: FieldConfig[];
+
+    @Input()
+    public tagValues: Map<string, string[]>;
+
+    @Output()
+    public update = new EventEmitter<void>();
+
+    @Output()
+    public removeFilter = new EventEmitter<number>();
+
+    constructor() {}
+
+    updateParentComponent() {
+        this.update.emit();
+    }
+
+    remove() {
+        this.removeFilter.emit();
+    }
+}
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
index 2d5242eaf3..f2dd8b0d27 100644
--- 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
@@ -31,134 +31,19 @@
         </button>
     </div>
     <div fxLayout="column">
-        <div
-            *ngFor="
-                let filter of sourceConfig.queryConfig.selectedFilters;
-                let i = index
-            "
-            fxFlex="100"
-            fxLayout="column"
-        >
-            <div fxFlex="100" fxLayout="column" class="form-field-small">
-                <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
-                    <mat-form-field
-                        color="accent"
-                        class="w-140-px mr-5"
-                        appearance="outline"
-                    >
-                        <mat-label>Field</mat-label>
-                        <mat-select
-                            [(value)]="filter.field"
-                            (selectionChange)="updateWidget()"
-                            [compareWith]="compare"
-                            panelClass="form-field-small min-w-200"
-                            data-cy="design-panel-data-settings-filter-field"
-                        >
-                            <mat-option
-                                *ngFor="
-                                    let field of 
sourceConfig.queryConfig.fields
-                                "
-                                [value]="field"
-                                >{{ field.runtimeName }}
-                            </mat-option>
-                        </mat-select>
-                    </mat-form-field>
-                    <mat-form-field
-                        color="accent"
-                        class="w-80-px mr-5"
-                        appearance="outline"
-                    >
-                        <mat-select
-                            [(value)]="filter.operator"
-                            panelClass="form-field-small min-w-100"
-                            (selectionChange)="updateWidget()"
-                            
data-cy="design-panel-data-settings-filter-operator"
-                        >
-                            <mat-option [value]="'='">
-                                <span class="pipeline-name">=</span>
-                            </mat-option>
-                            <mat-option [value]="'<'">
-                                <span class="pipeline-name"><</span>
-                            </mat-option>
-                            <mat-option [value]="'<='">
-                                <span class="pipeline-name"><=</span>
-                            </mat-option>
-                            <mat-option [value]="'>='">
-                                <span class="pipeline-name">>=</span>
-                            </mat-option>
-                            <mat-option [value]="'>'">
-                                <span class="pipeline-name">></span>
-                            </mat-option>
-                            <mat-option [value]="'!='">
-                                <span class="pipeline-name">!=</span>
-                            </mat-option>
-                        </mat-select>
-                    </mat-form-field>
-                    <mat-form-field
-                        color="accent"
-                        appearance="outline"
-                        class="w-140-px mr-5"
-                        *ngIf="
-                            !filter.field ||
-                            !tagValues.has(filter.field.runtimeName)
-                        "
-                    >
-                        <mat-label>Value</mat-label>
-                        <input
-                            type="text"
-                            placeholder="Value"
-                            aria-label="Number"
-                            matInput
-                            [(ngModel)]="filter.value"
-                            (change)="updateWidget()"
-                            data-cy="design-panel-data-settings-filter-value"
-                        />
-                    </mat-form-field>
-                    <mat-form-field
-                        color="accent"
-                        appearance="outline"
-                        class="w-140-px mr-5"
-                        *ngIf="
-                            filter.field &&
-                            tagValues.has(filter.field.runtimeName)
-                        "
-                    >
-                        <input
-                            type="text"
-                            placeholder="Value"
-                            aria-label="Number"
-                            matInput
-                            [(ngModel)]="filter.value"
-                            (change)="updateWidget()"
-                            data-cy="design-panel-data-settings-filter-value"
-                            [matAutocomplete]="auto"
-                        />
-                        <mat-autocomplete
-                            #auto="matAutocomplete"
-                            (optionSelected)="updateWidget()"
-                        >
-                            <mat-option
-                                *ngFor="
-                                    let value of tagValues.get(
-                                        filter.field.runtimeName
-                                    )
-                                "
-                                [value]="value"
-                            >
-                                {{ value }}
-                            </mat-option>
-                        </mat-autocomplete>
-                    </mat-form-field>
-                    <button
-                        mat-icon-button
-                        color="accent"
-                        (click)="remove(sourceConfig, i)"
-                        data-cy="design-panel-data-settings-remove-filter"
-                    >
-                        <i class="material-icons">remove</i>
-                    </button>
-                </div>
-            </div>
-        </div>
+        @for (
+            filter of sourceConfig.queryConfig.selectedFilters;
+            track filter;
+            let i = $index
+        ) {
+            <sp-filter-selection-panel-row
+                [filter]="filter"
+                [possibleFields]="sourceConfig.queryConfig.fields"
+                [tagValues]="tagValues"
+                (update)="updateWidget()"
+                (removeFilter)="remove(i)"
+            >
+            </sp-filter-selection-panel-row>
+        }
     </div>
 </sp-configuration-box>
diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
index 042d2515e2..fc251508c5 100644
--- 
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
@@ -19,7 +19,6 @@
 import { Component, Input, OnInit } from '@angular/core';
 import {
     DatalakeRestService,
-    FieldConfig,
     SelectedFilter,
     SourceConfig,
 } from '@streampipes/platform-services';
@@ -88,8 +87,8 @@ export class FilterSelectionPanelComponent implements OnInit {
         this.updateWidget();
     }
 
-    remove(sourceConfig: any, index: number) {
-        sourceConfig.queryConfig.selectedFilters.splice(index, 1);
+    remove(index: number) {
+        this.sourceConfig.queryConfig.selectedFilters.splice(index, 1);
 
         this.widgetConfigService.notify({
             refreshData: true,
@@ -113,8 +112,4 @@ export class FilterSelectionPanelComponent implements 
OnInit {
             });
         }
     }
-
-    compare(available: FieldConfig, selected: FieldConfig) {
-        return available.runtimeName === selected.runtimeName;
-    }
 }
diff --git a/ui/src/app/data-explorer/data-explorer.module.ts 
b/ui/src/app/data-explorer/data-explorer.module.ts
index 32991008ed..c3edd8cf6c 100644
--- a/ui/src/app/data-explorer/data-explorer.module.ts
+++ b/ui/src/app/data-explorer/data-explorer.module.ts
@@ -125,6 +125,11 @@ import { DataExplorerDataViewPreviewComponent } from 
'./components/dashboard/das
 import { DataExplorerDashboardToolbarComponent } from 
'./components/dashboard/dashboard-toolbar/dashboard-toolbar.component';
 import { OrderSelectionPanelComponent } from 
'./components/data-view/data-view-designer-panel/data-settings/order-selection-panel/order-selection-panel.component';
 import { GaugeWidgetConfigComponent } from 
'./components/widgets/gauge/config/gauge-widget-config.component';
+import { FilterSelectionPanelRowComponent } from 
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component';
+import { FilterSelectionPanelRowPropertySelectionComponent } from 
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-property-selection/filter-selection-panel-row-property-selection.component';
+import { FilterSelectionPanelRowOperationSelectionComponent } from 
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-operation-selection/filter-selection-panel-row-operation-selection.component';
+import { FilterSelectionPanelRowValueInputComponent } from 
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input/filter-selection-panel-row-value-input.component';
+import { FilterSelectionPanelRowValueInputAutocompleteComponent } from 
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row-value-input-autocomplete/filter-selection-panel-row-value-input-autocomplete.component';
 
 @NgModule({
     imports: [
@@ -224,6 +229,7 @@ import { GaugeWidgetConfigComponent } from 
'./components/widgets/gauge/config/ga
         FieldSelectionPanelComponent,
         FieldSelectionComponent,
         FilterSelectionPanelComponent,
+        FilterSelectionPanelRowComponent,
         GaugeWidgetConfigComponent,
         GroupConfigurationComponent,
         ImageWidgetComponent,
@@ -268,6 +274,10 @@ import { GaugeWidgetConfigComponent } from 
'./components/widgets/gauge/config/ga
         SpEchartsWidgetAppearanceConfigComponent,
         SpTimeSeriesAppearanceConfigComponent,
         SpDataZoomConfigComponent,
+        FilterSelectionPanelRowPropertySelectionComponent,
+        FilterSelectionPanelRowOperationSelectionComponent,
+        FilterSelectionPanelRowValueInputComponent,
+        FilterSelectionPanelRowValueInputAutocompleteComponent,
     ],
     exports: [],
 })


Reply via email to