Skip to content

Commit

Permalink
fix(c11n): change the return type of the operator getpresenter (#1301)
Browse files Browse the repository at this point in the history
## Proposed change
The return type of `getPresenter` was incorrect
  • Loading branch information
matthieu-crouzet authored Jan 31, 2024
2 parents b463843 + 80f29f5 commit 15a86ce
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { CommonModule, formatDate } from '@angular/common';
import { ChangeDetectionStrategy, Component, Input, OnChanges, Optional, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnChanges, Optional, SimpleChanges, Type, ViewEncapsulation } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { C11nModule, C11nService } from '@o3r/components';
import { ConfigObserver, ConfigurationBaseService, ConfigurationObserver, DynamicConfigurable } from '@o3r/configuration';
import { Context, O3rComponent } from '@o3r/core';
import { O3rComponent } from '@o3r/core';
import { Observable } from 'rxjs';
import { DatePickerInputPresComponent, DatePickerInputPresContextInput, DatePickerInputPresContextOutput } from '../../utilities/index';
import { DatePickerInputPresComponent, DatePickerInputPresContext } from '../../utilities/index';
import { COMPONENT_REPLACEMENT_PRES_DEFAULT_CONFIG } from './component-replacement-pres.config';
import { COMPONENT_REPLACEMENT_PRES_CONFIG_ID } from './component-replacement-pres.config';
import { ComponentReplacementPresConfig } from './component-replacement-pres.config';
Expand Down Expand Up @@ -35,7 +35,7 @@ export class ComponentReplacementPresComponent implements OnChanges, DynamicConf
public config: Partial<ComponentReplacementPresConfig> | undefined;

/** Observable of the presenter that we want to use, processed by the c11n directive */
public presenter$!: Observable<Context<DatePickerInputPresContextInput, DatePickerInputPresContextOutput>>;
public presenter$!: Observable<Type<DatePickerInputPresContext>>;

public dateFormControl = new FormControl<string | null>(this.formatDate(Date.now() + 7 * ONE_DAY_IN_MS));

Expand Down
6 changes: 3 additions & 3 deletions docs/components/COMPONENT_REPLACEMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,10 @@ Here we need to do a couple of things:
* Prepare an ``outputs`` object

```` typescript
import {ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, Optional} from '@angular/core';
import {ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, Optional, Type} from '@angular/core';
import {DummyContConfig, DUMMY_CONT_CONFIG_ID, DUMMY_CONT_DEFAULT_CONFIG} from './dummy-cont.config';
import {DummyContContext} from './dummy-cont.context';
import {DummyPresContextInput, DummyPresContextOutput} from '../presenter/index';
import {DummyPresContext} from '../presenter/index';
import {DummyPresComponent} from '../presenter/dummy-pres.component';
import {ConfigurationBaseService, ConfigurationObserver} from '@o3r/configuration';
import {Block} from '@o3r/core';
Expand All @@ -217,7 +217,7 @@ export class DummyContComponent implements DynamicConfigurable<DummyContConfig>,
public config$: Observable<DummyContConfig>;

/** Observable of the presenter that we want to use, processed by the c11n directive */
public presenter$: Observable<Context<DummyPresContextInput, DummyPresContextOutput>>;
public presenter$: Observable<Type<DummyPresContext>>;

/** Convenience object to prepare all the outputs binding in advance */
public outputs: Functionify<DummyPresContextOutput>;
Expand Down
42 changes: 35 additions & 7 deletions packages/@o3r/components/schematics/ng-update/v10-0/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ const migrationPath = path.join(__dirname, '..', '..', '..', 'migration.json');


describe('Update v10', () => {
let initialTree: Tree;
let runner: SchematicTestRunner;
beforeEach(() => {
initialTree = Tree.empty();
initialTree.create('angular.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', 'angular.mocks.json')));
initialTree.create('package.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', 'package.mocks.json')));
initialTree.create('.eslintrc.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', '__dot__eslintrc.mocks.json')));
runner = new SchematicTestRunner('schematics', migrationPath);
});

describe('Update pipes', () => {
let initialTree: Tree;
let runner: SchematicTestRunner;
beforeEach(() => {
initialTree = Tree.empty();
initialTree.create('angular.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', 'angular.mocks.json')));
initialTree.create('package.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', 'package.mocks.json')));
initialTree.create('.eslintrc.json', fs.readFileSync(path.resolve(__dirname, '..', '..', '..', 'testing', 'mocks', '__dot__eslintrc.mocks.json')));
initialTree.create('src/components/example.template.html', '{{ 120 | duration }}');
runner = new SchematicTestRunner('schematics', migrationPath);
});

it('should replace the pipe with standalone component', async () => {
Expand Down Expand Up @@ -74,4 +77,29 @@ describe('Update v10', () => {
expect(tree.readText('src/components/example.template.html')).not.toMatch('| duration');
});
});

describe('Update c11n presenter$ declaration', () => {
it('shoud update the c11n presenter$ declaration', async () => {
const componentPath = 'src/components/example.component.ts';
initialTree.create(componentPath, `
import { Component } from '@angular/core';
import { O3rComponent } from '@o3r/core';
import { Observable } from 'rxjs';
@O3rComponent({ componentType: 'Component' })
@Component({
selector: 'o3r-example',
standalone: true,
templateUrl: './example.template.html'
})
export class ExampleComponent {
public presenter$!: Observable<MyComp>;
}
`);
const tree = await runner.runSchematic('migration-v10_0', {}, initialTree);
const newContent = tree.readText(componentPath);
expect(newContent).toContain('public presenter$!: Observable<Type<MyComp>>;');
expect(newContent).toMatch(/import.*Type.*from '@angular\/core';/);
});
});
});
22 changes: 19 additions & 3 deletions packages/@o3r/components/schematics/ng-update/v10-0/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable camelcase, @typescript-eslint/naming-convention */
import { chain, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { createSchematicWithMetricsIfInstalled, PipeReplacementInfo, updatePipes } from '@o3r/schematics';
import { chain, noop, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { addImportsRule, createSchematicWithMetricsIfInstalled, findFilesInTree, PipeReplacementInfo, updatePipes } from '@o3r/schematics';

const pipeReplacementInfo: PipeReplacementInfo = {
capitalize: {
Expand Down Expand Up @@ -33,14 +33,30 @@ const pipeReplacementInfo: PipeReplacementInfo = {
}
};

const c11nPresenterDeclarationRegExp = /(presenter\$!?\s*:\s*Observable)<(.*)>/g;

const updateC11nPresenterDeclaration: Rule = (tree) => {
const files = findFilesInTree(tree.getDir('/'), (filePath) => /\.component.ts$/.test(filePath));
return chain(
files.map((file) => c11nPresenterDeclarationRegExp.test(file.content.toString())
? chain([
() => tree.overwrite(file.path, file.content.toString().replace(c11nPresenterDeclarationRegExp, '$1<Type<$2>>')),
addImportsRule(file.path, [{ from: '@angular/core', importNames: ['Type'] }])
])
: noop()
)
);
};

/**
* Update of Otter library V10.0
*/
function updateV10_0Fn(): Rule {
return (tree: Tree, context: SchematicContext) => {

const updateRules: Rule[] = [
updatePipes(pipeReplacementInfo)
updatePipes(pipeReplacementInfo),
updateC11nPresenterDeclaration
];

return chain(updateRules)(tree, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
ComponentRef, Directive, forwardRef, Injector, Input, KeyValueChangeRecord, KeyValueDiffer,
KeyValueDiffers, OnChanges, OnDestroy, SimpleChange, SimpleChanges, ViewContainerRef
KeyValueDiffers, OnChanges, OnDestroy, SimpleChange, SimpleChanges, Type, ViewContainerRef
} from '@angular/core';
import { AbstractControl, ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
import type { BaseContextOutput, Configuration, Context, ContextInput, Functionify } from '@o3r/core';
Expand Down Expand Up @@ -28,7 +28,7 @@ export class C11nDirective<
T extends Context<I, O> = Context<I, O>> implements OnChanges, OnDestroy {

/** The component information passed to the directive */
@Input() public component!: T;
@Input() public component!: Type<T>;

/** The information related to configuration */
@Input() public config?: D;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class C11nService {
* @param presKey The presenter key to retrieve
*/
public getPresenter<T extends Context>(defaultPres: Type<T>, presKey = 'customPresKey') {
return (source: Observable<Configuration>): Observable<T> =>
return (source: Observable<Configuration>): Observable<Type<T>> =>
source.pipe(
distinctUntilChanged((p, q) => p[presKey] === q[presKey]),
map((config) => {
Expand Down

0 comments on commit 15a86ce

Please sign in to comment.