From 10d5b0431d87b9b6a75872b2c7c101f54f0f24fe Mon Sep 17 00:00:00 2001 From: Abdi Mo Date: Tue, 7 Apr 2026 17:04:12 +0200 Subject: [PATCH 1/7] added enterToApply config with hint placed in search parameters dialog(condition), text/multiselect filters and search boxes --- cypress/e2e/datasets/datasets-general.cy.js | 44 ++++++++++++++----- cypress/e2e/samples/samples-general.cy.js | 2 +- .../schema/frontend.config.jsonforms.json | 4 +- src/app/app-config.service.ts | 1 + .../datasets-filter.component.html | 1 + .../files-dashboard.component.html | 2 +- .../proposal-side-filter.component.html | 1 + .../table/dynamic-mat-table.component.html | 4 +- .../table/dynamic-mat-table.component.ts | 17 +++++++ .../filters/multiselect-filter.component.html | 7 ++- .../search-parameters-dialog.component.html | 13 +++--- .../search-parameters-dialog.component.ts | 8 ++++ .../shared-filter.component.html | 17 +++++-- .../shared-filter/shared-filter.component.ts | 14 +++++- src/assets/config.json | 1 + src/styles.scss | 23 +++++++++- 16 files changed, 131 insertions(+), 28 deletions(-) diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index 0fa2518924..386cea11d5 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -138,7 +138,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -162,7 +162,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".snackbar-warning") .should("contain", "Field already used") @@ -187,7 +187,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); // expand the condition cy.get(".condition-panel").first().click(); @@ -231,7 +231,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("Temperature Human Name"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -247,7 +247,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("temperature"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -263,7 +263,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("invalid_field_name"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -279,7 +279,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("test_number"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -315,7 +315,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("test_number"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -363,6 +363,26 @@ describe("Datasets general", () => { cy.get('[data-cy="remove-condition-button"]').click(); }); + + it.only("should add a condition when pressing Enter", () => { + cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { + const testConfig = { + ...baseConfig, + enterToApply: true, + }; + cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); + }); + + cy.visit("/datasets"); + cy.wait("@getConfig", { timeout: 20000 }); + cy.finishedLoading(); + + cy.get('[data-cy="scientific-condition-filter-list"]').within(() => { + cy.get('[data-cy="add-condition-button"]').click(); + }); + + cy.get('input[name="lhs"]').type("extra_entry_end_time{enter}"); + }); }); describe("Units options in condition panel units dropdown", () => { @@ -585,7 +605,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); @@ -680,7 +700,7 @@ describe("Datasets general", () => { cy.get('[data-cy="add-condition-button"]').click(); cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").should("have.length", 1); @@ -689,7 +709,7 @@ describe("Datasets general", () => { cy.get('[data-cy="add-condition-button"]').click(); cy.get('input[name="lhs"]').type("test_characteristic"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").should("have.length", 1); @@ -740,7 +760,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("run_number"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); diff --git a/cypress/e2e/samples/samples-general.cy.js b/cypress/e2e/samples/samples-general.cy.js index 1b7656a2dc..37494a04b1 100644 --- a/cypress/e2e/samples/samples-general.cy.js +++ b/cypress/e2e/samples/samples-general.cy.js @@ -19,7 +19,7 @@ describe("Samples general", () => { cy.get('[data-cy="add-condition-button"]').click(); cy.get('input[name="lhs"]').type("test_characteristic"); - cy.get("mat-dialog-container").find('button[type="submit"]').click(); + cy.get("mat-dialog-container").contains("button", "Apply").click(); cy.get(".condition-panel").first().click(); cy.get(".condition-panel") diff --git a/src/app/admin/schema/frontend.config.jsonforms.json b/src/app/admin/schema/frontend.config.jsonforms.json index 72de2ae41d..9a1d46e718 100644 --- a/src/app/admin/schema/frontend.config.jsonforms.json +++ b/src/app/admin/schema/frontend.config.jsonforms.json @@ -56,6 +56,7 @@ "logbookEnabled": { "type": "boolean" }, "loginFormEnabled": { "type": "boolean" }, "metadataPreviewEnabled": { "type": "boolean" }, + "enterToApply": { "type": "boolean" }, "metadataStructure": { "type": "string" }, "multipleDownloadAction": { "type": "string" }, "multipleDownloadEnabled": { "type": "boolean" }, @@ -461,7 +462,8 @@ { "type": "Control", "scope": "#/properties/jsonMetadataEnabled" }, { "type": "Control", "scope": "#/properties/logbookEnabled" }, { "type": "Control", "scope": "#/properties/loginFormEnabled" }, - { "type": "Control", "scope": "#/properties/metadataPreviewEnabled" } + { "type": "Control", "scope": "#/properties/metadataPreviewEnabled" }, + { "type": "Control", "scope": "#/properties/enterToApply" } ] }, { diff --git a/src/app/app-config.service.ts b/src/app/app-config.service.ts index e669d360f7..51884072cc 100644 --- a/src/app/app-config.service.ts +++ b/src/app/app-config.service.ts @@ -170,6 +170,7 @@ export interface AppConfigInterface { defaultTab?: DefaultTab; statusBannerMessage?: string; statusBannerCode?: "INFO" | "WARN"; + enterToApply?: boolean; } function isMainPageConfiguration(obj: any): obj is MainPageConfiguration { diff --git a/src/app/datasets/datasets-filter/datasets-filter.component.html b/src/app/datasets/datasets-filter/datasets-filter.component.html index e919e6791f..f7568c2c35 100644 --- a/src/app/datasets/datasets-filter/datasets-filter.component.html +++ b/src/app/datasets/datasets-filter/datasets-filter.component.html @@ -51,6 +51,7 @@ (numericRangeChange)="numericRangeChange(filter.key, $event)" [filterValue]="activeFilters[filter.key]" [collapsible]="filter.type === 'checkbox'" + (applyEnterKey)="applyFilters()" > diff --git a/src/app/files/files-dashboard/files-dashboard.component.html b/src/app/files/files-dashboard/files-dashboard.component.html index ff827a6658..6738ab4bd0 100644 --- a/src/app/files/files-dashboard/files-dashboard.component.html +++ b/src/app/files/files-dashboard/files-dashboard.component.html @@ -8,7 +8,7 @@ [pagination]="pagination" [rowSelectionMode]="rowSelectionMode" [globalTextSearch]="globalTextSearch" - globalTextSearchPlaceholder="Global text search" + globalTextSearchPlaceholder="Filename, Size..." (paginationChange)="onPaginationChange($event)" (globalTextSearchChange)="onGlobalTextSearchChange($event)" (onRowEvent)="onRowClick($event)" diff --git a/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.html b/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.html index 43ac47fbb5..e4d87fbc43 100644 --- a/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.html +++ b/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.html @@ -26,6 +26,7 @@ (dateRangeChange)="setDateFilter(filter.key, $event)" [filterValue]="activeFilters[filter.key]" [collapsible]="filter.type === 'checkbox'" + (applyEnterKey)="applyFilters()" >
diff --git a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html index eda41dacf9..9e24e539e9 100644 --- a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html +++ b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html @@ -11,9 +11,9 @@ [(ngModel)]="globalTextSearch" type="text" matInput - [placeholder]="globalTextSearchPlaceholder" + [placeholder]="globalSearchPlaceholderWithHint()" (ngModelChange)="this.globalSearchUpdate.next($event)" - (keydown.enter)="onGlobalTextSearchApply()" + (keydown.enter)="onGlobalTextSearchEnter($event)" (keydown.escape)="onGlobalTextSearchClear()" data-cy="text-search" /> diff --git a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts index b940087ccc..eb1ace8a1b 100644 --- a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts +++ b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts @@ -77,6 +77,7 @@ import { } from "../models/table-menu.model"; import { TableDataSource } from "../cores/table-data-source"; import { DatePipe } from "@angular/common"; +import { AppConfigService } from "app-config.service"; export interface IDynamicCell { row: TableRow; @@ -319,6 +320,8 @@ export class DynamicMatTableComponent @Input() emptyIcon = "info"; @Input() sideFilterCollapsed = false; + appConfig = this.appConfigService.getConfig(); + constructor( public dialog: MatDialog, private renderer: Renderer2, @@ -329,6 +332,7 @@ export class DynamicMatTableComponent private overlayPositionBuilder: OverlayPositionBuilder, public readonly config: TableSetting, private datePipe: DatePipe, + public appConfigService: AppConfigService, ) { super(tableService, cdr, config); @@ -874,6 +878,19 @@ export class DynamicMatTableComponent this.globalTextSearchApply.emit(""); } + globalSearchPlaceholderWithHint() { + const base = this.globalTextSearchPlaceholder || ""; + return this.appConfig.enterToApply ? `${base} ↵ Enter to apply` : base; + } + + onGlobalTextSearchEnter(event: Event) { + if (this.appConfig.enterToApply === false) { + return; + } + event.preventDefault(); + this.onGlobalTextSearchApply(); + } + autoHeight() { const minHeight = this.headerHeight + diff --git a/src/app/shared/modules/filters/multiselect-filter.component.html b/src/app/shared/modules/filters/multiselect-filter.component.html index 5b13685bfa..469b12aa1b 100644 --- a/src/app/shared/modules/filters/multiselect-filter.component.html +++ b/src/app/shared/modules/filters/multiselect-filter.component.html @@ -1,5 +1,10 @@ - {{ label }} + {{ label }} + Enter to apply + Add Characteristic
- - - Field + + + Field* + Enter to apply + Add Characteristic
- - + diff --git a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts index 65e692c176..5b0aa26722 100644 --- a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts +++ b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts @@ -147,4 +147,12 @@ export class SearchParametersDialogComponent { getHumanName(key: string): string { return this.humanNameMap[key] || ""; } + + onEnterToApply(event: Event) { + if (this.appConfig.enterToApply === false) { + return; + } + event.preventDefault(); + this.add(); + } } diff --git a/src/app/shared/modules/shared-filter/shared-filter.component.html b/src/app/shared/modules/shared-filter/shared-filter.component.html index 676ddb34af..b797fd80f3 100644 --- a/src/app/shared/modules/shared-filter/shared-filter.component.html +++ b/src/app/shared/modules/shared-filter/shared-filter.component.html @@ -3,12 +3,16 @@ - {{ label }} + {{ label }} + Enter to apply + - {{ label }} + {{ label }} + Enter to apply + @@ -110,6 +120,7 @@ (selectionChange)="onSelectionChange($event)" [facetCounts$]="facetCounts$" [currentFilter$]="currentFilter$" + (keydown.enter)="onApplyEnter($event)" > @@ -121,4 +132,4 @@ > - \ No newline at end of file + diff --git a/src/app/shared/modules/shared-filter/shared-filter.component.ts b/src/app/shared/modules/shared-filter/shared-filter.component.ts index bbdbd24415..19987b357e 100644 --- a/src/app/shared/modules/shared-filter/shared-filter.component.ts +++ b/src/app/shared/modules/shared-filter/shared-filter.component.ts @@ -18,6 +18,7 @@ import { INumericRange } from "../numeric-range/form/model/numeric-range-field.m import { FilterType } from "state-management/state/user.store"; import { toIsoUtc } from "../filters/utils"; import { orderBy } from "lodash-es"; +import { AppConfigService } from "app-config.service"; type FacetItem = { _id: string; label?: string; count: number }; @Component({ @@ -90,7 +91,10 @@ export class SharedFilterComponent implements OnChanges { end?: string; }>(); - constructor() {} + @Output() applyEnterKey = new EventEmitter(); + appConfig = this.appConfigService.getConfig(); + + constructor(public appConfigService: AppConfigService) {} ngOnInit() { // Reset display limit whenever the text search changes this.filterForm.get("textField")!.valueChanges.subscribe(() => { @@ -247,5 +251,13 @@ export class SharedFilterComponent implements OnChanges { } } + onApplyEnter(event?: Event) { + if (this.appConfig.enterToApply === false) { + return; + } + event?.preventDefault(); + this.applyEnterKey.emit(); + } + /** Checkbox filter helpers END*/ } diff --git a/src/assets/config.json b/src/assets/config.json index 963da3b18c..d8e6b56865 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -891,6 +891,7 @@ "authenticatedUser": "DATASETS" }, "checkBoxFilterClickTrigger": false, + "enterToApply": false, "mainMenu": { "nonAuthenticatedUser": { "datasets": true, diff --git a/src/styles.scss b/src/styles.scss index e8cff2f3d9..965ddf74af 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -451,4 +451,25 @@ jsonforms { max-width: 400px; border-radius: 8px; white-space: normal; -} \ No newline at end of file +} + +// Enter hint for input fields + +.enter-hint { + display: none; + align-items: center; + gap: 4px; + padding: 2px 6px; + border-radius: 6px; + background: #f3f4f6; + color: #6b7280; + font-size: 1em; +} + +.enter-hint::before { + content: "↵"; +} + +.mat-mdc-form-field.mat-focused .enter-hint { + display: inline-flex; +} From 530e27d7c176fcacf6d09f52bd8ea76ac2e8f66e Mon Sep 17 00:00:00 2001 From: abdimo101 Date: Tue, 7 Apr 2026 18:57:04 +0200 Subject: [PATCH 2/7] Update src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .../search-parameters-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts index 5b0aa26722..30c758de57 100644 --- a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts +++ b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts @@ -149,7 +149,7 @@ export class SearchParametersDialogComponent { } onEnterToApply(event: Event) { - if (this.appConfig.enterToApply === false) { + if (!this.appConfig.enterToApply) { return; } event.preventDefault(); From 71891a505679b7386dd5932e01d2a7be07dfd306 Mon Sep 17 00:00:00 2001 From: abdimo101 Date: Tue, 7 Apr 2026 18:58:32 +0200 Subject: [PATCH 3/7] Remove '.only' --- cypress/e2e/datasets/datasets-general.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index 386cea11d5..575151b5d8 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -364,7 +364,7 @@ describe("Datasets general", () => { cy.get('[data-cy="remove-condition-button"]').click(); }); - it.only("should add a condition when pressing Enter", () => { + it("should add a condition when pressing Enter", () => { cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { const testConfig = { ...baseConfig, From 0617b0680b77558140586f802010787d383c6345 Mon Sep 17 00:00:00 2001 From: Abdi Mo Date: Wed, 8 Apr 2026 09:43:19 +0200 Subject: [PATCH 4/7] fixed tests --- cypress/e2e/datasets/datasets-general.cy.js | 4 ++++ .../instruments-dashboard.component.spec.ts | 2 ++ .../related-proposals/related-proposals.component.spec.ts | 2 ++ .../table/dynamic-mat-table.component.ts | 2 +- .../modules/shared-filter/shared-filter.component.spec.ts | 4 ++++ .../shared/modules/shared-filter/shared-filter.component.ts | 2 +- 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index aceb304387..8b0a5986b6 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -382,6 +382,10 @@ describe("Datasets general", () => { }); cy.get('input[name="lhs"]').type("extra_entry_end_time{enter}"); + + cy.get(".condition-panel").first().click(); + + cy.get('[data-cy="remove-condition-button"]').click(); }); }); diff --git a/src/app/instruments/instruments-dashboard/instruments-dashboard.component.spec.ts b/src/app/instruments/instruments-dashboard/instruments-dashboard.component.spec.ts index df5c266502..2112475049 100644 --- a/src/app/instruments/instruments-dashboard/instruments-dashboard.component.spec.ts +++ b/src/app/instruments/instruments-dashboard/instruments-dashboard.component.spec.ts @@ -16,6 +16,7 @@ import { import { TablePagination } from "shared/modules/dynamic-material-table/models/table-pagination.model"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { TranslateService } from "@ngx-translate/core"; +import { AppConfigService } from "app-config.service"; describe("InstrumentsDashboardComponent", () => { let component: InstrumentsDashboardComponent; @@ -49,6 +50,7 @@ describe("InstrumentsDashboardComponent", () => { ], }), { provide: TranslateService, useValue: { instant: (k: string) => k } }, + { provide: AppConfigService, useValue: { getConfig: () => ({}) } }, ], }); TestBed.overrideComponent(InstrumentsDashboardComponent, { diff --git a/src/app/proposals/related-proposals/related-proposals.component.spec.ts b/src/app/proposals/related-proposals/related-proposals.component.spec.ts index 16d6730e9d..54728a05f8 100644 --- a/src/app/proposals/related-proposals/related-proposals.component.spec.ts +++ b/src/app/proposals/related-proposals/related-proposals.component.spec.ts @@ -15,6 +15,7 @@ import { DynamicMatTableModule } from "shared/modules/dynamic-material-table/tab import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { TranslateService } from "@ngx-translate/core"; import { SharedScicatFrontendModule } from "shared/shared.module"; +import { AppConfigService } from "app-config.service"; describe("RelatedProposalsComponent", () => { let component: RelatedProposalsComponent; @@ -56,6 +57,7 @@ describe("RelatedProposalsComponent", () => { { provide: Router, useValue: router }, { provide: ActivatedRoute, useClass: MockActivatedRoute }, { provide: TranslateService, useValue: { instant: (k: string) => k } }, + { provide: AppConfigService, useValue: { getConfig: () => ({}) } }, ], }).compileComponents(); diff --git a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts index eb1ace8a1b..4b25cb8b43 100644 --- a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts +++ b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.ts @@ -884,7 +884,7 @@ export class DynamicMatTableComponent } onGlobalTextSearchEnter(event: Event) { - if (this.appConfig.enterToApply === false) { + if (!this.appConfig.enterToApply) { return; } event.preventDefault(); diff --git a/src/app/shared/modules/shared-filter/shared-filter.component.spec.ts b/src/app/shared/modules/shared-filter/shared-filter.component.spec.ts index 742f0966f9..faf71a4d29 100644 --- a/src/app/shared/modules/shared-filter/shared-filter.component.spec.ts +++ b/src/app/shared/modules/shared-filter/shared-filter.component.spec.ts @@ -5,6 +5,7 @@ import { MatDatepickerInputEvent } from "@angular/material/datepicker"; import { DateTime } from "luxon"; import { SharedFilterComponent } from "./shared-filter.component"; import { of } from "rxjs"; +import { AppConfigService } from "app-config.service"; describe("SharedFilterComponent", () => { let component: SharedFilterComponent; @@ -15,6 +16,9 @@ describe("SharedFilterComponent", () => { declarations: [SharedFilterComponent], imports: [ReactiveFormsModule], schemas: [NO_ERRORS_SCHEMA], + providers: [ + { provide: AppConfigService, useValue: { getConfig: () => ({}) } }, + ], }).compileComponents(); fixture = TestBed.createComponent(SharedFilterComponent); diff --git a/src/app/shared/modules/shared-filter/shared-filter.component.ts b/src/app/shared/modules/shared-filter/shared-filter.component.ts index 19987b357e..01be3fa6be 100644 --- a/src/app/shared/modules/shared-filter/shared-filter.component.ts +++ b/src/app/shared/modules/shared-filter/shared-filter.component.ts @@ -252,7 +252,7 @@ export class SharedFilterComponent implements OnChanges { } onApplyEnter(event?: Event) { - if (this.appConfig.enterToApply === false) { + if (!this.appConfig.enterToApply) { return; } event?.preventDefault(); From 79c7eea9402ad4b2ca2abf5040ad0ec38503d7c5 Mon Sep 17 00:00:00 2001 From: Abdi Mo Date: Wed, 8 Apr 2026 14:51:12 +0200 Subject: [PATCH 5/7] unified enterToApply and checkBoxFilterClickTrigger into autoApplyfilters --- CI/e2e/frontend.config.e2e.json | 2 +- cypress/e2e/datasets/datasets-general.cy.js | 2 +- .../schema/frontend.config.jsonforms.json | 9 ++---- src/app/app-config.service.ts | 3 +- .../datasets-filter.component.ts | 2 +- .../proposal-side-filter.component.spec.ts | 6 ++-- .../proposal-side-filter.component.ts | 2 +- .../table/dynamic-mat-table.component.html | 12 ++++++-- .../table/dynamic-mat-table.component.ts | 8 ------ .../filters/multiselect-filter.component.html | 16 +++++++---- .../search-parameters-dialog.component.html | 11 ++++---- .../search-parameters-dialog.component.ts | 8 ------ .../shared-filter.component.html | 28 +++++++++++++------ .../shared-filter/shared-filter.component.ts | 2 +- src/assets/config.json | 3 +- src/styles.scss | 26 +++++++++-------- 16 files changed, 72 insertions(+), 68 deletions(-) diff --git a/CI/e2e/frontend.config.e2e.json b/CI/e2e/frontend.config.e2e.json index 462b82d983..79008598c9 100644 --- a/CI/e2e/frontend.config.e2e.json +++ b/CI/e2e/frontend.config.e2e.json @@ -3,7 +3,7 @@ "nonAuthenticatedUser": "DATASETS", "authenticatedUser": "PROPOSALS" }, - "checkBoxFilterClickTrigger": false, + "autoApplyFilters": false, "accessTokenPrefix": "Bearer ", "addDatasetEnabled": false, "allowConfigOverrides": true, diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index 8b0a5986b6..21546af508 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -368,7 +368,7 @@ describe("Datasets general", () => { cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { const testConfig = { ...baseConfig, - enterToApply: true, + autoApplyFilters: true, }; cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); }); diff --git a/src/app/admin/schema/frontend.config.jsonforms.json b/src/app/admin/schema/frontend.config.jsonforms.json index 5a328fa785..15891fc577 100644 --- a/src/app/admin/schema/frontend.config.jsonforms.json +++ b/src/app/admin/schema/frontend.config.jsonforms.json @@ -18,7 +18,6 @@ }, "skipSciCatLoginPageEnabled": { "type": "boolean" }, "allowConfigOverrides": { "type": "boolean" }, - "checkBoxFilterClickTrigger": { "type": "boolean" }, "dateFormat": { "type": "string" }, "accessTokenPrefix": { "type": "string" }, "addDatasetEnabled": { "type": "boolean" }, @@ -56,7 +55,7 @@ "logbookEnabled": { "type": "boolean" }, "loginFormEnabled": { "type": "boolean" }, "metadataPreviewEnabled": { "type": "boolean" }, - "enterToApply": { "type": "boolean" }, + "autoApplyFilters": { "type": "boolean" }, "metadataStructure": { "type": "string" }, "multipleDownloadAction": { "type": "string" }, "multipleDownloadEnabled": { "type": "boolean" }, @@ -438,10 +437,6 @@ { "type": "Control", "scope": "#/properties/siteSciCatLogo" }, { "type": "Control", "scope": "#/properties/statusBannerMessage" }, { "type": "Control", "scope": "#/properties/statusBannerCode" }, - { - "type": "Control", - "scope": "#/properties/checkBoxFilterClickTrigger" - }, { "type": "Control", "scope": "#/properties/addDatasetEnabled" }, { "type": "Control", @@ -469,7 +464,7 @@ { "type": "Control", "scope": "#/properties/logbookEnabled" }, { "type": "Control", "scope": "#/properties/loginFormEnabled" }, { "type": "Control", "scope": "#/properties/metadataPreviewEnabled" }, - { "type": "Control", "scope": "#/properties/enterToApply" } + { "type": "Control", "scope": "#/properties/autoApplyFilters" } ] }, { diff --git a/src/app/app-config.service.ts b/src/app/app-config.service.ts index 51884072cc..ad2655cee3 100644 --- a/src/app/app-config.service.ts +++ b/src/app/app-config.service.ts @@ -164,13 +164,12 @@ export interface AppConfigInterface { siteHeaderLogoUrl?: string; mainMenu?: MainMenuConfiguration; supportEmail?: string; - checkBoxFilterClickTrigger?: boolean; hideEmptyMetadataTable?: boolean; ingestorComponent?: IngestorComponentConfig; defaultTab?: DefaultTab; statusBannerMessage?: string; statusBannerCode?: "INFO" | "WARN"; - enterToApply?: boolean; + autoApplyFilters?: boolean; } function isMainPageConfiguration(obj: any): obj is MainPageConfiguration { diff --git a/src/app/datasets/datasets-filter/datasets-filter.component.ts b/src/app/datasets/datasets-filter/datasets-filter.component.ts index 334dbfdead..5cb315bc96 100644 --- a/src/app/datasets/datasets-filter/datasets-filter.component.ts +++ b/src/app/datasets/datasets-filter/datasets-filter.component.ts @@ -298,7 +298,7 @@ export class DatasetsFilterComponent implements OnInit, OnDestroy { // This applies to both multiselect type and checkBoxFilter // skip PID text input to avoid triggering on keystrokes // Array check can be removed when we remove text input filter type - if (Array.isArray(value) && this.appConfig.checkBoxFilterClickTrigger) { + if (Array.isArray(value) && this.appConfig.autoApplyFilters) { this.applyFilters(); } } diff --git a/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.spec.ts b/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.spec.ts index bdf35a3e79..8534e8c2d8 100644 --- a/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.spec.ts +++ b/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.spec.ts @@ -34,7 +34,7 @@ describe("ProposalSideFilterComponent", () => { mockRoute = { snapshot: { queryParams: {} } }; mockRouter = { navigate: jasmine.createSpy("navigate") }; const getConfig = () => ({ - checkBoxFilterClickTrigger: false, + autoApplyFilters: false, defaultProposalsListSettings: { filters: [ { @@ -188,8 +188,8 @@ describe("ProposalSideFilterComponent", () => { expect(component.clearFilters).toBeFalse(); })); - it("should call applyFilters on setFilter if checkBoxFilterClickTrigger is true", () => { - component.appConfig.checkBoxFilterClickTrigger = true; + it("should call applyFilters on setFilter if autoApplyFilters is true", () => { + component.appConfig.autoApplyFilters = true; spyOn(component, "applyFilters"); component.setFilter("proposalId", ["test123"]); diff --git a/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.ts b/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.ts index dd38543a03..245c3d9845 100644 --- a/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.ts +++ b/src/app/proposals/proposal-filters/side-bar-filter/proposal-side-filter.component.ts @@ -133,7 +133,7 @@ export class ProposalSideFilterComponent implements OnInit { }), ); } - if (this.appConfig.checkBoxFilterClickTrigger) { + if (this.appConfig.autoApplyFilters) { this.applyFilters(); } } diff --git a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html index 9e24e539e9..33c0945b91 100644 --- a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html +++ b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html @@ -11,12 +11,20 @@ [(ngModel)]="globalTextSearch" type="text" matInput - [placeholder]="globalSearchPlaceholderWithHint()" + [placeholder]="globalTextSearchPlaceholder" (ngModelChange)="this.globalSearchUpdate.next($event)" - (keydown.enter)="onGlobalTextSearchEnter($event)" + (keydown.enter)="onGlobalTextSearchApply()" (keydown.escape)="onGlobalTextSearchClear()" data-cy="text-search" /> + - + diff --git a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts index 30c758de57..65e692c176 100644 --- a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts +++ b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts @@ -147,12 +147,4 @@ export class SearchParametersDialogComponent { getHumanName(key: string): string { return this.humanNameMap[key] || ""; } - - onEnterToApply(event: Event) { - if (!this.appConfig.enterToApply) { - return; - } - event.preventDefault(); - this.add(); - } } diff --git a/src/app/shared/modules/shared-filter/shared-filter.component.html b/src/app/shared/modules/shared-filter/shared-filter.component.html index 416280c29e..e66172c94c 100644 --- a/src/app/shared/modules/shared-filter/shared-filter.component.html +++ b/src/app/shared/modules/shared-filter/shared-filter.component.html @@ -3,10 +3,7 @@ - {{ label }} - Enter to apply - + {{ label }} + @@ -27,10 +33,7 @@ matTooltipPosition="after" [id]="key" > - {{ label }} - Enter to apply - + {{ label }} + diff --git a/src/app/shared/modules/shared-filter/shared-filter.component.ts b/src/app/shared/modules/shared-filter/shared-filter.component.ts index 01be3fa6be..15b2b06374 100644 --- a/src/app/shared/modules/shared-filter/shared-filter.component.ts +++ b/src/app/shared/modules/shared-filter/shared-filter.component.ts @@ -252,7 +252,7 @@ export class SharedFilterComponent implements OnChanges { } onApplyEnter(event?: Event) { - if (!this.appConfig.enterToApply) { + if (!this.appConfig.autoApplyFilters) { return; } event?.preventDefault(); diff --git a/src/assets/config.json b/src/assets/config.json index d8e6b56865..363feb278a 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -890,8 +890,7 @@ "nonAuthenticatedUser": "DATASETS", "authenticatedUser": "DATASETS" }, - "checkBoxFilterClickTrigger": false, - "enterToApply": false, + "autoApplyFilters": false, "mainMenu": { "nonAuthenticatedUser": { "datasets": true, diff --git a/src/styles.scss b/src/styles.scss index 965ddf74af..59d65484af 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -456,20 +456,24 @@ jsonforms { // Enter hint for input fields .enter-hint { - display: none; - align-items: center; - gap: 4px; - padding: 2px 6px; - border-radius: 6px; - background: #f3f4f6; - color: #6b7280; - font-size: 1em; +align-items: center; +gap: 4px; +padding: 2px 6px; +font-size: 1.2em; +line-height: 1; +color: var(--theme-primary-default); } -.enter-hint::before { - content: "↵"; +.enter-hint { +display: none; } .mat-mdc-form-field.mat-focused .enter-hint { - display: inline-flex; +display: inline-flex; } + +.mat-mdc-form-field-text-suffix, +.mat-mdc-form-field-icon-suffix { +display: flex; +align-items: center; +} \ No newline at end of file From 5a80dceb46c6370f3857b9ae56ecead08c322545 Mon Sep 17 00:00:00 2001 From: Abdi Mo Date: Wed, 8 Apr 2026 15:41:30 +0200 Subject: [PATCH 6/7] updated e2e tests --- cypress/e2e/datasets/datasets-general.cy.js | 100 +++++++++++++----- .../table/dynamic-mat-table.component.html | 2 +- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index 21546af508..b8ab1f48e7 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -363,30 +363,6 @@ describe("Datasets general", () => { cy.get('[data-cy="remove-condition-button"]').click(); }); - - it("should add a condition when pressing Enter", () => { - cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { - const testConfig = { - ...baseConfig, - autoApplyFilters: true, - }; - cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); - }); - - cy.visit("/datasets"); - cy.wait("@getConfig", { timeout: 20000 }); - cy.finishedLoading(); - - cy.get('[data-cy="scientific-condition-filter-list"]').within(() => { - cy.get('[data-cy="add-condition-button"]').click(); - }); - - cy.get('input[name="lhs"]').type("extra_entry_end_time{enter}"); - - cy.get(".condition-panel").first().click(); - - cy.get('[data-cy="remove-condition-button"]').click(); - }); }); describe("Units options in condition panel units dropdown", () => { @@ -801,4 +777,80 @@ describe("Datasets general", () => { cy.get('[data-cy="remove-condition-button"]').click(); }); }); + + describe("Auto apply filters", () => { + beforeEach(() => { + cy.clearLocalStorage(); + cy.createDataset({ keywords: ["test auto apply"] }); + cy.createDataset({ keywords: ["another keyword"] }); + }); + + it("should apply checkbox filters when user clicks", () => { + cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { + const testConfig = { + ...baseConfig, + autoApplyFilters: true, + defaultDatasetsListSettings: { + ...baseConfig.defaultDatasetsListSettings, + filters: [ + { + key: "keywords", + label: "Keyword", + type: "checkbox", + description: "Filter by keywords in the dataset", + enabled: true, + }, + ], + }, + }; + + cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); + }); + + cy.visit("/datasets"); + cy.wait("@getConfig", { timeout: 20000 }); + cy.finishedLoading(); + + cy.get(".checkbox-list mat-checkbox") + .contains(/test auto apply/i) + .click({ force: true }); + }); + + it("should apply typed filters when user presses Enter", () => { + cy.readFile("CI/e2e/frontend.config.e2e.json").then((baseConfig) => { + const testConfig = { + ...baseConfig, + autoApplyFilters: true, + defaultDatasetsListSettings: { + ...baseConfig.defaultDatasetsListSettings, + filters: [ + { + key: "keywords", + label: "Keyword", + type: "multiSelect", + description: "Filter by keywords in the dataset", + enabled: true, + }, + ], + }, + }; + + cy.intercept("GET", "**/admin/config", testConfig).as("getConfig"); + }); + + cy.visit("/datasets"); + cy.wait("@getConfig", { timeout: 20000 }); + cy.finishedLoading(); + + cy.get("mat-form-field#keywords input.item-input") + .click() + .type("test auto apply"); + cy.get("mat-option") + .contains(/test auto apply/i) + .click(); + cy.get("mat-form-field#keywords input.item-input") + .click() + .type("{enter}"); + }); + }); }); diff --git a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html index 33c0945b91..187d805a01 100644 --- a/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html +++ b/src/app/shared/modules/dynamic-material-table/table/dynamic-mat-table.component.html @@ -20,7 +20,7 @@ Date: Wed, 8 Apr 2026 15:45:20 +0200 Subject: [PATCH 7/7] updated e2e tests --- cypress/e2e/datasets/datasets-general.cy.js | 24 ++++++++++----------- cypress/e2e/samples/samples-general.cy.js | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cypress/e2e/datasets/datasets-general.cy.js b/cypress/e2e/datasets/datasets-general.cy.js index b8ab1f48e7..31c4b66e56 100644 --- a/cypress/e2e/datasets/datasets-general.cy.js +++ b/cypress/e2e/datasets/datasets-general.cy.js @@ -138,7 +138,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -162,7 +162,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".snackbar-warning") .should("contain", "Field already used") @@ -187,7 +187,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); // expand the condition cy.get(".condition-panel").first().click(); @@ -231,7 +231,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("Temperature Human Name"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -247,7 +247,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("temperature"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -263,7 +263,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("invalid_field_name"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -279,7 +279,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("test_number"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -315,7 +315,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("test_number"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -585,7 +585,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); @@ -680,7 +680,7 @@ describe("Datasets general", () => { cy.get('[data-cy="add-condition-button"]').click(); cy.get('input[name="lhs"]').type("extra_entry_end_time"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").should("have.length", 1); @@ -689,7 +689,7 @@ describe("Datasets general", () => { cy.get('[data-cy="add-condition-button"]').click(); cy.get('input[name="lhs"]').type("test_characteristic"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").should("have.length", 1); @@ -740,7 +740,7 @@ describe("Datasets general", () => { cy.get('input[name="lhs"]').type("run_number"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); diff --git a/cypress/e2e/samples/samples-general.cy.js b/cypress/e2e/samples/samples-general.cy.js index 37494a04b1..1b7656a2dc 100644 --- a/cypress/e2e/samples/samples-general.cy.js +++ b/cypress/e2e/samples/samples-general.cy.js @@ -19,7 +19,7 @@ describe("Samples general", () => { cy.get('[data-cy="add-condition-button"]').click(); cy.get('input[name="lhs"]').type("test_characteristic"); - cy.get("mat-dialog-container").contains("button", "Apply").click(); + cy.get("mat-dialog-container").find('button[type="submit"]').click(); cy.get(".condition-panel").first().click(); cy.get(".condition-panel")