-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(workbench-client/popup): allow providing a microfrontend for dis…
…play in a workbench popup A micro application can provide a microfrontend for display in a workbench popup. Popup microfrontends are contributed in the form of popup capabilities. A popup is a visual workbench component for displaying content above other content. Unlike views, popups are not part of the persistent workbench navigation, meaning that popups do not survive a page reload.
- Loading branch information
1 parent
9f1b69b
commit bc23e65
Showing
48 changed files
with
3,706 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
apps/workbench-client-testing-app/src/app/popup-opener-page/popup-opener-page.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
<ng-container [formGroup]="form"> | ||
<section> | ||
<sci-form-field label="Qualifier"> | ||
<sci-params-enter [paramsFormArray]="$any(form.get(QUALIFIER))" [addable]="true" [removable]="true" class="e2e-qualifier"></sci-params-enter> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Params"> | ||
<sci-params-enter [paramsFormArray]="$any(form.get(PARAMS))" [addable]="true" [removable]="true" class="e2e-params"></sci-params-enter> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Align"> | ||
<select [formControlName]="ALIGN" class="e2e-align"> | ||
<option value="east">east</option> | ||
<option value="west">west</option> | ||
<option value="north">north</option> | ||
<option value="south">south</option> | ||
<option value=""><default></option> | ||
</select> | ||
</sci-form-field> | ||
</section> | ||
|
||
<sci-accordion class="anchor e2e-anchor" variant="solid" [formGroupName]="ANCHOR"> | ||
<ng-template sciAccordionItem [panel]="panel_anchor"> | ||
<header>Anchor</header> | ||
</ng-template> | ||
<ng-template #panel_anchor> | ||
<sci-form-field label="Binding"> | ||
<select [formControlName]="BINDING" class="e2e-anchor"> | ||
<option value="element">Element</option> | ||
<option value="coordinate">Coordinate</option> | ||
</select> | ||
</sci-form-field> | ||
<ng-container *ngIf="form.get([ANCHOR, BINDING]).value === 'coordinate'"> | ||
<sci-form-field label="X"> | ||
<input [formControlName]="X" class="e2e-anchor-x"> | ||
</sci-form-field> | ||
<sci-form-field label="Y"> | ||
<input [formControlName]="Y" class="e2e-anchor-y"> | ||
</sci-form-field> | ||
<sci-form-field label="Width"> | ||
<input [formControlName]="WIDTH" class="e2e-anchor-width"> | ||
</sci-form-field> | ||
<sci-form-field label="Height"> | ||
<input [formControlName]="HEIGHT" class="e2e-anchor-height"> | ||
</sci-form-field> | ||
</ng-container> | ||
</ng-template> | ||
</sci-accordion> | ||
|
||
<sci-accordion [formGroupName]="CLOSE_STRATEGY" variant="solid" class="e2e-close-strategy"> | ||
<ng-template sciAccordionItem [panel]="panel_close_strategy"> | ||
<header>Close Strategy</header> | ||
</ng-template> | ||
<ng-template #panel_close_strategy> | ||
<sci-form-field label="onFocusLost"> | ||
<sci-checkbox [formControlName]="ON_FOCUS_LOST" class="e2e-close-on-focus-lost"></sci-checkbox> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="onEscape"> | ||
<sci-checkbox [formControlName]="ON_ESCAPE" class="e2e-close-on-escape"></sci-checkbox> | ||
</sci-form-field> | ||
</ng-template> | ||
</sci-accordion> | ||
</ng-container> | ||
|
||
<button (click)="onPopupOpen()" class="open e2e-open" [disabled]="form.invalid" #open_button> | ||
Open popup | ||
</button> | ||
|
||
<output class="return-value e2e-return-value" *ngIf="returnValue"> | ||
{{returnValue}} | ||
</output> | ||
|
||
<output class="popup-error e2e-popup-error" *ngIf="popupError"> | ||
{{popupError}} | ||
</output> |
38 changes: 38 additions & 0 deletions
38
apps/workbench-client-testing-app/src/app/popup-opener-page/popup-opener-page.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
@use 'sci-toolkit-styles' as sci-toolkit-styles; | ||
|
||
:host { | ||
display: grid; | ||
grid-auto-rows: max-content; | ||
row-gap: 1em; | ||
padding: 1em; | ||
|
||
> section { | ||
display: grid; | ||
grid-row-gap: .5em; | ||
border: 1px solid var(--sci-color-P400); | ||
border-radius: 5px; | ||
padding: 1em; | ||
} | ||
|
||
> sci-accordion header { | ||
font-weight: bold; | ||
} | ||
|
||
> button.open { | ||
justify-self: center; | ||
} | ||
|
||
> output.return-value { | ||
border: 1px solid var(--sci-color-accent); | ||
background-color: var(--sci-color-A100); | ||
border-radius: 3px; | ||
padding: 1em; | ||
} | ||
|
||
> output.popup-error { | ||
border: 1px solid var(--sci-color-warn); | ||
background-color: var(--sci-color-W100); | ||
border-radius: 3px; | ||
padding: 1em; | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
apps/workbench-client-testing-app/src/app/popup-opener-page/popup-opener-page.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* | ||
* Copyright (c) 2018-2019 Swiss Federal Railways | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
|
||
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; | ||
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; | ||
import { CloseStrategy, WorkbenchPopupService } from '@scion/workbench-client'; | ||
import { SciParamsEnterComponent } from '@scion/toolkit.internal/widgets'; | ||
import { undefinedIfEmpty } from '../util/util'; | ||
import { defer, Observable } from 'rxjs'; | ||
import { PopupOrigin } from '@scion/workbench'; | ||
import { map, startWith } from 'rxjs/operators'; | ||
|
||
const QUALIFIER = 'qualifier'; | ||
const PARAMS = 'params'; | ||
const ANCHOR = 'anchor'; | ||
const BINDING = 'binding'; | ||
const ALIGN = 'align'; | ||
const CLOSE_STRATEGY = 'closeStrategy'; | ||
const ON_FOCUS_LOST = 'onFocusLost'; | ||
const ON_ESCAPE = 'onEscape'; | ||
const X = 'x'; | ||
const Y = 'y'; | ||
const HEIGHT = 'height'; | ||
const WIDTH = 'width'; | ||
|
||
@Component({ | ||
selector: 'app-popup-opener-page', | ||
templateUrl: './popup-opener-page.component.html', | ||
styleUrls: ['./popup-opener-page.component.scss'], | ||
}) | ||
export class PopupOpenerPageComponent implements AfterViewInit { | ||
|
||
public readonly QUALIFIER = QUALIFIER; | ||
public readonly PARAMS = PARAMS; | ||
public readonly ANCHOR = ANCHOR; | ||
public readonly BINDING = BINDING; | ||
public readonly ALIGN = ALIGN; | ||
public readonly CLOSE_STRATEGY = CLOSE_STRATEGY; | ||
public readonly ON_FOCUS_LOST = ON_FOCUS_LOST; | ||
public readonly ON_ESCAPE = ON_ESCAPE; | ||
public readonly HEIGHT = HEIGHT; | ||
public readonly WIDTH = WIDTH; | ||
public readonly X = X; | ||
public readonly Y = Y; | ||
|
||
private _coordinateAnchor$: Observable<PopupOrigin>; | ||
|
||
public form: FormGroup; | ||
|
||
public popupError: string; | ||
public returnValue: string; | ||
|
||
@ViewChild('open_button', {static: true}) | ||
private _openButton: ElementRef<HTMLButtonElement>; | ||
|
||
constructor(private _host: ElementRef<HTMLElement>, | ||
private _popupService: WorkbenchPopupService, | ||
formBuilder: FormBuilder) { | ||
this.form = formBuilder.group({ | ||
[QUALIFIER]: formBuilder.array([ | ||
new FormGroup({ | ||
paramName: new FormControl('component'), | ||
paramValue: new FormControl('popup'), | ||
}), | ||
new FormGroup({ | ||
paramName: new FormControl('app'), | ||
paramValue: new FormControl('app1'), | ||
}, | ||
)], Validators.required), | ||
[PARAMS]: formBuilder.array([]), | ||
[ANCHOR]: formBuilder.group({ | ||
[BINDING]: formBuilder.control('element', Validators.required), | ||
[X]: formBuilder.control('0'), | ||
[Y]: formBuilder.control('0'), | ||
[WIDTH]: formBuilder.control('0'), | ||
[HEIGHT]: formBuilder.control('0'), | ||
}), | ||
[ALIGN]: formBuilder.control(''), | ||
[CLOSE_STRATEGY]: formBuilder.group({ | ||
[ON_FOCUS_LOST]: formBuilder.control(true), | ||
[ON_ESCAPE]: formBuilder.control(true), | ||
}), | ||
}); | ||
|
||
this._coordinateAnchor$ = defer(() => this.form.get(ANCHOR) | ||
.valueChanges | ||
.pipe( | ||
startWith(this.form.get(ANCHOR).value as object), | ||
map(formValue => ({ | ||
x: Number(formValue[X]), | ||
y: Number(formValue[Y]), | ||
width: Number(formValue[WIDTH]), | ||
height: Number(formValue[HEIGHT]), | ||
}), | ||
), | ||
)); | ||
} | ||
|
||
public ngAfterViewInit(): void { | ||
const {left, top, width, height} = this._host.nativeElement.getBoundingClientRect(); | ||
this.form.get(ANCHOR).patchValue({ | ||
[X]: left + width / 2, | ||
[Y]: top + height / 2, | ||
}); | ||
} | ||
|
||
public async onPopupOpen(): Promise<void> { | ||
const qualifier = SciParamsEnterComponent.toParamsDictionary(this.form.get(QUALIFIER) as FormArray); | ||
const params = SciParamsEnterComponent.toParamsDictionary(this.form.get(PARAMS) as FormArray); | ||
|
||
this.popupError = null; | ||
this.returnValue = null; | ||
|
||
await this._popupService.open<string>(qualifier, { | ||
params, | ||
anchor: this.form.get([ANCHOR, BINDING]).value === 'element' ? this._openButton.nativeElement : this._coordinateAnchor$, | ||
align: this.form.get(ALIGN).value || undefined, | ||
closeStrategy: undefinedIfEmpty<CloseStrategy>({ | ||
onFocusLost: this.form.get([CLOSE_STRATEGY, ON_FOCUS_LOST]).value ?? undefined, | ||
onEscape: this.form.get([CLOSE_STRATEGY, ON_ESCAPE]).value ?? undefined, | ||
}), | ||
}) | ||
.then(result => this.returnValue = result) | ||
.catch(error => this.popupError = error ?? 'Popup was closed with an error'); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
apps/workbench-client-testing-app/src/app/popup-opener-page/popup-opener-page.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright (c) 2018-2019 Swiss Federal Railways | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
|
||
import { NgModule } from '@angular/core'; | ||
import { CommonModule } from '@angular/common'; | ||
import { SciAccordionModule, SciCheckboxModule, SciFormFieldModule, SciParamsEnterModule } from '@scion/toolkit.internal/widgets'; | ||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||
import { RouterModule, Routes } from '@angular/router'; | ||
import { PopupOpenerPageComponent } from './popup-opener-page.component'; | ||
|
||
const routes: Routes = [ | ||
{path: '', component: PopupOpenerPageComponent}, | ||
]; | ||
|
||
@NgModule({ | ||
imports: [ | ||
CommonModule, | ||
FormsModule, | ||
ReactiveFormsModule, | ||
RouterModule.forChild(routes), | ||
SciFormFieldModule, | ||
SciCheckboxModule, | ||
SciAccordionModule, | ||
SciParamsEnterModule, | ||
], | ||
declarations: [ | ||
PopupOpenerPageComponent, | ||
], | ||
}) | ||
export class PopupOpenerPageModule { | ||
} |
Oops, something went wrong.