From 870d6ee782257167234f4de312dee7699877fd38 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:29:37 -0700 Subject: [PATCH] Fix: handle overflowing dropdowns on mobile (#7758) See https://github.com/ng-bootstrap/ng-bootstrap/pull/4760 --- .../dates-dropdown.component.html | 2 +- .../dates-dropdown.component.ts | 3 +++ .../filterable-dropdown.component.html | 2 +- .../filterable-dropdown.component.ts | 3 +++ src-ui/src/app/utils/popper-options.spec.ts | 24 +++++++++++++++++++ src-ui/src/app/utils/popper-options.ts | 24 +++++++++++++++++++ 6 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src-ui/src/app/utils/popper-options.spec.ts create mode 100644 src-ui/src/app/utils/popper-options.ts diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html index 8991363d2..b9528805b 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html @@ -1,4 +1,4 @@ -<div class="btn-group w-100" ngbDropdown role="group"> +<div class="btn-group w-100" ngbDropdown role="group" [popperOptions]="popperOptions"> <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="createdDateBefore || createdDateAfter ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled"> <i-bs width="1em" height="1em" name="calendar-event-fill"></i-bs> <div class="d-none d-sm-inline"> {{title}}</div> diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts index 966e9640a..21b39f0cb 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts @@ -11,6 +11,7 @@ import { Subject, Subscription } from 'rxjs' import { debounceTime } from 'rxjs/operators' import { SettingsService } from 'src/app/services/settings.service' import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter' +import { popperOptionsReenablePreventOverflow } from 'src/app/utils/popper-options' export interface DateSelection { createdBefore?: string @@ -35,6 +36,8 @@ export enum RelativeDate { providers: [{ provide: NgbDateAdapter, useClass: ISODateAdapter }], }) export class DatesDropdownComponent implements OnInit, OnDestroy { + public popperOptions = popperOptionsReenablePreventOverflow + constructor(settings: SettingsService) { this.datePlaceHolder = settings.getLocalizedDateInputFormat() } diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html index 9e9a73124..413ed8d3b 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -1,4 +1,4 @@ -<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown" (keydown)="listKeyDown($event)"> +<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown" (keydown)="listKeyDown($event)" [popperOptions]="popperOptions"> <button class="btn btn-sm" id="dropdown_{{name}}" ngbDropdownToggle [ngClass]="!editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled"> <i-bs name="{{icon}}"></i-bs> <div class="d-none d-sm-inline"> {{title}}</div> diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts index 7830e3909..5e39b1d2d 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts @@ -16,6 +16,7 @@ import { Subject, filter, take, takeUntil } from 'rxjs' import { SelectionDataItem } from 'src/app/services/rest/document.service' import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' import { HotKeyService } from 'src/app/services/hot-key.service' +import { popperOptionsReenablePreventOverflow } from 'src/app/utils/popper-options' export interface ChangedItems { itemsToAdd: MatchingModel[] @@ -330,6 +331,8 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit { @ViewChild('dropdown') dropdown: NgbDropdown @ViewChild('buttonItems') buttonItems: ElementRef + public popperOptions = popperOptionsReenablePreventOverflow + filterText: string @Input() diff --git a/src-ui/src/app/utils/popper-options.spec.ts b/src-ui/src/app/utils/popper-options.spec.ts new file mode 100644 index 000000000..9d7671cf6 --- /dev/null +++ b/src-ui/src/app/utils/popper-options.spec.ts @@ -0,0 +1,24 @@ +import { popperOptionsReenablePreventOverflow } from './popper-options' +import { Options } from '@popperjs/core' + +describe('popperOptionsReenablePreventOverflow', () => { + it('should return the config without the empty fun preventOverflow, add padding to other', () => { + const config: Partial<Options> = { + modifiers: [ + { name: 'preventOverflow', fn: function () {} }, + { + name: 'preventOverflow', + fn: function () { + return + }, + }, + ], + } + + const result = popperOptionsReenablePreventOverflow(config) + + expect(result.modifiers.length).toBe(1) + expect(result.modifiers[0].name).toBe('preventOverflow') + expect(result.modifiers[0].options).toEqual({ padding: 10 }) + }) +}) diff --git a/src-ui/src/app/utils/popper-options.ts b/src-ui/src/app/utils/popper-options.ts new file mode 100644 index 000000000..71ac715ce --- /dev/null +++ b/src-ui/src/app/utils/popper-options.ts @@ -0,0 +1,24 @@ +import { Options } from '@popperjs/core' + +export function popperOptionsReenablePreventOverflow( + config: Partial<Options> +): Partial<Options> { + const preventOverflowModifier = config.modifiers?.find( + (m) => m.name === 'preventOverflow' && m.fn?.length === 0 + ) + if (preventOverflowModifier) { + config.modifiers.splice( + config.modifiers.indexOf(preventOverflowModifier), + 1 + ) + } + const ogPreventOverflowModifier = config.modifiers.find( + (m) => m.name === 'preventOverflow' + ) + if (ogPreventOverflowModifier) { + ogPreventOverflowModifier.options = { + padding: 10, + } + } + return config +}