Skip to content

Commit

Permalink
feat(contextualization): expose api on window (#1137)
Browse files Browse the repository at this point in the history
## Proposed change
Expose an api on window for the contextualization functionalities
  • Loading branch information
matthieu-crouzet authored Jan 2, 2024
2 parents 5f7396b + 67e2294 commit d17cd90
Show file tree
Hide file tree
Showing 20 changed files with 533 additions and 70 deletions.
24 changes: 23 additions & 1 deletion apps/showcase/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import { Provider } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { StoreModule } from '@ngrx/store';
import { ConfigurationDevtoolsModule } from '@o3r/configuration';
import { LocalizationDevtoolsModule } from '@o3r/localization';
import { mockTranslationModules } from '@o3r/testing/localization';
import { TranslateCompiler } from '@ngx-translate/core';
import { TranslateFakeCompiler } from '@ngx-translate/core';
import { AppComponent } from './app.component';

const localizationConfiguration = { language: 'en' };
const mockTranslations = {
en: {}
};
const mockTranslationsCompilerProvider: Provider = {
provide: TranslateCompiler,
useClass: TranslateFakeCompiler
};

describe('AppComponent', () => {
beforeEach(() => TestBed.configureTestingModule({
declarations: [AppComponent]
declarations: [AppComponent],
imports: [
StoreModule.forRoot(),
...mockTranslationModules(localizationConfiguration, mockTranslations, mockTranslationsCompilerProvider),
ConfigurationDevtoolsModule,
LocalizationDevtoolsModule
]
}));

it('should create the app', () => {
Expand Down
7 changes: 5 additions & 2 deletions apps/showcase/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ export class AppComponent implements OnDestroy {

private subscriptions = new Subscription();

constructor(router: Router, private offcanvasService: NgbOffcanvas) {
constructor(
router: Router,
private offcanvasService: NgbOffcanvas
) {
const onNavigationEnd$ = router.events.pipe(
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
share()
);
this.activeUrl$ = onNavigationEnd$.pipe(
map((event) => event.urlAfterRedirects),
shareReplay({bufferSize: 1, refCount: true})
shareReplay({ bufferSize: 1, refCount: true })
);
this.subscriptions.add(onNavigationEnd$.subscribe((event) => {
if (this.offcanvasRef) {
Expand Down
11 changes: 8 additions & 3 deletions apps/showcase/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import { RuntimeChecks, StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { TranslateCompiler, TranslateModule } from '@ngx-translate/core';
import { prefersReducedMotion } from '@o3r/application';
import { ConfigurationDevtoolsModule } from '@o3r/configuration';
import {
LocalizationConfiguration,
LocalizationDevtoolsModule,
LocalizationModule,
MESSAGE_FORMAT_CONFIG,
translateLoaderProvider,
Expand Down Expand Up @@ -60,7 +62,8 @@ export function localizationConfigurationFactory(): Partial<LocalizationConfigur
},
fallbackLanguage: 'en-GB',
bundlesOutputPath: 'localizations/',
useDynamicContent: !isDevMode()
useDynamicContent: !isDevMode(),
enableTranslationDeactivation: true
};
}

Expand All @@ -86,7 +89,9 @@ export function localizationConfigurationFactory(): Partial<LocalizationConfigur
AppRoutingModule,
SidenavPresComponent,
NgbOffcanvasModule,
ScrollBackTopPresComponent
ScrollBackTopPresComponent,
LocalizationDevtoolsModule,
ConfigurationDevtoolsModule
],
providers: [
{provide: MESSAGE_FORMAT_CONFIG, useValue: {}},
Expand All @@ -107,4 +112,4 @@ export function localizationConfigurationFactory(): Partial<LocalizationConfigur
],
bootstrap: [AppComponent]
})
export class AppModule { }
export class AppModule {}
9 changes: 9 additions & 0 deletions apps/showcase/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import 'bootstrap';
import { AppModule } from './app/app.module';
import '@angular/localize/init';
import { inject, runInInjectionContext } from '@angular/core';
import { ConfigurationDevtoolsConsoleService } from '@o3r/configuration';
import { LocalizationDevtoolsConsoleService } from '@o3r/localization';

document.body.dataset.dynamiccontentpath = localStorage.getItem('dynamicPath') || '';
platformBrowserDynamic().bootstrapModule(AppModule)
.then((m) => {
runInInjectionContext(m.injector, () => {
inject(ConfigurationDevtoolsConsoleService);
inject(LocalizationDevtoolsConsoleService);
});
})
// eslint-disable-next-line no-console
.catch(err => console.error(err));
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Rule } from '@angular-devkit/schematics';
import { chain, Rule } from '@angular-devkit/schematics';
import * as path from 'node:path';
import type { NgAddSchematicsSchema } from '../schema';

const DEVTOOL_MODULE_NAME = 'ApplicationDevtoolsModule';
const DEVTOOL_SERVICE_NAME = 'ApplicationDevtoolsService';
const CONSOLE_DEVTOOL_SERVICE_NAME = 'ApplicationDevtoolsConsoleService';
const MESSAGE_DEVTOOL_SERVICE_NAME = 'ApplicationDevtoolsMessageService';
const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', 'package.json')).name;

/**
Expand All @@ -14,10 +15,18 @@ const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', '
*/
export const registerDevtools = async (options: NgAddSchematicsSchema): Promise<Rule> => {
const { registerDevtoolsToApplication } = await import('@o3r/schematics');
return registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: DEVTOOL_SERVICE_NAME,
projectName: options.projectName
});
return chain([
registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: CONSOLE_DEVTOOL_SERVICE_NAME,
projectName: options.projectName
}),
registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: MESSAGE_DEVTOOL_SERVICE_NAME,
projectName: options.projectName
})
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as path from 'node:path';
import type { NgAddSchematicsSchema } from '../schema';

const DEVTOOL_MODULE_NAME = 'ComponentsDevtoolsModule';
const DEVTOOL_SERVICE_NAME = 'ComponentsDevtoolsService';
const DEVTOOL_SERVICE_NAME = 'ComponentsDevtoolsMessageService';
const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', 'package.json')).name;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Rule } from '@angular-devkit/schematics';
import { chain, Rule } from '@angular-devkit/schematics';
import * as path from 'node:path';
import type { NgAddSchematicsSchema } from '../schema';

const DEVTOOL_MODULE_NAME = 'ConfigurationDevtoolsModule';
const DEVTOOL_SERVICE_NAME = 'ConfigurationDevtoolsService';
const MESSAGE_DEVTOOL_SERVICE_NAME = 'ConfigurationDevtoolsMessageService';
const CONSOLE_DEVTOOL_SERVICE_NAME = 'ConfigurationDevtoolsConsoleService';
const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', 'package.json')).name;

/**
Expand All @@ -15,10 +16,18 @@ const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', '
*/
export const registerDevtools = async (options: NgAddSchematicsSchema): Promise<Rule> => {
const { registerDevtoolsToApplication } = await import('@o3r/schematics');
return registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: DEVTOOL_SERVICE_NAME,
projectName: options.projectName
});
return chain([
registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: MESSAGE_DEVTOOL_SERVICE_NAME,
projectName: options.projectName
}),
registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: CONSOLE_DEVTOOL_SERVICE_NAME,
projectName: options.projectName
})
]);
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* eslint-disable no-console */
import { Inject, Injectable, Optional } from '@angular/core';
import type { Configuration, CustomConfig, DevtoolsServiceInterface, WindowWithDevtools } from '@o3r/core';
import type { Configuration, ContextualizationDataset, CustomConfig, DevtoolsServiceInterface, WindowWithDevtools } from '@o3r/core';
import { firstValueFrom } from 'rxjs';
import { ConfigurationDevtoolsServiceOptions } from './configuration-devtools.interface';
import { ConfigurationContextualizationDevtools, ConfigurationDevtoolsServiceOptions } from './configuration-devtools.interface';
import { OtterConfigurationDevtools } from './configuration-devtools.service';
import { OTTER_CONFIGURATION_DEVTOOLS_DEFAULT_OPTIONS, OTTER_CONFIGURATION_DEVTOOLS_OPTIONS } from './configuration-devtools.token';

@Injectable({
providedIn: 'root'
})
export class ConfigurationDevtoolsConsoleService implements DevtoolsServiceInterface {
export class ConfigurationDevtoolsConsoleService implements DevtoolsServiceInterface, ConfigurationContextualizationDevtools {

/** Name of the Window property to access to the devtools */
public static readonly windowModuleName = 'configuration';
Expand All @@ -20,7 +20,13 @@ export class ConfigurationDevtoolsConsoleService implements DevtoolsServiceInter
) {
this.options = { ...OTTER_CONFIGURATION_DEVTOOLS_DEFAULT_OPTIONS, ...options };

if (this.options.isActivatedOnBootstrap) {
if (
this.options.isActivatedOnBootstrap
|| (
this.options.isActivatedOnBootstrapWhenCMSContext
&& (document.body.dataset as ContextualizationDataset).cmscontext === 'true'
)
) {
this.activate();
}
}
Expand Down Expand Up @@ -112,7 +118,7 @@ export class ConfigurationDevtoolsConsoleService implements DevtoolsServiceInter
const content = await this.configurationDevtools.getConfiguration();

console.log('BOOKMARK');
console.log(`javascript:window._OTTER_DEVTOOLS_.loadConfiguration('${JSON.stringify(content).replace(/[']/g, '\\\'')}')`);
console.log(`javascript:window._OTTER_DEVTOOLS_.updateConfigurations('${JSON.stringify(content).replace(/[']/g, '\\\'')}')`);
}

/**
Expand All @@ -125,10 +131,18 @@ export class ConfigurationDevtoolsConsoleService implements DevtoolsServiceInter

/**
* Load a json configuration
*
* @param configurations configurations to load
* @deprecated please use `updateConfigurations` instead, will be removed in Otter v12.
*/
public loadConfiguration(configurations: string | CustomConfig<Configuration>[]): void {
this.configurationDevtools.loadConfiguration(configurations);
}

/**
* Replace N configurations in one shot
* @param configurations array of configurations to update
*/
public updateConfigurations(configurations: string | CustomConfig<Configuration>[]): void {
this.configurationDevtools.loadConfiguration(configurations);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('Configuration DevTools console', () => {

it('should upsert new configurations', () => {
mockStore.dispatch = jest.fn();
service.loadConfiguration('[{"library":"@scope/package","name":"componentTest","config":{"lolProp":123}}]');
service.updateConfigurations('[{"library":"@scope/package","name":"componentTest","config":{"lolProp":123}}]');

expect(mockStore.dispatch).toHaveBeenCalledWith(expect.objectContaining(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import type { Dictionary } from '@ngrx/entity';
import type { ConnectContentMessage, DevtoolsCommonOptions, MessageDataTypes, OtterMessageContent, RequestMessagesContentMessage } from '@o3r/core';
import type {
Configuration,
ConnectContentMessage,
ContextualizationDevtoolsCommonOptions,
CustomConfig,
DevtoolsCommonOptions,
MessageDataTypes,
OtterMessageContent,
RequestMessagesContentMessage
} from '@o3r/core';
import type { ConfigurationModel } from '../stores/index';

/** Option for Configuration devtools service */
export interface ConfigurationDevtoolsServiceOptions extends DevtoolsCommonOptions {
export interface ConfigurationDevtoolsServiceOptions extends DevtoolsCommonOptions, ContextualizationDevtoolsCommonOptions {
/**
* Default library name to use if not specified in the function call
*
* @default `@o3r/components`
*/
defaultLibraryName: string;
/**
* Default JSON file name if not specified in the function
*
* @default partial-static-config.json
*/
defaultJsonFilename: string;
Expand Down Expand Up @@ -45,7 +52,6 @@ export type AvailableConfigurationMessageContents =

/**
* Determine if the given message is a Configuration message
*
* @param message message to check
*/
export const isConfigurationMessage = (message: any): message is AvailableConfigurationMessageContents => {
Expand All @@ -55,3 +61,14 @@ export const isConfigurationMessage = (message: any): message is AvailableConfig
message.dataType === 'requestMessages' ||
message.dataType === 'connect');
};

/**
* Contextualization devtools exposed for configuration in CMS integration
*/
export interface ConfigurationContextualizationDevtools {
/**
* Replace N configurations in one shot
* @param configs array of configurations to update
*/
updateConfigurations: (configurations: CustomConfig<Configuration>[]) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { ConfigurationDevtoolsServiceOptions } from './configuration-devtools.in
export const OTTER_CONFIGURATION_DEVTOOLS_DEFAULT_OPTIONS: ConfigurationDevtoolsServiceOptions = {
defaultLibraryName: '@o3r/components',
defaultJsonFilename: 'partial-static-config.json',
isActivatedOnBootstrap: false
isActivatedOnBootstrap: false,
isActivatedOnBootstrapWhenCMSContext: true
};

// eslint-disable-next-line max-len
Expand Down
20 changes: 19 additions & 1 deletion packages/@o3r/core/src/core/devkit/devtools.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,31 @@ export interface WindowWithDevtools extends Window {
/** Common option used by the different DevKit services */
export interface DevtoolsCommonOptions {
/**
* Activated the application bootstrap
* Activated on the application bootstrap
*
* @default false
*/
isActivatedOnBootstrap: boolean;
}

/** Common option used by the different Contextualization DevKit services */
export interface ContextualizationDevtoolsCommonOptions {
/**
* Activated on the application bootstrap when integrated in CMS context
*
* @default true
*/
isActivatedOnBootstrapWhenCMSContext: boolean;
}

/**
* Dataset injected on the page when in CMS context
*/
export interface ContextualizationDataset {
/** `"true"` when in CMS context */
cmscontext?: string;
}

/** Interface describing an Otter Devtools service */
export interface DevtoolsServiceInterface {
/** Activate the devtools service */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Rule } from '@angular-devkit/schematics';
import { chain, Rule } from '@angular-devkit/schematics';
import * as path from 'node:path';
import type { NgAddSchematicsSchema } from '../schema';

const DEVTOOL_MODULE_NAME = 'LocalizationDevtoolsModule';
const DEVTOOL_SERVICE_NAME = 'LocalizationDevtoolsService';
const MESSAGE_DEVTOOL_SERVICE_NAME = 'LocalizationDevtoolsMessageService';
const CONSOLE_DEVTOOL_SERVICE_NAME = 'LocalizationDevtoolsConsoleService';
const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', 'package.json')).name;

/**
Expand All @@ -15,10 +16,18 @@ const PACKAGE_NAME: string = require(path.resolve(__dirname, '..', '..', '..', '
*/
export const registerDevtools = async (options: NgAddSchematicsSchema): Promise<Rule> => {
const { registerDevtoolsToApplication } = await import('@o3r/schematics');
return registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: DEVTOOL_SERVICE_NAME,
projectName: options.projectName
});
return chain([
registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: MESSAGE_DEVTOOL_SERVICE_NAME,
projectName: options.projectName
}),
registerDevtoolsToApplication({
moduleName: DEVTOOL_MODULE_NAME,
packageName: PACKAGE_NAME,
serviceName: CONSOLE_DEVTOOL_SERVICE_NAME,
projectName: options.projectName
})
]);
};
Loading

0 comments on commit d17cd90

Please sign in to comment.