Skip to content

Commit

Permalink
refactor: Replace ngx-bootstrap dropdown w/ cdk menu and overlay
Browse files Browse the repository at this point in the history
* Continues towards migrating away from the ngx-bootstrap library in favor of using cdk.  Cdk components have better a11y support and are style agnostic.  We can still apply bootstrap styles to them, but gives us the options to move away from bootstrap in the future.
* Still using bootstrap styling for visual consistency.
* Replaced all dropdown usages with menu and overlay
  • Loading branch information
jrassa committed Aug 23, 2023
1 parent 2acc842 commit 7274e58
Show file tree
Hide file tree
Showing 36 changed files with 534 additions and 414 deletions.
9 changes: 4 additions & 5 deletions src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import { TitleStrategy, provideRouter, withHashLocation } from '@angular/router'

import { AlertModule } from 'ngx-bootstrap/alert';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ModalModule } from 'ngx-bootstrap/modal';
import { PopoverModule } from 'ngx-bootstrap/popover';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { TypeaheadModule } from 'ngx-bootstrap/typeahead';

import { providerCdkDialog } from './common/dialog/provider';
import { authInterceptor } from './core/auth/auth.interceptor';
Expand All @@ -27,12 +25,13 @@ export const appConfig: ApplicationConfig = {
importProvidersFrom(
AlertModule.forRoot(),
BsDatepickerModule.forRoot(),
BsDropdownModule.forRoot(),
ModalModule.forRoot(),
PopoverModule.forRoot(),
TabsModule.forRoot(),
TooltipModule.forRoot(),
TypeaheadModule.forRoot()
TooltipModule.forRoot()
// If still using uncomment imports below.
// BsDropdownModule.forRoot(),
// TypeaheadModule.forRoot()
),
provideAnimations(),
provideHttpClient(
Expand Down
20 changes: 20 additions & 0 deletions src/app/common/cdk-menu-item-router-link.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { CdkMenuItem } from '@angular/cdk/menu';
import { Directive, inject } from '@angular/core';
import { RouterLink } from '@angular/router';

/**
* Work around for bug where CdkMenuItems w/ RouterLink don't trigger properly when using keyboard navigation.
*/
@Directive({
selector: '[cdkMenuItem][routerLink]',
standalone: true
})
export class CdkMenuItemRouterLinkDirective {
menuItem = inject(CdkMenuItem);
routerLink = inject(RouterLink);
constructor() {
this.menuItem.triggered.subscribe(() => {
this.routerLink.onClick(0, false, false, false, false);
});
}
}
1 change: 1 addition & 0 deletions src/app/common/search-input/search-input.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('SearchInputComponent', () => {
}));

it('should not apply the search if keyup is never triggered', fakeAsync(() => {
componentInstance.preferInputEvent = false;
inputElement.nativeElement.value = 'search value';
fixture.detectChanges();
// need to trigger input event here so ngModel will set `search` property correctly
Expand Down
2 changes: 1 addition & 1 deletion src/app/common/search-input/search-input.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class SearchInputComponent {
/**
* If true, searches will be made on `input` events, otherwise searches will be made on `keyup` events
*/
@Input() preferInputEvent = false;
@Input() preferInputEvent = true;

/**
* Specifies a minimum character count required to search.
Expand Down
2 changes: 1 addition & 1 deletion src/app/common/table/filter/_shared.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ button.dropdown-toggle {

.dropdown-menu {
font-size: $filter-dropdown-font-size;
margin-top: 0.5rem;
//margin-top: 0.5rem;
max-height: $filter-dropdown-max-height;

input {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export abstract class AsyAbstractHeaderFilterComponent

isFiltered = false;

isOpen = false;

private storage = new SessionStorageService();

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
<span class="dropdown" container="body" dropdown [insideClick]="true">
<button class="dropdown-toggle dropdown-toggle-hide-caret" dropdownToggle>
<span
class="filter fa-solid fa-list"
container="body"
placement="bottom"
tooltip="Apply Filters"
[hidden]="isFiltered"
></span>
<span
class="filter fa-solid fa-filter"
container="body"
placement="bottom"
tooltip="Edit Filters"
[hidden]="!isFiltered"
></span>
</button>
<button
class="dropdown-toggle dropdown-toggle-hide-caret"
cdkOverlayOrigin
#trigger
(click)="isOpen = !isOpen"
>
<span
class="filter fa-solid fa-list"
container="body"
placement="bottom"
tooltip="Apply Filters"
[hidden]="isFiltered"
></span>
<span
class="filter fa-solid fa-filter"
container="body"
placement="bottom"
tooltip="Edit Filters"
[hidden]="!isFiltered"
></span>
</button>

<div class="dropdown-menu" [class.dropdown-menu-right]="dropdownMenuRight" *dropdownMenu>
<ng-template
cdkConnectedOverlay
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayOpen]="isOpen"
[cdkConnectedOverlayOrigin]="trigger"
(backdropClick)="isOpen = false"
>
<div class="dropdown-menu" cdkTrapFocus cdkTrapFocusAutoCapture>
<div class="form-check">
<input
class="form-check-input"
Expand Down Expand Up @@ -108,4 +120,4 @@
/>
</div>
</div>
</span>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { DateTime, Settings } from 'luxon';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';

import { AsyFilterDirective } from '../asy-filter.directive';
import { AsyHeaderDateFilterComponent } from './asy-header-date-filter.component';
Expand Down Expand Up @@ -36,7 +35,7 @@ describe('AsyHeaderDateFilter', () => {
filterSpy.deregister.and.callFake(() => {});

await TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, BsDropdownModule, AsyHeaderDateFilterComponent],
imports: [BrowserAnimationsModule, AsyHeaderDateFilterComponent],
providers: [{ provide: AsyFilterDirective, useValue: filterSpy as AsyFilterDirective }]
}).compileComponents();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { A11yModule } from '@angular/cdk/a11y';
import { CdkConnectedOverlay, CdkOverlayOrigin, OverlayModule } from '@angular/cdk/overlay';
import { TitleCasePipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject, Optional } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { NgSelectModule } from '@ng-select/ng-select';
import { DateTime } from 'luxon';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TooltipModule } from 'ngx-bootstrap/tooltip';

import {
Expand All @@ -20,12 +21,15 @@ import {
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
BsDropdownModule,
FormsModule,
NgSelectModule,
BsDatepickerModule,
TitleCasePipe,
TooltipModule
TooltipModule,
CdkOverlayOrigin,
CdkConnectedOverlay,
A11yModule,
OverlayModule
]
})
export class AsyHeaderDateFilterComponent extends AsyAbstractHeaderFilterComponent {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
<span class="dropdown" container="body" dropdown [insideClick]="true">
<button class="dropdown-toggle dropdown-toggle-hide-caret" dropdownToggle>
<span
class="filter fa-solid fa-list"
container="body"
placement="bottom"
tooltip="Apply Filters"
[hidden]="isFiltered"
></span>
<span
class="filter fa-solid fa-filter"
container="body"
placement="bottom"
tooltip="Edit Filters"
[hidden]="!isFiltered"
></span>
</button>
<button
class="dropdown-toggle dropdown-toggle-hide-caret"
cdkOverlayOrigin
#trigger
(click)="isOpen = !isOpen"
>
<span
class="filter fa-solid fa-list"
container="body"
placement="bottom"
tooltip="Apply Filters"
[hidden]="isFiltered"
></span>
<span
class="filter fa-solid fa-filter"
container="body"
placement="bottom"
tooltip="Edit Filters"
[hidden]="!isFiltered"
></span>
</button>

<div
class="dropdown-menu d-flex flex-column"
[ngClass]="{ 'dropdown-menu-right': dropdownMenuRight }"
*dropdownMenu
>
<ng-template
cdkConnectedOverlay
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayOpen]="isOpen"
[cdkConnectedOverlayOrigin]="trigger"
(backdropClick)="isOpen = false"
>
<div class="dropdown-menu d-flex flex-column" cdkTrapFocus cdkTrapFocusAutoCapture>
<ng-container *ngIf="showSearch">
<div class="search mt-2">
<asy-search-input
Expand Down Expand Up @@ -86,4 +94,4 @@
</div>
</ng-container>
</div>
</span>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { A11yModule } from '@angular/cdk/a11y';
import { CdkConnectedOverlay, CdkOverlayOrigin, OverlayModule } from '@angular/cdk/overlay';
import { NgClass, NgFor, NgIf, TitleCasePipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject, Input, Optional, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TooltipModule } from 'ngx-bootstrap/tooltip';

import { SearchInputComponent } from '../../../search-input/search-input.component';
Expand All @@ -27,14 +28,17 @@ export type ListFilterOption = {
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
BsDropdownModule,
NgClass,
NgIf,
SearchInputComponent,
NgFor,
FormsModule,
TitleCasePipe,
TooltipModule
TooltipModule,
CdkOverlayOrigin,
CdkConnectedOverlay,
A11yModule,
OverlayModule
],
providers: [TitleCasePipe]
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
<span class="dropdown" container="body" dropdown [insideClick]="true">
<button class="dropdown-toggle dropdown-toggle-hide-caret" dropdownToggle>
<span
class="filter fa-solid fa-list"
container="body"
placement="bottom"
tooltip="Apply Filters"
[hidden]="isFiltered"
></span>
<span
class="filter fa-solid fa-filter"
container="body"
placement="bottom"
tooltip="Edit Filters"
[hidden]="!isFiltered"
></span>
</button>
<button
class="dropdown-toggle dropdown-toggle-hide-caret"
cdkOverlayOrigin
#trigger
(click)="isOpen = !isOpen"
>
<span
class="filter fa-solid fa-list"
container="body"
placement="bottom"
tooltip="Apply Filters"
[hidden]="isFiltered"
></span>
<span
class="filter fa-solid fa-filter"
container="body"
placement="bottom"
tooltip="Edit Filters"
[hidden]="!isFiltered"
></span>
</button>

<div
class="dropdown-menu d-flex flex-column"
[ngClass]="{ 'dropdown-menu-right': dropdownMenuRight }"
*dropdownMenu
>
<ng-template
cdkConnectedOverlay
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayOpen]="isOpen"
[cdkConnectedOverlayOrigin]="trigger"
(backdropClick)="isOpen = false"
>
<div class="dropdown-menu d-flex flex-column" cdkTrapFocus cdkTrapFocusAutoCapture>
<div class="mt-3">
<ng-select
[(ngModel)]="option"
Expand All @@ -41,4 +49,4 @@
</asy-search-input>
</div>
</div>
</span>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { A11yModule } from '@angular/cdk/a11y';
import { CdkConnectedOverlay, CdkOverlayOrigin, OverlayModule } from '@angular/cdk/overlay';
import { NgClass } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject, Input, Optional } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { NgSelectModule } from '@ng-select/ng-select';
import escapeRegExp from 'lodash/escapeRegExp';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TooltipModule } from 'ngx-bootstrap/tooltip';

import { SearchInputComponent } from '../../../search-input/search-input.component';
Expand All @@ -24,12 +25,15 @@ type BuildFilterFunction = (search: string, option: TextFilterOption) => any;
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
BsDropdownModule,
NgClass,
NgSelectModule,
FormsModule,
SearchInputComponent,
TooltipModule
TooltipModule,
A11yModule,
OverlayModule,
CdkConnectedOverlay,
CdkOverlayOrigin
]
})
export class AsyHeaderTextFilterComponent extends AsyAbstractHeaderFilterComponent {
Expand Down
Loading

0 comments on commit 7274e58

Please sign in to comment.