Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions app/components/filter/filter_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@
# See COPYRIGHT and LICENSE files for more details.
# ++
module Filter
# rubocop:disable OpenProject/AddPreviewForViewComponent
class FilterComponent < ApplicationComponent
OPERATORS_WITHOUT_VALUES = %w[* !* t w].freeze
TURBO_FRAME_ID = "filter_component"

# rubocop:enable OpenProject/AddPreviewForViewComponent
options :query
# The path used for fetching the filter section lazily from the backend upon opening it.
# If none is provided, the filters are rendered right away.
Expand Down Expand Up @@ -112,31 +110,34 @@
end
end

def custom_field_list_autocomplete_options(filter)
options = if filter.custom_field.version?
{
items: filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } },
groupBy: "project_name"
}
else
{ items: filter.allowed_values.map { |name, id| { name:, id: } } }
end
autocomplete_options.merge(options).merge(model: filter.values)
all_items = if filter.custom_field.version?
filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } }
else
filter.allowed_values.map { |name, id| { name:, id: } }
end
selected = filter.values
options = { items: all_items }
options[:groupBy] = "project_name" if filter.custom_field.version?
autocomplete_options.merge(options).merge(model: all_items.select { |item| selected.include?(item[:id]) })
end

Check notice on line 123 in app/components/filter/filter_component.rb

View workflow job for this annotation

GitHub Actions / rubocop

[rubocop] app/components/filter/filter_component.rb#L113-L123 <Metrics/AbcSize>

Assignment Branch Condition size for `custom_field_list_autocomplete_options` is too high. [<10, 16, 6> 19.8/17]
Raw output
app/components/filter/filter_component.rb:113:5: C: Metrics/AbcSize: Assignment Branch Condition size for `custom_field_list_autocomplete_options` is too high. [<10, 16, 6> 19.8/17]

def custom_field_hierarchy_autocomplete_options(filter)
items = filter.allowed_values.map do |name, id|
path = name.split(" / ")
{ name: path.last, id:, depth: path.length - 1 }
end
selected = filter.values

autocomplete_options.merge({ items: }).merge(model: filter.values)
autocomplete_options.merge({ items: }).merge(model: items.select { |item| selected.include?(item[:id]) })
end

def list_autocomplete_options(filter)
all_items = filter.allowed_values.map { |name, id| { name:, id: } }
selected = filter.values
autocomplete_options.merge(
items: filter.allowed_values.map { |name, id| { name:, id: } },
model: filter.values
items: all_items,
model: all_items.select { |item| selected.include?(item[:id]) }
)
end

Expand Down
41 changes: 21 additions & 20 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@
"@mantine/core": "^8.3.13",
"@mantine/hooks": "^8.3.6",
"@mantine/utils": "^6.0.22",
"@ng-select/ng-option-highlight": "^20.6.3",
"@ng-select/ng-select": "^20.1.0",
"@ng-select/ng-option-highlight": "^21.8.0",
"@ng-select/ng-select": "^21.8.0",
"@ngneat/content-loader": "^7.0.0",
"@openproject/octicons-angular": "^19.32.0",
"@openproject/primer-view-components": "^0.84.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import {
import { RecentItemsService } from 'core-app/core/recent-items.service';
import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs';
import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder';
import { NgOption } from '@ng-select/ng-select/index';
import { announce } from '@primer/live-region-element';
import { NgOption } from '@ng-select/ng-select';

interface SearchResultItem {
id:string;
Expand Down Expand Up @@ -82,9 +82,11 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {

public expanded = false;

private _searchTermInitialized = false;

// Computed placeholder that changes based on expanded state
public get effectivePlaceholder():string {
return this.expanded
return this.expanded
? this.I18n.t('js.global_search.search_placeholder_expanded')
: this.placeholder;
}
Expand Down Expand Up @@ -146,8 +148,6 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
}

ngAfterViewInit():void {
// check searchterm on init, expand / collapse search bar and set correct classes
this.searchTerm = this.currentQuery || '';
this.currentValue = '';
this.toggleTopMenuClass();
}
Expand All @@ -157,7 +157,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
}

public set searchTerm(searchTerm:string) {
this.ngSelectComponent.ngSelectInstance.searchTerm = searchTerm;
this.ngSelectComponent.ngSelectInstance.filter(searchTerm);
}

public get searchTerm():string {
Expand Down Expand Up @@ -224,6 +224,11 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
}

public onFocus():void {
if (!this._searchTermInitialized) {
this._searchTermInitialized = true;
this.searchTerm = this.currentQuery ?? '';
this.currentValue = this.searchTerm;
}
this.expanded = true;
this.toggleTopMenuClass();
this.ngSelectComponent.openSelect();
Expand All @@ -232,7 +237,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
public onFocusOut():void {
if (!this.deviceService.isMobile) {
this.expanded = (this.searchTerm !== null && this.searchTerm.length > 0);
this.ngSelectComponent.ngSelectInstance.isOpen = false;
this.ngSelectComponent.ngSelectInstance.isOpen.set(false);
this.selectedItem = undefined;
this.toggleTopMenuClass();
}
Expand Down Expand Up @@ -287,8 +292,10 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
}

private autocompleteWorkPackages():Observable<(WorkPackageResource|SearchOptionItem)[]> {
const query = this.searchTerm;
if (query === null || /^\s+$/.test(query)) {
// ng-select v21 initializes _searchTerm as null (signal). Treat null as '' so that
// the initial typeahead emission triggers loadRecentItems() instead of returning empty.
const query = this.searchTerm ?? '';
if (/^\s+$/.test(query)) {
return of([]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from 'core-app/features/work-packages/services/notifications/work-package-notification.service';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { TOpAutocompleterResource } from 'core-app/shared/components/autocompleter/op-autocompleter/typings';
import { repositionDropdownBugfix } from 'core-app/shared/components/autocompleter/op-autocompleter/autocompleter.helper';

export interface IWorkPackageAutocompleteItem extends WorkPackageResource {
id:string,
Expand Down Expand Up @@ -92,14 +93,7 @@ export class WorkPackageRelationsAutocompleteComponent extends OpAutocompleterCo
}

opened() {
// Force reposition as a workaround for BUG
// https://github.com/ng-select/ng-select/issues/1259
setTimeout(() => {
this.ngSelectInstance.dropdownPanel.adjustPosition();
document.querySelector(this.hiddenOverflowContainer)?.addEventListener('scroll', () => {
this.ngSelectInstance.close();
}, { once: true });
}, 25);
repositionDropdownBugfix(this.ngSelectInstance);
}

getAutocompleterData(query:string|null):Observable<HalResource[]> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
interface NgSelectShim {
appendTo?:string;
dropdownPanel?:{
_updateXPosition():void;
_updateYPosition():void;
}
dropdownPanel?:(() => { adjustPosition():void })|{ adjustPosition():void };
}

// Force reposition as a workaround for BUG
Expand All @@ -12,10 +9,10 @@ export function repositionDropdownBugfix(component?:unknown) {
const instance = component as NgSelectShim;
if (instance?.appendTo && instance?.dropdownPanel) {
setTimeout(() => {
// eslint-disable-next-line no-underscore-dangle
instance.dropdownPanel?._updateXPosition();
// eslint-disable-next-line no-underscore-dangle
instance.dropdownPanel?._updateYPosition();
// dropdownPanel is a Signal in ng-select v21+, call it to get the panel instance
const panelOrSignal = instance.dropdownPanel;
const panel = typeof panelOrSignal === 'function' ? panelOrSignal() : panelOrSignal;
panel?.adjustPosition();
}, 25);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ export class OpAutocompleterComponent<T extends IAutocompleteItem = IAutocomplet
}

private get debounceTimeForCurrentEnvironment():number {
return (window.OpenProject.environment === 'test') ? 0 : this.debounceTimeMs;
return (window.OpenProject?.environment === 'test') ? 0 : this.debounceTimeMs;
}

writeValue(value:T|T[]|null):void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ describe('autocompleter', () => {
fixture.detectChanges();
const select = fixture.componentInstance.ngSelectInstance;

expect(select.isOpen).toBeFalse();
expect(select.isOpen()).toBeFalse();
select.open();
select.focus();

expect(select.isOpen).toBeTrue();
expect(select.isOpen()).toBeTrue();

expect(select.itemsList.items.length).toEqual(0);

Expand Down Expand Up @@ -161,11 +161,11 @@ describe('autocompleter', () => {
fixture.detectChanges();
const select = fixture.componentInstance.ngSelectInstance;

expect(select.isOpen).toBeFalse();
expect(select.isOpen()).toBeFalse();
select.open();
select.focus();

expect(select.isOpen).toBeTrue();
expect(select.isOpen()).toBeTrue();

expect(select.itemsList.items.length).toEqual(0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { NgSelectComponent } from '@ng-select/ng-select';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
import { UserResource } from 'core-app/features/hal/resources/user-resource';
import { repositionDropdownBugfix } from 'core-app/shared/components/autocompleter/op-autocompleter/autocompleter.helper';

@Component({
templateUrl: './multi-select-edit-field.component.html',
Expand Down Expand Up @@ -152,9 +153,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements
}

public repositionDropdown() {
if (this.ngSelectComponent && this.ngSelectComponent.dropdownPanel) {
setTimeout(() => this.ngSelectComponent.dropdownPanel.adjustPosition(), 0);
}
repositionDropdownBugfix(this.ngSelectComponent);
}

private openAutocompleteSelectField() {
Expand Down
1 change: 0 additions & 1 deletion frontend/src/global_styles/content/_autocomplete.sass
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ div.autocomplete
@extend %autocomplete-description

.ng-option, .ng-optgroup
line-height: 22px
font-size: var(--body-font-size)

.op-avatar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ export default class FiltersFormController extends Controller {
this.formLoadedResolver = resolve;
});

private boundListener = this.sendForm.bind(this);
private boundListener:() => void;

initialize() {
// Initialize runs anytime an element with a controller connected to the DOM for the first time
this.sendForm = debounce(this.boundListener, 300);
this.boundListener = debounce(this.sendForm.bind(this), 300);
}

connect() {
Expand Down
Loading
Loading