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

feat(contextualization): expose api on window #1137

Merged
merged 1 commit into from
Jan 2, 2024
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
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'
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
)
) {
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;
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
}

/** 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
Loading