Skip to content

Commit

Permalink
feat: upgrade to Angular 17, add selectSignal
Browse files Browse the repository at this point in the history
  • Loading branch information
arturovt committed Jan 30, 2024
1 parent e0981de commit e0c4bc7
Show file tree
Hide file tree
Showing 84 changed files with 3,425 additions and 11,290 deletions.
16 changes: 0 additions & 16 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,22 +137,6 @@ jobs:
name: Run integration tests for the SSR build application
command: yarn test:ci:integration:ssr

integration_ng15_tests:
<<: *job_defaults
steps:
- *attach_workspace
# https://github.com/CircleCI-Public/browser-tools-orb/issues/75
- browser-tools/install-browser-tools:
chrome-version: '116.0.5845.96'
replace-existing-chrome: true
- run:
name: Run integration tests for ng15 application
command: yarn integration:ng15
- persist_to_workspace:
root: ~/workspace
paths:
- app/integrations/hello-world-ng15/dist-integration

integration_ng16_tests:
<<: *job_defaults
steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# - ./.github/actions/download-integration-test-artifacts

name: download-integration-test-artifact
description: Downloads all integration test artifacts with names such as 'hello-world-ng15'.
description: Downloads all integration test artifacts with names such as 'hello-world-ng16'.

inputs:
path:
Expand All @@ -18,8 +18,8 @@ inputs:
runs:
using: 'composite'
steps:
- name: Download hello-world-ng15 artifacts
- name: Download hello-world-ng16 artifacts
uses: actions/download-artifact@v3
with:
name: hello-world-ng15
name: hello-world-ng16
path: ${{ inputs.path }}
1 change: 0 additions & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ runs:
~/.cache
./node_modules
./@ngxs
./integrations/hello-world-ng15/dist-integration
./integrations/hello-world-ng16/dist-integration
./integrations/hello-world-ng17/dist-integration
key: ${{ runner.os }}-node-${{ inputs.node-version }}-yarn-${{ hashFiles('**/yarn.lock') }}-branch-${{ inputs.github-ref-name }}-sha-${{ inputs.github-sha }}
Expand Down
6 changes: 3 additions & 3 deletions .github/actions/upload-integration-test-artifact/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
# - ./.github/actions/upload-integration-test-artifact

name: upload-integration-test-artifact
description: Upload an integration test artifact with a name such as 'hello-world-ng15'.
description: Upload an integration test artifact with a name such as 'hello-world-ng16'.

inputs:
script:
description: A script with a name such as 'integration:ng15' that generates an integration test artifact.
description: A script with a name such as 'integration:ng16' that generates an integration test artifact.
required: true

runs:
Expand All @@ -21,7 +21,7 @@ runs:
id: artifact-name
shell: bash
run: |
echo "Replace colons with dashes, substring 'integration' with a substring 'hello-world'. Example result: hello-world-ng15"
echo "Replace colons with dashes, substring 'integration' with a substring 'hello-world'. Example result: hello-world-ng16"
echo "value=$(echo ${SCRIPT} | sed -r "s/:/-/g" | sed -r "s/integration/hello-world/g")" >> $GITHUB_OUTPUT
env:
SCRIPT: ${{ inputs.script }}
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ jobs:
strategy:
matrix:
script:
- 'integration:ng15'
- 'integration:ng16'
- 'integration:ng17'
- 'test:types'
Expand All @@ -106,7 +105,7 @@ jobs:
run: yarn ${{ matrix.script }}

- name: Upload an integration test artifact
if: ${{ matrix.script == 'integration:ng15' }}
if: ${{ matrix.script == 'integration:ng16' }}
uses: ./.github/actions/upload-integration-test-artifact
with:
script: ${{ matrix.script }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
strategy:
matrix:
script:
- 'integration:ng15'
- 'integration:ng16'
- 'test:types'

steps:
Expand All @@ -96,7 +96,7 @@ jobs:
run: yarn ${{ matrix.script }}

- name: Upload an integration test artifact
if: ${{ matrix.script == 'integration:ng15' }}
if: ${{ matrix.script == 'integration:ng16' }}
uses: ./.github/actions/upload-integration-test-artifact
with:
script: ${{ matrix.script }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/trunk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
strategy:
matrix:
script:
- 'integration:ng15'
- 'integration:ng16'
- 'test:types'

steps:
Expand All @@ -96,7 +96,7 @@ jobs:
run: yarn ${{ matrix.script }}

- name: Upload an integration test artifact
if: ${{ matrix.script == 'integration:ng15' }}
if: ${{ matrix.script == 'integration:ng16' }}
uses: ./.github/actions/upload-integration-test-artifact
with:
script: ${{ matrix.script }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@ yarn-error.log
package-lock.json
dist-integration-server/
.cache
migrations.json
.nx/cache
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ build/**
coverage/**
@ngxs/**
**/dist/**

/.nx/cache
9 changes: 4 additions & 5 deletions integration/app/app-browser.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ApplicationConfig } from '@angular/platform-browser';
import { mergeApplicationConfig } from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';

import { appConfig } from './app.config';

// TODO: use `mergeApplicationConfig` in v16.
export const appBrowserConfig: ApplicationConfig = {
providers: [provideAnimations(), ...appConfig.providers]
};
export const appBrowserConfig = mergeApplicationConfig(appConfig, {
providers: [provideAnimations()]
});
11 changes: 5 additions & 6 deletions integration/app/app-server.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ApplicationConfig } from '@angular/platform-browser';
import { provideNoopAnimations } from '@angular/platform-browser/animations';
import { mergeApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';

import { appConfig } from './app.config';

// TODO: use `mergeApplicationConfig` in v16.
export const appServerConfig: ApplicationConfig = {
providers: [provideNoopAnimations(), ...appConfig.providers]
};
export const appBrowserConfig = mergeApplicationConfig(appConfig, {
providers: [provideServerRendering()]
});
6 changes: 3 additions & 3 deletions integration/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="todo-list">
<div>
<h3>Reactive Form</h3>
<p>{{ injected$ | async }}</p>
<p>{{ injected() }}</p>
<form [formGroup]="pizzaForm" novalidate (ngSubmit)="onSubmit()" ngxsForm="todos.pizza">
Toppings: <input type="text" formControlName="toppings" /> <br />
Crust <input type="text" formControlName="crust" /> <br />
Expand All @@ -23,10 +23,10 @@ <h3>Todo Form</h3>
<input placeholder="New Todo" #text /> <button (click)="addTodo(text.value)">Add</button>
</div>
<ul>
<li class="todo" *ngFor="let todo of todos$ | async; let i = index">
<li class="todo" *ngFor="let todo of todos(); let i = index">
{{ todo }} <button (click)="removeTodo(i)">🗑</button>
</li>
<li *ngFor="let todo of pandas$ | async">🐼</li>
<li *ngFor="let todo of pandas()">🐼</li>
</ul>

<div class="menu">
Expand Down
66 changes: 22 additions & 44 deletions integration/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { take } from 'rxjs/operators';
import { Store, provideStore } from '@ngxs/store';
import { withNgxsFormPlugin } from '@ngxs/form-plugin';

Expand Down Expand Up @@ -38,49 +37,38 @@ describe('AppComponent', () => {
component.addTodo('Get Milk');
component.addTodo('Clean Bathroom');

component.todos$.subscribe((state: Todo[]) => {
expect(state.length).toBe(2);
});
const state: Todo[] = component.todos();
expect(state.length).toBe(2);
});

it('should remove a todo', () => {
component.addTodo('Get Milk');
component.addTodo('Clean Bathroom');
component.removeTodo(1);

component.todos$.subscribe((state: Todo[]) => {
expect(state.length).toBe(1);
expect(state[0]).toBe('Get Milk');
});
const state: Todo[] = component.todos();
expect(state.length).toBe(1);
expect(state[0]).toBe('Get Milk');
});

it('should set toppings using form control', fakeAsync(async () => {
const ngxsFormDebounce = 100;
const { pizza } = component;

fixture.detectChanges();
await fixture.whenStable();

component.pizzaForm.patchValue({ toppings: 'oli' });
tick(200);
let flag = false;
tick(ngxsFormDebounce);

component.pizza$.pipe(take(1)).subscribe((pizza: Pizza) => {
flag = true;
expect(pizza.model.toppings).toBe('oli');
expect(pizza.model.crust).toBe('thin');
});

expect(flag).toBe(true);
expect(pizza().model.toppings).toBe('oli');
expect(pizza().model.crust).toBe('thin');

component.pizzaForm.patchValue({ toppings: 'olives', crust: 'thick' });
tick(200);
flag = false;
tick(ngxsFormDebounce);

component.pizza$.pipe(take(1)).subscribe((pizza: Pizza) => {
flag = true;
expect(pizza.model.toppings).toBe('olives');
expect(pizza.model.crust).toBe('thick');
});

expect(flag).toBe(true);
expect(pizza().model.toppings).toBe('olives');
expect(pizza().model.crust).toBe('thick');
}));

it('should set toppings prefix', fakeAsync(async () => {
Expand All @@ -90,30 +78,20 @@ describe('AppComponent', () => {
component.pizzaForm.patchValue({ toppings: 'cheese' });
tick(100);
component.onPrefix();
let flag = false;
tick(100);

component.pizza$.pipe(take(1)).subscribe((pizza: Pizza) => {
flag = true;
expect(pizza.model).toBeDefined();
expect(pizza.model.toppings).toBe('Mr. cheese');
expect(pizza.model.crust).toBe('thin');
});

expect(flag).toBe(true);
const pizza: Pizza = component.pizza();
expect(pizza.model).toBeDefined();
expect(pizza.model.toppings).toBe('Mr. cheese');
expect(pizza.model.crust).toBe('thin');
}));

it('should load data in pizza form', () => {
component.onLoadData();
let flag = false;

component.pizza$.pipe(take(1)).subscribe((pizza: Pizza) => {
flag = true;
expect(pizza.model.toppings).toBe('pineapple');
expect(pizza.model.crust).toBe('medium');
expect(pizza.model.extras).toEqual([false, false, true]);
});

expect(flag).toBe(true);
const pizza: Pizza = component.pizza();
expect(pizza.model.toppings).toBe('pineapple');
expect(pizza.model.crust).toBe('medium');
expect(pizza.model.extras).toEqual([false, false, true]);
});
});
17 changes: 10 additions & 7 deletions integration/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import {
FormsModule,
ReactiveFormsModule
} from '@angular/forms';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnInit, Signal, effect } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { Store } from '@ngxs/store';
import { NgxsFormDirective } from '@ngxs/form-plugin';
import { Observable } from 'rxjs';

import { TodoState } from '@integration/store/todos/todo/todo.state';
import { TodosState } from '@integration/store/todos/todos.state';
Expand Down Expand Up @@ -39,12 +38,16 @@ export class AppComponent implements OnInit {
});

greeting: string;
todos$: Observable<Todo[]> = this._store.select(TodoState);
pandas$: Observable<Todo[]> = this._store.select(TodoState.getPandas);
pizza$: Observable<Pizza> = this._store.select(TodosState.getPizza);
injected$: Observable<string> = this._store.select(TodosState.getInjected);
todos: Signal<Todo[]> = this._store.selectSignal(TodoState);
pandas: Signal<Todo[]> = this._store.selectSignal(TodoState.getPandas);
pizza: Signal<Pizza> = this._store.selectSignal(TodosState.getPizza);
injected: Signal<string> = this._store.selectSignal(TodosState.getInjected);

constructor(private _store: Store, private _fb: FormBuilder) {}
constructor(private _store: Store, private _fb: FormBuilder) {
effect(() => {
console.log('todos() signal: ', this.todos());
});
}

get extras(): FormControl[] {
const extras = this.pizzaForm.get('extras') as FormArray;
Expand Down
5 changes: 2 additions & 3 deletions integration/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { APP_ID } from '@angular/core';
import { APP_ID, ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { ApplicationConfig } from '@angular/platform-browser';
import { provideStore } from '@ngxs/store';
import { withNgxsFormPlugin } from '@ngxs/form-plugin';
import { withNgxsLoggerPlugin } from '@ngxs/logger-plugin';
Expand Down Expand Up @@ -42,7 +41,7 @@ export const appConfig: ApplicationConfig = {
[TodosState, TodoState],
{ developmentMode: !environment.production, selectorOptions: {} },
withNgxsFormPlugin(),
withNgxsLoggerPlugin({ logger: console, collapsed: false, disabled: true }),
withNgxsLoggerPlugin({ logger: console, collapsed: false, disabled: false }),
withNgxsReduxDevtoolsPlugin({ disabled: environment.production }),
withNgxsRouterPlugin(),
withNgxsStoragePlugin({ key: [TODOS_STORAGE_KEY] })
Expand Down
2 changes: 1 addition & 1 deletion integration/app/counter/counter.component.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ counter$ | async | json }}
{{ counter() | json }}
5 changes: 2 additions & 3 deletions integration/app/counter/counter.component.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Signal } from '@angular/core';
import { Store } from '@ngxs/store';
import { CounterStateChangeAction } from '@integration/counter/counter.actions';
import { CounterState, CounterStateModel } from '@integration/counter/counter.state';
import { Observable } from 'rxjs';

@Component({
selector: 'counter',
templateUrl: './counter.component.html'
})
export class CounterComponent implements OnInit {
counter$: Observable<CounterStateModel> = this._store.select(CounterState);
counter: Signal<CounterStateModel> = this._store.selectSignal(CounterState);

constructor(private _store: Store) {}

Expand Down
2 changes: 1 addition & 1 deletion integration/app/detail/detail.component.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ detail$ | async | json }}
{{ detail() | json }}
3 changes: 1 addition & 2 deletions integration/app/detail/detail.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Component } from '@angular/core';
import { Store } from '@ngxs/store';
import { Observable } from 'rxjs';

import { DetailState } from '@integration/detail/detail.state';

Expand All @@ -9,7 +8,7 @@ import { DetailState } from '@integration/detail/detail.state';
templateUrl: './detail.component.html'
})
export class DetailComponent {
detail$: Observable<boolean> = this._store.select(DetailState);
detail = this._store.selectSignal(DetailState);

constructor(private _store: Store) {}
}
Loading

0 comments on commit e0c4bc7

Please sign in to comment.