Skip to content
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
50 changes: 50 additions & 0 deletions libs/angular/gov/flanders/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-class-suffix": [
"error",
{
"suffixes": ["Component", "Container", "Page"]
}
],
"@typescript-eslint/no-inferrable-types": "off",
"comma-dangle": [
"error",
{
"arrays": "always-multiline",
"exports": "always-multiline",
"functions": "never",
"imports": "always-multiline",
"objects": "always-multiline"
}
],
"import/order": "error"
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": [
"error",
{
"ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"]
}
]
}
}
]
}
29 changes: 29 additions & 0 deletions libs/angular/gov/flanders/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# # Angular Tools: NgxGovFlanders (`@studiohyperdrive/ngx-gov-flanders`)

This library provides multiple utilities for projects related to the Flemish Government.

## Installation

Install the package first:

```shell
npm install @studiohyperdrive/ngx-gov-flanders
```

## Versioning and build information

This package will follow a semver-like format, `major.minor.patch`, in which:

- `major`: Follows the Angular major version
- `minor`: Introduces new features and (potential) breaking changes
- `patch`: Introduces bugfixes and minor non-breaking changes

For more information about the build process, authors, contributions and issues, we refer to the [hyperdrive-opensource](https://github.com/studiohyperdrive/hyperdrive-opensource) repository.

## Concept

The `ngx-gov-flanders` package provides helpers and wrappers that will make it easier to implement services and components that are provided by the Flemish Government.

## Documentation

To find more information regarding this package, we refer to [our documentation platform](https://open-source.studiohyperdrive.be/docs/angular/gov/flanders/introduction).
21 changes: 21 additions & 0 deletions libs/angular/gov/flanders/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
displayName: 'ngx-gov-flanders',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/angular/gov/flanders',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};
7 changes: 7 additions & 0 deletions libs/angular/gov/flanders/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../../dist/libs/angular/gov/flanders",
"lib": {
"entryFile": "src/index.ts"
}
}
35 changes: 35 additions & 0 deletions libs/angular/gov/flanders/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@studiohyperdrive/ngx-gov-flanders",
"version": "0.0.0",
"description": "An integrated Angular approach to common use-cases for applications for the Flemish Government.",
"keywords": [
"angular",
"angular2",
"ACM",
"GlobalHeader",
"GlobalFooter",
"VO",
"Flanders",
"Vlaamse Overheid",
"Flemish Government"
],
"homepage": "https://open-source.studiohyperdrive.be/docs/angular/gov/flanders/introduction",
"license": "MIT",
"author": {
"name": "Studio Hyperdrive",
"url": "https://studiohyperdrive.be/"
},
"repository": {
"type": "git",
"url": "https://github.com/studiohyperdrive/hyperdrive-opensource",
"directory": "libs/angular/gov/flanders/src"
},
"peerDependencies": {
"@angular/core": "^19.0.0",
"@govflanders/vl-widget-global-footer-types": "^1.0.22",
"@govflanders/vl-widget-global-header-types": "^2.0.12",
"@studiohyperdrive/ngx-core": "^19.1.0",
"rxjs": "7.8.1"
},
"sideEffects": false
}
36 changes: 36 additions & 0 deletions libs/angular/gov/flanders/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "ngx-gov-flanders",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/angular/gov/flanders/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"build": {
"executor": "@nx/angular:package",
"outputs": ["{workspaceRoot}/dist/{projectRoot}"],
"options": {
"project": "libs/angular/gov/flanders/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/angular/gov/flanders/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "libs/angular/gov/flanders/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/angular/gov/flanders/jest.config.ts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
1 change: 1 addition & 0 deletions libs/angular/gov/flanders/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib';
2 changes: 2 additions & 0 deletions libs/angular/gov/flanders/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './services';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
import { GlobalFooterClient } from '@govflanders/vl-widget-global-footer-types';
import { GlobalHeaderClient } from '@govflanders/vl-widget-global-header-types';
import { NgxWindowService } from '@studiohyperdrive/ngx-core';
import { first, from, fromEvent, Observable, of, retry, switchMap, throwError } from 'rxjs';
import { map } from 'rxjs/operators';
import {
NgxAcmComponentConfiguration,
NgxAcmComponentInjectionConfiguration,
NgxAcmHeaderComponentConfiguration,
} from '../types';

/**
* The `NgxAcmComponentInjectionService` is a service that provides methods to inject either a GlobalHeader or a GlobalFooter by ACM to your application.
*/
@Injectable({
providedIn: 'root',
})
export class NgxAcmComponentInjectionService {
/**
* An instance of the RendererFactory2
*/
private rendererFactory: RendererFactory2 = inject(RendererFactory2);
/**
* An instance of the Renderer2
*/
private renderer: Renderer2 = this.rendererFactory.createRenderer(null, null);
/**
* An instance of the NgxWindowService
*/
private windowService: NgxWindowService = inject(NgxWindowService);

/**
* Initializes the VO Global Header widget and sets optional applicationMenuLinks.
*
* @param selector - The selector of the element where the header widget should be mounted.
* @param configuration - The configuration object
* @param configuration.url - The URL of the widget (e.g. 'https://<environment>.widgets.burgerprofiel.vlaanderen.be/')
* @param configuration.id - The ID of the widget (e.g. '7502b557-5d49-4ab3-9995-168718b81be5')
* @param configuration.profile - The profile configuration object
* @param links - Optional application menu links
*
* @returns The GlobalHeaderClient object will be returned to allow for custom implementations that are not supported out of the box, see: https://test.widgets.burgerprofiel.dev-vlaanderen.be/docs/global-header/ for more information.
*/
public injectGlobalHeaderComponent({
selector,
configuration,
links,
}: NgxAcmComponentInjectionConfiguration<NgxAcmHeaderComponentConfiguration>): Observable<GlobalHeaderClient> {
// Denis: The following code should only run in the browser.
return this.windowService.runInBrowser<Observable<GlobalHeaderClient>>(
({ browserWindow, browserDocument }) => {
// Denis: Create a new script tag
return this.injectComponent(
browserDocument,
selector,
configuration,
'GlobalHeaderComponent'
).pipe(
switchMap((headerElement: HTMLElement) => {
// Denis: Get the GlobalHeaderClient object
const headerClient: GlobalHeaderClient = browserWindow.globalHeaderClient;

return from(headerClient.mount(headerElement));
}),
switchMap(() => {
// Denis: If there are no links, fallback to the default links
if (!Array.isArray(links) || !links.length) {
return of(null);
}

// Denis: Get the GlobalHeaderClient object
const headerClient: GlobalHeaderClient = browserWindow.globalHeaderClient;

// Denis: Set the optional application menu links
return from(headerClient.accessMenu.setApplicationMenuLinks(links));
}),
switchMap(() => {
// Denis: Get the GlobalHeaderClient object
const headerClient: GlobalHeaderClient = browserWindow.globalHeaderClient;

// Denis: Set the provided ProfileConfig
return headerClient.accessMenu.setProfile(configuration.profile);
}),
map(() => browserWindow.globalHeaderClient)
);
}
);
}

/**
* Initializes the VO Global Footer widget and sets optional navigation links.
*
* @param selector - The selector of the element where the footer widget should be mounted.
* @param configuration - The configuration object
* @param configuration.url - The URL of the widget (e.g. 'https://<environment>.widgets.burgerprofiel.vlaanderen.be/')
* @param configuration.id - The ID of the widget (e.g. '7502b557-5d49-4ab3-9995-168718b81be5')
* @param links - Optional navigation links
*
* @returns The GlobalFooterClient object will be returned to allow for custom implementations that are not supported out of the box, see: https://test.widgets.burgerprofiel.dev-vlaanderen.be/docs/global-footer/ for more information.
*/
public injectGlobalFooterComponent({
selector,
configuration,
links,
}: NgxAcmComponentInjectionConfiguration): Observable<GlobalFooterClient> {
// Denis: The following code should only run in the browser.
return this.windowService.runInBrowser<Observable<GlobalFooterClient>>(
({ browserWindow, browserDocument }) => {
// Denis: Create a new script tag
return this.injectComponent(
browserDocument,
selector,
configuration,
'GlobalFooterComponent'
).pipe(
switchMap((footerElement: HTMLElement) => {
// Denis: When the script is loaded, get the GlobalHeaderClient object
const footerClient: GlobalFooterClient = browserWindow.globalFooterClient;

return from(footerClient.mount(footerElement));
}),
switchMap(() => {
// Denis: If there are no links, fallback to the default links
if (!Array.isArray(links) || !links.length) {
return of(null);
}

// Denis: Get the GlobalHeaderClient object
const footerClient: GlobalFooterClient = browserWindow.globalFooterClient;

// Denis: Set the provided navigation links
return from(footerClient.setNavigationLinks(links));
}),
map(() => browserWindow.globalFooterClient)
);
}
);
}

/**
* Creates a script tag for the ACMComponent and appends it to the browser
*
* @private
* @param browserDocument - An instance of the browser document
* @param configuration - The configuration of the ACM component
* @param selector - The selector of the component
* @param component - The name of the component we wish to append
*/
private injectComponent(
browserDocument: Document,
selector: string,
configuration: NgxAcmComponentConfiguration,
component: string
): Observable<HTMLElement> {
// Denis: Create a new script tag
const script = this.renderer.createElement('script');

// Denis: Set up the script to load the header widget
script.src = configuration.url + 'api/v2/widget/' + configuration.id + '/entry';
script.type = 'text/javascript';

// Denis: Append the script to the DOM to load it.
this.renderer.appendChild(browserDocument.head, script);

return fromEvent(script, 'load').pipe(
first(),
switchMap(() => {
// Denis: Mount the provided widget script
const element: HTMLElement = browserDocument.querySelector(selector);

// Iben: If no element was found, we throw an error
if (typeof element === 'undefined' || element === null) {
return throwError(
() =>
new Error(
`NGXGovFlanders: An error occurred when trying to mount the ${component}, the provided targetElement was not found.`
)
);
}

// Iben: Return the element
return of(element);
}),
retry({
count: 5,
delay: 100,
})
);
}
}
1 change: 1 addition & 0 deletions libs/angular/gov/flanders/src/lib/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './acm-component-injection.service';
Loading