Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ngssm-toolkit] Add helper to manage opening/closing dialog #188

Merged
merged 1 commit into from
Nov 19, 2023
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
2 changes: 2 additions & 0 deletions projects/ngssm-toolkit/src/lib/mat-dialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './provide-ngssm-mat-dialog';
export * from './ngssm-mat-dialog-config';
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TestBed } from '@angular/core/testing';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';

import { StoreMock } from 'ngssm-store/testing';

import { MatDialogOpeningEffect } from './mat-dialog-opening.effect';
import { provideNgssmMatDialogConfigs } from './ngssm-mat-dialog-config';

enum TestingActionType {
openAction = '[TestingActionType] openAction',
closeAction = '[TestingActionType] closeAction'
}

@Component({
selector: 'ngssm-testing',
standalone: true,
imports: [CommonModule],
template: ` nothing `,
styles: [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DialogDemoComponent {}

class MatDialogRefMock {
public close(): void {
// nothing to do here
}
}

describe('MatDialogOpeningEffect', () => {
let effect: MatDialogOpeningEffect;
let matDialog: MatDialog;
let dialog: MatDialogRefMock;
let store: StoreMock;

beforeEach(() => {
store = new StoreMock({});
TestBed.configureTestingModule({
imports: [MatDialogModule],
providers: [
MatDialogOpeningEffect,
provideNgssmMatDialogConfigs({
openingAction: TestingActionType.openAction,
closingAction: TestingActionType.closeAction,
component: DialogDemoComponent,
matDialogConfig: {
disableClose: true,
height: '400px',
width: '60vw'
}
})
]
});
effect = TestBed.inject(MatDialogOpeningEffect);
matDialog = TestBed.inject(MatDialog);
dialog = new MatDialogRefMock();
spyOn(matDialog, 'open').and.returnValue(dialog as any);
spyOn(dialog, 'close');
});

[TestingActionType.openAction, TestingActionType.closeAction].forEach((actionType: string) => {
it(`should process action of type '${actionType}'`, () => {
expect(effect.processedActions).toContain(actionType);
});
});

it(`should open the dialog when calling the action type '${TestingActionType.openAction}'`, () => {
effect.processAction(store as any, store.stateValue, { type: TestingActionType.openAction });

expect(matDialog.open).toHaveBeenCalledWith(DialogDemoComponent, {
disableClose: true,
height: '400px',
width: '60vw'
});
});

it(`should close the dialog when calling the action type '${TestingActionType.closeAction}'`, () => {
effect.processAction(store as any, store.stateValue, { type: TestingActionType.openAction });

effect.processAction(store as any, store.stateValue, { type: TestingActionType.closeAction });

expect(dialog.close).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Inject, Injectable, Optional } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';

import { Effect, Store, State, Action, Logger } from 'ngssm-store';

import { NGSSM_MAT_DIALOG_CONFIG, NgssmMatDialogConfig } from './ngssm-mat-dialog-config';

interface ExtendedConfig {
config: NgssmMatDialogConfig;
dialog?: MatDialogRef<any, any>;
}

@Injectable()
export class MatDialogOpeningEffect implements Effect {
private readonly extendedConfigs: ExtendedConfig[];

public readonly processedActions: string[] = [];

constructor(
@Inject(NGSSM_MAT_DIALOG_CONFIG) @Optional() private configs: NgssmMatDialogConfig[],
private logger: Logger,
private matDialog: MatDialog
) {
this.processedActions.push(...(this.configs ?? []).flatMap((c) => [c.openingAction, c.closingAction]));
this.extendedConfigs = (this.configs ?? []).map((c) => ({
config: c
}));
}

public processAction(store: Store, state: State, action: Action): void {
const extendedConfig = this.extendedConfigs.find(
(c) => c.config.openingAction === action.type || c.config.closingAction === action.type
);

if (!extendedConfig) {
this.logger.error(`Need to process action '${action.type}' with no associated config.`);
return;
}

if (action.type === extendedConfig.config.openingAction) {
extendedConfig.dialog = this.matDialog.open(extendedConfig.config.component, extendedConfig.config.matDialogConfig);
return;
}

extendedConfig.dialog?.close();
extendedConfig.dialog = undefined;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';
import { ComponentType } from '@angular/cdk/portal';
import { MatDialogConfig } from '@angular/material/dialog';

export interface NgssmMatDialogConfig<T = any, D = any> {
openingAction: string;
closingAction: string;
component: ComponentType<T>;
matDialogConfig?: MatDialogConfig<D>;
}

export const NGSSM_MAT_DIALOG_CONFIG = new InjectionToken<NgssmMatDialogConfig>('NGSSM_MAT_DIALOG_CONFIG');

export const provideNgssmMatDialogConfigs = (...configs: NgssmMatDialogConfig[]): EnvironmentProviders => {
return makeEnvironmentProviders(configs.map((config) => ({ provide: NGSSM_MAT_DIALOG_CONFIG, useValue: config, multi: true })));
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';

import { provideEffects } from 'ngssm-store';

import { MatDialogOpeningEffect } from './mat-dialog-opening.effect';

export interface ProvideNgssmMatDialog {}

export const provideNgssmMatDialog = (): EnvironmentProviders => {
return makeEnvironmentProviders([provideEffects(MatDialogOpeningEffect)]);
};
1 change: 1 addition & 0 deletions projects/ngssm-toolkit/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from './lib/ngssm-component-display.directive';
export * from './lib/ngssm-help/ngssm-help.component';
export * from './lib/blob-helpers';
export * from './lib/luxon-helpers';
export * from './lib/mat-dialog';
6 changes: 5 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
defaultRegexEditorValidator,
MaterialImportsModule,
NGSSM_REGEX_EDITOR_VALIDATOR,
provideNgssmMatDialog,
RegexEditorValidator,
useDefaultErrorStateMatcher
} from 'ngssm-toolkit';
Expand All @@ -29,6 +30,7 @@ import { ShellDemoModule } from './shell-demo/public-api';
import { TreeDataService } from './ngssm-tree-demo/tree-data.service';
import { provideRemoteDataDemo } from './remote-data-demo/public-api';
import { provideJsonBuilder } from './ngssm-expression-tree-demo/json-builder/provide-json-builder';
import { provideToolkitDemo } from './toolkit/public-api';

const dotnetRegexValidator: RegexEditorValidator = {
validatePattern: (pattern: string) => {
Expand Down Expand Up @@ -97,10 +99,12 @@ const dotnetRegexValidatorFactory = (): RegexEditorValidator => {
provideNgssmExpressionTree(),
provideNgssmVisibility(),
provideNgssmServiceInfo(),
provideNgssmMatDialog(),
{ provide: NGSSM_TREE_DATA_SERVICE, useClass: TreeDataService, multi: true },
{ provide: NGSSM_REGEX_EDITOR_VALIDATOR, useFactory: dotnetRegexValidatorFactory },
provideRemoteDataDemo(),
provideJsonBuilder()
provideJsonBuilder(),
provideToolkitDemo()
],
bootstrap: [AppComponent]
})
Expand Down
1 change: 1 addition & 0 deletions src/app/toolkit/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './toolkit-demo-action-type';
4 changes: 4 additions & 0 deletions src/app/toolkit/actions/toolkit-demo-action-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ToolkitDemoActionType {
openDialogDemo = '[ToolkitDemoActionType] openDialogDemo',
closeDialogDemo = '[ToolkitDemoActionType] closeDialogDemo'
}
10 changes: 10 additions & 0 deletions src/app/toolkit/components/dialog-demo/dialog-demo.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<h2 mat-dialog-title>Mat dialog helper demo</h2>
<mat-dialog-content>
Some content to be put here
</mat-dialog-content>
<mat-dialog-actions class="flex-row-center">
<span class="fxFlex"></span>
<button mat-stroked-button color="primary" (click)="close()">
Close dialog
</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:host {
display: flex;
flex-direction: column;
height: 100%;
}
25 changes: 25 additions & 0 deletions src/app/toolkit/components/dialog-demo/dialog-demo.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';

import { NgSsmComponent, Store } from 'ngssm-store';
import { ToolkitDemoActionType } from '../../actions';

@Component({
selector: 'app-dialog-demo',
standalone: true,
imports: [CommonModule, MatDialogModule, MatButtonModule],
templateUrl: './dialog-demo.component.html',
styleUrls: ['./dialog-demo.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DialogDemoComponent extends NgSsmComponent {
constructor(store: Store) {
super(store);
}

public close(): void {
this.dispatchActionType(ToolkitDemoActionType.closeDialogDemo);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<mat-card appearance="raised" class="flex-column-stretch fxFlex">
<mat-card class="flex-column-stretch fxFlex">
<mat-card-header>
<mat-card-title>Demo of the components provided by the ngssm-toolkit library</mat-card-title>
</mat-card-header>

<mat-card-content class="flex-row-start flex-wrap fxFlex">
<mat-card appearance="raised" class="sub-card file-picker-demo flex-column-stretch">
<mat-card class="sub-card file-picker-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>File Picker</mat-card-title>
</mat-card-header>
Expand All @@ -30,7 +30,7 @@
</mat-card-content>
</mat-card>
<app-overlay-demo></app-overlay-demo>
<mat-card appearance="raised" class="sub-card notifier-demo flex-column-stretch">
<mat-card class="sub-card notifier-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>Notifier</mat-card-title>
</mat-card-header>
Expand All @@ -49,7 +49,7 @@
</mat-card-content>
</mat-card>

<mat-card appearance="raised" class="sub-card confirmation-demo flex-column-stretch">
<mat-card class="sub-card confirmation-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>Confirmation dialog</mat-card-title>
</mat-card-header>
Expand All @@ -74,7 +74,7 @@
</mat-card-content>
</mat-card>

<mat-card appearance="raised" class="sub-card regex-editor-demo flex-column-stretch">
<mat-card class="sub-card regex-editor-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>Regex Editor</mat-card-title>
</mat-card-header>
Expand All @@ -96,7 +96,7 @@
</mat-card-content>
</mat-card>

<mat-card appearance="raised" class="sub-card component-display-directive-demo flex-column-stretch">
<mat-card class="sub-card component-display-directive-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>Component display directive</mat-card-title>
</mat-card-header>
Expand All @@ -119,7 +119,7 @@
</mat-card-content>
</mat-card>

<mat-card appearance="raised" class="sub-card help-button-demo flex-column-stretch">
<mat-card class="sub-card help-button-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>Help button</mat-card-title>
</mat-card-header>
Expand All @@ -140,5 +140,20 @@

</mat-card-content>
</mat-card>

<mat-card class="sub-card mat-dialog-demo flex-column-stretch">
<mat-card-header>
<mat-card-title>Mat dialog opener</mat-card-title>
</mat-card-header>

<mat-card-content class="flex-column-stretch fxFlex">
<button mat-stroked-button color="primary" (click)="openDialogDemo()">
Open dialog
</button>
</mat-card-content>
</mat-card>
</mat-card-content>



</mat-card>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { OverlayDemoComponent } from '../overlay-demo/overlay-demo.component';
import { Demo1Component } from '../demo1/demo1.component';
import { Demo2Component } from '../demo2/demo2.component';
import { ToolkitDemoActionType } from '../../actions';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -147,4 +148,8 @@ export class ToolkitDemoComponent extends NgSsmComponent {
this.fileControl.setValue(undefined, { emitEvent: false });
}, 1000);
}

public openDialogDemo(): void {
this.dispatchActionType(ToolkitDemoActionType.openDialogDemo);
}
}
21 changes: 21 additions & 0 deletions src/app/toolkit/provide-toolkit-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';

import { provideNgssmMatDialogConfigs } from 'ngssm-toolkit';

import { ToolkitDemoActionType } from './actions';
import { DialogDemoComponent } from './components/dialog-demo/dialog-demo.component';

export const provideToolkitDemo = (): EnvironmentProviders => {
return makeEnvironmentProviders([
provideNgssmMatDialogConfigs({
openingAction: ToolkitDemoActionType.openDialogDemo,
closingAction: ToolkitDemoActionType.closeDialogDemo,
component: DialogDemoComponent,
matDialogConfig: {
disableClose: true,
height: '400px',
width: '60vw'
}
})
]);
};
1 change: 1 addition & 0 deletions src/app/toolkit/public-api.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './toolkit-routes';
export * from './provide-toolkit-demo';