diff --git a/apps/webcomponents/src/app/AppOverlayContainer.ts b/apps/webcomponents/src/app/AppOverlayContainer.ts
deleted file mode 100644
index 73c6f3249f..0000000000
--- a/apps/webcomponents/src/app/AppOverlayContainer.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { OverlayContainer } from '@angular/cdk/overlay'
-import { Platform } from '@angular/cdk/platform'
-import { DOCUMENT } from '@angular/common'
-import { Inject, Injectable, OnDestroy } from '@angular/core'
-
-@Injectable()
-export class AppOverlayContainer extends OverlayContainer implements OnDestroy {
- private selector: string
-
- constructor(
- @Inject(DOCUMENT) private document: Document,
- platform: Platform
- ) {
- super(document, platform)
- }
-
- setSelector(selector: string) {
- this.selector = selector
- }
- ngOnDestroy() {
- super.ngOnDestroy()
- }
-
- protected _createContainer(): void {
- const container: HTMLDivElement = this.document.createElement('div')
- container.classList.add('app-overlay-container')
- const element: Element | null = this.document
- .querySelector(this.selector)
- .shadowRoot.querySelector('#angular-app-root')
- if (element !== null) {
- element.appendChild(container)
- this._containerElement = container
- } else {
- console.error(
- 'Material CDK Overlay creation failed ! ' +
- 'It can work only with gn-search-input webcomponent. ' +
- 'You have to add an element with id="angular-app-root" (in the shadowDOM) to which the overlay will be appended.'
- )
- }
- }
-}
diff --git a/apps/webcomponents/src/app/components/base.component.ts b/apps/webcomponents/src/app/components/base.component.ts
index 0c3a3b63e7..59394945c8 100644
--- a/apps/webcomponents/src/app/components/base.component.ts
+++ b/apps/webcomponents/src/app/components/base.component.ts
@@ -1,4 +1,11 @@
-import { Component, Injector, Input, OnChanges, OnInit } from '@angular/core'
+import {
+ Component,
+ ElementRef,
+ Injector,
+ Input,
+ OnChanges,
+ OnInit,
+} from '@angular/core'
import {
LinkClassifierService,
LinkUsage,
@@ -10,6 +17,8 @@ import { TranslateService } from '@ngx-translate/core'
import { firstValueFrom } from 'rxjs'
import { DatasetDistribution } from '@geonetwork-ui/common/domain/record'
import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface'
+import { OverlayContainer } from '@angular/cdk/overlay'
+import { WebcomponentOverlayContainer } from '../webcomponent-overlay-container'
export const apiConfiguration = new Configuration()
@@ -40,6 +49,12 @@ export class BaseComponent implements OnChanges, OnInit {
this.searchService = injector.get(SearchApiService)
this.recordsRepository = injector.get(RecordsRepositoryInterface)
this.linkClassifier = injector.get(LinkClassifierService)
+
+ const elementRef = injector.get(ElementRef)
+ const overlayContainer = injector.get(
+ OverlayContainer
+ ) as WebcomponentOverlayContainer
+ overlayContainer.setRoot(elementRef.nativeElement.shadowRoot)
}
ngOnInit() {
@@ -68,6 +83,7 @@ export class BaseComponent implements OnChanges, OnInit {
this.titleFont
)
this.facade.init(this.searchId)
+ this.copyFontFacesToDocument()
this.isInitialized = true
}
@@ -75,6 +91,27 @@ export class BaseComponent implements OnChanges, OnInit {
// to override
}
+ private copyFontFacesToDocument() {
+ // get the list of font face definitions in the Shadow DOM
+ const root = this.injector.get(ElementRef).nativeElement as HTMLElement
+ const styles = root.shadowRoot.styleSheets
+ const fontFaces = Array.from(styles).reduce(
+ (prev, curr) => [
+ ...prev,
+ ...Array.from(curr.cssRules)
+ .filter((rule) => rule.cssText.startsWith('@font-face'))
+ .map((rule) => rule.cssText),
+ ],
+ []
+ )
+
+ // all font faces are then copied to the document
+ const style = document.createElement('style')
+ const cssText = fontFaces.join('\n')
+ style.appendChild(document.createTextNode(cssText))
+ document.head.appendChild(style)
+ }
+
async getRecordLink(
uuid: string,
usages: LinkUsage[]
diff --git a/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html b/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html
index d875ef9b79..aa88dcf175 100644
--- a/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html
+++ b/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html
@@ -7,10 +7,6 @@
-
-
-
-
-
-
-
-
-
+
diff --git a/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.sample.html b/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.sample.html
index a9669e8fa7..2ac92548fa 100644
--- a/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.sample.html
+++ b/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.sample.html
@@ -7,10 +7,6 @@
-
BaseComponent, string][] = [
},
{
provide: OverlayContainer,
- useFactory: (document: Document, platform: Platform) => {
- const container = new AppOverlayContainer(document, platform)
- container.setSelector('gn-search-input')
- return container
- },
- deps: [DOCUMENT, Platform],
+ useClass: WebcomponentOverlayContainer,
},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
diff --git a/apps/webcomponents/src/styles.css b/apps/webcomponents/src/styles.css
index 4814c8c96c..799b8b6c3e 100644
--- a/apps/webcomponents/src/styles.css
+++ b/apps/webcomponents/src/styles.css
@@ -13,7 +13,7 @@
font-family: 'Material Symbols Outlined';
font-style: normal;
font-weight: 400;
- src: url(https://fonts.gstatic.com/s/materialiconsoutlined/v108/gok-H7zzDkdnRel8-DQ6KAXJ69wP1tGnf4ZGhUce.woff2)
+ src: url(https://fonts.gstatic.com/s/materialsymbolsoutlined/v138/kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsLjBuVY.woff2)
format('woff2');
}
.material-symbols-outlined {
@@ -32,7 +32,7 @@
-webkit-font-smoothing: antialiased;
}
-/* Material Theme */
+/* These classes were extracted from the full Material theme to save size */
.cdk-overlay-pane {
position: absolute;
pointer-events: auto;
@@ -42,7 +42,6 @@
max-width: 100%;
max-height: 100%;
}
-
.cdk-overlay-connected-position-bounding-box {
position: absolute;
z-index: 1000;
@@ -51,7 +50,31 @@
min-width: 1px;
min-height: 1px;
}
-.app-overlay-container {
+.cdk-overlay-backdrop {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ pointer-events: auto;
+ transition: opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
+ opacity: 0;
+}
+.cdk-overlay-backdrop.cdk-overlay-backdrop-showing {
+ opacity: 1;
+}
+.cdk-overlay-transparent-backdrop {
+ transition: visibility 1ms linear, opacity 1ms linear;
+ visibility: hidden;
+ opacity: 1;
+}
+.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing {
+ opacity: 0;
+ visibility: visible;
+}
+
+.gn-ui-overlay-container {
position: absolute;
z-index: 1000;
pointer-events: none;
@@ -60,7 +83,7 @@
height: 100%;
width: 100%;
}
-.app-overlay-container:empty {
+.gn-ui-overlay-container:empty {
display: none;
}
diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html
index ea7fcafe9e..0ce3113a7f 100644
--- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html
+++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html
@@ -4,6 +4,7 @@
[action]="autoCompleteAction"
(itemSelected)="handleItemSelection($event)"
(inputSubmitted)="handleInputSubmission($event)"
+ (inputCleared)="handleInputCleared()"
[value]="searchInputValue$ | async"
[clearOnSelection]="true"
>
diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts
index 39328db8d9..b4f31ca72a 100644
--- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts
+++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts
@@ -109,7 +109,6 @@ describe('FuzzySearchComponent', () => {
})
describe('search enter key press', () => {
- let outputValue
describe('when no output defined', () => {
beforeEach(() => {
component.handleInputSubmission('blarg')
@@ -123,8 +122,7 @@ describe('FuzzySearchComponent', () => {
describe('when output is defined', () => {
beforeEach(() => {
jest.resetAllMocks()
- outputValue = null
- component.inputSubmitted.subscribe((event) => (outputValue = event))
+ component.inputSubmitted.subscribe()
jest.spyOn(component.inputSubmitted, 'emit')
component.handleInputSubmission('blarg')
})
@@ -139,6 +137,25 @@ describe('FuzzySearchComponent', () => {
})
})
+ describe('search input clear', () => {
+ describe('when output is defined', () => {
+ beforeEach(() => {
+ jest.resetAllMocks()
+ component.inputSubmitted.subscribe()
+ jest.spyOn(component.inputSubmitted, 'emit')
+ component.handleInputCleared()
+ })
+ it('clears the search filters', () => {
+ expect(searchService.updateFilters).toHaveBeenCalledWith({
+ any: '',
+ })
+ })
+ it('does not emit inputSubmitted', () => {
+ expect(component.inputSubmitted.emit).not.toHaveBeenCalled()
+ })
+ })
+ })
+
describe('search suggestion selection', () => {
describe('when no output defined', () => {
beforeEach(() => {
diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts
index b13e99b905..2e80a32aec 100644
--- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts
+++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts
@@ -72,4 +72,8 @@ export class FuzzySearchComponent implements OnInit {
this.searchService.updateFilters({ any })
}
}
+
+ handleInputCleared() {
+ this.searchService.updateFilters({ any: '' })
+ }
}
diff --git a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts
index be2eb5d4b1..7c11ac9daa 100644
--- a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts
+++ b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts
@@ -111,34 +111,38 @@ describe('AutocompleteComponent', () => {
})
})
describe('when input is not empty', () => {
- let anyEmitted
+ let anyEmitted: boolean
+ let clearEmitted: boolean
let button
beforeEach(() => {
+ anyEmitted = false
+ clearEmitted = false
component.inputRef.nativeElement.value = 'blar'
component.inputRef.nativeElement.dispatchEvent(new InputEvent('input'))
component.triggerRef.closePanel = jest.fn()
- component.inputSubmitted.subscribe((event) => (anyEmitted = event))
+ component.inputSubmitted.subscribe(() => (anyEmitted = true))
+ component.inputCleared.subscribe(() => (clearEmitted = true))
fixture.detectChanges()
button = fixture.debugElement.query(By.css('.clear-btn'))
+ button.nativeElement.click()
})
it('is visible', () => {
expect(button).not.toBeNull()
})
it('resets the text input', () => {
- button.nativeElement.click()
expect(component.inputRef.nativeElement.value).toBe('')
})
it('sets the text input of the focus', () => {
- button.nativeElement.click()
expect(document.activeElement).toBe(component.inputRef.nativeElement)
})
it('closes the autocomplete panel', () => {
- button.nativeElement.click()
expect(component.triggerRef.closePanel).toHaveBeenCalled()
})
- it('clears search result by emitting empty string', () => {
- button.nativeElement.click()
- expect(anyEmitted).toEqual('')
+ it('does not emit an inputSubmitted event', () => {
+ expect(anyEmitted).toEqual(false)
+ })
+ it('emits an inputCleared event', () => {
+ expect(clearEmitted).toEqual(true)
})
})
})
diff --git a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts
index d8c9c25441..2442fe4c83 100644
--- a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts
+++ b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts
@@ -49,6 +49,7 @@ export class AutocompleteComponent
@Input() clearOnSelection = false
@Output() itemSelected = new EventEmitter()
@Output() inputSubmitted = new EventEmitter()
+ @Output() inputCleared = new EventEmitter()
@ViewChild(MatAutocompleteTrigger) triggerRef: MatAutocompleteTrigger
@ViewChild(MatAutocomplete) autocomplete: MatAutocomplete
@ViewChild('searchInput') inputRef: ElementRef
@@ -126,7 +127,7 @@ export class AutocompleteComponent
clear(): void {
this.inputRef.nativeElement.value = ''
- this.inputSubmitted.emit('')
+ this.inputCleared.emit()
this.selectionSubject
.pipe(take(1))
.subscribe((selection) => selection && selection.option.deselect())
diff --git a/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html b/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html
index c938fe3cc1..439d197378 100644
--- a/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html
+++ b/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html
@@ -43,7 +43,7 @@
[cdkConnectedOverlayPositions]="overlayPositions"
[cdkConnectedOverlayScrollStrategy]="scrollStrategy"
[cdkConnectedOverlayFlexibleDimensions]="true"
- (backdropClick)="closeOverlay()"
+ (overlayOutsideClick)="closeOverlay()"
(detach)="closeOverlay()"
>