Skip to content

Commit

Permalink
feat(pipes): prefix pipes with o3r
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieu-crouzet committed Jan 30, 2024
1 parent 44c1ce6 commit 273169b
Show file tree
Hide file tree
Showing 58 changed files with 831 additions and 181 deletions.
2 changes: 1 addition & 1 deletion apps/showcase/src/app/home/home.template.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="d-flex flex-column gap-2 align-items-center justify-center flex-fill">
<img width="350" [src]="'otter-a.svg' | dynamicContent" class="otter-logo" alt="Otter">
<img width="350" [src]="'otter-a.svg' | o3rDynamicContent" class="otter-logo" alt="Otter">
<h1 class="h1 display-1 text-center mt-0 mb-5"><b>Easily build a customizable Angular based application</b></h1>
<h2 class="h4 mt-5 pt-5 text-secondary">
The Otter project is a highly modular framework whose goal is to provide a common platform to <b>accelerate</b> and <b>facilitate</b> the development of <span class="text-danger">Angular</span> based Web Applications.<br />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="card my-3">
<div class="row g-0">
<div class="col-4 bg-body-tertiary d-flex align-items-center justify-center">
<img [src]="'otter-dynamic.svg' | dynamicContent" class="img-fluid rounded-start" aria-hidden="true">
<img [src]="'otter-dynamic.svg' | o3rDynamicContent" class="img-fluid rounded-start" aria-hidden="true">
</div>
<div class="col-8 container p-3">
<div class="card-bod d-flex flex-column h-100">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,29 @@
<div class="row card-title flex-fill d-flex align-items-center m-auto">
<h2 class="bg-body-tertiary text-secondary border border-light-subtle border-3 bubble py-3 px-5 position-relative">
@if (form.value.destination) {
{{ translations.welcomeWithCityName | translate: { cityName: (translations.cityName + '.' + form.value.destination) | translate } }}
{{ translations.welcomeWithCityName | o3rTranslate: { cityName: (translations.cityName + '.' + form.value.destination) | o3rTranslate } }}
} @else {
{{ translations.welcome | translate }}
{{ translations.welcome | o3rTranslate }}
}
<div aria-hidden="true" class="bg-body-tertiary position-absolute start-0 top-50 border border-light-subtle border-3 border-end-0 border-bottom-0"></div>
</h2>
</div>
<h3 class="row card-text mt-auto ms-1 h5">{{ translations.question | translate }}</h3>
<h3 class="row card-text mt-auto ms-1 h5">{{ translations.question | o3rTranslate }}</h3>
<form class="row g-2" [formGroup]="form">
<div class="col-12 col-xl-6">
<div class="input-group">
<label class="input-group-text w-50" for="destination">{{ translations.destinationLabel | translate }}</label>
<label class="input-group-text w-50" for="destination">{{ translations.destinationLabel | o3rTranslate }}</label>
<select class="form-select" id="destination" formControlName="destination">
<option disabled selected value>{{ translations.destinationPlaceholder | translate }}</option>
<option value="LON">{{ (translations.cityName + '.LON') | translate }}</option>
<option value="PAR">{{ (translations.cityName + '.PAR') | translate }}</option>
<option value="NYC">{{ (translations.cityName + '.NYC') | translate }}</option>
<option disabled selected value>{{ translations.destinationPlaceholder | o3rTranslate }}</option>
<option value="LON">{{ (translations.cityName + '.LON') | o3rTranslate }}</option>
<option value="PAR">{{ (translations.cityName + '.PAR') | o3rTranslate }}</option>
<option value="NYC">{{ (translations.cityName + '.NYC') | o3rTranslate }}</option>
</select>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="input-group">
<label for="date-outbound" class="input-group-text w-50">{{ translations.departureLabel | translate }}</label>
<label for="date-outbound" class="input-group-text w-50">{{ translations.departureLabel | o3rTranslate }}</label>
<o3r-date-picker-input-pres [id]="'date-outbound'" class="w-50" formControlName="outboundDate"></o3r-date-picker-input-pres>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
<div class="card my-3">
<div class="row g-0">
<div class="col-4 bg-body-tertiary d-flex align-items-center justify-content-center">
<img [src]="'otter.svg' | dynamicContent" class="img-fluid rounded-start" aria-hidden="true">
<img [src]="'otter.svg' | o3rDynamicContent" class="img-fluid rounded-start" aria-hidden="true">
</div>
<div class="col-8 container p-3">
<div class="card-bod d-flex flex-column h-100">
<div class="row card-title flex-fill d-flex align-items-center m-auto">
<h2 class="bg-body-tertiary text-secondary border border-light-subtle border-3 bubble py-3 px-5 position-relative">
@if (form.value.destination) {
{{ translations.welcomeWithCityName | translate: { cityName: (translations.cityName + '.' + form.value.destination) | translate } }}
{{ translations.welcomeWithCityName | o3rTranslate: { cityName: (translations.cityName + '.' + form.value.destination) | o3rTranslate } }}
} @else {
{{ translations.welcome | translate }}
{{ translations.welcome | o3rTranslate }}
}
<div aria-hidden="true" class="bg-body-tertiary position-absolute start-0 top-50 border border-light-subtle border-3 border-end-0 border-bottom-0"></div>
</h2>
</div>
<h3 class="row card-text mt-auto ms-1 h5">{{ translations.question | translate }}</h3>
<h3 class="row card-text mt-auto ms-1 h5">{{ translations.question | o3rTranslate }}</h3>
<form class="row g-2" [formGroup]="form">
<div class="col-12 col-xl-6" [class.col-xl-12]="(config$ | async)?.shouldProposeRoundTrip">
<div class="input-group">
<label class="input-group-text w-50" for="destination">{{ translations.destinationLabel | translate }}</label>
<label class="input-group-text w-50" for="destination">{{ translations.destinationLabel | o3rTranslate }}</label>
<select class="form-select" id="destination" formControlName="destination">
<option disabled selected value>{{ translations.destinationPlaceholder | translate }}</option>
<option disabled selected value>{{ translations.destinationPlaceholder | o3rTranslate }}</option>
@for (destination of (config$ | async)?.destinations; track destination.cityCode) {
<option [disabled]="!destination.available" [value]="destination.cityCode">
{{ (translations.cityName + '.' + destination.cityCode) | translate }}
{{ (translations.cityName + '.' + destination.cityCode) | o3rTranslate }}
</option>
}
</select>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="input-group">
<label for="date-outbound" class="input-group-text w-50">{{ translations.departureLabel | translate }}</label>
<label for="date-outbound" class="input-group-text w-50">{{ translations.departureLabel | o3rTranslate }}</label>
<o3r-date-picker-input-pres [id]="'date-outbound'" class="w-50" formControlName="outboundDate"></o3r-date-picker-input-pres>
</div>
</div>
@if ((config$ | async)?.shouldProposeRoundTrip) {
<div class="col-12 col-xl-6">
<div class="input-group">
<label for="date-inbound" class="input-group-text w-50">{{ translations.returnLabel | translate }}</label>
<label for="date-inbound" class="input-group-text w-50">{{ translations.returnLabel | o3rTranslate }}</label>
<o3r-date-picker-input-pres [id]="'date-inbound'" class="w-50" formControlName="inboundDate"></o3r-date-picker-input-pres>
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions docs/components/PLACEHOLDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Your second option is to manage your placeholder in a single template and use th

In that use case, you can refer to localization keys in your master placeholder template.
The module will then translate the template based on the localization service and keep it updated after every language
change.
change.
As your placeholder URL remains the same, it will be updated dynamically without any server call.

#### Implementation
Expand Down Expand Up @@ -139,7 +139,7 @@ Let's consider what this placeholder would look like if it were completely integ

```html
"
<div style=\"border-radius:10%; background:red;\">{{'o3r-increment-key' | translate}}</div>"
<div style=\"border-radius:10%; background:red;\">{{'o3r-increment-key' | o3rTranslate}}</div>"
```

Then, let's create a new localization key for each of your supported languages:
Expand All @@ -161,7 +161,7 @@ Then, let's create a new localization key for each of your supported languages:
```

Note that the ``o3r-increment-key`` translations take ``increment`` as a parameter. This means you need to create
an ``increment`` fact to fill the value.
an ``increment`` fact to fill the value.
You can follow the [fact creation documentation](../rules-engine/how-to-use/create-custom-fact.md).

```typescript
Expand All @@ -188,7 +188,7 @@ export class PageFactsService extends FactsService<PageFacts> {
}
```

Once the translation keys and the referenced fact exist, you can link them to your placeholder.
Once the translation keys and the referenced fact exist, you can link them to your placeholder.
See how the original translation pipe has been replaced and how the localization key is bound to the ``increment`` fact:

```json
Expand Down Expand Up @@ -269,5 +269,5 @@ You will probably want to reuse your placeholder in different pages for differen
You might be tempted to use this generic template for all your events but the value of your counter parameter will
depend on the event itself (Easter or next Summer Holidays for example).
This means that ``increment`` might have a different value depending on the context of the page which might be tricky to
maintain and to debug.
maintain and to debug.
Try to keep it as simple as possible.
4 changes: 2 additions & 2 deletions docs/dynamic-content/DYNAMIC_CONTENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ The module provides two things:
A pipe to be used in your component templates:

```html
<img src="{{'assets-otter/imgs/logo.png' | dynamicContent}}" /> or
<img [src]="'assets-otter/imgs/logo.png' | dynamicContent" />
<img src="{{'assets-otter/imgs/logo.png' | o3rDynamicContent}}" /> or
<img [src]="'assets-otter/imgs/logo.png' | o3rDynamicContent" />
```

and a service to be used in your component classes, for example:
Expand Down
4 changes: 2 additions & 2 deletions docs/forms/FORM_ERRORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ This is only an example of implementation. The _translationKey_ and _translation
<input type="date" formControlName="dateOfBirth" [id]="id + 'dateOfBirth'"></input>
<mat-error *ngIf="travelerForm.controls.dateOfBirth.errors?.max">
// use the translation object for the translationKey and get the translationParams from the error object returned by 'date-inline-input'.
{{translations.maxMonthInDate | translate: {max: travelerForm.controls.dateOfBirth.errors?.max.max} }}
{{translations.maxMonthInDate | o3rTranslate: {max: travelerForm.controls.dateOfBirth.errors?.max.max} }}
</mat-error>
```

Expand All @@ -324,7 +324,7 @@ This is only an example of implementation. The _translationKey_ and _translation
<input type="date" formControlName="dateOfBirth" [id]="id + 'dateOfBirth'"></input>
<mat-error *ngFor="let customError of travelerForm.controls.dateOfBirth.errors?.customErrors">
// translation key and params are already accessible in the error object returned by the custom validator
{{customError.translationKey | translate: customError.translationParams }}
{{customError.translationKey | o3rTranslate: customError.translationParams }}
</mat-error>
```

Expand Down
14 changes: 7 additions & 7 deletions docs/localization/LOCALIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export class SimpleHeaderPresModule {}

<!-- using a pipe -->

{{ "o3r-simple-header-pres.motto" | translate }} // => this will output Let's shape the future of travel
{{ "o3r-simple-header-pres.motto" | o3rTranslate }} // => this will output Let's shape the future of travel
<!-- using a directive -->

<div [translate]="o3r-simple-header-pres.locWithArg" [translateParams]="{user: 'otter friend'}"> // => this will output Hello, otter friend!
Expand All @@ -221,7 +221,7 @@ export class SimpleHeaderPresModule {}

<!-- for resource with HTML markup use binding -->

<span [innerHTML]="'someKeyWithHtml' | translate"></span>
<span [innerHTML]="'someKeyWithHtml' | o3rTranslate"></span>

```
Expand All @@ -233,7 +233,7 @@ As a result "**hello bold**" will be printed inside the span element.

<!-- dynamic resource -->

{{ "someBagsAdded" | translate:{bags: 5} }} // => will output "You have added 5 bags"
{{ "someBagsAdded" | o3rTranslate:{bags: 5} }} // => will output "You have added 5 bags"

```
Expand Down Expand Up @@ -485,7 +485,7 @@ import {MESSAGE_FORMAT_CONFIG} from 'ngx-translate-messageformat-compiler';
```typescript
// component html template
...
</span> {{translations.nbOfErrors | translate: {count: countMessages} }}
</span> {{translations.nbOfErrors | o3rTranslate: {count: countMessages} }}
...
```
The value of _translations.nbOfErrors_ is the translation key 'o3r-list-inline-messages-pres.nbOfErrors'. The next step translates the key passing some parameters to translate pipe.
Expand All @@ -508,9 +508,9 @@ Sometimes you may want to display a different resource based on some property va
```typescript
// in component html
<ul>
<li>{{ translations.people | translate: { gender: 'female', how: 'influential' } }}</li>
<li>{{ translations.people | translate: { gender: 'male', how: 'funny' } }}</li>
<li>{{ translations.people | translate: { how: 'affectionate' } }}</li>
<li>{{ translations.people | o3rTranslate: { gender: 'female', how: 'influential' } }}</li>
<li>{{ translations.people | o3rTranslate: { gender: 'male', how: 'funny' } }}</li>
<li>{{ translations.people | o3rTranslate: { how: 'affectionate' } }}</li>
</ul>
```
Note again that _translations.people_ matches _global.people_ key
Expand Down
10 changes: 10 additions & 0 deletions packages/@o3r/components/migration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://raw.githubusercontent.com/angular/angular-cli/master/packages/angular_devkit/schematics/collection-schema.json",
"schematics": {
"migration-v10_0": {
"version": "10.0.0-alpha.0",
"description": "Updates of the Otter Library to v10.0.*",
"factory": "./schematics/ng-update/v10-0/index#updateV10_0"
}
}
}
2 changes: 1 addition & 1 deletion packages/@o3r/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"nx": "nx",
"ng": "yarn nx",
"copy:schemas": "yarn cpy 'schemas/*.json' dist/schemas",
"prepare:build:builders": "yarn cpy 'builders/**/*.json' dist/builders && yarn cpy 'schematics/**/*.json' 'schematics/**/templates/**' dist/schematics && yarn cpy '{builders,collection}.json' dist && yarn copy:schemas",
"prepare:build:builders": "yarn cpy 'builders/**/*.json' dist/builders && yarn cpy 'schematics/**/*.json' 'schematics/**/templates/**' dist/schematics && yarn cpy '{builders,collection,migration}.json' dist && yarn copy:schemas",
"prepare:publish": "prepare-publish ./dist",
"prepare:compile": "cp-package-json",
"build:builders": "tsc -b tsconfig.builders.json --pretty && yarn generate-cjs-manifest",
Expand Down
77 changes: 77 additions & 0 deletions packages/@o3r/components/schematics/ng-update/v10-0/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import * as fs from 'node:fs';
import * as path from 'node:path';

const migrationPath = path.join(__dirname, '..', '..', '..', 'migration.json');


describe('Update v10', () => {
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 () => {
initialTree.create('src/components/example.component.ts', `
import { Component } from '@angular/core';
import { O3rComponent } from '@o3r/core';
import { DurationPipeModule } from '@o3r/components';
@O3rComponent({ componentType: 'Component' })
@Component({
selector: 'o3r-example',
standalone: true,
imports: [DurationPipeModule],
templateUrl: './example.template.html'
})
export class ExampleComponent {
}
`);
const tree = await runner.runSchematic('migration-v10_0', {}, initialTree);
expect(tree.readText('src/components/example.component.ts')).toMatch('O3rDurationPipe');
expect(tree.readText('src/components/example.component.ts')).not.toMatch('DurationPipeModule');
expect(tree.readText('src/components/example.template.html')).toMatch('| o3rDuration');
expect(tree.readText('src/components/example.template.html')).not.toMatch('| duration');
});

it('should replace the pipe with module based component', async () => {
initialTree.create('src/components/example.component.ts', `
import { Component } from '@angular/core';
import { O3rComponent } from '@o3r/core';
@O3rComponent({ componentType: 'Component' })
@Component({
selector: 'o3r-example',
templateUrl: './example.template.html'
})
export class ExampleComponent {
}
`);
initialTree.create('src/components/example.module.ts', `
import { NgModule } from '@angular/core';
import { DurationPipeModule } from '@o3r/components';
import { ExampleComponent } from './example.component';
@NgModule({
imports: [DurationPipeModule],
declarations: [ExampleComponent],
exports: [ExampleComponent]
})
export class ExampleModule {}
`);
const tree = await runner.runSchematic('migration-v10_0', {}, initialTree);
expect(tree.readText('src/components/example.module.ts')).toMatch('O3rDurationPipe');
expect(tree.readText('src/components/example.module.ts')).not.toMatch('DurationPipeModule');
expect(tree.readText('src/components/example.template.html')).toMatch('| o3rDuration');
expect(tree.readText('src/components/example.template.html')).not.toMatch('| duration');
});
});
});
53 changes: 53 additions & 0 deletions packages/@o3r/components/schematics/ng-update/v10-0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-disable camelcase, @typescript-eslint/naming-convention */
import { chain, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { createSchematicWithMetricsIfInstalled, PipeReplacementInfo, updatePipes } from '@o3r/schematics';

const pipeReplacementInfo: PipeReplacementInfo = {
capitalize: {
new: {
name: 'o3rCapitalize',
import: 'O3rCapitalizePipe'
},
import: 'CapitalizePipeModule'
},
duration: {
new: {
name: 'o3rDuration',
import: 'O3rDurationPipe'
},
import: 'DurationPipeModule'
},
keepWhiteSpace: {
new: {
name: 'o3rKeepWhiteSpace',
import: 'O3rKeepWhiteSpacePipe'
},
import: 'KeepWhiteSpacePipeModule'
},
replaceWithBold: {
new: {
name: 'o3rReplaceWithBold',
import: 'O3rReplaceWithBoldPipe'
},
import: 'ReplaceWithBoldPipeModule'
}
};

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

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

return chain(updateRules)(tree, context);
};
}

/**
* Update of Otter library V10.0
*/
export const updateV10_0 = createSchematicWithMetricsIfInstalled(updateV10_0Fn);
Loading

0 comments on commit 273169b

Please sign in to comment.