diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index 406829bd6..94bba61b6 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -5,9 +5,9 @@ describe("Datasets general", () => { cy.login(Cypress.env("username"), Cypress.env("password")); }); - after(() => { - cy.removeDatasets(); - }); + after(() => { + cy.removeDatasets(); + }); describe("Show dataset table after logout and login", () => { it("should be able to see datasets after visiting details page logout and login again", () => { @@ -112,16 +112,18 @@ describe("Datasets general", () => { cy.visit("/datasets"); - cy.get('[data-cy="more-filters-button"]').click(); - - cy.get('[data-cy="add-scientific-condition-button"]').click(); + cy.get('[data-cy="scientific-condition-filter-list"]').within(() => { + cy.get('button').contains('Add Conditions').click(); + }); cy.get('input[name="lhs"]').type("test"); cy.get('input[name="rhs"]').type("1"); cy.get('button[type="submit"]').click(); - cy.get('[data-cy="add-scientific-condition-button"]').click(); + cy.get('[data-cy="scientific-condition-filter-list"]').within(() => { + cy.get('button').contains('Add Conditions').click(); + }); cy.get('input[name="lhs"]').type("test"); cy.get('input[name="rhs"]').type("1"); @@ -133,12 +135,13 @@ describe("Datasets general", () => { .contains("Close") .click(); - cy.get('[data-cy="scientific-condition-filter-list"').should( - "have.length", - 1, - ); + cy.get('[data-cy="scientific-condition-filter-list"]') + .find('.condition-panel') + .should("have.length", 1); - cy.get('[data-cy="add-scientific-condition-button"]').click(); + cy.get('[data-cy="scientific-condition-filter-list"]').within(() => { + cy.get('button').contains('Add Conditions').click(); + }); cy.get('input[name="lhs"]').type("test"); cy.get('input[name="rhs"]').type("2"); @@ -146,8 +149,160 @@ describe("Datasets general", () => { cy.get('button[type="submit"]').click(); cy.get('[data-cy="scientific-condition-filter-list"]') - .find("input") + .find('.condition-panel') .should("have.length", 2); }); + + it("should be able to manage conditions using expansion panels", () => { + cy.visit("/datasets"); + + cy.get(".user-button").click(); + + cy.get("[data-cy=logout-button]").click(); + + cy.finishedLoading(); + + cy.visit("/datasets"); + + cy.get('[data-cy="scientific-condition-filter-list"]').within(() => { + cy.get('button').contains('Add Conditions').click(); + }); + + cy.get('input[name="lhs"]').type("test 3"); + cy.get('input[name="rhs"]').type("3"); + + cy.get('button[type="submit"]').click(); + + cy.get('.condition-panel').first().click(); + + cy.get('.condition-details').should('be.visible'); + + cy.get('.condition-details').within(() => { + cy.get('mat-select').should('exist'); + cy.get('input[matInput]').should('exist'); + cy.get('mat-slide-toggle').should('exist'); + cy.get('button').contains('Remove').should('exist'); + }); + + cy.get('.condition-details').within(() => { + cy.get('mat-slide-toggle').click(); + }); + + cy.get('.condition-panel').should('have.class', 'disabled'); + + cy.get('.condition-details').within(() => { + cy.get('button').contains('Remove').click(); + }); + + cy.get('[data-cy="scientific-condition-filter-list"]') + .find('.condition-panel') + .should("have.length", 0); + }); + + }); + + describe("Pre-configured filters test", () => { + beforeEach(() => { + cy.clearCookies(); + cy.clearLocalStorage(); + cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { + const testConfig = { + ...baseConfig, + defaultDatasetsListSettings: { + ...baseConfig.defaultDatasetsListSettings, + filters: [ + { "TypeFilter": true }, + { "TextFilter": true } + ] + } + }; + + cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); + cy.visit("/datasets"); + cy.wait("@getConfig", { timeout: 10000 }); + cy.finishedLoading(); + }); + }); + + it("should automatically apply pre-configured filters from config", () => { + cy.contains("Type").should("exist"); + + cy.get('[data-cy="text-search"]').should("exist"); + + cy.contains("Location").should("not.exist"); + cy.contains("Keyword").should("not.exist"); + }); + }); + + describe("Pre-configured conditions test", () => { + beforeEach(() => { + cy.login(Cypress.env("username"), Cypress.env("password")); + cy.createDataset( + "raw", + testData.rawDataset.datasetName, + undefined, + "small", + { + scientificMetadata: { + extra_entry_end_time: { value: "2", unit: "" }, + }, + isPublished: true, + }, + ).then(() => { + cy.clearCookies(); + cy.clearLocalStorage(); + + cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { + const testConfig = { + ...baseConfig, + defaultDatasetsListSettings: { + ...baseConfig.defaultDatasetsListSettings, + conditions: [ + { + condition: { + lhs: "extra_entry_end_time", + relation: "GREATER_THAN", + rhs: 1, + unit: "", + }, + enabled: true, + }, + ], + }, + }; + + cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); + cy.visit("/datasets"); + cy.wait("@getConfig"); + cy.finishedLoading(); + }); + }); + }); + + it("should check if pre-configured conditions are applied", () => { + cy.get('[data-cy="scientific-condition-filter-list"] .condition-panel') + .should("contain.text", "extra_entry_end_time") + .and("contain.text", ">") + .and("contain.text", "1"); + + cy.get(".dataset-table mat-table").should("exist"); + cy.get(".dataset-table mat-row").first().click(); + cy.get(".metadataTable", { timeout: 10000 }).scrollIntoView(); + + cy.get(".metadataTable mat-row").within(() => { + cy.get(".mat-column-human_name label") + .invoke("text") + .then((fieldName) => { + if (fieldName && fieldName.trim() === "Extra Entry End Time") { + cy.get(".mat-column-value label") + .invoke("text") + .then((valueText) => { + const value = parseFloat(valueText.trim()); + expect(value).to.be.greaterThan(1); + }); + } + }); + }); + }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 3c4da2539..b471dac93 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -113,13 +113,14 @@ Cypress.Commands.add( datasetName = testData.rawDataset.datasetName, proposalId = "20170266", dataFileSize = "small", + overrides = {} ) => { cy.getCookie("user").then((userCookie) => { const user = JSON.parse(decodeURIComponent(userCookie.value)); cy.getToken().then((token) => { if (type === "raw") { - const dataset = { ...testData.rawDataset, datasetName, proposalId }; + const dataset = { ...testData.rawDataset, datasetName, proposalId, ...overrides, }; cy.log("Raw Dataset 1: " + JSON.stringify(dataset, null, 2)); cy.log("User: " + JSON.stringify(user, null, 2)); diff --git a/src/app/datasets/datasets-filter/datasets-filter.component.html b/src/app/datasets/datasets-filter/datasets-filter.component.html index 7886cfd33..196cc7a45 100644 --- a/src/app/datasets/datasets-filter/datasets-filter.component.html +++ b/src/app/datasets/datasets-filter/datasets-filter.component.html @@ -1,6 +1,15 @@ - Filters and Conditions + Filters +
+ +
@@ -16,27 +25,125 @@ -
- - - -
- -
- + + + +
+
+ {{ conditionConfig.condition.lhs }} +
+
+ {{ getConditionDisplayText(conditionConfig.condition) }} +
+
+
+ + {{ getConditionDisplayText(conditionConfig.condition) }} + +
+ +
+ + Operator + + is greater than + is less than + is equal to (numeric) + is equal to (string) + + + + + Value + + + + + + Unit + + + + {{ unit }} + + + + + +
+ + {{ conditionConfig.enabled ? "Enabled" : "Disabled" }} + + + +
+
+
+ + +
+ +
diff --git a/src/app/datasets/datasets-filter/datasets-filter.component.scss b/src/app/datasets/datasets-filter/datasets-filter.component.scss index 92dbd784e..7039524d9 100644 --- a/src/app/datasets/datasets-filter/datasets-filter.component.scss +++ b/src/app/datasets/datasets-filter/datasets-filter.component.scss @@ -67,6 +67,95 @@ mat-card { } .section-container:first-child { + padding-top: 0.1rem; margin-top: unset; + display: flex; + align-items: center; + justify-content: space-between; + + .settings-button { + display: flex; + align-items: center; + } + } +} + +.condition-title-section { + display: flex; + flex-direction: column; + + .condition-field-name { + font-weight: 500; + margin-bottom: 0.25rem; + } + + .condition-description { + font-size: 0.9em; + color: rgba(0, 0, 0, 0.6); + } +} + +.mat-accordion .mat-expansion-panel { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.condition-panel { + &.disabled { + background-color: #f5f5f5; + opacity: 0.7; + } + .mat-expansion-panel-header { + height: auto !important; + padding: 0.5rem !important; + + .mat-expansion-panel-header-title { + white-space: normal !important; + overflow: visible !important; + } + .mat-expansion-panel-header-description { + display: none !important; + } + } + + .condition-details { + padding: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + + .condition-actions-section { + display: flex; + align-items: center; + justify-content: center; + padding-top: 0.5rem; + + .condition-toggle { + margin: 0; + } + + .condition-remove { + display: flex; + align-items: center; + margin-left: 1rem; + color: #d32f2f; + font-size: 1rem; + line-height: 1; + + mat-icon { + margin-right: 4px; + } + + &:hover { + background-color: #ffebee; + color: #b71c1c; + } + } + } + + .condition-fields { + margin-left: -20px; + width: calc(100% + 40px); + } } } diff --git a/src/app/datasets/datasets-filter/datasets-filter.component.spec.ts b/src/app/datasets/datasets-filter/datasets-filter.component.spec.ts index 8aec69bdf..6c68c528c 100644 --- a/src/app/datasets/datasets-filter/datasets-filter.component.spec.ts +++ b/src/app/datasets/datasets-filter/datasets-filter.component.spec.ts @@ -201,7 +201,7 @@ describe("DatasetsFilterComponent", () => { component.reset(); - expect(dispatchSpy).toHaveBeenCalledTimes(4); + expect(dispatchSpy).toHaveBeenCalledTimes(5); expect(dispatchSpy).toHaveBeenCalledWith(clearFacetsAction()); expect(dispatchSpy).toHaveBeenCalledWith( deselectAllCustomColumnsAction(), @@ -222,12 +222,13 @@ describe("DatasetsFilterComponent", () => { expect(component.dialog.open).toHaveBeenCalledWith( DatasetsFilterSettingsComponent, { - width: "60vw", + width: "400px", data: { filterConfigs: filterConfigs, conditionConfigs: [], labelMaps: labelMaps, }, + restoreFocus: false, }, ); }); diff --git a/src/app/datasets/datasets-filter/datasets-filter.component.ts b/src/app/datasets/datasets-filter/datasets-filter.component.ts index fde7399fa..502cfc8a6 100644 --- a/src/app/datasets/datasets-filter/datasets-filter.component.ts +++ b/src/app/datasets/datasets-filter/datasets-filter.component.ts @@ -20,6 +20,7 @@ import { } from "state-management/actions/datasets.actions"; import { deselectAllCustomColumnsAction, + updateConditionsConfigs, updateUserSettingsAction, } from "state-management/actions/user.actions"; import { AppConfigService } from "app-config.service"; @@ -43,6 +44,20 @@ import { Filters, FilterConfig } from "shared/modules/filters/filters.module"; import { FilterComponentInterface } from "shared/modules/filters/interface/filter-component.interface"; import { Subscription } from "rxjs"; import { take } from "rxjs/operators"; +import { SearchParametersDialogComponent } from "../../shared/modules/search-parameters-dialog/search-parameters-dialog.component"; +import { selectMetadataKeys } from "state-management/selectors/datasets.selectors"; +import { ConditionConfig } from "shared/modules/filters/filters.module"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { + addScientificConditionAction, + removeScientificConditionAction, +} from "state-management/actions/datasets.actions"; +import { + selectColumnAction, + deselectColumnAction, +} from "state-management/actions/user.actions"; +import { UnitsService } from "shared/services/units.service"; +import { ScientificCondition } from "state-management/models"; const COMPONENT_MAP: { [K in Filters]: Type } = { PidFilter: PidFilterComponent, @@ -81,16 +96,21 @@ export class DatasetsFilterComponent implements OnInit, OnDestroy { labelMaps: { [key: string]: string } = {}; + metadataKeys$ = this.store.select(selectMetadataKeys); + constructor( public appConfigService: AppConfigService, public dialog: MatDialog, private store: Store, private asyncPipe: AsyncPipe, private viewContainerRef: ViewContainerRef, + private snackBar: MatSnackBar, + private unitsService: UnitsService, ) {} ngOnInit() { this.getAllComponentLabels(); + this.applyEnabledConditions(); } getAllComponentLabels() { @@ -107,11 +127,31 @@ export class DatasetsFilterComponent implements OnInit, OnDestroy { }); } + applyEnabledConditions() { + this.conditionConfigs$.pipe(take(1)).subscribe((conditionConfigs) => { + (conditionConfigs || []).forEach((config) => { + if (config.enabled && config.condition.lhs && config.condition.rhs) { + this.store.dispatch( + addScientificConditionAction({ + condition: config.condition, + }), + ); + } + }); + }); + } + reset() { this.clearSearchBar = true; this.store.dispatch(clearFacetsAction()); this.store.dispatch(deselectAllCustomColumnsAction()); + this.store.dispatch( + updateConditionsConfigs({ + conditionConfigs: [], + }), + ); + this.applyFilters(); // we need to treat JS event loop here, otherwise this.clearSearchBar is false for the components setTimeout(() => { @@ -135,12 +175,13 @@ export class DatasetsFilterComponent implements OnInit, OnDestroy { const initialConditionConfigsCopy = cloneDeep(initialConditionConfigs); const dialogRef = this.dialog.open(DatasetsFilterSettingsComponent, { - width: "60vw", + width: "400px", data: { filterConfigs: this.asyncPipe.transform(this.filterConfigs$), conditionConfigs: this.asyncPipe.transform(this.conditionConfigs$), labelMaps: this.labelMaps, }, + restoreFocus: false, }); dialogRef.afterClosed().subscribe((result) => { @@ -179,6 +220,229 @@ export class DatasetsFilterComponent implements OnInit, OnDestroy { this.store.dispatch(fetchFacetCountsAction()); } + trackByCondition(index: number, conditionConfig: ConditionConfig): string { + const condition = conditionConfig.condition; + return `${condition.lhs}-${index}`; + } + + getConditionDisplayText(condition: ScientificCondition): string { + if (!condition.lhs || !condition.rhs) return "Configure condition..."; + + let relationSymbol = ""; + switch (condition.relation) { + case "EQUAL_TO_NUMERIC": + case "EQUAL_TO_STRING": + relationSymbol = "="; + break; + case "LESS_THAN": + relationSymbol = "<"; + break; + case "GREATER_THAN": + relationSymbol = ">"; + break; + default: + relationSymbol = ""; + } + + const rhsValue = + condition.relation === "EQUAL_TO_STRING" + ? `"${condition.rhs}"` + : condition.rhs; + + const unit = condition.unit ? ` ${condition.unit}` : ""; + return `${relationSymbol} ${rhsValue}${unit}`; + } + + toggleConditionEnabled(index: number, enabled: boolean) { + const currentConditions = + this.asyncPipe.transform(this.conditionConfigs$) || []; + const updatedConditions = [...currentConditions]; + updatedConditions[index] = { ...updatedConditions[index], enabled }; + const condition = updatedConditions[index].condition; + + if (enabled && condition.lhs && condition.rhs) { + this.store.dispatch(addScientificConditionAction({ condition })); + this.store.dispatch( + selectColumnAction({ name: condition.lhs, columnType: "custom" }), + ); + } else { + this.store.dispatch(removeScientificConditionAction({ condition })); + this.store.dispatch( + deselectColumnAction({ name: condition.lhs, columnType: "custom" }), + ); + } + + this.store.dispatch( + updateUserSettingsAction({ property: { conditions: updatedConditions } }), + ); + + this.updateConditionInStore(updatedConditions); + } + + addCondition() { + this.dialog + .open(SearchParametersDialogComponent, { + data: { + parameterKeys: this.asyncPipe.transform(this.metadataKeys$), + }, + restoreFocus: false, + }) + .afterClosed() + .subscribe((res) => { + if (res) { + const { data } = res; + + this.conditionConfigs$ + .pipe(take(1)) + .subscribe((currentConditions) => { + const existingConditionIndex = currentConditions.findIndex( + (config) => isEqual(config.condition, data), + ); + if (existingConditionIndex !== -1) { + this.snackBar.open("Condition already exists", "Close", { + duration: 2000, + panelClass: ["snackbar-warning"], + }); + return; + } + + const newCondition: ConditionConfig = { + condition: data, + enabled: true, + }; + + const updatedConditions = [ + ...(currentConditions || []), + newCondition, + ]; + + this.store.dispatch( + updateConditionsConfigs({ + conditionConfigs: updatedConditions, + }), + ); + + this.store.dispatch( + updateUserSettingsAction({ + property: { conditions: updatedConditions }, + }), + ); + + this.store.dispatch( + addScientificConditionAction({ condition: data }), + ); + this.store.dispatch( + selectColumnAction({ + name: data.lhs, + columnType: "custom", + }), + ); + + this.snackBar.open("Condition added successfully", "Close", { + duration: 2000, + panelClass: ["snackbar-success"], + }); + }); + } + }); + } + + getUnits(parameterKey: string): string[] { + return this.unitsService.getUnits(parameterKey); + } + + updateCondition(index: number, updates: Partial) { + const currentConditions = + this.asyncPipe.transform(this.conditionConfigs$) || []; + const updatedConditions = [...currentConditions]; + const condition = updatedConditions[index]; + const oldCondition = condition.condition; + + // Removes the old condition if enabled + if (condition.enabled) { + this.store.dispatch( + removeScientificConditionAction({ condition: oldCondition }), + ); + this.store.dispatch( + deselectColumnAction({ name: oldCondition.lhs, columnType: "custom" }), + ); + } + + // Updates the condition + updatedConditions[index] = { + ...condition, + condition: { ...oldCondition, ...updates }, + }; + + // Adds the updated condition if enabled + if (condition.enabled) { + this.store.dispatch( + addScientificConditionAction({ + condition: updatedConditions[index].condition, + }), + ); + this.store.dispatch( + selectColumnAction({ + name: updatedConditions[index].condition.lhs, + columnType: "custom", + }), + ); + } + + this.updateConditionInStore(updatedConditions); + } + + updateConditionOperator(index: number, newOperator: string) { + this.updateCondition(index, { + relation: newOperator, + unit: newOperator === "EQUAL_TO_STRING" ? "" : undefined, + }); + } + + updateConditionValue(index: number, event: Event) { + const newValue = (event.target as HTMLInputElement).value; + this.updateCondition(index, { rhs: newValue }); + } + + updateConditionUnit(index: number, event: Event) { + const newUnit = (event.target as HTMLInputElement).value; + this.updateCondition(index, { unit: newUnit || undefined }); + } + + updateConditionInStore(updatedConditions: ConditionConfig[]) { + this.store.dispatch( + updateConditionsConfigs({ + conditionConfigs: updatedConditions, + }), + ); + } + + removeCondition(condition: ConditionConfig, index: number) { + const currentConditions = + this.asyncPipe.transform(this.conditionConfigs$) || []; + const updatedConditions = [...currentConditions]; + + // Removes the condition from the array + updatedConditions.splice(index, 1); + + if (condition.enabled) { + this.store.dispatch( + removeScientificConditionAction({ condition: condition.condition }), + ); + this.store.dispatch( + deselectColumnAction({ + name: condition.condition.lhs, + columnType: "custom", + }), + ); + } + + this.updateConditionInStore(updatedConditions); + this.store.dispatch( + updateUserSettingsAction({ property: { conditions: updatedConditions } }), + ); + } + renderComponent(filterObj: FilterConfig): any { const key = Object.keys(filterObj)[0]; const isEnabled = filterObj[key]; diff --git a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.html b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.html index c8634ea14..5014a41d1 100644 --- a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.html +++ b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.html @@ -41,103 +41,6 @@

Configure Filters

- -
-
- Conditions -
- - - - edit - delete - - - - - - {{ condition.condition.lhs }} - - - -  =  - - -  =  - - -  <  - - -  >  - - - - - - "{{ - condition.condition.rhs - }}" - - - {{ - condition.condition.rhs - }} - - - - {{ condition.condition.unit | prettyUnit }} - - - - -
- - -
- -
diff --git a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.scss b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.scss index 24081b65f..f1e5d360c 100644 --- a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.scss +++ b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.scss @@ -9,6 +9,7 @@ mat-dialog-title { display: flex; gap: 20px; background-color: #f0f0f0; /* Light grey background for contrast */ + max-width: 400px; } .filters-container { diff --git a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.spec.ts b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.spec.ts index 4fe2a2321..f09904379 100644 --- a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.spec.ts +++ b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.spec.ts @@ -10,10 +10,7 @@ import { MockMatDialogRef, MockStore } from "shared/MockStubs"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { removeScientificConditionAction } from "state-management/actions/datasets.actions"; import { of } from "rxjs"; -import { deselectColumnAction } from "state-management/actions/user.actions"; -import { ScientificCondition } from "state-management/models"; import { SharedScicatFrontendModule } from "shared/shared.module"; import { MatAutocompleteModule } from "@angular/material/autocomplete"; import { @@ -35,7 +32,6 @@ import { MatButtonModule } from "@angular/material/button"; import { MatIconModule } from "@angular/material/icon"; import { AppConfigService } from "app-config.service"; import { DatasetsFilterSettingsComponent } from "./datasets-filter-settings.component"; -import { ConditionConfig } from "../../../shared/modules/filters/filters.module"; import { MatSnackBar, MatSnackBarModule } from "@angular/material/snack-bar"; export class MockMatDialog { @@ -50,13 +46,6 @@ const getConfig = () => ({ scienceSearchEnabled: false, }); -const condition: ScientificCondition = { - lhs: "test", - relation: "EQUAL_TO_NUMERIC", - rhs: 5, - unit: "s", -}; - describe("DatasetsFilterSettingsComponent", () => { let component: DatasetsFilterSettingsComponent; let fixture: ComponentFixture; @@ -103,12 +92,7 @@ describe("DatasetsFilterSettingsComponent", () => { { provide: MAT_DIALOG_DATA, useValue: { - conditionConfigs: [ - { - condition, - enabled: true, - }, - ], + conditionConfigs: [], }, }, ], @@ -134,50 +118,4 @@ describe("DatasetsFilterSettingsComponent", () => { it("should be created", () => { expect(component).toBeTruthy(); }); - - describe("#showDatasetsFilterSettingsDialog()", () => { - it("should open DatasetsFilterSettingsComponent", () => { - spyOn(component.dialog, "open").and.callThrough(); - dispatchSpy = spyOn(store, "dispatch"); - - // Spy or stub other side effects in addCondition as needed - spyOn(component, "toggleCondition").and.callFake( - (ignored: ConditionConfig) => ignored, - ); - - component.metadataKeys$ = of(["test", "keys"]); - component.addCondition(); - - expect(component.dialog.open).toHaveBeenCalledTimes(1); - expect(component.dialog.open).toHaveBeenCalledWith( - SearchParametersDialogComponent, - { - data: { - parameterKeys: ["test", "keys"], - }, - }, - ); - }); - }); - - describe("#removeCondition()", () => { - it("should dispatch a removeScientificConditionAction and a deselectColumnAction", () => { - dispatchSpy = spyOn(store, "dispatch"); - - const conditionConfig: ConditionConfig = { - condition, - enabled: true, - }; - - component.removeCondition(conditionConfig, 0); - - expect(dispatchSpy).toHaveBeenCalledTimes(2); - expect(dispatchSpy).toHaveBeenCalledWith( - removeScientificConditionAction({ condition }), - ); - expect(dispatchSpy).toHaveBeenCalledWith( - deselectColumnAction({ name: condition.lhs, columnType: "custom" }), - ); - }); - }); }); diff --git a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts index c9be4d062..a33794e00 100644 --- a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts +++ b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts @@ -4,26 +4,11 @@ import { MatDialog, MatDialogRef, } from "@angular/material/dialog"; -import { SearchParametersDialogComponent } from "../../../shared/modules/search-parameters-dialog/search-parameters-dialog.component"; import { AppConfigService } from "app-config.service"; -import { AsyncPipe } from "@angular/common"; -import { - addScientificConditionAction, - removeScientificConditionAction, -} from "../../../state-management/actions/datasets.actions"; -import { - deselectColumnAction, - selectColumnAction, -} from "../../../state-management/actions/user.actions"; import { Store } from "@ngrx/store"; import { selectMetadataKeys } from "../../../state-management/selectors/datasets.selectors"; import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop"; -import { - ConditionConfig, - FilterConfig, -} from "../../../shared/modules/filters/filters.module"; -import { isEqual } from "lodash-es"; -import { MatSnackBar } from "@angular/material/snack-bar"; +import { FilterConfig } from "../../../shared/modules/filters/filters.module"; @Component({ selector: "app-type-datasets-filter-settings", @@ -41,121 +26,11 @@ export class DatasetsFilterSettingsComponent { constructor( public dialogRef: MatDialogRef, public dialog: MatDialog, - private snackBar: MatSnackBar, private store: Store, - private asyncPipe: AsyncPipe, private appConfigService: AppConfigService, @Inject(MAT_DIALOG_DATA) public data: any, ) {} - addCondition() { - this.dialog - .open(SearchParametersDialogComponent, { - data: { - parameterKeys: this.asyncPipe.transform(this.metadataKeys$), - }, - }) - .afterClosed() - .subscribe((res) => { - if (res) { - const { data } = res; - - // If the condition already exists, do nothing - const existingConditionIndex = this.data.conditionConfigs.findIndex( - (config) => isEqual(config.condition, data), - ); - if (existingConditionIndex !== -1) { - this.snackBar.open("Condition already exists", "Close", { - duration: 2000, - panelClass: ["snackbar-warning"], - }); - return; - } - const condition = this.toggleCondition({ - condition: data, - enabled: false, - }); - this.data.conditionConfigs.push(condition); - } - }); - } - - editCondition(condition: ConditionConfig, i: number) { - this.dialog - .open(SearchParametersDialogComponent, { - data: { - parameterKeys: this.asyncPipe.transform(this.metadataKeys$), - condition: condition.condition, - }, - }) - .afterClosed() - .subscribe((res) => { - if (res) { - const { data } = res; - - // If the condition is unchanged, do nothing - if (isEqual(condition.condition, data)) { - return; - } - - this.store.dispatch( - removeScientificConditionAction({ - condition: condition.condition, - }), - ); - this.store.dispatch( - deselectColumnAction({ - name: condition.condition.lhs, - columnType: "custom", - }), - ); - - this.data.conditionConfigs[i] = { - ...condition, - condition: data, - }; - this.store.dispatch( - addScientificConditionAction({ condition: data }), - ); - this.store.dispatch( - selectColumnAction({ name: data.lhs, columnType: "custom" }), - ); - } - }); - } - - removeCondition(condition: ConditionConfig, index: number) { - this.data.conditionConfigs.splice(index, 1); - if (condition.enabled) { - this.store.dispatch( - removeScientificConditionAction({ condition: condition.condition }), - ); - this.store.dispatch( - deselectColumnAction({ - name: condition.condition.lhs, - columnType: "custom", - }), - ); - } - } - - toggleCondition(condition: ConditionConfig) { - condition.enabled = !condition.enabled; - const data = condition.condition; - if (condition.enabled) { - this.store.dispatch(addScientificConditionAction({ condition: data })); - this.store.dispatch( - selectColumnAction({ name: data.lhs, columnType: "custom" }), - ); - } else { - this.store.dispatch(removeScientificConditionAction({ condition: data })); - this.store.dispatch( - deselectColumnAction({ name: data.lhs, columnType: "custom" }), - ); - } - return condition; - } - toggleVisibility(filter: FilterConfig): void { const key = this.getFilterKey(filter); filter[key] = !filter[key]; diff --git a/src/app/datasets/datasets.module.ts b/src/app/datasets/datasets.module.ts index b350c0fcb..8a47b13d1 100644 --- a/src/app/datasets/datasets.module.ts +++ b/src/app/datasets/datasets.module.ts @@ -86,6 +86,7 @@ import { DatasetDetailDynamicComponent } from "./dataset-detail/dataset-detail-d import { DatasetDetailWrapperComponent } from "./dataset-detail/dataset-detail-wrapper.component"; import { JsonHeadPipe } from "shared/pipes/json-head.pipe"; import { ThumbnailPipe } from "shared/pipes/thumbnail.pipe"; +import { MatExpansionModule } from "@angular/material/expansion"; @NgModule({ imports: [ CommonModule, @@ -147,6 +148,7 @@ import { ThumbnailPipe } from "shared/pipes/thumbnail.pipe"; CdkDrag, CdkDragHandle, FiltersModule, + MatExpansionModule, ], declarations: [ BatchViewComponent, diff --git a/src/app/shared/modules/filters/filters.module.ts b/src/app/shared/modules/filters/filters.module.ts index 2086077c7..3d635fc1f 100644 --- a/src/app/shared/modules/filters/filters.module.ts +++ b/src/app/shared/modules/filters/filters.module.ts @@ -18,6 +18,10 @@ import { MatChipsModule } from "@angular/material/chips"; import { MatIconModule } from "@angular/material/icon"; import { MatAutocompleteModule } from "@angular/material/autocomplete"; import { MatTooltipModule } from "@angular/material/tooltip"; +import { MatSelectModule } from "@angular/material/select"; +import { MatExpansionModule } from "@angular/material/expansion"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatButtonModule } from "@angular/material/button"; @NgModule({ declarations: [ @@ -42,6 +46,10 @@ import { MatTooltipModule } from "@angular/material/tooltip"; MatIconModule, MatAutocompleteModule, NgForOf, + MatSelectModule, + MatExpansionModule, + MatFormFieldModule, + MatButtonModule, ], exports: [ ClearableInputComponent,