diff --git a/.all-contributorsrc b/.all-contributorsrc index 0079dd719..6f2f57401 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -29,6 +29,10 @@ "translation-ru": { "symbol": "🇷🇺", "description": "Translate in Russian" + }, + "translation-ch": { + "symbol": "🇨🇳", + "description": "Translate in Chinese" } }, "contributors": [ @@ -100,6 +104,34 @@ "challenge" ] }, + { + "login": "alcaidio", + "name": "Timothy Alcaide", + "avatar_url": "https://avatars.githubusercontent.com/u/17033036?v=4", + "profile": "https://twitter.com/alcaidio", + "contributions": [ + "challenge" + ] + }, + { + "login": "LMFinney", + "name": "Lance Finney", + "avatar_url": "https://avatars.githubusercontent.com/u/6683747?v=4", + "profile": "https://github.com/LMFinney", + "contributions": [ + "doc", + "challenge" + ] + }, + { + "login": "tsironis13", + "name": "Tsironis Ioannis", + "avatar_url": "https://avatars.githubusercontent.com/u/7561447?v=4", + "profile": "https://github.com/tsironis13", + "contributions": [ + "challenge" + ] + }, { "login": "alan-bio", "name": "Alan Dragicevich", @@ -240,6 +272,97 @@ "contributions": [ "translation-ru" ] + }, + { + "login": "vimulatus", + "name": "Vimulatus", + "avatar_url": "https://avatars.githubusercontent.com/u/63696128?v=4", + "profile": "https://github.com/vimulatus", + "contributions": [ + "doc" + ] + }, + { + "login": "alannelucq", + "name": "Arthur LANNELUCQ", + "avatar_url": "https://avatars.githubusercontent.com/u/44091408?v=4", + "profile": "https://github.com/alannelucq", + "contributions": [ + "translation-fr" + ] + }, + { + "login": "fixedmichal", + "name": "fixed_michal", + "avatar_url": "https://avatars.githubusercontent.com/u/26270192?v=4", + "profile": "https://github.com/fixedmichal", + "contributions": [ + "bug" + ] + }, + { + "login": "Tenessy", + "name": "Tenessy", + "avatar_url": "https://avatars.githubusercontent.com/u/65855673?v=4", + "profile": "https://github.com/Tenessy", + "contributions": [ + "bug" + ] + }, + { + "login": "EnochGao", + "name": "Enoch Gao", + "avatar_url": "https://avatars.githubusercontent.com/u/41459067?v=4", + "profile": "https://enochgao.github.io/", + "contributions": [ + "doc", + "translation-ch" + ] + }, + { + "login": "fpalmab", + "name": "Francisco Palma", + "avatar_url": "https://avatars.githubusercontent.com/u/7729812?v=4", + "profile": "https://github.com/fpalmab", + "contributions": [ + "bug" + ] + }, + { + "login": "michalgrzegorczyk-dev", + "name": "Michał Grzegorczyk", + "avatar_url": "https://avatars.githubusercontent.com/u/47832176?v=4", + "profile": "https://github.com/michalgrzegorczyk-dev", + "contributions": [ + "doc" + ] + }, + { + "login": "tamim36", + "name": "Tamim Arefin Anik", + "avatar_url": "https://avatars.githubusercontent.com/u/42251521?v=4", + "profile": "https://github.com/tamim36", + "contributions": [ + "bug" + ] + }, + { + "login": "WhoisBsa", + "name": "Matheus B.", + "avatar_url": "https://avatars.githubusercontent.com/u/36895235?v=4", + "profile": "https://github.com/WhoisBsa", + "contributions": [ + "bug" + ] + }, + { + "login": "StefH", + "name": "Stef Heyenrath", + "avatar_url": "https://avatars.githubusercontent.com/u/249938?v=4", + "profile": "https://sourcerer.io/stefh", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, diff --git a/.eslintrc.json b/.eslintrc.json index c222fb084..de9b234b6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,6 +6,7 @@ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": { + "@angular-eslint/no-host-metadata-property": "off", "@nx/enforce-module-boundaries": [ "error", { @@ -18,24 +19,24 @@ } ] } - ], - "@angular-eslint/no-host-metadata-property": [ - "error", - { - "allowStatic": true - } ] } }, { "files": ["*.ts", "*.tsx"], "extends": ["plugin:@nx/typescript"], - "rules": {} + "rules": { + "@typescript-eslint/no-extra-semi": "error", + "no-extra-semi": "off" + } }, { "files": ["*.js", "*.jsx"], "extends": ["plugin:@nx/javascript"], - "rules": {} + "rules": { + "@typescript-eslint/no-extra-semi": "error", + "no-extra-semi": "off" + } }, { "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], diff --git a/.github/funding.yml b/.github/funding.yml new file mode 100644 index 000000000..f1327048f --- /dev/null +++ b/.github/funding.yml @@ -0,0 +1 @@ +github: [tomalaforge] diff --git a/.github/github-action/action.yml b/.github/github-action/action.yml new file mode 100644 index 000000000..8019592af --- /dev/null +++ b/.github/github-action/action.yml @@ -0,0 +1,14 @@ +name: 'Hello World' +description: 'Greet someone and record the time' +inputs: + github_token: + description: A GitHub token. + required: false + default: ${{ github.token }} + repo: + description: The owner and repository name. + required: false + default: ${{ github.repository }} +runs: + using: 'node20' + main: 'index.js' diff --git a/.github/github-action/contributors.js b/.github/github-action/contributors.js new file mode 100644 index 000000000..9555e6633 --- /dev/null +++ b/.github/github-action/contributors.js @@ -0,0 +1,34 @@ +const contributors = [ + 'alcaidio', + 'svenson95', + 'jdegand', + 'DeveshChau', + 'stillst', + 'wandri', + 'webbomj', + 'kabrunko-dev', + 'Sanjar1304', + 'tsironis13', + 'EnochGao', +]; + +const sponsors = [ + 'ddotx', + 'LMFinney', + 'alannelucq', + 'SidV2', + 'fpalmab', + 'CivilEngeneer', + 'apalaio', + 'amosISA', + 'michalgrzegorczyk-dev', + 'zealotrahl', + 'DzoeL123', + 'allan1989', + 'pchessah', +]; + +module.exports = { + contributors, + sponsors, +}; diff --git a/.github/github-action/index.js b/.github/github-action/index.js new file mode 100644 index 000000000..a73e6c293 --- /dev/null +++ b/.github/github-action/index.js @@ -0,0 +1,45 @@ +const github = require('@actions/github'); +const core = require('@actions/core'); +const { contributors, sponsors } = require('./contributors'); + +async function run() { + try { + const title = github.context.payload.pull_request.title; + const labels = ['answer']; + + const match = title.match(/Answer(:?)\s*(\d+)/); + if (match) { + labels.push(String(parseInt(match[2], 10))); + } + + const actor = github.context.actor; + if (contributors.includes(actor)) { + labels.push('contributor'); + labels.push('to be reviewed'); + } + + if (sponsors.includes(actor)) { + labels.push('sponsor'); + labels.push('to be reviewed'); + } + + const githubToken = core.getInput('github_token'); + + const number = github.context.payload.pull_request.number; + + const octokit = github.getOctokit(githubToken); + await octokit.rest.issues.addLabels({ + labels, + owner: github.context.repo.owner, + repo: github.context.repo.repo, + issue_number: number, + }); + } catch (e) { + if (e instanceof Error) { + core.error(e); + core.setFailed(e.message); + } + } +} + +run(); diff --git a/.github/instructions/nx.instructions.md b/.github/instructions/nx.instructions.md new file mode 100644 index 000000000..ceaed9595 --- /dev/null +++ b/.github/instructions/nx.instructions.md @@ -0,0 +1,31 @@ +--- +applyTo: '**' +--- + +// This file is automatically generated by Nx Console + +You are in an nx workspace using Nx 20.6.4 and npm as the package manager. + +You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user: + +# General Guidelines + +- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture +- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration +- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors +- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool + +# Generation Guidelines + +If the user wants to generate something, use the following flow: + +- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable +- get the available generators using the 'nx_generators' tool +- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them +- get generator details using the 'nx_generator_schema' tool +- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure +- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic +- open the generator UI using the 'nx_open_generate_ui' tool +- wait for the user to finish the generator +- read the generator log file using the 'nx_read_generator_log' tool +- use the information provided in the log file to answer the user's question or continue with what they were doing diff --git a/.github/workflows/close-inactive-pr.yml b/.github/workflows/close-inactive-pr.yml index d92c25cdb..d5e0e9c95 100644 --- a/.github/workflows/close-inactive-pr.yml +++ b/.github/workflows/close-inactive-pr.yml @@ -1,7 +1,7 @@ name: Close inactive issues on: schedule: - - cron: '20 1 * * *' + - cron: '0 0 * * *' jobs: close-issues: @@ -10,18 +10,19 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@v5 + - uses: actions/stale@v9 with: days-before-issue-stale: 20 days-before-issue-close: -1 stale-issue-label: 'stale' - stale-issue-message: 'This issue is stale because it has been open for 20 days with no activity.' + stale-issue-message: 'This issue is stale because it has been open for 15 days with no activity.' exempt-issue-labels: 'long-term' days-before-pr-stale: 20 days-before-pr-close: 7 stale-pr-label: 'stale' - stale-pr-message: 'This pull request is stale because it has been open for 20 days with no activity.' - close-pr-message: 'This pull request was closed because it has been inactive for 7 days since being marked as stale.' + stale-pr-message: 'This pull request is stale because it has been open for 15 days with no activity.' + close-pr-message: 'This pull request was closed because it has been inactive for 5 days since being marked as stale.' only-pr-labels: 'answer' - exempt-pr-labels: 'challenge-creation, long-term' + exempt-pr-labels: 'challenge-creation, long-term, to be reviewed' + remove-pr-stale-when-updated: true repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/label-issue-update.yml b/.github/workflows/label-issue-update.yml new file mode 100644 index 000000000..5f79b0568 --- /dev/null +++ b/.github/workflows/label-issue-update.yml @@ -0,0 +1,21 @@ +name: updates Labels + +on: + push: + branches-ignore: + - main + +jobs: + update_labels: + runs-on: ubuntu-latest + if: | + contains(github.event.pull_request.labels.*.name, 'sponsor') || + contains(github.event.pull_request.labels.*.name, 'contributor') + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: Add labels + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: to be reviewed/update diff --git a/.github/workflows/label-issue.yml b/.github/workflows/label-issue.yml index 5c621375d..a5fd40632 100644 --- a/.github/workflows/label-issue.yml +++ b/.github/workflows/label-issue.yml @@ -1,29 +1,37 @@ name: Add Labels on: - pull_request: + pull_request_target: types: [ opened, edited, synchronize ] jobs: - add_supporters: + check-title: runs-on: ubuntu-latest steps: - - name: checkout - uses: actions/checkout@v2 - - - name: add labels - uses: actions-ecosystem/action-add-labels@v1 - if: ${{ contains( fromJson('[ "tomalaforge", "alcaidio" , "svenson95", "jdegand", "DeveshChau", "stillst", "wandri", "webbomj", "kabrunko-dev", "Sanjar1304"]'), github.actor ) && startsWith(github.event.pull_request.title, 'Answer') }} - with: - labels: supporter - add_answers: + - name: Check PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + echo "Checking PR Title: '$PR_TITLE'" + if [[ ! "$PR_TITLE" =~ ^Answer: ]]; then + echo "❌ PR title should start with 'Answer:[#challenge number]'" + echo "### ❌ PR title should start with 'Answer:[#challenge number]'" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "✅ PR title format is correct." + echo "### ✅ PR title format is correct." >> $GITHUB_STEP_SUMMARY + fi + add_labels: runs-on: ubuntu-latest + if: ${{ startsWith(github.event.pull_request.title, 'Answer') }} steps: - name: checkout uses: actions/checkout@v2 - - name: add labels - uses: actions-ecosystem/action-add-labels@v1 - if: ${{ startsWith(github.event.pull_request.title, 'Answer') }} + - name: Install dependencies + run: npm i @actions/core @actions/github + + - name: Add labels + uses: ./.github/github-action/ with: - labels: answer + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 5472d6bdf..cad0abc01 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ Thumbs.db TODO.md .nx/cache +.nx/workspace-data + +.cursorrules \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index ab639d33d..a4684ea74 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,5 @@ .angular -/.nx/cache \ No newline at end of file +/.nx/cache +/.nx/workspace-data \ No newline at end of file diff --git a/README.md b/README.md index b516e561b..2965ecc08 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f ## Challenges -Check [all 47 challenges](https://angular-challenges.vercel.app/) +Check [all 59 challenges](https://angular-challenges.vercel.app/) ## Contributors ✨ @@ -40,25 +40,42 @@ Check [all 47 challenges](https://angular-challenges.vercel.app/) Devesh Chaudhari
Devesh Chaudhari

💻 🐛 🧩 stillst
stillst

🧩 🇷🇺 Wandrille
Wandrille

🧩 - Alan Dragicevich
Alan Dragicevich

📖 + Timothy Alcaide
Timothy Alcaide

🧩 + Lance Finney
Lance Finney

📖 🧩 + Tsironis Ioannis
Tsironis Ioannis

🧩 + Alan Dragicevich
Alan Dragicevich

📖 Michel EDIGHOFFER
Michel EDIGHOFFER

📖 Gerardo Sebastian Gonzalez
Gerardo Sebastian Gonzalez

📖 Evseev Yuriy
Evseev Yuriy

🐛 Tomer953
Tomer953

🐛 📖 💻 + + Dmitriy Mishchenko
Dmitriy Mishchenko

📖 Sagar Devkota
Sagar Devkota

📖 💻 Nelson Gutierrez
Nelson Gutierrez

🇪🇸 - - Hossain K. M.
Hossain K. M.

📖 Diogo Nishikawa
Diogo Nishikawa

💻 🇵🇹 📖 Erick Rodriguez
Erick Rodriguez

🇪🇸 Eduardo Roth
Eduardo Roth

📖 🇪🇸 + + Fernando Bello
Fernando Bello

📖 Лапин Андрей (Lapin Andrey)
Лапин Андрей (Lapin Andrey)

🇷🇺 Dinar Shagaliev
Dinar Shagaliev

🇷🇺 + Vimulatus
Vimulatus

📖 + Arthur LANNELUCQ
Arthur LANNELUCQ

🇫🇷 + fixed_michal
fixed_michal

🐛 + Tenessy
Tenessy

🐛 + + + Enoch Gao
Enoch Gao

📖 🇨🇳 + Francisco Palma
Francisco Palma

🐛 + Michał Grzegorczyk
Michał Grzegorczyk

📖 + Tamim Arefin Anik
Tamim Arefin Anik

🐛 + Matheus B.
Matheus B.

🐛 + Stef Heyenrath
Stef Heyenrath

📖 @@ -81,6 +98,6 @@ Contributions of any kind are welcome. If I have forgotten to add you as a contributor, please reach out to me. 🙏 -## Licensev +## License MIT diff --git a/apps/angular/projection/.eslintrc.json b/apps/angular/1-projection/.eslintrc.json similarity index 100% rename from apps/angular/projection/.eslintrc.json rename to apps/angular/1-projection/.eslintrc.json diff --git a/apps/angular/projection/README.md b/apps/angular/1-projection/README.md similarity index 100% rename from apps/angular/projection/README.md rename to apps/angular/1-projection/README.md diff --git a/apps/angular/1-projection/jest.config.ts b/apps/angular/1-projection/jest.config.ts new file mode 100644 index 000000000..8eb2510a9 --- /dev/null +++ b/apps/angular/1-projection/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'angular-projection', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/apps/angular/1-projection', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/1-projection/project.json b/apps/angular/1-projection/project.json new file mode 100644 index 000000000..d40912c35 --- /dev/null +++ b/apps/angular/1-projection/project.json @@ -0,0 +1,84 @@ +{ + "name": "angular-projection", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/1-projection/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/1-projection", + "index": "apps/angular/1-projection/src/index.html", + "main": "apps/angular/1-projection/src/main.ts", + "polyfills": ["apps/angular/1-projection/src/polyfills.ts"], + "tsConfig": "apps/angular/1-projection/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/1-projection/src/favicon.ico", + "apps/angular/1-projection/src/assets" + ], + "styles": ["apps/angular/1-projection/src/styles.scss"], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-projection:build:production" + }, + "development": { + "buildTarget": "angular-projection:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-projection:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/1-projection/src/app/app.component.ts b/apps/angular/1-projection/src/app/app.component.ts new file mode 100644 index 000000000..df654bbc2 --- /dev/null +++ b/apps/angular/1-projection/src/app/app.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { CityCardComponent } from './component/city-card/city-card.component'; +import { StudentCardComponent } from './component/student-card/student-card.component'; +import { TeacherCardComponent } from './component/teacher-card/teacher-card.component'; + +@Component({ + selector: 'app-root', + template: ` +
+ + + +
+ `, + imports: [TeacherCardComponent, StudentCardComponent, CityCardComponent], +}) +export class AppComponent {} diff --git a/apps/angular/1-projection/src/app/component/_lib/base-card.directive.ts b/apps/angular/1-projection/src/app/component/_lib/base-card.directive.ts new file mode 100644 index 000000000..34baf799c --- /dev/null +++ b/apps/angular/1-projection/src/app/component/_lib/base-card.directive.ts @@ -0,0 +1,27 @@ +import { Directive, inject, OnInit, Signal } from '@angular/core'; +import { Observable } from 'rxjs'; +import { BaseDataAccessStore } from '../../data-access/_lib/base-service-data-access-store'; +import { FakeHttpService } from '../../data-access/fake-http.service'; + +@Directive() +export abstract class BaseCardComponent implements OnInit { + private _store = inject(BaseDataAccessStore); + protected http = inject(FakeHttpService); + + abstract randMethod: () => T; + items: Signal = this._store.items; + + abstract httpItems$: Observable; + + ngOnInit(): void { + this.httpItems$.subscribe((t) => this._store.addAll(t)); + } + + addNewItem() { + this._store.addOne(this.randMethod()); + } + + removeItem(id: number) { + this._store.deleteOne(id); + } +} diff --git a/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts b/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts new file mode 100644 index 000000000..6d499d80f --- /dev/null +++ b/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts @@ -0,0 +1,65 @@ +import { Component } from '@angular/core'; +import { CityStore } from '../../data-access/city.store'; +import { randomCity } from '../../data-access/fake-http.service'; // Use randomCity as defined +import { City } from '../../model/city.model'; +import { CardComponent } from '../../ui/card/card.component'; +import { ListItemComponent } from '../../ui/list-item/list-item.component'; +import { BaseCardComponent } from '../_lib/base-card.directive'; + +import { NgOptimizedImage } from '@angular/common'; +import { BaseDataAccessStore } from '../../data-access/_lib/base-service-data-access-store'; +import { CardActionsDirective } from '../../ui/card/card-actions.directive'; +import { CardHeaderDirective } from '../../ui/card/card-header.directive'; +import { ListItemActionsDirective } from '../../ui/list-item/list-item-actions.directive'; + +@Component({ + selector: 'app-city-card', + template: ` + + City Icon + + + + {{ city.name }} - {{ city.country }} + + + + + + + `, + imports: [ + CardComponent, + ListItemComponent, + CardHeaderDirective, + CardActionsDirective, + ListItemActionsDirective, + NgOptimizedImage, + ], + providers: [ + { + provide: BaseDataAccessStore, + useClass: CityStore, + }, + ], + standalone: true, +}) +export class CityCardComponent extends BaseCardComponent { + override randMethod = () => randomCity(); + override httpItems$ = this.http.fetchCities$; +} diff --git a/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts b/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts new file mode 100644 index 000000000..c2bf471ca --- /dev/null +++ b/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts @@ -0,0 +1,64 @@ +import { NgOptimizedImage } from '@angular/common'; +import { Component } from '@angular/core'; +import { BaseDataAccessStore } from '../../data-access/_lib/base-service-data-access-store'; +import { randStudent } from '../../data-access/fake-http.service'; +import { StudentStore } from '../../data-access/student.store'; +import { Student } from '../../model/student.model'; +import { CardActionsDirective } from '../../ui/card/card-actions.directive'; +import { CardHeaderDirective } from '../../ui/card/card-header.directive'; +import { CardComponent } from '../../ui/card/card.component'; +import { ListItemActionsDirective } from '../../ui/list-item/list-item-actions.directive'; +import { ListItemComponent } from '../../ui/list-item/list-item.component'; +import { BaseCardComponent } from '../_lib/base-card.directive'; + +@Component({ + selector: 'app-student-card', + template: ` + + Student Icon + + + + {{ student.firstName }} + + + + + + + `, + imports: [ + CardComponent, + ListItemComponent, + CardHeaderDirective, + CardActionsDirective, + ListItemActionsDirective, + NgOptimizedImage, + ], + providers: [ + { + provide: BaseDataAccessStore, + useClass: StudentStore, + }, + ], + standalone: true, +}) +export class StudentCardComponent extends BaseCardComponent { + override randMethod = () => randStudent(); + override httpItems$ = this.http.fetchStudents$; +} diff --git a/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts b/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts new file mode 100644 index 000000000..f313964fa --- /dev/null +++ b/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts @@ -0,0 +1,65 @@ +import { NgOptimizedImage } from '@angular/common'; +import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; +import { BaseDataAccessStore } from '../../data-access/_lib/base-service-data-access-store'; +import { randTeacher } from '../../data-access/fake-http.service'; +import { TeacherStore } from '../../data-access/teacher.store'; +import { Teacher } from '../../model/teacher.model'; +import { CardActionsDirective } from '../../ui/card/card-actions.directive'; +import { CardHeaderDirective } from '../../ui/card/card-header.directive'; +import { CardComponent } from '../../ui/card/card.component'; +import { ListItemActionsDirective } from '../../ui/list-item/list-item-actions.directive'; +import { ListItemComponent } from '../../ui/list-item/list-item.component'; +import { BaseCardComponent } from '../_lib/base-card.directive'; + +@Component({ + selector: 'app-teacher-card', + template: ` + + Teacher Icon + + + + {{ teacher.firstName }} + + + + + + + `, + imports: [ + CardComponent, + ListItemComponent, + CardHeaderDirective, + CardActionsDirective, + ListItemActionsDirective, + NgOptimizedImage, + ], + providers: [ + { + provide: BaseDataAccessStore, + useClass: TeacherStore, + }, + ], + standalone: true, +}) +export class TeacherCardComponent extends BaseCardComponent { + override randMethod = () => randTeacher(); + override httpItems$: Observable = this.http.fetchTeachers$; +} diff --git a/apps/angular/1-projection/src/app/data-access/_lib/base-service-data-access-store.ts b/apps/angular/1-projection/src/app/data-access/_lib/base-service-data-access-store.ts new file mode 100644 index 000000000..3a7ecd072 --- /dev/null +++ b/apps/angular/1-projection/src/app/data-access/_lib/base-service-data-access-store.ts @@ -0,0 +1,21 @@ +import { signal } from '@angular/core'; +import { IModel } from '../../model/base.model'; +import { IServiceDataAccessStore } from './store.utils'; + +export abstract class BaseDataAccessStore + implements IServiceDataAccessStore +{ + items = signal([]); + + addAll(items: T[]) { + this.items.set(items); + } + + addOne(item: T) { + this.items.set([...this.items(), item]); + } + + deleteOne(id: number) { + this.items.set(this.items().filter((item: T) => item.id !== id)); + } +} diff --git a/apps/angular/1-projection/src/app/data-access/_lib/store.utils.ts b/apps/angular/1-projection/src/app/data-access/_lib/store.utils.ts new file mode 100644 index 000000000..1ab3447be --- /dev/null +++ b/apps/angular/1-projection/src/app/data-access/_lib/store.utils.ts @@ -0,0 +1,9 @@ +import { WritableSignal } from '@angular/core'; + +export interface IServiceDataAccessStore { + items: WritableSignal; + + addAll(teachers: T[]): void; + addOne(teacher: T): void; + deleteOne(id: number): void; +} diff --git a/apps/angular/1-projection/src/app/data-access/city.store.ts b/apps/angular/1-projection/src/app/data-access/city.store.ts new file mode 100644 index 000000000..e7cc94501 --- /dev/null +++ b/apps/angular/1-projection/src/app/data-access/city.store.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; +import { City } from '../model/city.model'; +import { BaseDataAccessStore } from './_lib/base-service-data-access-store'; + +@Injectable({ + providedIn: 'root', +}) +export class CityStore extends BaseDataAccessStore {} diff --git a/apps/angular/projection/src/app/data-access/fake-http.service.ts b/apps/angular/1-projection/src/app/data-access/fake-http.service.ts similarity index 100% rename from apps/angular/projection/src/app/data-access/fake-http.service.ts rename to apps/angular/1-projection/src/app/data-access/fake-http.service.ts diff --git a/apps/angular/1-projection/src/app/data-access/student.store.ts b/apps/angular/1-projection/src/app/data-access/student.store.ts new file mode 100644 index 000000000..ce5e52dff --- /dev/null +++ b/apps/angular/1-projection/src/app/data-access/student.store.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@angular/core'; +import { Student } from '../model/student.model'; +import { BaseDataAccessStore } from './_lib/base-service-data-access-store'; + +@Injectable() +export class StudentStore extends BaseDataAccessStore {} diff --git a/apps/angular/1-projection/src/app/data-access/teacher.store.ts b/apps/angular/1-projection/src/app/data-access/teacher.store.ts new file mode 100644 index 000000000..8c978058c --- /dev/null +++ b/apps/angular/1-projection/src/app/data-access/teacher.store.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@angular/core'; +import { Teacher } from '../model/teacher.model'; +import { BaseDataAccessStore } from './_lib/base-service-data-access-store'; + +@Injectable() +export class TeacherStore extends BaseDataAccessStore {} diff --git a/apps/angular/1-projection/src/app/model/base.model.ts b/apps/angular/1-projection/src/app/model/base.model.ts new file mode 100644 index 000000000..aada4a883 --- /dev/null +++ b/apps/angular/1-projection/src/app/model/base.model.ts @@ -0,0 +1,3 @@ +export interface IModel { + id: number; +} diff --git a/apps/angular/projection/src/app/model/card.model.ts b/apps/angular/1-projection/src/app/model/card.model.ts similarity index 100% rename from apps/angular/projection/src/app/model/card.model.ts rename to apps/angular/1-projection/src/app/model/card.model.ts diff --git a/apps/angular/1-projection/src/app/model/city.model.ts b/apps/angular/1-projection/src/app/model/city.model.ts new file mode 100644 index 000000000..8689c82d4 --- /dev/null +++ b/apps/angular/1-projection/src/app/model/city.model.ts @@ -0,0 +1,6 @@ +import { IModel } from './base.model'; + +export interface City extends IModel { + name: string; + country: string; +} diff --git a/apps/angular/1-projection/src/app/model/student.model.ts b/apps/angular/1-projection/src/app/model/student.model.ts new file mode 100644 index 000000000..e346e480e --- /dev/null +++ b/apps/angular/1-projection/src/app/model/student.model.ts @@ -0,0 +1,9 @@ +import { IModel } from './base.model'; +import { Teacher } from './teacher.model'; + +export interface Student extends IModel { + firstName: string; + lastName: string; + mainTeacher: Teacher; + school: string; +} diff --git a/apps/angular/1-projection/src/app/model/teacher.model.ts b/apps/angular/1-projection/src/app/model/teacher.model.ts new file mode 100644 index 000000000..dc67afdba --- /dev/null +++ b/apps/angular/1-projection/src/app/model/teacher.model.ts @@ -0,0 +1,16 @@ +import { IModel } from './base.model'; + +export const subject = [ + 'Sciences', + 'History', + 'English', + 'Maths', + 'Sport', +] as const; +export type Subject = (typeof subject)[number]; + +export interface Teacher extends IModel { + firstName: string; + lastName: string; + subject: Subject; +} diff --git a/apps/angular/1-projection/src/app/ui/card/card-actions.directive.ts b/apps/angular/1-projection/src/app/ui/card/card-actions.directive.ts new file mode 100644 index 000000000..cb114d0bc --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/card/card-actions.directive.ts @@ -0,0 +1,7 @@ +import { Directive } from '@angular/core'; + +@Directive({ + selector: '[appCardActions]', + standalone: true, +}) +export class CardActionsDirective {} diff --git a/apps/angular/1-projection/src/app/ui/card/card-header.directive.ts b/apps/angular/1-projection/src/app/ui/card/card-header.directive.ts new file mode 100644 index 000000000..105d26ede --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/card/card-header.directive.ts @@ -0,0 +1,7 @@ +import { Directive } from '@angular/core'; + +@Directive({ + selector: '[appCardHeader]', + standalone: true, +}) +export class CardHeaderDirective {} diff --git a/apps/angular/1-projection/src/app/ui/card/card.component.ts b/apps/angular/1-projection/src/app/ui/card/card.component.ts new file mode 100644 index 000000000..db7b0679e --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/card/card.component.ts @@ -0,0 +1,30 @@ +import { NgTemplateOutlet } from '@angular/common'; +import { Component, input, TemplateRef } from '@angular/core'; + +@Component({ + selector: 'app-card', + template: ` +
+ + +
+ @for (item of list(); track $index) { + + } +
+ + +
+ `, + imports: [NgTemplateOutlet], + standalone: true, +}) +export class CardComponent { + readonly list = input(null); + readonly customClass = input(''); + readonly itemTpl = input.required>(); +} diff --git a/apps/angular/1-projection/src/app/ui/list-item/list-item-actions.directive.ts b/apps/angular/1-projection/src/app/ui/list-item/list-item-actions.directive.ts new file mode 100644 index 000000000..b49298200 --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/list-item/list-item-actions.directive.ts @@ -0,0 +1,7 @@ +import { Directive } from '@angular/core'; + +@Directive({ + selector: '[appListItemActions]', + standalone: true, +}) +export class ListItemActionsDirective {} diff --git a/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts b/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts new file mode 100644 index 000000000..76824d923 --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts @@ -0,0 +1,14 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'app-list-item', + template: ` +
+ + +
+ `, + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ListItemComponent {} diff --git a/apps/angular/anchor-scrolling/src/assets/.gitkeep b/apps/angular/1-projection/src/assets/.gitkeep similarity index 100% rename from apps/angular/anchor-scrolling/src/assets/.gitkeep rename to apps/angular/1-projection/src/assets/.gitkeep diff --git a/apps/angular/projection/src/assets/img/city.png b/apps/angular/1-projection/src/assets/img/city.png similarity index 100% rename from apps/angular/projection/src/assets/img/city.png rename to apps/angular/1-projection/src/assets/img/city.png diff --git a/apps/angular/projection/src/assets/img/student.webp b/apps/angular/1-projection/src/assets/img/student.webp similarity index 100% rename from apps/angular/projection/src/assets/img/student.webp rename to apps/angular/1-projection/src/assets/img/student.webp diff --git a/apps/angular/projection/src/assets/img/teacher.png b/apps/angular/1-projection/src/assets/img/teacher.png similarity index 100% rename from apps/angular/projection/src/assets/img/teacher.png rename to apps/angular/1-projection/src/assets/img/teacher.png diff --git a/apps/angular/projection/src/assets/svg/trash.svg b/apps/angular/1-projection/src/assets/svg/trash.svg similarity index 100% rename from apps/angular/projection/src/assets/svg/trash.svg rename to apps/angular/1-projection/src/assets/svg/trash.svg diff --git a/apps/angular/anchor-scrolling/src/favicon.ico b/apps/angular/1-projection/src/favicon.ico similarity index 100% rename from apps/angular/anchor-scrolling/src/favicon.ico rename to apps/angular/1-projection/src/favicon.ico diff --git a/apps/angular/1-projection/src/index.html b/apps/angular/1-projection/src/index.html new file mode 100644 index 000000000..bb9c8bc84 --- /dev/null +++ b/apps/angular/1-projection/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-projection + + + + + + + + diff --git a/apps/angular/context-outlet-type/src/main.ts b/apps/angular/1-projection/src/main.ts similarity index 100% rename from apps/angular/context-outlet-type/src/main.ts rename to apps/angular/1-projection/src/main.ts diff --git a/apps/angular/context-outlet-type/src/polyfills.ts b/apps/angular/1-projection/src/polyfills.ts similarity index 100% rename from apps/angular/context-outlet-type/src/polyfills.ts rename to apps/angular/1-projection/src/polyfills.ts diff --git a/apps/angular/1-projection/src/styles.scss b/apps/angular/1-projection/src/styles.scss new file mode 100644 index 000000000..9b10c550f --- /dev/null +++ b/apps/angular/1-projection/src/styles.scss @@ -0,0 +1,15 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +.bg-light-red { + background-color: rgba(250, 0, 0, 0.1); +} + +.bg-light-blue { + background-color: rgba(0, 0, 250, 0.1); +} + +.bg-light-green { + background-color: rgba(0, 250, 0, 0.1); +} diff --git a/apps/angular/anchor-scrolling/src/test-setup.ts b/apps/angular/1-projection/src/test-setup.ts similarity index 100% rename from apps/angular/anchor-scrolling/src/test-setup.ts rename to apps/angular/1-projection/src/test-setup.ts diff --git a/apps/angular/anchor-scrolling/tailwind.config.js b/apps/angular/1-projection/tailwind.config.js similarity index 100% rename from apps/angular/anchor-scrolling/tailwind.config.js rename to apps/angular/1-projection/tailwind.config.js diff --git a/apps/angular/context-outlet-type/tsconfig.app.json b/apps/angular/1-projection/tsconfig.app.json similarity index 100% rename from apps/angular/context-outlet-type/tsconfig.app.json rename to apps/angular/1-projection/tsconfig.app.json diff --git a/apps/angular/projection/tsconfig.editor.json b/apps/angular/1-projection/tsconfig.editor.json similarity index 100% rename from apps/angular/projection/tsconfig.editor.json rename to apps/angular/1-projection/tsconfig.editor.json diff --git a/apps/angular/crud/tsconfig.json b/apps/angular/1-projection/tsconfig.json similarity index 100% rename from apps/angular/crud/tsconfig.json rename to apps/angular/1-projection/tsconfig.json diff --git a/apps/angular/projection/tsconfig.spec.json b/apps/angular/1-projection/tsconfig.spec.json similarity index 100% rename from apps/angular/projection/tsconfig.spec.json rename to apps/angular/1-projection/tsconfig.spec.json diff --git a/apps/angular/crud/.eslintrc.json b/apps/angular/10-utility-wrapper-pipe/.eslintrc.json similarity index 100% rename from apps/angular/crud/.eslintrc.json rename to apps/angular/10-utility-wrapper-pipe/.eslintrc.json diff --git a/apps/angular/10-utility-wrapper-pipe/README.md b/apps/angular/10-utility-wrapper-pipe/README.md new file mode 100644 index 000000000..aac426271 --- /dev/null +++ b/apps/angular/10-utility-wrapper-pipe/README.md @@ -0,0 +1,13 @@ +# Utility Wrapper Pipe + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-utility-wrapper-pipe +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/10-pipe-utility/). diff --git a/apps/angular/10-utility-wrapper-pipe/project.json b/apps/angular/10-utility-wrapper-pipe/project.json new file mode 100644 index 000000000..233fc183d --- /dev/null +++ b/apps/angular/10-utility-wrapper-pipe/project.json @@ -0,0 +1,72 @@ +{ + "name": "angular-utility-wrapper-pipe", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/10-utility-wrapper-pipe/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/10-utility-wrapper-pipe", + "index": "apps/angular/10-utility-wrapper-pipe/src/index.html", + "main": "apps/angular/10-utility-wrapper-pipe/src/main.ts", + "polyfills": "apps/angular/10-utility-wrapper-pipe/src/polyfills.ts", + "tsConfig": "apps/angular/10-utility-wrapper-pipe/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/10-utility-wrapper-pipe/src/favicon.ico", + "apps/angular/10-utility-wrapper-pipe/src/assets" + ], + "styles": ["apps/angular/10-utility-wrapper-pipe/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-utility-wrapper-pipe:build:production" + }, + "development": { + "buildTarget": "angular-utility-wrapper-pipe:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-utility-wrapper-pipe:build" + } + } + } +} diff --git a/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts b/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts new file mode 100644 index 000000000..9a8156a5f --- /dev/null +++ b/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { PersonUtils } from './person.utils'; + +@Component({ + selector: 'app-root', + template: ` + @for (activity of activities; track activity.name) { + {{ activity.name }} : + @for ( + person of persons; + track person.name; + let index = $index; + let isFirst = $first + ) { + {{ showName(person.name, index) }} + {{ isAllowed(person.age, isFirst, activity.minimumAge) }} + } + } + `, +}) +export class AppComponent { + persons = [ + { name: 'Toto', age: 10 }, + { name: 'Jack', age: 15 }, + { name: 'John', age: 30 }, + ]; + + activities = [ + { name: 'biking', minimumAge: 12 }, + { name: 'hiking', minimumAge: 25 }, + { name: 'dancing', minimumAge: 1 }, + ]; + + showName = PersonUtils.showName; + + isAllowed = PersonUtils.isAllowed; +} diff --git a/apps/angular/pipe-hard/src/app/person.utils.ts b/apps/angular/10-utility-wrapper-pipe/src/app/person.utils.ts similarity index 100% rename from apps/angular/pipe-hard/src/app/person.utils.ts rename to apps/angular/10-utility-wrapper-pipe/src/app/person.utils.ts diff --git a/apps/angular/bug-cd/src/assets/.gitkeep b/apps/angular/10-utility-wrapper-pipe/src/assets/.gitkeep similarity index 100% rename from apps/angular/bug-cd/src/assets/.gitkeep rename to apps/angular/10-utility-wrapper-pipe/src/assets/.gitkeep diff --git a/apps/angular/bug-cd/src/favicon.ico b/apps/angular/10-utility-wrapper-pipe/src/favicon.ico similarity index 100% rename from apps/angular/bug-cd/src/favicon.ico rename to apps/angular/10-utility-wrapper-pipe/src/favicon.ico diff --git a/apps/angular/10-utility-wrapper-pipe/src/index.html b/apps/angular/10-utility-wrapper-pipe/src/index.html new file mode 100644 index 000000000..01d31a163 --- /dev/null +++ b/apps/angular/10-utility-wrapper-pipe/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-utility-wrapper-pipe + + + + + + + + diff --git a/apps/angular/decoupling/src/main.ts b/apps/angular/10-utility-wrapper-pipe/src/main.ts similarity index 100% rename from apps/angular/decoupling/src/main.ts rename to apps/angular/10-utility-wrapper-pipe/src/main.ts diff --git a/apps/angular/crud/src/polyfills.ts b/apps/angular/10-utility-wrapper-pipe/src/polyfills.ts similarity index 100% rename from apps/angular/crud/src/polyfills.ts rename to apps/angular/10-utility-wrapper-pipe/src/polyfills.ts diff --git a/apps/angular/context-outlet-type/src/styles.scss b/apps/angular/10-utility-wrapper-pipe/src/styles.scss similarity index 100% rename from apps/angular/context-outlet-type/src/styles.scss rename to apps/angular/10-utility-wrapper-pipe/src/styles.scss diff --git a/apps/angular/crud/tsconfig.app.json b/apps/angular/10-utility-wrapper-pipe/tsconfig.app.json similarity index 100% rename from apps/angular/crud/tsconfig.app.json rename to apps/angular/10-utility-wrapper-pipe/tsconfig.app.json diff --git a/apps/angular/context-outlet-type/tsconfig.editor.json b/apps/angular/10-utility-wrapper-pipe/tsconfig.editor.json similarity index 100% rename from apps/angular/context-outlet-type/tsconfig.editor.json rename to apps/angular/10-utility-wrapper-pipe/tsconfig.editor.json diff --git a/apps/angular/context-outlet-type/tsconfig.json b/apps/angular/10-utility-wrapper-pipe/tsconfig.json similarity index 100% rename from apps/angular/context-outlet-type/tsconfig.json rename to apps/angular/10-utility-wrapper-pipe/tsconfig.json diff --git a/apps/angular/anchor-scrolling/.eslintrc.json b/apps/angular/13-highly-customizable-css/.eslintrc.json similarity index 100% rename from apps/angular/anchor-scrolling/.eslintrc.json rename to apps/angular/13-highly-customizable-css/.eslintrc.json diff --git a/apps/angular/13-highly-customizable-css/README.md b/apps/angular/13-highly-customizable-css/README.md new file mode 100644 index 000000000..d63171ae6 --- /dev/null +++ b/apps/angular/13-highly-customizable-css/README.md @@ -0,0 +1,13 @@ +# Highly Customizable CSS + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-highly-customizable-css +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/13-styling/). diff --git a/apps/angular/13-highly-customizable-css/project.json b/apps/angular/13-highly-customizable-css/project.json new file mode 100644 index 000000000..8bdda5dba --- /dev/null +++ b/apps/angular/13-highly-customizable-css/project.json @@ -0,0 +1,72 @@ +{ + "name": "angular-highly-customizable-css", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/13-highly-customizable-css/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/13-highly-customizable-css", + "index": "apps/angular/13-highly-customizable-css/src/index.html", + "main": "apps/angular/13-highly-customizable-css/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/13-highly-customizable-css/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/13-highly-customizable-css/src/favicon.ico", + "apps/angular/13-highly-customizable-css/src/assets" + ], + "styles": ["apps/angular/13-highly-customizable-css/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-highly-customizable-css:build:production" + }, + "development": { + "buildTarget": "angular-highly-customizable-css:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-highly-customizable-css:build" + } + } + } +} diff --git a/apps/angular/13-highly-customizable-css/src/app/page.component.ts b/apps/angular/13-highly-customizable-css/src/app/page.component.ts new file mode 100644 index 000000000..029ca52d2 --- /dev/null +++ b/apps/angular/13-highly-customizable-css/src/app/page.component.ts @@ -0,0 +1,16 @@ +/* eslint-disable @angular-eslint/component-selector */ +import { Component } from '@angular/core'; +import { TextStaticComponent } from './static-text.component'; +import { TextComponent } from './text.component'; + +@Component({ + selector: 'page', + imports: [TextStaticComponent, TextComponent], + template: ` + + + + This is a blue text + `, +}) +export class PageComponent {} diff --git a/apps/angular/styling/src/app/static-text.component.ts b/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts similarity index 97% rename from apps/angular/styling/src/app/static-text.component.ts rename to apps/angular/13-highly-customizable-css/src/app/static-text.component.ts index cdfd1c19f..70d57d9a3 100644 --- a/apps/angular/styling/src/app/static-text.component.ts +++ b/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts @@ -6,7 +6,6 @@ export type StaticTextType = 'normal' | 'warning' | 'error'; @Component({ selector: 'static-text', - standalone: true, imports: [TextComponent], template: ` This is a static text diff --git a/apps/angular/styling/src/app/text.component.ts b/apps/angular/13-highly-customizable-css/src/app/text.component.ts similarity index 100% rename from apps/angular/styling/src/app/text.component.ts rename to apps/angular/13-highly-customizable-css/src/app/text.component.ts diff --git a/apps/angular/crud/src/assets/.gitkeep b/apps/angular/13-highly-customizable-css/src/assets/.gitkeep similarity index 100% rename from apps/angular/crud/src/assets/.gitkeep rename to apps/angular/13-highly-customizable-css/src/assets/.gitkeep diff --git a/apps/angular/context-outlet-type/src/favicon.ico b/apps/angular/13-highly-customizable-css/src/favicon.ico similarity index 100% rename from apps/angular/context-outlet-type/src/favicon.ico rename to apps/angular/13-highly-customizable-css/src/favicon.ico diff --git a/apps/angular/13-highly-customizable-css/src/index.html b/apps/angular/13-highly-customizable-css/src/index.html new file mode 100644 index 000000000..e4a84b456 --- /dev/null +++ b/apps/angular/13-highly-customizable-css/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-highly-customizable-css + + + + + + + + diff --git a/apps/angular/styling/src/main.ts b/apps/angular/13-highly-customizable-css/src/main.ts similarity index 100% rename from apps/angular/styling/src/main.ts rename to apps/angular/13-highly-customizable-css/src/main.ts diff --git a/apps/angular/ngfor-enhancement/src/styles.scss b/apps/angular/13-highly-customizable-css/src/styles.scss similarity index 100% rename from apps/angular/ngfor-enhancement/src/styles.scss rename to apps/angular/13-highly-customizable-css/src/styles.scss diff --git a/apps/angular/anchor-scrolling/tsconfig.app.json b/apps/angular/13-highly-customizable-css/tsconfig.app.json similarity index 100% rename from apps/angular/anchor-scrolling/tsconfig.app.json rename to apps/angular/13-highly-customizable-css/tsconfig.app.json diff --git a/apps/angular/di/tsconfig.editor.json b/apps/angular/13-highly-customizable-css/tsconfig.editor.json similarity index 100% rename from apps/angular/di/tsconfig.editor.json rename to apps/angular/13-highly-customizable-css/tsconfig.editor.json diff --git a/apps/angular/decoupling/tsconfig.json b/apps/angular/13-highly-customizable-css/tsconfig.json similarity index 100% rename from apps/angular/decoupling/tsconfig.json rename to apps/angular/13-highly-customizable-css/tsconfig.json diff --git a/apps/angular/di/.eslintrc.json b/apps/angular/16-master-dependency-injection/.eslintrc.json similarity index 100% rename from apps/angular/di/.eslintrc.json rename to apps/angular/16-master-dependency-injection/.eslintrc.json diff --git a/apps/angular/16-master-dependency-injection/README.md b/apps/angular/16-master-dependency-injection/README.md new file mode 100644 index 000000000..be19c1ba3 --- /dev/null +++ b/apps/angular/16-master-dependency-injection/README.md @@ -0,0 +1,13 @@ +# Master Dependancy Injection + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-master-dependency-injection +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/16-di/). diff --git a/apps/angular/16-master-dependency-injection/project.json b/apps/angular/16-master-dependency-injection/project.json new file mode 100644 index 000000000..280ed3df4 --- /dev/null +++ b/apps/angular/16-master-dependency-injection/project.json @@ -0,0 +1,74 @@ +{ + "name": "angular-master-dependency-injection", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/16-master-dependency-injection/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/16-master-dependency-injection", + "index": "apps/angular/16-master-dependency-injection/src/index.html", + "main": "apps/angular/16-master-dependency-injection/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/16-master-dependency-injection/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/16-master-dependency-injection/src/favicon.ico", + "apps/angular/16-master-dependency-injection/src/assets" + ], + "styles": [ + "apps/angular/16-master-dependency-injection/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-master-dependency-injection:build:production" + }, + "development": { + "buildTarget": "angular-master-dependency-injection:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-master-dependency-injection:build" + } + } + } +} diff --git a/apps/angular/16-master-dependency-injection/src/app/app.component.ts b/apps/angular/16-master-dependency-injection/src/app/app.component.ts new file mode 100644 index 000000000..5bb91c2b2 --- /dev/null +++ b/apps/angular/16-master-dependency-injection/src/app/app.component.ts @@ -0,0 +1,52 @@ +import { TableComponent } from '@angular-challenges/shared/ui'; +import { AsyncPipe, NgFor } from '@angular/common'; +import { Component, Directive } from '@angular/core'; +import { CurrencyPipe } from './currency.pipe'; +import { CurrencyService } from './currency.service'; +import { Product, products } from './product.model'; + +interface ProductContext { + $implicit: Product; +} + +@Directive({ + selector: 'ng-template[product]', + standalone: true, +}) +export class ProductDirective { + static ngTemplateContextGuard( + dir: ProductDirective, + ctx: unknown, + ): ctx is ProductContext { + return true; + } +} + +@Component({ + imports: [TableComponent, CurrencyPipe, AsyncPipe, NgFor, ProductDirective], + providers: [CurrencyService], + selector: 'app-root', + template: ` + + + + + + + + + + + + + + +
+ {{ col }} +
{{ product.name }}{{ product.priceA | currency | async }}{{ product.priceB | currency | async }}{{ product.priceC | currency | async }}
+ `, +}) +export class AppComponent { + products = products; + displayedColumns = ['name', 'priceA', 'priceB', 'priceC']; +} diff --git a/apps/angular/di/src/app/currency.pipe.ts b/apps/angular/16-master-dependency-injection/src/app/currency.pipe.ts similarity index 100% rename from apps/angular/di/src/app/currency.pipe.ts rename to apps/angular/16-master-dependency-injection/src/app/currency.pipe.ts diff --git a/apps/angular/di/src/app/currency.service.ts b/apps/angular/16-master-dependency-injection/src/app/currency.service.ts similarity index 100% rename from apps/angular/di/src/app/currency.service.ts rename to apps/angular/16-master-dependency-injection/src/app/currency.service.ts diff --git a/apps/angular/di/src/app/product.model.ts b/apps/angular/16-master-dependency-injection/src/app/product.model.ts similarity index 100% rename from apps/angular/di/src/app/product.model.ts rename to apps/angular/16-master-dependency-injection/src/app/product.model.ts diff --git a/apps/angular/decoupling/src/assets/.gitkeep b/apps/angular/16-master-dependency-injection/src/assets/.gitkeep similarity index 100% rename from apps/angular/decoupling/src/assets/.gitkeep rename to apps/angular/16-master-dependency-injection/src/assets/.gitkeep diff --git a/apps/angular/crud/src/favicon.ico b/apps/angular/16-master-dependency-injection/src/favicon.ico similarity index 100% rename from apps/angular/crud/src/favicon.ico rename to apps/angular/16-master-dependency-injection/src/favicon.ico diff --git a/apps/angular/16-master-dependency-injection/src/index.html b/apps/angular/16-master-dependency-injection/src/index.html new file mode 100644 index 000000000..be35bf8c8 --- /dev/null +++ b/apps/angular/16-master-dependency-injection/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-master-dependancy-injection + + + + + + + + diff --git a/apps/angular/di/src/main.ts b/apps/angular/16-master-dependency-injection/src/main.ts similarity index 100% rename from apps/angular/di/src/main.ts rename to apps/angular/16-master-dependency-injection/src/main.ts diff --git a/apps/angular/di/src/styles.scss b/apps/angular/16-master-dependency-injection/src/styles.scss similarity index 100% rename from apps/angular/di/src/styles.scss rename to apps/angular/16-master-dependency-injection/src/styles.scss diff --git a/apps/angular/bug-cd/tsconfig.app.json b/apps/angular/16-master-dependency-injection/tsconfig.app.json similarity index 100% rename from apps/angular/bug-cd/tsconfig.app.json rename to apps/angular/16-master-dependency-injection/tsconfig.app.json diff --git a/apps/angular/router-input/tsconfig.editor.json b/apps/angular/16-master-dependency-injection/tsconfig.editor.json similarity index 100% rename from apps/angular/router-input/tsconfig.editor.json rename to apps/angular/16-master-dependency-injection/tsconfig.editor.json diff --git a/apps/angular/di/tsconfig.json b/apps/angular/16-master-dependency-injection/tsconfig.json similarity index 100% rename from apps/angular/di/tsconfig.json rename to apps/angular/16-master-dependency-injection/tsconfig.json diff --git a/apps/angular/bug-cd/.eslintrc.json b/apps/angular/21-anchor-navigation/.eslintrc.json similarity index 100% rename from apps/angular/bug-cd/.eslintrc.json rename to apps/angular/21-anchor-navigation/.eslintrc.json diff --git a/apps/angular/21-anchor-navigation/README.md b/apps/angular/21-anchor-navigation/README.md new file mode 100644 index 000000000..3683899ba --- /dev/null +++ b/apps/angular/21-anchor-navigation/README.md @@ -0,0 +1,13 @@ +# Anchor Navigation + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-anchor-navigation +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/21-achor-scrolling/). diff --git a/apps/angular/21-anchor-navigation/jest.config.ts b/apps/angular/21-anchor-navigation/jest.config.ts new file mode 100644 index 000000000..7347f70b5 --- /dev/null +++ b/apps/angular/21-anchor-navigation/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'anchor-navigation-anchor-navigation', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/21-anchor-navigation', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/21-anchor-navigation/project.json b/apps/angular/21-anchor-navigation/project.json new file mode 100644 index 000000000..b8ba74b04 --- /dev/null +++ b/apps/angular/21-anchor-navigation/project.json @@ -0,0 +1,83 @@ +{ + "name": "angular-anchor-navigation", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/21-anchor-navigation/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/21-anchor-navigation", + "index": "apps/angular/21-anchor-navigation/src/index.html", + "main": "apps/angular/21-anchor-navigation/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/21-anchor-navigation/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/21-anchor-navigation/src/favicon.ico", + "apps/angular/21-anchor-navigation/src/assets" + ], + "styles": ["apps/angular/21-anchor-navigation/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-anchor-navigation:build:production" + }, + "development": { + "buildTarget": "angular-anchor-navigation:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-anchor-navigation:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/21-anchor-navigation/src/app/app.component.ts b/apps/angular/21-anchor-navigation/src/app/app.component.ts new file mode 100644 index 000000000..5caca0271 --- /dev/null +++ b/apps/angular/21-anchor-navigation/src/app/app.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet], + selector: 'app-root', + template: ` + + `, +}) +export class AppComponent {} diff --git a/apps/angular/anchor-scrolling/src/app/app.config.ts b/apps/angular/21-anchor-navigation/src/app/app.config.ts similarity index 100% rename from apps/angular/anchor-scrolling/src/app/app.config.ts rename to apps/angular/21-anchor-navigation/src/app/app.config.ts diff --git a/apps/angular/anchor-scrolling/src/app/app.routes.ts b/apps/angular/21-anchor-navigation/src/app/app.routes.ts similarity index 100% rename from apps/angular/anchor-scrolling/src/app/app.routes.ts rename to apps/angular/21-anchor-navigation/src/app/app.routes.ts diff --git a/apps/angular/anchor-scrolling/src/app/foo.component.ts b/apps/angular/21-anchor-navigation/src/app/foo.component.ts similarity index 95% rename from apps/angular/anchor-scrolling/src/app/foo.component.ts rename to apps/angular/21-anchor-navigation/src/app/foo.component.ts index 87f9b59d9..6744c3662 100644 --- a/apps/angular/anchor-scrolling/src/app/foo.component.ts +++ b/apps/angular/21-anchor-navigation/src/app/foo.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { NavButtonComponent } from './nav-button.component'; @Component({ - standalone: true, imports: [NavButtonComponent], selector: 'app-foo', template: ` diff --git a/apps/angular/21-anchor-navigation/src/app/home.component.ts b/apps/angular/21-anchor-navigation/src/app/home.component.ts new file mode 100644 index 000000000..6ef9bc2b6 --- /dev/null +++ b/apps/angular/21-anchor-navigation/src/app/home.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { NavButtonComponent } from './nav-button.component'; + +@Component({ + imports: [NavButtonComponent], + selector: 'app-home', + template: ` + Foo Page +
+ Empty + Scroll Bottom +
+
+ I want to scroll each + Scroll Top +
+ `, +}) +export class HomeComponent {} diff --git a/apps/angular/anchor-scrolling/src/app/nav-button.component.ts b/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts similarity index 100% rename from apps/angular/anchor-scrolling/src/app/nav-button.component.ts rename to apps/angular/21-anchor-navigation/src/app/nav-button.component.ts diff --git a/apps/angular/di/src/assets/.gitkeep b/apps/angular/21-anchor-navigation/src/assets/.gitkeep similarity index 100% rename from apps/angular/di/src/assets/.gitkeep rename to apps/angular/21-anchor-navigation/src/assets/.gitkeep diff --git a/apps/angular/decoupling/src/favicon.ico b/apps/angular/21-anchor-navigation/src/favicon.ico similarity index 100% rename from apps/angular/decoupling/src/favicon.ico rename to apps/angular/21-anchor-navigation/src/favicon.ico diff --git a/apps/angular/21-anchor-navigation/src/index.html b/apps/angular/21-anchor-navigation/src/index.html new file mode 100644 index 000000000..06a706a0a --- /dev/null +++ b/apps/angular/21-anchor-navigation/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-anchor-navigation + + + + + + + + diff --git a/apps/angular/anchor-scrolling/src/main.ts b/apps/angular/21-anchor-navigation/src/main.ts similarity index 100% rename from apps/angular/anchor-scrolling/src/main.ts rename to apps/angular/21-anchor-navigation/src/main.ts diff --git a/apps/angular/anchor-scrolling/src/styles.scss b/apps/angular/21-anchor-navigation/src/styles.scss similarity index 100% rename from apps/angular/anchor-scrolling/src/styles.scss rename to apps/angular/21-anchor-navigation/src/styles.scss diff --git a/apps/angular/projection/src/test-setup.ts b/apps/angular/21-anchor-navigation/src/test-setup.ts similarity index 100% rename from apps/angular/projection/src/test-setup.ts rename to apps/angular/21-anchor-navigation/src/test-setup.ts diff --git a/apps/angular/bug-cd/tailwind.config.js b/apps/angular/21-anchor-navigation/tailwind.config.js similarity index 100% rename from apps/angular/bug-cd/tailwind.config.js rename to apps/angular/21-anchor-navigation/tailwind.config.js diff --git a/apps/angular/decoupling/tsconfig.app.json b/apps/angular/21-anchor-navigation/tsconfig.app.json similarity index 100% rename from apps/angular/decoupling/tsconfig.app.json rename to apps/angular/21-anchor-navigation/tsconfig.app.json diff --git a/apps/angular/anchor-scrolling/tsconfig.editor.json b/apps/angular/21-anchor-navigation/tsconfig.editor.json similarity index 100% rename from apps/angular/anchor-scrolling/tsconfig.editor.json rename to apps/angular/21-anchor-navigation/tsconfig.editor.json diff --git a/apps/angular/anchor-scrolling/tsconfig.json b/apps/angular/21-anchor-navigation/tsconfig.json similarity index 100% rename from apps/angular/anchor-scrolling/tsconfig.json rename to apps/angular/21-anchor-navigation/tsconfig.json diff --git a/apps/angular/anchor-scrolling/tsconfig.spec.json b/apps/angular/21-anchor-navigation/tsconfig.spec.json similarity index 100% rename from apps/angular/anchor-scrolling/tsconfig.spec.json rename to apps/angular/21-anchor-navigation/tsconfig.spec.json diff --git a/apps/angular/decoupling/.eslintrc.json b/apps/angular/22-router-input/.eslintrc.json similarity index 100% rename from apps/angular/decoupling/.eslintrc.json rename to apps/angular/22-router-input/.eslintrc.json diff --git a/apps/angular/router-input/README.md b/apps/angular/22-router-input/README.md similarity index 100% rename from apps/angular/router-input/README.md rename to apps/angular/22-router-input/README.md diff --git a/apps/angular/22-router-input/project.json b/apps/angular/22-router-input/project.json new file mode 100644 index 000000000..58cd889c5 --- /dev/null +++ b/apps/angular/22-router-input/project.json @@ -0,0 +1,71 @@ +{ + "name": "angular-router-input", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/22-router-input/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/22-router-input", + "index": "apps/angular/22-router-input/src/index.html", + "main": "apps/angular/22-router-input/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/22-router-input/tsconfig.app.json", + "assets": [ + "apps/angular/22-router-input/src/favicon.ico", + "apps/angular/22-router-input/src/assets" + ], + "styles": ["apps/angular/22-router-input/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-router-input:build:production" + }, + "development": { + "buildTarget": "angular-router-input:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-router-input:build" + } + } + } +} diff --git a/apps/angular/22-router-input/src/app/app.component.ts b/apps/angular/22-router-input/src/app/app.component.ts new file mode 100644 index 000000000..9dfc11200 --- /dev/null +++ b/apps/angular/22-router-input/src/app/app.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink, RouterModule } from '@angular/router'; + +@Component({ + imports: [RouterLink, RouterModule, ReactiveFormsModule], + selector: 'app-root', + template: ` + + + + + + + + `, +}) +export class AppComponent { + userName = new FormControl(); + testId = new FormControl(); +} diff --git a/apps/angular/router-input/src/app/app.config.ts b/apps/angular/22-router-input/src/app/app.config.ts similarity index 100% rename from apps/angular/router-input/src/app/app.config.ts rename to apps/angular/22-router-input/src/app/app.config.ts diff --git a/apps/angular/router-input/src/app/app.routes.ts b/apps/angular/22-router-input/src/app/app.routes.ts similarity index 100% rename from apps/angular/router-input/src/app/app.routes.ts rename to apps/angular/22-router-input/src/app/app.routes.ts diff --git a/apps/angular/22-router-input/src/app/home.component.ts b/apps/angular/22-router-input/src/app/home.component.ts new file mode 100644 index 000000000..0ddc1501d --- /dev/null +++ b/apps/angular/22-router-input/src/app/home.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; +@Component({ + selector: 'app-home', + imports: [], + template: ` +
Home
+ `, +}) +export default class HomeComponent {} diff --git a/apps/angular/router-input/src/app/test.component.ts b/apps/angular/22-router-input/src/app/test.component.ts similarity index 97% rename from apps/angular/router-input/src/app/test.component.ts rename to apps/angular/22-router-input/src/app/test.component.ts index 88c1465f3..747ab4483 100644 --- a/apps/angular/router-input/src/app/test.component.ts +++ b/apps/angular/22-router-input/src/app/test.component.ts @@ -5,7 +5,6 @@ import { map } from 'rxjs'; @Component({ selector: 'app-subscription', - standalone: true, imports: [AsyncPipe], template: `
TestId: {{ testId$ | async }}
diff --git a/apps/angular/injection-token/src/assets/.gitkeep b/apps/angular/22-router-input/src/assets/.gitkeep similarity index 100% rename from apps/angular/injection-token/src/assets/.gitkeep rename to apps/angular/22-router-input/src/assets/.gitkeep diff --git a/apps/angular/di/src/favicon.ico b/apps/angular/22-router-input/src/favicon.ico similarity index 100% rename from apps/angular/di/src/favicon.ico rename to apps/angular/22-router-input/src/favicon.ico diff --git a/apps/angular/22-router-input/src/index.html b/apps/angular/22-router-input/src/index.html new file mode 100644 index 000000000..30e74f7b4 --- /dev/null +++ b/apps/angular/22-router-input/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-router-input + + + + + + + + diff --git a/apps/angular/bug-cd/src/main.ts b/apps/angular/22-router-input/src/main.ts similarity index 100% rename from apps/angular/bug-cd/src/main.ts rename to apps/angular/22-router-input/src/main.ts diff --git a/apps/angular/pipe-easy/src/styles.scss b/apps/angular/22-router-input/src/styles.scss similarity index 100% rename from apps/angular/pipe-easy/src/styles.scss rename to apps/angular/22-router-input/src/styles.scss diff --git a/apps/angular/di/tsconfig.app.json b/apps/angular/22-router-input/tsconfig.app.json similarity index 100% rename from apps/angular/di/tsconfig.app.json rename to apps/angular/22-router-input/tsconfig.app.json diff --git a/apps/angular/signal-input/tsconfig.editor.json b/apps/angular/22-router-input/tsconfig.editor.json similarity index 100% rename from apps/angular/signal-input/tsconfig.editor.json rename to apps/angular/22-router-input/tsconfig.editor.json diff --git a/apps/angular/module-to-standalone/tsconfig.json b/apps/angular/22-router-input/tsconfig.json similarity index 100% rename from apps/angular/module-to-standalone/tsconfig.json rename to apps/angular/22-router-input/tsconfig.json diff --git a/apps/angular/interop-rxjs-signal/.eslintrc.json b/apps/angular/31-module-to-standalone/.eslintrc.json similarity index 100% rename from apps/angular/interop-rxjs-signal/.eslintrc.json rename to apps/angular/31-module-to-standalone/.eslintrc.json diff --git a/apps/angular/module-to-standalone/README.md b/apps/angular/31-module-to-standalone/README.md similarity index 100% rename from apps/angular/module-to-standalone/README.md rename to apps/angular/31-module-to-standalone/README.md diff --git a/apps/angular/31-module-to-standalone/project.json b/apps/angular/31-module-to-standalone/project.json new file mode 100644 index 000000000..c1a51b5ed --- /dev/null +++ b/apps/angular/31-module-to-standalone/project.json @@ -0,0 +1,71 @@ +{ + "name": "angular-module-to-standalone", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/31-module-to-standalone/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/31-module-to-standalone", + "index": "apps/angular/31-module-to-standalone/src/index.html", + "main": "apps/angular/31-module-to-standalone/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/31-module-to-standalone/tsconfig.app.json", + "assets": [ + "apps/angular/31-module-to-standalone/src/favicon.ico", + "apps/angular/31-module-to-standalone/src/assets" + ], + "styles": ["apps/angular/31-module-to-standalone/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-module-to-standalone:build:production" + }, + "development": { + "buildTarget": "angular-module-to-standalone:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-module-to-standalone:build" + } + } + } +} diff --git a/apps/angular/31-module-to-standalone/src/app/app.component.ts b/apps/angular/31-module-to-standalone/src/app/app.component.ts new file mode 100644 index 000000000..986df84b5 --- /dev/null +++ b/apps/angular/31-module-to-standalone/src/app/app.component.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` +
+ + + +
+ + `, + host: { + class: 'flex flex-col p-4 gap-3', + }, + standalone: false, +}) +export class AppComponent {} diff --git a/apps/angular/module-to-standalone/src/app/app.module.ts b/apps/angular/31-module-to-standalone/src/app/app.module.ts similarity index 100% rename from apps/angular/module-to-standalone/src/app/app.module.ts rename to apps/angular/31-module-to-standalone/src/app/app.module.ts diff --git a/apps/angular/interop-rxjs-signal/src/assets/.gitkeep b/apps/angular/31-module-to-standalone/src/assets/.gitkeep similarity index 100% rename from apps/angular/interop-rxjs-signal/src/assets/.gitkeep rename to apps/angular/31-module-to-standalone/src/assets/.gitkeep diff --git a/apps/angular/injection-token/src/favicon.ico b/apps/angular/31-module-to-standalone/src/favicon.ico similarity index 100% rename from apps/angular/injection-token/src/favicon.ico rename to apps/angular/31-module-to-standalone/src/favicon.ico diff --git a/apps/angular/31-module-to-standalone/src/index.html b/apps/angular/31-module-to-standalone/src/index.html new file mode 100644 index 000000000..fe0d5b978 --- /dev/null +++ b/apps/angular/31-module-to-standalone/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-module-to-standalone + + + + + + + + diff --git a/apps/angular/module-to-standalone/src/main.ts b/apps/angular/31-module-to-standalone/src/main.ts similarity index 100% rename from apps/angular/module-to-standalone/src/main.ts rename to apps/angular/31-module-to-standalone/src/main.ts diff --git a/apps/angular/bug-cd/src/styles.scss b/apps/angular/31-module-to-standalone/src/styles.scss similarity index 100% rename from apps/angular/bug-cd/src/styles.scss rename to apps/angular/31-module-to-standalone/src/styles.scss diff --git a/apps/angular/module-to-standalone/tailwind.config.js b/apps/angular/31-module-to-standalone/tailwind.config.js similarity index 100% rename from apps/angular/module-to-standalone/tailwind.config.js rename to apps/angular/31-module-to-standalone/tailwind.config.js diff --git a/apps/angular/injection-token/tsconfig.app.json b/apps/angular/31-module-to-standalone/tsconfig.app.json similarity index 100% rename from apps/angular/injection-token/tsconfig.app.json rename to apps/angular/31-module-to-standalone/tsconfig.app.json diff --git a/apps/angular/module-to-standalone/tsconfig.editor.json b/apps/angular/31-module-to-standalone/tsconfig.editor.json similarity index 100% rename from apps/angular/module-to-standalone/tsconfig.editor.json rename to apps/angular/31-module-to-standalone/tsconfig.editor.json diff --git a/apps/angular/router-input/tsconfig.json b/apps/angular/31-module-to-standalone/tsconfig.json similarity index 100% rename from apps/angular/router-input/tsconfig.json rename to apps/angular/31-module-to-standalone/tsconfig.json diff --git a/apps/angular/module-to-standalone/.eslintrc.json b/apps/angular/32-change-detection-bug/.eslintrc.json similarity index 100% rename from apps/angular/module-to-standalone/.eslintrc.json rename to apps/angular/32-change-detection-bug/.eslintrc.json diff --git a/apps/angular/32-change-detection-bug/README.md b/apps/angular/32-change-detection-bug/README.md new file mode 100644 index 000000000..41e533388 --- /dev/null +++ b/apps/angular/32-change-detection-bug/README.md @@ -0,0 +1,13 @@ +# Change Detection Bug + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-change-detection-bug +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/32-bug-cd/). diff --git a/apps/angular/32-change-detection-bug/jest.config.ts b/apps/angular/32-change-detection-bug/jest.config.ts new file mode 100644 index 000000000..d0412f028 --- /dev/null +++ b/apps/angular/32-change-detection-bug/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'angular-change-detection-bug', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/32-change-detection-bug', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/32-change-detection-bug/project.json b/apps/angular/32-change-detection-bug/project.json new file mode 100644 index 000000000..48eb9ef1e --- /dev/null +++ b/apps/angular/32-change-detection-bug/project.json @@ -0,0 +1,82 @@ +{ + "name": "angular-change-detection-bug", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/32-change-detection-bug/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/32-change-detection-bug", + "index": "apps/angular/32-change-detection-bug/src/index.html", + "main": "apps/angular/32-change-detection-bug/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/32-change-detection-bug/tsconfig.app.json", + "assets": [ + "apps/angular/32-change-detection-bug/src/favicon.ico", + "apps/angular/32-change-detection-bug/src/assets" + ], + "styles": ["apps/angular/32-change-detection-bug/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-change-detection-bug:build:production" + }, + "development": { + "buildTarget": "angular-change-detection-bug:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-change-detection-bug:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/32-change-detection-bug/src/app/app.component.ts b/apps/angular/32-change-detection-bug/src/app/app.component.ts new file mode 100644 index 000000000..217999c3a --- /dev/null +++ b/apps/angular/32-change-detection-bug/src/app/app.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet], + selector: 'app-root', + template: ` +

My Application

+
+ +
+ +
+
+ `, + host: { + class: 'flex flex-col gap-2', + }, +}) +export class AppComponent {} diff --git a/apps/angular/bug-cd/src/app/app.config.ts b/apps/angular/32-change-detection-bug/src/app/app.config.ts similarity index 100% rename from apps/angular/bug-cd/src/app/app.config.ts rename to apps/angular/32-change-detection-bug/src/app/app.config.ts diff --git a/apps/angular/bug-cd/src/app/bar.component.ts b/apps/angular/32-change-detection-bug/src/app/bar.component.ts similarity index 100% rename from apps/angular/bug-cd/src/app/bar.component.ts rename to apps/angular/32-change-detection-bug/src/app/bar.component.ts diff --git a/apps/angular/bug-cd/src/app/fake.service.ts b/apps/angular/32-change-detection-bug/src/app/fake.service.ts similarity index 100% rename from apps/angular/bug-cd/src/app/fake.service.ts rename to apps/angular/32-change-detection-bug/src/app/fake.service.ts diff --git a/apps/angular/bug-cd/src/app/foo.component.ts b/apps/angular/32-change-detection-bug/src/app/foo.component.ts similarity index 100% rename from apps/angular/bug-cd/src/app/foo.component.ts rename to apps/angular/32-change-detection-bug/src/app/foo.component.ts diff --git a/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts b/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts new file mode 100644 index 000000000..bc32fbaa0 --- /dev/null +++ b/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts @@ -0,0 +1,63 @@ +import { AsyncPipe } from '@angular/common'; +import { Component, inject, Input } from '@angular/core'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { FakeServiceService } from './fake.service'; + +interface MenuItem { + path: string; + name: string; +} + +@Component({ + selector: 'app-nav', + imports: [RouterLink, RouterLinkActive], + template: ` + @for (menu of menus; track menu.path) { + + {{ menu.name }} + + } + `, + styles: [ + ` + a.isSelected { + @apply bg-gray-600 text-white; + } + `, + ], + host: { + class: 'flex flex-col p-2 gap-2', + }, +}) +export class NavigationComponent { + @Input() menus!: MenuItem[]; +} + +@Component({ + imports: [NavigationComponent, AsyncPipe], + template: ` + @if (info$ | async; as info) { + @if (info !== null) { + + } @else { + + } + } + `, + host: {}, +}) +export class MainNavigationComponent { + private fakeBackend = inject(FakeServiceService); + + readonly info$ = this.fakeBackend.getInfoFromBackend(); + + getMenu(prop: string) { + return [ + { path: '/foo', name: `Foo ${prop}` }, + { path: '/bar', name: `Bar ${prop}` }, + ]; + } +} diff --git a/apps/angular/module-to-standalone/src/assets/.gitkeep b/apps/angular/32-change-detection-bug/src/assets/.gitkeep similarity index 100% rename from apps/angular/module-to-standalone/src/assets/.gitkeep rename to apps/angular/32-change-detection-bug/src/assets/.gitkeep diff --git a/apps/angular/interop-rxjs-signal/src/favicon.ico b/apps/angular/32-change-detection-bug/src/favicon.ico similarity index 100% rename from apps/angular/interop-rxjs-signal/src/favicon.ico rename to apps/angular/32-change-detection-bug/src/favicon.ico diff --git a/apps/angular/32-change-detection-bug/src/index.html b/apps/angular/32-change-detection-bug/src/index.html new file mode 100644 index 000000000..350759387 --- /dev/null +++ b/apps/angular/32-change-detection-bug/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-change-detection-bug + + + + + + + + diff --git a/apps/angular/injection-token/src/main.ts b/apps/angular/32-change-detection-bug/src/main.ts similarity index 100% rename from apps/angular/injection-token/src/main.ts rename to apps/angular/32-change-detection-bug/src/main.ts diff --git a/apps/angular/decoupling/src/styles.scss b/apps/angular/32-change-detection-bug/src/styles.scss similarity index 100% rename from apps/angular/decoupling/src/styles.scss rename to apps/angular/32-change-detection-bug/src/styles.scss diff --git a/apps/angular/bug-cd/src/test-setup.ts b/apps/angular/32-change-detection-bug/src/test-setup.ts similarity index 100% rename from apps/angular/bug-cd/src/test-setup.ts rename to apps/angular/32-change-detection-bug/src/test-setup.ts diff --git a/apps/angular/decoupling/tailwind.config.js b/apps/angular/32-change-detection-bug/tailwind.config.js similarity index 100% rename from apps/angular/decoupling/tailwind.config.js rename to apps/angular/32-change-detection-bug/tailwind.config.js diff --git a/apps/angular/interop-rxjs-signal/tsconfig.app.json b/apps/angular/32-change-detection-bug/tsconfig.app.json similarity index 100% rename from apps/angular/interop-rxjs-signal/tsconfig.app.json rename to apps/angular/32-change-detection-bug/tsconfig.app.json diff --git a/apps/angular/bug-cd/tsconfig.editor.json b/apps/angular/32-change-detection-bug/tsconfig.editor.json similarity index 100% rename from apps/angular/bug-cd/tsconfig.editor.json rename to apps/angular/32-change-detection-bug/tsconfig.editor.json diff --git a/apps/angular/bug-cd/tsconfig.json b/apps/angular/32-change-detection-bug/tsconfig.json similarity index 100% rename from apps/angular/bug-cd/tsconfig.json rename to apps/angular/32-change-detection-bug/tsconfig.json diff --git a/apps/angular/bug-cd/tsconfig.spec.json b/apps/angular/32-change-detection-bug/tsconfig.spec.json similarity index 100% rename from apps/angular/bug-cd/tsconfig.spec.json rename to apps/angular/32-change-detection-bug/tsconfig.spec.json diff --git a/apps/angular/router-input/.eslintrc.json b/apps/angular/33-decoupling-components/.eslintrc.json similarity index 100% rename from apps/angular/router-input/.eslintrc.json rename to apps/angular/33-decoupling-components/.eslintrc.json diff --git a/apps/angular/33-decoupling-components/README.md b/apps/angular/33-decoupling-components/README.md new file mode 100644 index 000000000..4af70458f --- /dev/null +++ b/apps/angular/33-decoupling-components/README.md @@ -0,0 +1,13 @@ +# Decoupling Components + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-decoupling-components +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/33-decoupling/). diff --git a/apps/angular/33-decoupling-components/project.json b/apps/angular/33-decoupling-components/project.json new file mode 100644 index 000000000..762fc01b5 --- /dev/null +++ b/apps/angular/33-decoupling-components/project.json @@ -0,0 +1,71 @@ +{ + "name": "angular-decoupling-components", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/33-decoupling-components/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/33-decoupling-components", + "index": "apps/angular/33-decoupling-components/src/index.html", + "main": "apps/angular/33-decoupling-components/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/33-decoupling-components/tsconfig.app.json", + "assets": [ + "apps/angular/33-decoupling-components/src/favicon.ico", + "apps/angular/33-decoupling-components/src/assets" + ], + "styles": ["apps/angular/33-decoupling-components/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-decoupling-components:build:production" + }, + "development": { + "buildTarget": "angular-decoupling-components:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-decoupling-components:build" + } + } + } +} diff --git a/apps/angular/33-decoupling-components/src/app/app.component.ts b/apps/angular/33-decoupling-components/src/app/app.component.ts new file mode 100644 index 000000000..0d78f4d34 --- /dev/null +++ b/apps/angular/33-decoupling-components/src/app/app.component.ts @@ -0,0 +1,12 @@ +import { BtnDisabledDirective } from '@angular-challenges/decoupling/brain'; +import { BtnHelmetDirective } from '@angular-challenges/decoupling/helmet'; +import { Component } from '@angular/core'; + +@Component({ + imports: [BtnDisabledDirective, BtnHelmetDirective], + selector: 'app-root', + template: ` + + `, +}) +export class AppComponent {} diff --git a/apps/angular/ngfor-enhancement/src/assets/.gitkeep b/apps/angular/33-decoupling-components/src/assets/.gitkeep similarity index 100% rename from apps/angular/ngfor-enhancement/src/assets/.gitkeep rename to apps/angular/33-decoupling-components/src/assets/.gitkeep diff --git a/apps/angular/module-to-standalone/src/favicon.ico b/apps/angular/33-decoupling-components/src/favicon.ico similarity index 100% rename from apps/angular/module-to-standalone/src/favicon.ico rename to apps/angular/33-decoupling-components/src/favicon.ico diff --git a/apps/angular/33-decoupling-components/src/index.html b/apps/angular/33-decoupling-components/src/index.html new file mode 100644 index 000000000..b946b0cfd --- /dev/null +++ b/apps/angular/33-decoupling-components/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-decoupling-components + + + + + + + + diff --git a/apps/angular/ngfor-enhancement/src/main.ts b/apps/angular/33-decoupling-components/src/main.ts similarity index 100% rename from apps/angular/ngfor-enhancement/src/main.ts rename to apps/angular/33-decoupling-components/src/main.ts diff --git a/apps/angular/injection-token/src/styles.scss b/apps/angular/33-decoupling-components/src/styles.scss similarity index 100% rename from apps/angular/injection-token/src/styles.scss rename to apps/angular/33-decoupling-components/src/styles.scss diff --git a/apps/angular/injection-token/tailwind.config.js b/apps/angular/33-decoupling-components/tailwind.config.js similarity index 100% rename from apps/angular/injection-token/tailwind.config.js rename to apps/angular/33-decoupling-components/tailwind.config.js diff --git a/apps/angular/module-to-standalone/tsconfig.app.json b/apps/angular/33-decoupling-components/tsconfig.app.json similarity index 100% rename from apps/angular/module-to-standalone/tsconfig.app.json rename to apps/angular/33-decoupling-components/tsconfig.app.json diff --git a/apps/angular/decoupling/tsconfig.editor.json b/apps/angular/33-decoupling-components/tsconfig.editor.json similarity index 100% rename from apps/angular/decoupling/tsconfig.editor.json rename to apps/angular/33-decoupling-components/tsconfig.editor.json diff --git a/apps/angular/styling/tsconfig.json b/apps/angular/33-decoupling-components/tsconfig.json similarity index 100% rename from apps/angular/styling/tsconfig.json rename to apps/angular/33-decoupling-components/tsconfig.json diff --git a/apps/angular/injection-token/.eslintrc.json b/apps/angular/39-injection-token/.eslintrc.json similarity index 100% rename from apps/angular/injection-token/.eslintrc.json rename to apps/angular/39-injection-token/.eslintrc.json diff --git a/apps/angular/injection-token/README.md b/apps/angular/39-injection-token/README.md similarity index 100% rename from apps/angular/injection-token/README.md rename to apps/angular/39-injection-token/README.md diff --git a/apps/angular/39-injection-token/jest.config.ts b/apps/angular/39-injection-token/jest.config.ts new file mode 100644 index 000000000..541d663e7 --- /dev/null +++ b/apps/angular/39-injection-token/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'angular-injection-token', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/39-injection-token', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/39-injection-token/project.json b/apps/angular/39-injection-token/project.json new file mode 100644 index 000000000..8dba72d55 --- /dev/null +++ b/apps/angular/39-injection-token/project.json @@ -0,0 +1,82 @@ +{ + "name": "angular-injection-token", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/39-injection-token/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/39-injection-token", + "index": "apps/angular/39-injection-token/src/index.html", + "main": "apps/angular/39-injection-token/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/39-injection-token/tsconfig.app.json", + "assets": [ + "apps/angular/39-injection-token/src/favicon.ico", + "apps/angular/39-injection-token/src/assets" + ], + "styles": ["apps/angular/39-injection-token/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-injection-token:build:production" + }, + "development": { + "buildTarget": "angular-injection-token:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-injection-token:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/39-injection-token/src/app/app.component.ts b/apps/angular/39-injection-token/src/app/app.component.ts new file mode 100644 index 000000000..280dc090a --- /dev/null +++ b/apps/angular/39-injection-token/src/app/app.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet, RouterLink], + selector: 'app-root', + template: ` +
+ + +
+ + `, + host: { + class: 'p-10 flex flex-col', + }, +}) +export class AppComponent {} diff --git a/apps/angular/injection-token/src/app/app.config.ts b/apps/angular/39-injection-token/src/app/app.config.ts similarity index 100% rename from apps/angular/injection-token/src/app/app.config.ts rename to apps/angular/39-injection-token/src/app/app.config.ts diff --git a/apps/angular/injection-token/src/app/data.ts b/apps/angular/39-injection-token/src/app/data.ts similarity index 100% rename from apps/angular/injection-token/src/app/data.ts rename to apps/angular/39-injection-token/src/app/data.ts diff --git a/apps/angular/injection-token/src/app/phone.component.ts b/apps/angular/39-injection-token/src/app/phone.component.ts similarity index 95% rename from apps/angular/injection-token/src/app/phone.component.ts rename to apps/angular/39-injection-token/src/app/phone.component.ts index a58b3cd99..41ee3cfc0 100644 --- a/apps/angular/injection-token/src/app/phone.component.ts +++ b/apps/angular/39-injection-token/src/app/phone.component.ts @@ -3,7 +3,6 @@ import { TimerContainerComponent } from './timer-container.component'; @Component({ selector: 'app-phone', - standalone: true, imports: [TimerContainerComponent], template: `
diff --git a/apps/angular/injection-token/src/app/timer-container.component.ts b/apps/angular/39-injection-token/src/app/timer-container.component.ts similarity index 96% rename from apps/angular/injection-token/src/app/timer-container.component.ts rename to apps/angular/39-injection-token/src/app/timer-container.component.ts index 30af69354..67db6059a 100644 --- a/apps/angular/injection-token/src/app/timer-container.component.ts +++ b/apps/angular/39-injection-token/src/app/timer-container.component.ts @@ -3,7 +3,6 @@ import { DEFAULT_TIMER } from './data'; import { TimerComponent } from './timer.component'; @Component({ selector: 'timer-container', - standalone: true, imports: [TimerComponent], template: `
diff --git a/apps/angular/injection-token/src/app/timer.component.ts b/apps/angular/39-injection-token/src/app/timer.component.ts similarity index 100% rename from apps/angular/injection-token/src/app/timer.component.ts rename to apps/angular/39-injection-token/src/app/timer.component.ts diff --git a/apps/angular/injection-token/src/app/video.component.ts b/apps/angular/39-injection-token/src/app/video.component.ts similarity index 95% rename from apps/angular/injection-token/src/app/video.component.ts rename to apps/angular/39-injection-token/src/app/video.component.ts index 2c218071a..ba0a218b4 100644 --- a/apps/angular/injection-token/src/app/video.component.ts +++ b/apps/angular/39-injection-token/src/app/video.component.ts @@ -3,7 +3,6 @@ import { TimerContainerComponent } from './timer-container.component'; @Component({ selector: 'app-video', - standalone: true, imports: [TimerContainerComponent], template: `
diff --git a/apps/angular/permissions/src/assets/.gitkeep b/apps/angular/39-injection-token/src/assets/.gitkeep similarity index 100% rename from apps/angular/permissions/src/assets/.gitkeep rename to apps/angular/39-injection-token/src/assets/.gitkeep diff --git a/apps/angular/ngfor-enhancement/src/favicon.ico b/apps/angular/39-injection-token/src/favicon.ico similarity index 100% rename from apps/angular/ngfor-enhancement/src/favicon.ico rename to apps/angular/39-injection-token/src/favicon.ico diff --git a/apps/angular/injection-token/src/index.html b/apps/angular/39-injection-token/src/index.html similarity index 100% rename from apps/angular/injection-token/src/index.html rename to apps/angular/39-injection-token/src/index.html diff --git a/apps/angular/interop-rxjs-signal/src/main.ts b/apps/angular/39-injection-token/src/main.ts similarity index 100% rename from apps/angular/interop-rxjs-signal/src/main.ts rename to apps/angular/39-injection-token/src/main.ts diff --git a/apps/angular/interop-rxjs-signal/src/styles.scss b/apps/angular/39-injection-token/src/styles.scss similarity index 100% rename from apps/angular/interop-rxjs-signal/src/styles.scss rename to apps/angular/39-injection-token/src/styles.scss diff --git a/apps/angular/crud/src/test-setup.ts b/apps/angular/39-injection-token/src/test-setup.ts similarity index 100% rename from apps/angular/crud/src/test-setup.ts rename to apps/angular/39-injection-token/src/test-setup.ts diff --git a/apps/angular/interop-rxjs-signal/tailwind.config.js b/apps/angular/39-injection-token/tailwind.config.js similarity index 100% rename from apps/angular/interop-rxjs-signal/tailwind.config.js rename to apps/angular/39-injection-token/tailwind.config.js diff --git a/apps/angular/react-in-angular/tsconfig.app.json b/apps/angular/39-injection-token/tsconfig.app.json similarity index 100% rename from apps/angular/react-in-angular/tsconfig.app.json rename to apps/angular/39-injection-token/tsconfig.app.json diff --git a/apps/angular/injection-token/tsconfig.editor.json b/apps/angular/39-injection-token/tsconfig.editor.json similarity index 100% rename from apps/angular/injection-token/tsconfig.editor.json rename to apps/angular/39-injection-token/tsconfig.editor.json diff --git a/apps/angular/injection-token/tsconfig.json b/apps/angular/39-injection-token/tsconfig.json similarity index 100% rename from apps/angular/injection-token/tsconfig.json rename to apps/angular/39-injection-token/tsconfig.json diff --git a/apps/angular/injection-token/tsconfig.spec.json b/apps/angular/39-injection-token/tsconfig.spec.json similarity index 100% rename from apps/angular/injection-token/tsconfig.spec.json rename to apps/angular/39-injection-token/tsconfig.spec.json diff --git a/apps/angular/context-outlet-type/.eslintrc.json b/apps/angular/4-typed-context-outlet/.eslintrc.json similarity index 100% rename from apps/angular/context-outlet-type/.eslintrc.json rename to apps/angular/4-typed-context-outlet/.eslintrc.json diff --git a/apps/angular/4-typed-context-outlet/README.md b/apps/angular/4-typed-context-outlet/README.md new file mode 100644 index 000000000..e81915167 --- /dev/null +++ b/apps/angular/4-typed-context-outlet/README.md @@ -0,0 +1,13 @@ +# Typed ContextOutlet + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-typed-context-outlet +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/4-context-outlet-typed/). diff --git a/apps/angular/4-typed-context-outlet/project.json b/apps/angular/4-typed-context-outlet/project.json new file mode 100644 index 000000000..ccebf62c7 --- /dev/null +++ b/apps/angular/4-typed-context-outlet/project.json @@ -0,0 +1,72 @@ +{ + "name": "angular-typed-context-outlet", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/4-typed-context-outlet/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/4-typed-context-outlet", + "index": "apps/angular/4-typed-context-outlet/src/index.html", + "main": "apps/angular/4-typed-context-outlet/src/main.ts", + "polyfills": "apps/angular/4-typed-context-outlet/src/polyfills.ts", + "tsConfig": "apps/angular/4-typed-context-outlet/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/4-typed-context-outlet/src/favicon.ico", + "apps/angular/4-typed-context-outlet/src/assets" + ], + "styles": ["apps/angular/4-typed-context-outlet/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-typed-context-outlet:build:production" + }, + "development": { + "buildTarget": "angular-typed-context-outlet:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-typed-context-outlet:build" + } + } + } +} diff --git a/apps/angular/4-typed-context-outlet/src/app/app.component.ts b/apps/angular/4-typed-context-outlet/src/app/app.component.ts new file mode 100644 index 000000000..23be9dac6 --- /dev/null +++ b/apps/angular/4-typed-context-outlet/src/app/app.component.ts @@ -0,0 +1,45 @@ +import { NgTemplateOutlet } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ListComponent } from './list.component'; +import { PersonComponent } from './person.component'; + +@Component({ + imports: [NgTemplateOutlet, PersonComponent, ListComponent], + selector: 'app-root', + template: ` + + + {{ name }}: {{ age }} + + + + + + {{ student.name }}: {{ student.age }} - {{ i }} + + + + + + {{ city.name }}: {{ city.country }} - {{ i }} + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent { + person = { + name: 'toto', + age: 3, + }; + + students = [ + { name: 'toto', age: 3 }, + { name: 'titi', age: 4 }, + ]; + + cities = [ + { name: 'Paris', country: 'France' }, + { name: 'Berlin', country: 'Germany' }, + ]; +} diff --git a/apps/angular/4-typed-context-outlet/src/app/list.component.ts b/apps/angular/4-typed-context-outlet/src/app/list.component.ts new file mode 100644 index 000000000..b9946e428 --- /dev/null +++ b/apps/angular/4-typed-context-outlet/src/app/list.component.ts @@ -0,0 +1,31 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ContentChild, + Input, + TemplateRef, +} from '@angular/core'; + +@Component({ + selector: 'list', + imports: [CommonModule], + template: ` +
+ +
+ + No Template + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ListComponent { + @Input() list!: TItem[]; + + @ContentChild('listRef', { read: TemplateRef }) + listTemplateRef!: TemplateRef; +} diff --git a/apps/angular/context-outlet-type/src/app/person.component.ts b/apps/angular/4-typed-context-outlet/src/app/person.component.ts similarity index 97% rename from apps/angular/context-outlet-type/src/app/person.component.ts rename to apps/angular/4-typed-context-outlet/src/app/person.component.ts index 1550cf274..59eb00ab1 100644 --- a/apps/angular/context-outlet-type/src/app/person.component.ts +++ b/apps/angular/4-typed-context-outlet/src/app/person.component.ts @@ -7,7 +7,6 @@ interface Person { } @Component({ - standalone: true, imports: [NgTemplateOutlet], selector: 'person', template: ` diff --git a/apps/angular/permissions/src/favicon.ico b/apps/angular/4-typed-context-outlet/src/favicon.ico similarity index 100% rename from apps/angular/permissions/src/favicon.ico rename to apps/angular/4-typed-context-outlet/src/favicon.ico diff --git a/apps/angular/4-typed-context-outlet/src/index.html b/apps/angular/4-typed-context-outlet/src/index.html new file mode 100644 index 000000000..a9c0b5484 --- /dev/null +++ b/apps/angular/4-typed-context-outlet/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-typed-context-outlet + + + + + + + + diff --git a/apps/angular/pipe-easy/src/main.ts b/apps/angular/4-typed-context-outlet/src/main.ts similarity index 100% rename from apps/angular/pipe-easy/src/main.ts rename to apps/angular/4-typed-context-outlet/src/main.ts diff --git a/apps/angular/ngfor-enhancement/src/polyfills.ts b/apps/angular/4-typed-context-outlet/src/polyfills.ts similarity index 100% rename from apps/angular/ngfor-enhancement/src/polyfills.ts rename to apps/angular/4-typed-context-outlet/src/polyfills.ts diff --git a/apps/angular/pipe-hard/src/styles.scss b/apps/angular/4-typed-context-outlet/src/styles.scss similarity index 100% rename from apps/angular/pipe-hard/src/styles.scss rename to apps/angular/4-typed-context-outlet/src/styles.scss diff --git a/apps/angular/ngfor-enhancement/tsconfig.app.json b/apps/angular/4-typed-context-outlet/tsconfig.app.json similarity index 100% rename from apps/angular/ngfor-enhancement/tsconfig.app.json rename to apps/angular/4-typed-context-outlet/tsconfig.app.json diff --git a/apps/angular/crud/tsconfig.editor.json b/apps/angular/4-typed-context-outlet/tsconfig.editor.json similarity index 100% rename from apps/angular/crud/tsconfig.editor.json rename to apps/angular/4-typed-context-outlet/tsconfig.editor.json diff --git a/apps/angular/ngfor-enhancement/tsconfig.json b/apps/angular/4-typed-context-outlet/tsconfig.json similarity index 100% rename from apps/angular/ngfor-enhancement/tsconfig.json rename to apps/angular/4-typed-context-outlet/tsconfig.json diff --git a/apps/angular/view-transition/.eslintrc.json b/apps/angular/44-view-transition/.eslintrc.json similarity index 100% rename from apps/angular/view-transition/.eslintrc.json rename to apps/angular/44-view-transition/.eslintrc.json diff --git a/apps/angular/view-transition/README.md b/apps/angular/44-view-transition/README.md similarity index 100% rename from apps/angular/view-transition/README.md rename to apps/angular/44-view-transition/README.md diff --git a/apps/angular/44-view-transition/project.json b/apps/angular/44-view-transition/project.json new file mode 100644 index 000000000..9f4046607 --- /dev/null +++ b/apps/angular/44-view-transition/project.json @@ -0,0 +1,69 @@ +{ + "name": "angular-view-transition", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/44-view-transition/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/44-view-transition", + "index": "apps/angular/44-view-transition/src/index.html", + "browser": "apps/angular/44-view-transition/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/44-view-transition/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/44-view-transition/src/favicon.ico", + "apps/angular/44-view-transition/src/assets" + ], + "styles": ["apps/angular/44-view-transition/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-view-transition:build:production" + }, + "development": { + "buildTarget": "angular-view-transition:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-view-transition:build" + } + } + } +} diff --git a/apps/angular/44-view-transition/src/app/app.component.ts b/apps/angular/44-view-transition/src/app/app.component.ts new file mode 100644 index 000000000..1fcb0c548 --- /dev/null +++ b/apps/angular/44-view-transition/src/app/app.component.ts @@ -0,0 +1,12 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet], + selector: 'app-root', + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent {} diff --git a/apps/angular/view-transition/src/app/app.config.ts b/apps/angular/44-view-transition/src/app/app.config.ts similarity index 100% rename from apps/angular/view-transition/src/app/app.config.ts rename to apps/angular/44-view-transition/src/app/app.config.ts diff --git a/apps/angular/view-transition/src/app/blog/blog.component.ts b/apps/angular/44-view-transition/src/app/blog/blog.component.ts similarity index 97% rename from apps/angular/view-transition/src/app/blog/blog.component.ts rename to apps/angular/44-view-transition/src/app/blog/blog.component.ts index 68ffcde50..29291d21e 100644 --- a/apps/angular/view-transition/src/app/blog/blog.component.ts +++ b/apps/angular/44-view-transition/src/app/blog/blog.component.ts @@ -4,7 +4,6 @@ import { ThumbnailComponent } from './thumbnail.component'; @Component({ selector: 'blog', - standalone: true, imports: [ThumbnailComponent], template: `
diff --git a/apps/angular/view-transition/src/app/blog/thumbnail.component.ts b/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts similarity index 98% rename from apps/angular/view-transition/src/app/blog/thumbnail.component.ts rename to apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts index 6263e97ab..dd2e25e26 100644 --- a/apps/angular/view-transition/src/app/blog/thumbnail.component.ts +++ b/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts @@ -6,7 +6,6 @@ import { ThumbnailHeaderComponent } from './thumbnail-header.component'; @Component({ selector: 'blog-thumbnail', - standalone: true, imports: [NgOptimizedImage, ThumbnailHeaderComponent, RouterLinkWithHref], template: ` diff --git a/apps/angular/view-transition/src/app/data.ts b/apps/angular/44-view-transition/src/app/data.ts similarity index 100% rename from apps/angular/view-transition/src/app/data.ts rename to apps/angular/44-view-transition/src/app/data.ts diff --git a/apps/angular/view-transition/src/app/post.model.ts b/apps/angular/44-view-transition/src/app/post.model.ts similarity index 100% rename from apps/angular/view-transition/src/app/post.model.ts rename to apps/angular/44-view-transition/src/app/post.model.ts diff --git a/apps/angular/view-transition/src/app/post/post-header.component.ts b/apps/angular/44-view-transition/src/app/post/post-header.component.ts similarity index 97% rename from apps/angular/view-transition/src/app/post/post-header.component.ts rename to apps/angular/44-view-transition/src/app/post/post-header.component.ts index 8b62a6c48..6d5f30e54 100644 --- a/apps/angular/view-transition/src/app/post/post-header.component.ts +++ b/apps/angular/44-view-transition/src/app/post/post-header.component.ts @@ -3,7 +3,6 @@ import { Component, input } from '@angular/core'; @Component({ selector: 'post-header', - standalone: true, imports: [NgOptimizedImage], template: `
diff --git a/apps/angular/view-transition/src/app/post/post.component.ts b/apps/angular/44-view-transition/src/app/post/post.component.ts similarity index 98% rename from apps/angular/view-transition/src/app/post/post.component.ts rename to apps/angular/44-view-transition/src/app/post/post.component.ts index 1e1c6fd89..edb87f780 100644 --- a/apps/angular/view-transition/src/app/post/post.component.ts +++ b/apps/angular/44-view-transition/src/app/post/post.component.ts @@ -12,7 +12,6 @@ import { PostHeaderComponent } from './post-header.component'; @Component({ selector: 'post', - standalone: true, imports: [ ThumbnailHeaderComponent, NgOptimizedImage, diff --git a/apps/angular/pipe-easy/src/assets/.gitkeep b/apps/angular/44-view-transition/src/assets/.gitkeep similarity index 100% rename from apps/angular/pipe-easy/src/assets/.gitkeep rename to apps/angular/44-view-transition/src/assets/.gitkeep diff --git a/apps/angular/view-transition/src/assets/angular.webp b/apps/angular/44-view-transition/src/assets/angular.webp similarity index 100% rename from apps/angular/view-transition/src/assets/angular.webp rename to apps/angular/44-view-transition/src/assets/angular.webp diff --git a/apps/angular/view-transition/src/assets/guard.full.webp b/apps/angular/44-view-transition/src/assets/guard.full.webp similarity index 100% rename from apps/angular/view-transition/src/assets/guard.full.webp rename to apps/angular/44-view-transition/src/assets/guard.full.webp diff --git a/apps/angular/view-transition/src/assets/highly-custom.full.webp b/apps/angular/44-view-transition/src/assets/highly-custom.full.webp similarity index 100% rename from apps/angular/view-transition/src/assets/highly-custom.full.webp rename to apps/angular/44-view-transition/src/assets/highly-custom.full.webp diff --git a/apps/angular/view-transition/src/assets/profil.webp b/apps/angular/44-view-transition/src/assets/profil.webp similarity index 100% rename from apps/angular/view-transition/src/assets/profil.webp rename to apps/angular/44-view-transition/src/assets/profil.webp diff --git a/apps/angular/view-transition/src/assets/signal-cd.full.webp b/apps/angular/44-view-transition/src/assets/signal-cd.full.webp similarity index 100% rename from apps/angular/view-transition/src/assets/signal-cd.full.webp rename to apps/angular/44-view-transition/src/assets/signal-cd.full.webp diff --git a/apps/angular/pipe-easy/src/favicon.ico b/apps/angular/44-view-transition/src/favicon.ico similarity index 100% rename from apps/angular/pipe-easy/src/favicon.ico rename to apps/angular/44-view-transition/src/favicon.ico diff --git a/apps/angular/view-transition/src/index.html b/apps/angular/44-view-transition/src/index.html similarity index 100% rename from apps/angular/view-transition/src/index.html rename to apps/angular/44-view-transition/src/index.html diff --git a/apps/angular/react-in-angular/src/main.ts b/apps/angular/44-view-transition/src/main.ts similarity index 100% rename from apps/angular/react-in-angular/src/main.ts rename to apps/angular/44-view-transition/src/main.ts diff --git a/apps/angular/projection/src/styles.scss b/apps/angular/44-view-transition/src/styles.scss similarity index 100% rename from apps/angular/projection/src/styles.scss rename to apps/angular/44-view-transition/src/styles.scss diff --git a/apps/angular/permissions/tailwind.config.js b/apps/angular/44-view-transition/tailwind.config.js similarity index 100% rename from apps/angular/permissions/tailwind.config.js rename to apps/angular/44-view-transition/tailwind.config.js diff --git a/apps/angular/router-input/tsconfig.app.json b/apps/angular/44-view-transition/tsconfig.app.json similarity index 100% rename from apps/angular/router-input/tsconfig.app.json rename to apps/angular/44-view-transition/tsconfig.app.json diff --git a/apps/angular/styling/tsconfig.editor.json b/apps/angular/44-view-transition/tsconfig.editor.json similarity index 100% rename from apps/angular/styling/tsconfig.editor.json rename to apps/angular/44-view-transition/tsconfig.editor.json diff --git a/apps/angular/signal-input/tsconfig.json b/apps/angular/44-view-transition/tsconfig.json similarity index 100% rename from apps/angular/signal-input/tsconfig.json rename to apps/angular/44-view-transition/tsconfig.json diff --git a/apps/angular/ngfor-enhancement/.eslintrc.json b/apps/angular/45-react-in-angular/.eslintrc.json similarity index 100% rename from apps/angular/ngfor-enhancement/.eslintrc.json rename to apps/angular/45-react-in-angular/.eslintrc.json diff --git a/apps/angular/react-in-angular/README.md b/apps/angular/45-react-in-angular/README.md similarity index 100% rename from apps/angular/react-in-angular/README.md rename to apps/angular/45-react-in-angular/README.md diff --git a/apps/angular/45-react-in-angular/jest.config.ts b/apps/angular/45-react-in-angular/jest.config.ts new file mode 100644 index 000000000..cda19a635 --- /dev/null +++ b/apps/angular/45-react-in-angular/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'angular-react-in-angular', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/45-react-in-angular', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/45-react-in-angular/project.json b/apps/angular/45-react-in-angular/project.json new file mode 100644 index 000000000..bce6fe70e --- /dev/null +++ b/apps/angular/45-react-in-angular/project.json @@ -0,0 +1,80 @@ +{ + "name": "angular-react-in-angular", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/45-react-in-angular/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/45-react-in-angular", + "index": "apps/angular/45-react-in-angular/src/index.html", + "browser": "apps/angular/45-react-in-angular/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/45-react-in-angular/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/45-react-in-angular/src/favicon.ico", + "apps/angular/45-react-in-angular/src/assets" + ], + "styles": ["apps/angular/45-react-in-angular/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-react-in-angular:build:production" + }, + "development": { + "buildTarget": "angular-react-in-angular:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-react-in-angular:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/45-react-in-angular/src/app/app.component.ts b/apps/angular/45-react-in-angular/src/app/app.component.ts new file mode 100644 index 000000000..87b9675cc --- /dev/null +++ b/apps/angular/45-react-in-angular/src/app/app.component.ts @@ -0,0 +1,61 @@ +import { Component, signal } from '@angular/core'; +import { PostComponent } from './react/post.component'; + +type Post = { title: string; description: string }; + +@Component({ + imports: [PostComponent], + selector: 'app-root', + template: ` +
+
+ @for (post of posts; track post.title) { +
+ +
+ } +
+
+ Selected Post: + + {{ selectedPost()?.title ?? '-' }} + +
+
+ `, + styles: [''], +}) +export class AppComponent { + readonly posts = [ + { + title: 'A Deep Dive into Angular', + description: + "Explore Angular's core features, its evolution, and best practices in development for creating dynamic, efficient web applications in our comprehensive guide.", + pictureLink: + 'https://images.unsplash.com/photo-1471958680802-1345a694ba6d', + }, + { + title: 'The Perfect Combination', + description: + 'Unveil the power of combining Angular & React in web development, maximizing efficiency and flexibility for building scalable, sophisticated applications.', + pictureLink: + 'https://images.unsplash.com/photo-1518717202715-9fa9d099f58a', + }, + { + title: 'Taking Angular to the Next Level', + description: + "Discover how integrating React with Angular elevates web development, blending Angular's structure with React's UI prowess for advanced applications.", + pictureLink: + 'https://images.unsplash.com/photo-1532103050105-860af53bc6aa', + }, + ]; + + readonly selectedPost = signal(null); + + selectPost(post: Post) { + this.selectedPost.set(post); + } +} diff --git a/apps/angular/react-in-angular/src/app/app.config.ts b/apps/angular/45-react-in-angular/src/app/app.config.ts similarity index 100% rename from apps/angular/react-in-angular/src/app/app.config.ts rename to apps/angular/45-react-in-angular/src/app/app.config.ts diff --git a/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx b/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx new file mode 100644 index 000000000..3f6b9e4cd --- /dev/null +++ b/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx @@ -0,0 +1,29 @@ +// import React from 'react'; + +export default function ReactPost(props: { + title?: string; + description?: string; + pictureLink?: string; + selected?: boolean; + handleClick: () => void; +}) { + return ( +
+
+ {props.title} +
+
{props.title}
+

{props.description}

+ +
+
+
+ ); +} diff --git a/apps/angular/react-in-angular/src/app/react/post.component.ts b/apps/angular/45-react-in-angular/src/app/react/post.component.ts similarity index 100% rename from apps/angular/react-in-angular/src/app/react/post.component.ts rename to apps/angular/45-react-in-angular/src/app/react/post.component.ts diff --git a/apps/angular/pipe-hard/src/assets/.gitkeep b/apps/angular/45-react-in-angular/src/assets/.gitkeep similarity index 100% rename from apps/angular/pipe-hard/src/assets/.gitkeep rename to apps/angular/45-react-in-angular/src/assets/.gitkeep diff --git a/apps/angular/pipe-hard/src/favicon.ico b/apps/angular/45-react-in-angular/src/favicon.ico similarity index 100% rename from apps/angular/pipe-hard/src/favicon.ico rename to apps/angular/45-react-in-angular/src/favicon.ico diff --git a/apps/angular/react-in-angular/src/index.html b/apps/angular/45-react-in-angular/src/index.html similarity index 100% rename from apps/angular/react-in-angular/src/index.html rename to apps/angular/45-react-in-angular/src/index.html diff --git a/apps/angular/router-input/src/main.ts b/apps/angular/45-react-in-angular/src/main.ts similarity index 100% rename from apps/angular/router-input/src/main.ts rename to apps/angular/45-react-in-angular/src/main.ts diff --git a/apps/angular/module-to-standalone/src/styles.scss b/apps/angular/45-react-in-angular/src/styles.scss similarity index 100% rename from apps/angular/module-to-standalone/src/styles.scss rename to apps/angular/45-react-in-angular/src/styles.scss diff --git a/apps/angular/injection-token/src/test-setup.ts b/apps/angular/45-react-in-angular/src/test-setup.ts similarity index 100% rename from apps/angular/injection-token/src/test-setup.ts rename to apps/angular/45-react-in-angular/src/test-setup.ts diff --git a/apps/angular/projection/tailwind.config.js b/apps/angular/45-react-in-angular/tailwind.config.js similarity index 100% rename from apps/angular/projection/tailwind.config.js rename to apps/angular/45-react-in-angular/tailwind.config.js diff --git a/apps/angular/signal-input/tsconfig.app.json b/apps/angular/45-react-in-angular/tsconfig.app.json similarity index 100% rename from apps/angular/signal-input/tsconfig.app.json rename to apps/angular/45-react-in-angular/tsconfig.app.json diff --git a/apps/angular/interop-rxjs-signal/tsconfig.editor.json b/apps/angular/45-react-in-angular/tsconfig.editor.json similarity index 100% rename from apps/angular/interop-rxjs-signal/tsconfig.editor.json rename to apps/angular/45-react-in-angular/tsconfig.editor.json diff --git a/apps/forms/control-value-accessor/tsconfig.json b/apps/angular/45-react-in-angular/tsconfig.json similarity index 100% rename from apps/forms/control-value-accessor/tsconfig.json rename to apps/angular/45-react-in-angular/tsconfig.json diff --git a/apps/angular/react-in-angular/tsconfig.spec.json b/apps/angular/45-react-in-angular/tsconfig.spec.json similarity index 100% rename from apps/angular/react-in-angular/tsconfig.spec.json rename to apps/angular/45-react-in-angular/tsconfig.spec.json diff --git a/apps/angular/permissions/.eslintrc.json b/apps/angular/46-simple-animations/.eslintrc.json similarity index 100% rename from apps/angular/permissions/.eslintrc.json rename to apps/angular/46-simple-animations/.eslintrc.json diff --git a/apps/angular/simple-animations/README.md b/apps/angular/46-simple-animations/README.md similarity index 100% rename from apps/angular/simple-animations/README.md rename to apps/angular/46-simple-animations/README.md diff --git a/apps/angular/46-simple-animations/jest.config.ts b/apps/angular/46-simple-animations/jest.config.ts new file mode 100644 index 000000000..2ad20837f --- /dev/null +++ b/apps/angular/46-simple-animations/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'angular-simple-animations', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/46-simple-animations', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/46-simple-animations/project.json b/apps/angular/46-simple-animations/project.json new file mode 100644 index 000000000..1d00fd246 --- /dev/null +++ b/apps/angular/46-simple-animations/project.json @@ -0,0 +1,80 @@ +{ + "name": "angular-simple-animations", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/46-simple-animations/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/46-simple-animations", + "index": "apps/angular/46-simple-animations/src/index.html", + "browser": "apps/angular/46-simple-animations/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/46-simple-animations/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/46-simple-animations/src/favicon.ico", + "apps/angular/46-simple-animations/src/assets" + ], + "styles": ["apps/angular/46-simple-animations/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-simple-animations:build:production" + }, + "development": { + "buildTarget": "angular-simple-animations:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-simple-animations:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/46-simple-animations/src/app/app.component.ts b/apps/angular/46-simple-animations/src/app/app.component.ts new file mode 100644 index 000000000..ae63db419 --- /dev/null +++ b/apps/angular/46-simple-animations/src/app/app.component.ts @@ -0,0 +1,87 @@ +import { Component } from '@angular/core'; + +@Component({ + imports: [], + selector: 'app-root', + styles: ` + section { + @apply flex flex-1 flex-col gap-5; + } + + .list-item { + @apply flex flex-row border-b px-5 pb-2; + + span { + @apply flex-1; + } + } + `, + template: ` +
+
+
+

2008

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae + mollitia sequi accusantium, distinctio similique laudantium eveniet + quidem sit placeat possimus tempore dolorum inventore corporis atque + quae ad, nobis explicabo delectus. +

+
+ +
+

2010

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae + mollitia sequi accusantium, distinctio similique laudantium eveniet + quidem sit placeat possimus tempore dolorum inventore corporis atque + quae ad, nobis explicabo delectus. +

+
+ +
+

2012

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae + mollitia sequi accusantium, distinctio similique laudantium eveniet + quidem sit placeat possimus tempore dolorum inventore corporis atque + quae ad, nobis explicabo delectus. +

+
+
+ +
+
+ Name: + Samuel +
+ +
+ Age: + 28 +
+ +
+ Birthdate: + 02.11.1995 +
+ +
+ City: + Berlin +
+ +
+ Language: + English +
+ +
+ Like Pizza: + Hell yeah +
+
+
+ `, +}) +export class AppComponent {} diff --git a/apps/angular/signal-input/src/app/app.config.ts b/apps/angular/46-simple-animations/src/app/app.config.ts similarity index 100% rename from apps/angular/signal-input/src/app/app.config.ts rename to apps/angular/46-simple-animations/src/app/app.config.ts diff --git a/apps/angular/pipe-intermediate/src/assets/.gitkeep b/apps/angular/46-simple-animations/src/assets/.gitkeep similarity index 100% rename from apps/angular/pipe-intermediate/src/assets/.gitkeep rename to apps/angular/46-simple-animations/src/assets/.gitkeep diff --git a/apps/angular/pipe-intermediate/src/favicon.ico b/apps/angular/46-simple-animations/src/favicon.ico similarity index 100% rename from apps/angular/pipe-intermediate/src/favicon.ico rename to apps/angular/46-simple-animations/src/favicon.ico diff --git a/apps/angular/simple-animations/src/index.html b/apps/angular/46-simple-animations/src/index.html similarity index 100% rename from apps/angular/simple-animations/src/index.html rename to apps/angular/46-simple-animations/src/index.html diff --git a/apps/angular/signal-input/src/main.ts b/apps/angular/46-simple-animations/src/main.ts similarity index 100% rename from apps/angular/signal-input/src/main.ts rename to apps/angular/46-simple-animations/src/main.ts diff --git a/apps/angular/permissions/src/styles.scss b/apps/angular/46-simple-animations/src/styles.scss similarity index 100% rename from apps/angular/permissions/src/styles.scss rename to apps/angular/46-simple-animations/src/styles.scss diff --git a/apps/angular/interop-rxjs-signal/src/test-setup.ts b/apps/angular/46-simple-animations/src/test-setup.ts similarity index 100% rename from apps/angular/interop-rxjs-signal/src/test-setup.ts rename to apps/angular/46-simple-animations/src/test-setup.ts diff --git a/apps/angular/react-in-angular/tailwind.config.js b/apps/angular/46-simple-animations/tailwind.config.js similarity index 100% rename from apps/angular/react-in-angular/tailwind.config.js rename to apps/angular/46-simple-animations/tailwind.config.js diff --git a/apps/angular/simple-animations/tsconfig.app.json b/apps/angular/46-simple-animations/tsconfig.app.json similarity index 100% rename from apps/angular/simple-animations/tsconfig.app.json rename to apps/angular/46-simple-animations/tsconfig.app.json diff --git a/apps/angular/react-in-angular/tsconfig.editor.json b/apps/angular/46-simple-animations/tsconfig.editor.json similarity index 100% rename from apps/angular/react-in-angular/tsconfig.editor.json rename to apps/angular/46-simple-animations/tsconfig.editor.json diff --git a/apps/angular/46-simple-animations/tsconfig.json b/apps/angular/46-simple-animations/tsconfig.json new file mode 100644 index 000000000..25ca437b4 --- /dev/null +++ b/apps/angular/46-simple-animations/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/angular/simple-animations/tsconfig.spec.json b/apps/angular/46-simple-animations/tsconfig.spec.json similarity index 100% rename from apps/angular/simple-animations/tsconfig.spec.json rename to apps/angular/46-simple-animations/tsconfig.spec.json diff --git a/apps/angular/pipe-easy/.eslintrc.json b/apps/angular/5-crud-application/.eslintrc.json similarity index 100% rename from apps/angular/pipe-easy/.eslintrc.json rename to apps/angular/5-crud-application/.eslintrc.json diff --git a/apps/angular/5-crud-application/README.md b/apps/angular/5-crud-application/README.md new file mode 100644 index 000000000..8992014e3 --- /dev/null +++ b/apps/angular/5-crud-application/README.md @@ -0,0 +1,13 @@ +# Crud application + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-crud-application +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/5-crud/). diff --git a/apps/angular/5-crud-application/jest.config.ts b/apps/angular/5-crud-application/jest.config.ts new file mode 100644 index 000000000..f290dd70a --- /dev/null +++ b/apps/angular/5-crud-application/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'angular-crud-application', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/apps/angular/5-crud-application', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/5-crud-application/project.json b/apps/angular/5-crud-application/project.json new file mode 100644 index 000000000..cdad9374a --- /dev/null +++ b/apps/angular/5-crud-application/project.json @@ -0,0 +1,87 @@ +{ + "name": "angular-crud-application", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/5-crud-application/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/5-crud-application", + "index": "apps/angular/5-crud-application/src/index.html", + "main": "apps/angular/5-crud-application/src/main.ts", + "polyfills": "apps/angular/5-crud-application/src/polyfills.ts", + "tsConfig": "apps/angular/5-crud-application/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/5-crud-application/src/favicon.ico", + "apps/angular/5-crud-application/src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", + "apps/angular/5-crud-application/src/styles.scss" + ], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-crud-application:build:production" + }, + "development": { + "buildTarget": "angular-crud-application:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-crud-application:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/5-crud-application/src/app/app.component.ts b/apps/angular/5-crud-application/src/app/app.component.ts new file mode 100644 index 000000000..b756e8074 --- /dev/null +++ b/apps/angular/5-crud-application/src/app/app.component.ts @@ -0,0 +1,50 @@ +import { CommonModule } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { randText } from '@ngneat/falso'; + +@Component({ + imports: [CommonModule], + selector: 'app-root', + template: ` + @for (todo of todos; track todo.id) { + {{ todo.title }} + + } + `, + styles: [], +}) +export class AppComponent implements OnInit { + todos!: any[]; + + constructor(private http: HttpClient) {} + + ngOnInit(): void { + this.http + .get('https://jsonplaceholder.typicode.com/todos') + .subscribe((todos) => { + this.todos = todos; + }); + } + + update(todo: any) { + this.http + .put( + `https://jsonplaceholder.typicode.com/todos/${todo.id}`, + JSON.stringify({ + todo: todo.id, + title: randText(), + body: todo.body, + userId: todo.userId, + }), + { + headers: { + 'Content-type': 'application/json; charset=UTF-8', + }, + }, + ) + .subscribe((todoUpdated: any) => { + this.todos[todoUpdated.id - 1] = todoUpdated; + }); + } +} diff --git a/apps/angular/5-crud-application/src/app/app.config.ts b/apps/angular/5-crud-application/src/app/app.config.ts new file mode 100644 index 000000000..1c0c9422f --- /dev/null +++ b/apps/angular/5-crud-application/src/app/app.config.ts @@ -0,0 +1,6 @@ +import { provideHttpClient } from '@angular/common/http'; +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [provideHttpClient()], +}; diff --git a/apps/angular/projection/src/assets/.gitkeep b/apps/angular/5-crud-application/src/assets/.gitkeep similarity index 100% rename from apps/angular/projection/src/assets/.gitkeep rename to apps/angular/5-crud-application/src/assets/.gitkeep diff --git a/apps/angular/projection/src/favicon.ico b/apps/angular/5-crud-application/src/favicon.ico similarity index 100% rename from apps/angular/projection/src/favicon.ico rename to apps/angular/5-crud-application/src/favicon.ico diff --git a/apps/angular/5-crud-application/src/index.html b/apps/angular/5-crud-application/src/index.html new file mode 100644 index 000000000..b9ec0b609 --- /dev/null +++ b/apps/angular/5-crud-application/src/index.html @@ -0,0 +1,20 @@ + + + + + angular-crud-application + + + + + + + + + + + diff --git a/apps/angular/crud/src/main.ts b/apps/angular/5-crud-application/src/main.ts similarity index 100% rename from apps/angular/crud/src/main.ts rename to apps/angular/5-crud-application/src/main.ts diff --git a/apps/angular/permissions/src/polyfills.ts b/apps/angular/5-crud-application/src/polyfills.ts similarity index 100% rename from apps/angular/permissions/src/polyfills.ts rename to apps/angular/5-crud-application/src/polyfills.ts diff --git a/apps/angular/crud/src/styles.scss b/apps/angular/5-crud-application/src/styles.scss similarity index 100% rename from apps/angular/crud/src/styles.scss rename to apps/angular/5-crud-application/src/styles.scss diff --git a/apps/angular/react-in-angular/src/test-setup.ts b/apps/angular/5-crud-application/src/test-setup.ts similarity index 100% rename from apps/angular/react-in-angular/src/test-setup.ts rename to apps/angular/5-crud-application/src/test-setup.ts diff --git a/apps/angular/permissions/tsconfig.app.json b/apps/angular/5-crud-application/tsconfig.app.json similarity index 100% rename from apps/angular/permissions/tsconfig.app.json rename to apps/angular/5-crud-application/tsconfig.app.json diff --git a/apps/angular/ngfor-enhancement/tsconfig.editor.json b/apps/angular/5-crud-application/tsconfig.editor.json similarity index 100% rename from apps/angular/ngfor-enhancement/tsconfig.editor.json rename to apps/angular/5-crud-application/tsconfig.editor.json diff --git a/apps/angular/projection/tsconfig.json b/apps/angular/5-crud-application/tsconfig.json similarity index 100% rename from apps/angular/projection/tsconfig.json rename to apps/angular/5-crud-application/tsconfig.json diff --git a/apps/angular/crud/tsconfig.spec.json b/apps/angular/5-crud-application/tsconfig.spec.json similarity index 100% rename from apps/angular/crud/tsconfig.spec.json rename to apps/angular/5-crud-application/tsconfig.spec.json diff --git a/apps/angular/pipe-hard/.eslintrc.json b/apps/angular/52-lazy-load-component/.eslintrc.json similarity index 100% rename from apps/angular/pipe-hard/.eslintrc.json rename to apps/angular/52-lazy-load-component/.eslintrc.json diff --git a/apps/angular/52-lazy-load-component/README.md b/apps/angular/52-lazy-load-component/README.md new file mode 100644 index 000000000..7da25f89b --- /dev/null +++ b/apps/angular/52-lazy-load-component/README.md @@ -0,0 +1,13 @@ +# lazy-load-component + +> author: lance-finney + +### Run Application + +```bash +npx nx serve angular-lazy-load-component +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/52-lazy-load-component/). diff --git a/apps/angular/52-lazy-load-component/jest.config.ts b/apps/angular/52-lazy-load-component/jest.config.ts new file mode 100644 index 000000000..a0fd1100f --- /dev/null +++ b/apps/angular/52-lazy-load-component/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'angular-lazy-load-component', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/52-lazy-load-component', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/52-lazy-load-component/project.json b/apps/angular/52-lazy-load-component/project.json new file mode 100644 index 000000000..d3b28088a --- /dev/null +++ b/apps/angular/52-lazy-load-component/project.json @@ -0,0 +1,80 @@ +{ + "name": "angular-lazy-load-component", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/52-lazy-load-component/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/52-lazy-load-component", + "index": "apps/angular/52-lazy-load-component/src/index.html", + "browser": "apps/angular/52-lazy-load-component/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/52-lazy-load-component/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/52-lazy-load-component/src/favicon.ico", + "apps/angular/52-lazy-load-component/src/assets" + ], + "styles": ["apps/angular/52-lazy-load-component/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-lazy-load-component:build:production" + }, + "development": { + "buildTarget": "angular-lazy-load-component:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-lazy-load-component:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/52-lazy-load-component/src/app/app.component.ts b/apps/angular/52-lazy-load-component/src/app/app.component.ts new file mode 100644 index 000000000..6d8c03d29 --- /dev/null +++ b/apps/angular/52-lazy-load-component/src/app/app.component.ts @@ -0,0 +1,23 @@ +import { Component, signal } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` +
+ @if (topLoaded()) { + + } @else { + + + } +
+ `, + standalone: false, +}) +export class AppComponent { + topLoaded = signal(false); +} diff --git a/apps/angular/52-lazy-load-component/src/app/app.module.ts b/apps/angular/52-lazy-load-component/src/app/app.module.ts new file mode 100644 index 000000000..b5d430e67 --- /dev/null +++ b/apps/angular/52-lazy-load-component/src/app/app.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { AppComponent } from './app.component'; +import { PlaceholderComponent } from './placeholder.component'; +import { TopComponent } from './top.component'; + +@NgModule({ + declarations: [AppComponent, PlaceholderComponent, TopComponent], + imports: [BrowserModule], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts b/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts new file mode 100644 index 000000000..cbb2b5fa6 --- /dev/null +++ b/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-placeholder', + template: ` + I'm a placeholder component. + `, + styles: ` + :host { + display: grid; + padding: 20px; + background-color: #f0f0f0; + height: 50%; + } + `, + standalone: false, +}) +export class PlaceholderComponent {} diff --git a/apps/angular/52-lazy-load-component/src/app/top.component.ts b/apps/angular/52-lazy-load-component/src/app/top.component.ts new file mode 100644 index 000000000..e1ca9012c --- /dev/null +++ b/apps/angular/52-lazy-load-component/src/app/top.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-top', + template: ` + I am a very heavy, expensive component that should be lazy loaded. + `, + styles: ` + :host { + display: grid; + padding: 20px; + background-color: #f0f0f0; + height: 50%; + } + `, + standalone: false, +}) +export class TopComponent {} diff --git a/apps/angular/react-in-angular/src/assets/.gitkeep b/apps/angular/52-lazy-load-component/src/assets/.gitkeep similarity index 100% rename from apps/angular/react-in-angular/src/assets/.gitkeep rename to apps/angular/52-lazy-load-component/src/assets/.gitkeep diff --git a/apps/angular/react-in-angular/src/favicon.ico b/apps/angular/52-lazy-load-component/src/favicon.ico similarity index 100% rename from apps/angular/react-in-angular/src/favicon.ico rename to apps/angular/52-lazy-load-component/src/favicon.ico diff --git a/apps/angular/52-lazy-load-component/src/index.html b/apps/angular/52-lazy-load-component/src/index.html new file mode 100644 index 000000000..242ec3e3f --- /dev/null +++ b/apps/angular/52-lazy-load-component/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-lazy-load-component + + + + + + + + diff --git a/apps/angular/52-lazy-load-component/src/main.ts b/apps/angular/52-lazy-load-component/src/main.ts new file mode 100644 index 000000000..16de2365d --- /dev/null +++ b/apps/angular/52-lazy-load-component/src/main.ts @@ -0,0 +1,6 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app/app.module'; + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/apps/angular/react-in-angular/src/styles.scss b/apps/angular/52-lazy-load-component/src/styles.scss similarity index 100% rename from apps/angular/react-in-angular/src/styles.scss rename to apps/angular/52-lazy-load-component/src/styles.scss diff --git a/apps/angular/simple-animations/src/test-setup.ts b/apps/angular/52-lazy-load-component/src/test-setup.ts similarity index 100% rename from apps/angular/simple-animations/src/test-setup.ts rename to apps/angular/52-lazy-load-component/src/test-setup.ts diff --git a/apps/angular/signal-input/tailwind.config.js b/apps/angular/52-lazy-load-component/tailwind.config.js similarity index 100% rename from apps/angular/signal-input/tailwind.config.js rename to apps/angular/52-lazy-load-component/tailwind.config.js diff --git a/apps/angular/styling/tsconfig.app.json b/apps/angular/52-lazy-load-component/tsconfig.app.json similarity index 100% rename from apps/angular/styling/tsconfig.app.json rename to apps/angular/52-lazy-load-component/tsconfig.app.json diff --git a/apps/angular/52-lazy-load-component/tsconfig.editor.json b/apps/angular/52-lazy-load-component/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/52-lazy-load-component/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/52-lazy-load-component/tsconfig.json b/apps/angular/52-lazy-load-component/tsconfig.json new file mode 100644 index 000000000..4383e7eb8 --- /dev/null +++ b/apps/angular/52-lazy-load-component/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/forms/control-value-accessor/tsconfig.spec.json b/apps/angular/52-lazy-load-component/tsconfig.spec.json similarity index 100% rename from apps/forms/control-value-accessor/tsconfig.spec.json rename to apps/angular/52-lazy-load-component/tsconfig.spec.json diff --git a/apps/angular/pipe-intermediate/.eslintrc.json b/apps/angular/55-back-button-navigation/.eslintrc.json similarity index 100% rename from apps/angular/pipe-intermediate/.eslintrc.json rename to apps/angular/55-back-button-navigation/.eslintrc.json diff --git a/apps/angular/55-back-button-navigation/README.md b/apps/angular/55-back-button-navigation/README.md new file mode 100644 index 000000000..2534d270c --- /dev/null +++ b/apps/angular/55-back-button-navigation/README.md @@ -0,0 +1,13 @@ +# Back-Button-Navigation + +> author: ioannis-tsironis + +### Run Application + +```bash +npx nx serve angular-back-button-navigation +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/55-back-button-navigation/). diff --git a/apps/angular/55-back-button-navigation/jest.config.ts b/apps/angular/55-back-button-navigation/jest.config.ts new file mode 100644 index 000000000..845a03a01 --- /dev/null +++ b/apps/angular/55-back-button-navigation/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'angular-back-button-navigation', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/angular/55-back-button-navigation', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/angular/55-back-button-navigation/project.json b/apps/angular/55-back-button-navigation/project.json new file mode 100644 index 000000000..f53d65e27 --- /dev/null +++ b/apps/angular/55-back-button-navigation/project.json @@ -0,0 +1,85 @@ +{ + "name": "angular-back-button-navigation", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/55-back-button-navigation/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/55-back-button-navigation", + "index": "apps/angular/55-back-button-navigation/src/index.html", + "browser": "apps/angular/55-back-button-navigation/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/55-back-button-navigation/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/55-back-button-navigation/public" + } + ], + "styles": [ + "apps/angular/55-back-button-navigation/src/styles.scss", + "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-back-button-navigation:build:production" + }, + "development": { + "buildTarget": "angular-back-button-navigation:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-back-button-navigation:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/angular/55-back-button-navigation/src/app/app.component.html b/apps/angular/55-back-button-navigation/src/app/app.component.html new file mode 100644 index 000000000..0680b43f9 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/app.component.html @@ -0,0 +1 @@ + diff --git a/apps/angular/55-back-button-navigation/src/app/app.component.ts b/apps/angular/55-back-button-navigation/src/app/app.component.ts new file mode 100644 index 000000000..baffdae25 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/app.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet, RouterLink], + selector: 'app-root', + templateUrl: './app.component.html', +}) +export class AppComponent {} diff --git a/apps/angular/55-back-button-navigation/src/app/app.config.ts b/apps/angular/55-back-button-navigation/src/app/app.config.ts new file mode 100644 index 000000000..440cdf2c3 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/app.config.ts @@ -0,0 +1,10 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { APP_ROUTES } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(APP_ROUTES), + ], +}; diff --git a/apps/angular/55-back-button-navigation/src/app/app.routes.ts b/apps/angular/55-back-button-navigation/src/app/app.routes.ts new file mode 100644 index 000000000..7deecd57a --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/app.routes.ts @@ -0,0 +1,24 @@ +import { Routes } from '@angular/router'; +import { HomeComponent } from './home/home.component'; +import { SensitiveActionComponent } from './sensitive-action/sensitive-action.component'; +import { SimpleActionComponent } from './simple-action/simple-action.component'; + +export const APP_ROUTES: Routes = [ + { + path: '', + pathMatch: 'full', + redirectTo: 'home', + }, + { + path: 'home', + component: HomeComponent, + }, + { + path: 'simple-action', + component: SimpleActionComponent, + }, + { + path: 'sensitive-action', + component: SensitiveActionComponent, + }, +]; diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.html b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.html new file mode 100644 index 000000000..ff00ea965 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.html @@ -0,0 +1,6 @@ +

Delete file

+Would you like to delete cat.jpeg? + + + + diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts new file mode 100644 index 000000000..9a9dd0fef --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts @@ -0,0 +1,25 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogRef, + MatDialogTitle, +} from '@angular/material/dialog'; + +@Component({ + selector: 'app-dialog-dialog', + templateUrl: './dialog.component.html', + imports: [ + MatButtonModule, + MatDialogActions, + MatDialogClose, + MatDialogTitle, + MatDialogContent, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DialogComponent { + readonly dialogRef = inject(MatDialogRef); +} diff --git a/apps/angular/55-back-button-navigation/src/app/home/home.component.html b/apps/angular/55-back-button-navigation/src/app/home/home.component.html new file mode 100644 index 000000000..cce9e6d4f --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/home/home.component.html @@ -0,0 +1,7 @@ +
+ Go to simple dialog action page + + + + Go to sensitive dialog action page + diff --git a/apps/angular/55-back-button-navigation/src/app/home/home.component.ts b/apps/angular/55-back-button-navigation/src/app/home/home.component.ts new file mode 100644 index 000000000..18c4147b1 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/home/home.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { RouterLink } from '@angular/router'; + +@Component({ + imports: [MatButtonModule, RouterLink], + selector: 'app-home', + templateUrl: './home.component.html', +}) +export class HomeComponent {} diff --git a/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.html b/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.html new file mode 100644 index 000000000..bcb7382e9 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.html @@ -0,0 +1,3 @@ + diff --git a/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts b/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts new file mode 100644 index 000000000..a97282c33 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts @@ -0,0 +1,19 @@ +import { Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialog } from '@angular/material/dialog'; +import { DialogComponent } from '../dialog/dialog.component'; + +@Component({ + imports: [MatButtonModule], + selector: 'app-sensitive-action', + templateUrl: './sensitive-action.component.html', +}) +export class SensitiveActionComponent { + readonly #dialog = inject(MatDialog); + + openDialog(): void { + this.#dialog.open(DialogComponent, { + width: '250px', + }); + } +} diff --git a/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.html b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.html new file mode 100644 index 000000000..95f63e65e --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.html @@ -0,0 +1 @@ + diff --git a/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts new file mode 100644 index 000000000..fe97e7368 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts @@ -0,0 +1,19 @@ +import { Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialog } from '@angular/material/dialog'; +import { DialogComponent } from '../dialog/dialog.component'; + +@Component({ + imports: [MatButtonModule], + selector: 'app-simple-action', + templateUrl: './simple-action.component.html', +}) +export class SimpleActionComponent { + readonly #dialog = inject(MatDialog); + + openDialog(): void { + this.#dialog.open(DialogComponent, { + width: '250px', + }); + } +} diff --git a/apps/angular/55-back-button-navigation/src/index.html b/apps/angular/55-back-button-navigation/src/index.html new file mode 100644 index 000000000..4e657d614 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-back-button-navigation + + + + + + + + diff --git a/apps/angular/simple-animations/src/main.ts b/apps/angular/55-back-button-navigation/src/main.ts similarity index 100% rename from apps/angular/simple-animations/src/main.ts rename to apps/angular/55-back-button-navigation/src/main.ts diff --git a/apps/angular/55-back-button-navigation/src/styles.scss b/apps/angular/55-back-button-navigation/src/styles.scss new file mode 100644 index 000000000..acd290007 --- /dev/null +++ b/apps/angular/55-back-button-navigation/src/styles.scss @@ -0,0 +1,29 @@ +@use '@angular/material' as mat; + +/* You can add global styles to this file, and also import other style files */ + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@include mat.elevation-classes(); +@include mat.app-background(); + +$theme-primary: mat.m2-define-palette(mat.$m2-indigo-palette); +$theme-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400); + +$theme-warn: mat.m2-define-palette(mat.$m2-red-palette); + +$theme: mat.m2-define-light-theme( + ( + color: ( + primary: $theme-primary, + accent: $theme-accent, + warn: $theme-warn, + ), + typography: mat.m2-define-typography-config(), + ) +); + +@include mat.dialog-theme($theme); +@include mat.button-theme($theme); diff --git a/apps/forms/control-value-accessor/src/test-setup.ts b/apps/angular/55-back-button-navigation/src/test-setup.ts similarity index 100% rename from apps/forms/control-value-accessor/src/test-setup.ts rename to apps/angular/55-back-button-navigation/src/test-setup.ts diff --git a/apps/angular/simple-animations/tailwind.config.js b/apps/angular/55-back-button-navigation/tailwind.config.js similarity index 100% rename from apps/angular/simple-animations/tailwind.config.js rename to apps/angular/55-back-button-navigation/tailwind.config.js diff --git a/apps/angular/view-transition/tsconfig.app.json b/apps/angular/55-back-button-navigation/tsconfig.app.json similarity index 100% rename from apps/angular/view-transition/tsconfig.app.json rename to apps/angular/55-back-button-navigation/tsconfig.app.json diff --git a/apps/angular/55-back-button-navigation/tsconfig.editor.json b/apps/angular/55-back-button-navigation/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/55-back-button-navigation/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/55-back-button-navigation/tsconfig.json b/apps/angular/55-back-button-navigation/tsconfig.json new file mode 100644 index 000000000..4383e7eb8 --- /dev/null +++ b/apps/angular/55-back-button-navigation/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/performance/ngfor-optimize/tsconfig.spec.json b/apps/angular/55-back-button-navigation/tsconfig.spec.json similarity index 100% rename from apps/performance/ngfor-optimize/tsconfig.spec.json rename to apps/angular/55-back-button-navigation/tsconfig.spec.json diff --git a/apps/angular/react-in-angular/.eslintrc.json b/apps/angular/57-content-projection-default/.eslintrc.json similarity index 100% rename from apps/angular/react-in-angular/.eslintrc.json rename to apps/angular/57-content-projection-default/.eslintrc.json diff --git a/apps/angular/57-content-projection-default/README.md b/apps/angular/57-content-projection-default/README.md new file mode 100644 index 000000000..fc4579558 --- /dev/null +++ b/apps/angular/57-content-projection-default/README.md @@ -0,0 +1,13 @@ +# Content Projection Default + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-content-projection-default +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/57-content-projection-default/). diff --git a/apps/angular/57-content-projection-default/project.json b/apps/angular/57-content-projection-default/project.json new file mode 100644 index 000000000..a57738bff --- /dev/null +++ b/apps/angular/57-content-projection-default/project.json @@ -0,0 +1,81 @@ +{ + "name": "angular-content-projection-default", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/57-content-projection-default/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/57-content-projection-default", + "index": "apps/angular/57-content-projection-default/src/index.html", + "browser": "apps/angular/57-content-projection-default/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/57-content-projection-default/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/57-content-projection-default/public" + } + ], + "styles": [ + "apps/angular/57-content-projection-default/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kb", + "maximumError": "8kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-content-projection-default:build:production" + }, + "development": { + "buildTarget": "angular-content-projection-default:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-content-projection-default:build" + } + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "angular-content-projection-default:build", + "staticFilePath": "dist/apps/angular/57-content-projection-default/browser", + "spa": true + } + } + } +} diff --git a/apps/angular/router-input/src/favicon.ico b/apps/angular/57-content-projection-default/public/favicon.ico similarity index 100% rename from apps/angular/router-input/src/favicon.ico rename to apps/angular/57-content-projection-default/public/favicon.ico diff --git a/apps/angular/57-content-projection-default/src/app/app.component.ts b/apps/angular/57-content-projection-default/src/app/app.component.ts new file mode 100644 index 000000000..b3e370a34 --- /dev/null +++ b/apps/angular/57-content-projection-default/src/app/app.component.ts @@ -0,0 +1,16 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CardComponent } from './card.component'; + +@Component({ + imports: [CardComponent], + selector: 'app-root', + template: ` + + + `, + host: { + class: 'p-4 block flex flex-col gap-1', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent {} diff --git a/apps/angular/57-content-projection-default/src/app/app.config.ts b/apps/angular/57-content-projection-default/src/app/app.config.ts new file mode 100644 index 000000000..034603cfd --- /dev/null +++ b/apps/angular/57-content-projection-default/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [provideZoneChangeDetection({ eventCoalescing: true })], +}; diff --git a/apps/angular/57-content-projection-default/src/app/card.component.ts b/apps/angular/57-content-projection-default/src/app/card.component.ts new file mode 100644 index 000000000..851a6619d --- /dev/null +++ b/apps/angular/57-content-projection-default/src/app/card.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +@Component({ + selector: 'app-card', + imports: [], + template: ` +
{{ title() }}
+ @if (message()) { +
{{ message() }}
+ } @else { +
Aucun message
+ } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'p-4 border border-grey rounded-sm flex flex-col w-[200px]', + }, +}) +export class CardComponent { + title = input.required(); + message = input(undefined); +} diff --git a/apps/angular/57-content-projection-default/src/index.html b/apps/angular/57-content-projection-default/src/index.html new file mode 100644 index 000000000..8b3015d4b --- /dev/null +++ b/apps/angular/57-content-projection-default/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-content-projection-default + + + + + + + + diff --git a/apps/angular/view-transition/src/main.ts b/apps/angular/57-content-projection-default/src/main.ts similarity index 100% rename from apps/angular/view-transition/src/main.ts rename to apps/angular/57-content-projection-default/src/main.ts diff --git a/apps/angular/signal-input/src/styles.scss b/apps/angular/57-content-projection-default/src/styles.scss similarity index 100% rename from apps/angular/signal-input/src/styles.scss rename to apps/angular/57-content-projection-default/src/styles.scss diff --git a/apps/angular/view-transition/tailwind.config.js b/apps/angular/57-content-projection-default/tailwind.config.js similarity index 100% rename from apps/angular/view-transition/tailwind.config.js rename to apps/angular/57-content-projection-default/tailwind.config.js diff --git a/apps/forms/control-value-accessor/tsconfig.app.json b/apps/angular/57-content-projection-default/tsconfig.app.json similarity index 100% rename from apps/forms/control-value-accessor/tsconfig.app.json rename to apps/angular/57-content-projection-default/tsconfig.app.json diff --git a/apps/angular/57-content-projection-default/tsconfig.editor.json b/apps/angular/57-content-projection-default/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/57-content-projection-default/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/57-content-projection-default/tsconfig.json b/apps/angular/57-content-projection-default/tsconfig.json new file mode 100644 index 000000000..1b86db04e --- /dev/null +++ b/apps/angular/57-content-projection-default/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "es2022", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/angular/signal-input/.eslintrc.json b/apps/angular/58-content-projection-condition/.eslintrc.json similarity index 100% rename from apps/angular/signal-input/.eslintrc.json rename to apps/angular/58-content-projection-condition/.eslintrc.json diff --git a/apps/angular/58-content-projection-condition/README.md b/apps/angular/58-content-projection-condition/README.md new file mode 100644 index 000000000..755bd8854 --- /dev/null +++ b/apps/angular/58-content-projection-condition/README.md @@ -0,0 +1,13 @@ +# Content Projection Condition + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-content-projection-condition +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/58-content-projection-condition/). diff --git a/apps/angular/58-content-projection-condition/project.json b/apps/angular/58-content-projection-condition/project.json new file mode 100644 index 000000000..42869e68b --- /dev/null +++ b/apps/angular/58-content-projection-condition/project.json @@ -0,0 +1,84 @@ +{ + "name": "angular-content-projection-condition", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/58-content-projection-condition/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/58-content-projection-condition", + "index": "apps/angular/58-content-projection-condition/src/index.html", + "browser": "apps/angular/58-content-projection-condition/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/58-content-projection-condition/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/58-content-projection-condition/public" + } + ], + "styles": [ + "apps/angular/58-content-projection-condition/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kb", + "maximumError": "8kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-content-projection-condition:build:production" + }, + "development": { + "buildTarget": "angular-content-projection-condition:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-content-projection-condition:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "angular-content-projection-condition:build", + "staticFilePath": "dist/apps/angular/58-content-projection-condition/browser", + "spa": true + } + } + } +} diff --git a/apps/angular/signal-input/src/favicon.ico b/apps/angular/58-content-projection-condition/public/favicon.ico similarity index 100% rename from apps/angular/signal-input/src/favicon.ico rename to apps/angular/58-content-projection-condition/public/favicon.ico diff --git a/apps/angular/58-content-projection-condition/src/app/app.component.ts b/apps/angular/58-content-projection-condition/src/app/app.component.ts new file mode 100644 index 000000000..afad56f22 --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/app/app.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CardComponent } from './card.component'; + +@Component({ + imports: [CardComponent], + selector: 'app-root', + template: ` + +
Card 1
+
Message 1
+
+ +
Card 2
+
Message 2
+
+ `, + host: { + class: 'p-4 block flex flex-col gap-1', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent {} diff --git a/apps/angular/58-content-projection-condition/src/app/app.config.ts b/apps/angular/58-content-projection-condition/src/app/app.config.ts new file mode 100644 index 000000000..034603cfd --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [provideZoneChangeDetection({ eventCoalescing: true })], +}; diff --git a/apps/angular/58-content-projection-condition/src/app/card.component.ts b/apps/angular/58-content-projection-condition/src/app/card.component.ts new file mode 100644 index 000000000..46925977c --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/app/card.component.ts @@ -0,0 +1,25 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +@Component({ + selector: 'app-card', + template: ` + @if (small()) { + + + } @else { +
+
+ +
+ +
+ } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'p-4 border border-grey rounded-sm flex flex-col w-[200px]', + }, +}) +export class CardComponent { + small = input(false); +} diff --git a/apps/angular/58-content-projection-condition/src/index.html b/apps/angular/58-content-projection-condition/src/index.html new file mode 100644 index 000000000..f1768ec71 --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-content-projection-condition + + + + + + + + diff --git a/apps/nx/static-dynamic-import/src/main.ts b/apps/angular/58-content-projection-condition/src/main.ts similarity index 100% rename from apps/nx/static-dynamic-import/src/main.ts rename to apps/angular/58-content-projection-condition/src/main.ts diff --git a/apps/angular/simple-animations/src/styles.scss b/apps/angular/58-content-projection-condition/src/styles.scss similarity index 100% rename from apps/angular/simple-animations/src/styles.scss rename to apps/angular/58-content-projection-condition/src/styles.scss diff --git a/apps/forms/control-value-accessor/tailwind.config.js b/apps/angular/58-content-projection-condition/tailwind.config.js similarity index 100% rename from apps/forms/control-value-accessor/tailwind.config.js rename to apps/angular/58-content-projection-condition/tailwind.config.js diff --git a/apps/nx/static-dynamic-import/tsconfig.app.json b/apps/angular/58-content-projection-condition/tsconfig.app.json similarity index 100% rename from apps/nx/static-dynamic-import/tsconfig.app.json rename to apps/angular/58-content-projection-condition/tsconfig.app.json diff --git a/apps/angular/58-content-projection-condition/tsconfig.editor.json b/apps/angular/58-content-projection-condition/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/58-content-projection-condition/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/58-content-projection-condition/tsconfig.json b/apps/angular/58-content-projection-condition/tsconfig.json new file mode 100644 index 000000000..1b86db04e --- /dev/null +++ b/apps/angular/58-content-projection-condition/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "es2022", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/angular/59-content-projection-defer/.eslintrc.json b/apps/angular/59-content-projection-defer/.eslintrc.json new file mode 100644 index 000000000..995177b5b --- /dev/null +++ b/apps/angular/59-content-projection-defer/.eslintrc.json @@ -0,0 +1,37 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/component-class-suffix": "off", + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/angular/59-content-projection-defer/README.md b/apps/angular/59-content-projection-defer/README.md new file mode 100644 index 000000000..f726842d1 --- /dev/null +++ b/apps/angular/59-content-projection-defer/README.md @@ -0,0 +1,13 @@ +# content-projection-defer + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-content-projection-defer +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/59-content-projection-defer/). diff --git a/apps/angular/59-content-projection-defer/project.json b/apps/angular/59-content-projection-defer/project.json new file mode 100644 index 000000000..5572ab573 --- /dev/null +++ b/apps/angular/59-content-projection-defer/project.json @@ -0,0 +1,82 @@ +{ + "name": "angular-content-projection-defer", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/59-content-projection-defer/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/59-content-projection-defer", + "index": "apps/angular/59-content-projection-defer/src/index.html", + "browser": "apps/angular/59-content-projection-defer/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/59-content-projection-defer/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/59-content-projection-defer/public" + } + ], + "styles": ["apps/angular/59-content-projection-defer/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kb", + "maximumError": "8kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-content-projection-defer:build:production" + }, + "development": { + "buildTarget": "angular-content-projection-defer:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-content-projection-defer:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "angular-content-projection-defer:build", + "staticFilePath": "dist/apps/angular/59-content-projection-defer/browser", + "spa": true + } + } + } +} diff --git a/apps/angular/simple-animations/src/favicon.ico b/apps/angular/59-content-projection-defer/public/favicon.ico similarity index 100% rename from apps/angular/simple-animations/src/favicon.ico rename to apps/angular/59-content-projection-defer/public/favicon.ico diff --git a/apps/angular/59-content-projection-defer/src/app/app.component.ts b/apps/angular/59-content-projection-defer/src/app/app.component.ts new file mode 100644 index 000000000..ae40bc880 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/app.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet, RouterLink], + selector: 'app-root', + template: ` +
+ + +
+ + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'flex flex-col gap-2 ', + }, +}) +export class AppComponent {} diff --git a/apps/angular/59-content-projection-defer/src/app/app.config.ts b/apps/angular/59-content-projection-defer/src/app/app.config.ts new file mode 100644 index 000000000..faf4d099a --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/app.config.ts @@ -0,0 +1,12 @@ +import { provideHttpClient } from '@angular/common/http'; +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { appRoutes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(appRoutes), + provideHttpClient(), + ], +}; diff --git a/apps/angular/59-content-projection-defer/src/app/app.routes.ts b/apps/angular/59-content-projection-defer/src/app/app.routes.ts new file mode 100644 index 000000000..3ca1b67cc --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/app.routes.ts @@ -0,0 +1,13 @@ +import { Route } from '@angular/router'; + +export const appRoutes: Route[] = [ + { + path: 'page-1', + loadComponent: () => import('./page-1').then((m) => m.Page1), + }, + { + path: 'page-2', + loadComponent: () => import('./page-2').then((m) => m.Page2), + }, + { path: '**', redirectTo: 'page-1' }, +]; diff --git a/apps/angular/59-content-projection-defer/src/app/expandable-card.ts b/apps/angular/59-content-projection-defer/src/app/expandable-card.ts new file mode 100644 index 000000000..8f446ed80 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/expandable-card.ts @@ -0,0 +1,54 @@ +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; + +@Component({ + selector: 'app-expandable-card', + template: ` + + +
+ +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'flex flex-col gap-2 ', + }, +}) +export class ExpandableCard { + public isExpanded = signal(false); +} diff --git a/apps/angular/59-content-projection-defer/src/app/page-1.ts b/apps/angular/59-content-projection-defer/src/app/page-1.ts new file mode 100644 index 000000000..868d76959 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/page-1.ts @@ -0,0 +1,10 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'app-page-1', + template: ` + page1 + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Page1 {} diff --git a/apps/angular/59-content-projection-defer/src/app/page-2.ts b/apps/angular/59-content-projection-defer/src/app/page-2.ts new file mode 100644 index 000000000..5665466d8 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/page-2.ts @@ -0,0 +1,43 @@ +import { httpResource } from '@angular/common/http'; +import { + ChangeDetectionStrategy, + Component, + ResourceStatus, +} from '@angular/core'; +import { ExpandableCard } from './expandable-card'; + +interface Post { + id: number; + title: string; + body: string; + userId: number; +} + +@Component({ + selector: 'app-page-2', + template: ` + page2 + +
Load Post
+
+ @if (postResource.isLoading()) { + Loading... + } @else if (postResource.status() === ResourceStatus.Error) { + Error... + } @else { + @for (post of postResource.value(); track post.id) { +
{{ post.title }}
+ } + } +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ExpandableCard], +}) +export class Page2 { + public postResource = httpResource( + 'https://jsonplaceholder.typicode.com/posts', + ); + protected readonly ResourceStatus = ResourceStatus; +} diff --git a/apps/angular/59-content-projection-defer/src/index.html b/apps/angular/59-content-projection-defer/src/index.html new file mode 100644 index 000000000..79f435fa6 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-content-projection-defer + + + + + + + + diff --git a/apps/performance/default-onpush/src/main.ts b/apps/angular/59-content-projection-defer/src/main.ts similarity index 100% rename from apps/performance/default-onpush/src/main.ts rename to apps/angular/59-content-projection-defer/src/main.ts diff --git a/apps/forms/control-value-accessor/src/styles.scss b/apps/angular/59-content-projection-defer/src/styles.scss similarity index 100% rename from apps/forms/control-value-accessor/src/styles.scss rename to apps/angular/59-content-projection-defer/src/styles.scss diff --git a/apps/nx/static-dynamic-import/tailwind.config.js b/apps/angular/59-content-projection-defer/tailwind.config.js similarity index 100% rename from apps/nx/static-dynamic-import/tailwind.config.js rename to apps/angular/59-content-projection-defer/tailwind.config.js diff --git a/apps/performance/christmas-web-worker/tsconfig.app.json b/apps/angular/59-content-projection-defer/tsconfig.app.json similarity index 100% rename from apps/performance/christmas-web-worker/tsconfig.app.json rename to apps/angular/59-content-projection-defer/tsconfig.app.json diff --git a/apps/angular/59-content-projection-defer/tsconfig.editor.json b/apps/angular/59-content-projection-defer/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/59-content-projection-defer/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/59-content-projection-defer/tsconfig.json b/apps/angular/59-content-projection-defer/tsconfig.json new file mode 100644 index 000000000..1b86db04e --- /dev/null +++ b/apps/angular/59-content-projection-defer/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "es2022", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/angular/simple-animations/.eslintrc.json b/apps/angular/6-structural-directive/.eslintrc.json similarity index 100% rename from apps/angular/simple-animations/.eslintrc.json rename to apps/angular/6-structural-directive/.eslintrc.json diff --git a/apps/angular/6-structural-directive/README.md b/apps/angular/6-structural-directive/README.md new file mode 100644 index 000000000..775b40981 --- /dev/null +++ b/apps/angular/6-structural-directive/README.md @@ -0,0 +1,13 @@ +# Structural Directive + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-structural-directive +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/6-permissions/). diff --git a/apps/angular/6-structural-directive/project.json b/apps/angular/6-structural-directive/project.json new file mode 100644 index 000000000..f803b0cb5 --- /dev/null +++ b/apps/angular/6-structural-directive/project.json @@ -0,0 +1,72 @@ +{ + "name": "angular-structural-directive", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/6-structural-directive/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/6-structural-directive", + "index": "apps/angular/6-structural-directive/src/index.html", + "main": "apps/angular/6-structural-directive/src/main.ts", + "polyfills": "apps/angular/6-structural-directive/src/polyfills.ts", + "tsConfig": "apps/angular/6-structural-directive/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/6-structural-directive/src/favicon.ico", + "apps/angular/6-structural-directive/src/assets" + ], + "styles": ["apps/angular/6-structural-directive/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-structural-directive:build:production" + }, + "development": { + "buildTarget": "angular-structural-directive:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-structural-directive:build" + } + } + } +} diff --git a/apps/angular/6-structural-directive/src/app/app.component.ts b/apps/angular/6-structural-directive/src/app/app.component.ts new file mode 100644 index 000000000..a3d63b374 --- /dev/null +++ b/apps/angular/6-structural-directive/src/app/app.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet], + selector: 'app-root', + template: ` + + `, + styles: [], +}) +export class AppComponent {} diff --git a/apps/angular/permissions/src/app/app.config.ts b/apps/angular/6-structural-directive/src/app/app.config.ts similarity index 100% rename from apps/angular/permissions/src/app/app.config.ts rename to apps/angular/6-structural-directive/src/app/app.config.ts diff --git a/apps/angular/permissions/src/app/button.component.ts b/apps/angular/6-structural-directive/src/app/button.component.ts similarity index 100% rename from apps/angular/permissions/src/app/button.component.ts rename to apps/angular/6-structural-directive/src/app/button.component.ts diff --git a/apps/angular/permissions/src/app/dashboard/admin.component.ts b/apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts similarity index 95% rename from apps/angular/permissions/src/app/dashboard/admin.component.ts rename to apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts index 72e4b3bef..26bb23284 100644 --- a/apps/angular/permissions/src/app/dashboard/admin.component.ts +++ b/apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts @@ -4,7 +4,6 @@ import { ButtonComponent } from '../button.component'; @Component({ selector: 'app-dashboard', - standalone: true, imports: [RouterLink, ButtonComponent], template: `

dashboard for Admin works!

diff --git a/apps/angular/permissions/src/app/dashboard/manager.component.ts b/apps/angular/6-structural-directive/src/app/dashboard/manager.component.ts similarity index 100% rename from apps/angular/permissions/src/app/dashboard/manager.component.ts rename to apps/angular/6-structural-directive/src/app/dashboard/manager.component.ts diff --git a/apps/angular/permissions/src/app/information.component.ts b/apps/angular/6-structural-directive/src/app/information.component.ts similarity index 97% rename from apps/angular/permissions/src/app/information.component.ts rename to apps/angular/6-structural-directive/src/app/information.component.ts index e4adeb1b9..81b339520 100644 --- a/apps/angular/permissions/src/app/information.component.ts +++ b/apps/angular/6-structural-directive/src/app/information.component.ts @@ -4,7 +4,6 @@ import { UserStore } from './user.store'; @Component({ selector: 'app-information', - standalone: true, imports: [CommonModule], template: `

Information Panel

diff --git a/apps/angular/permissions/src/app/login.component.ts b/apps/angular/6-structural-directive/src/app/login.component.ts similarity index 98% rename from apps/angular/permissions/src/app/login.component.ts rename to apps/angular/6-structural-directive/src/app/login.component.ts index cd36d9603..b8644ed38 100644 --- a/apps/angular/permissions/src/app/login.component.ts +++ b/apps/angular/6-structural-directive/src/app/login.component.ts @@ -14,7 +14,6 @@ import { import { UserStore } from './user.store'; @Component({ - standalone: true, imports: [InformationComponent, RouterLink, ButtonComponent], selector: 'app-login', template: ` diff --git a/apps/angular/permissions/src/app/routes.ts b/apps/angular/6-structural-directive/src/app/routes.ts similarity index 100% rename from apps/angular/permissions/src/app/routes.ts rename to apps/angular/6-structural-directive/src/app/routes.ts diff --git a/apps/angular/permissions/src/app/user.model.ts b/apps/angular/6-structural-directive/src/app/user.model.ts similarity index 100% rename from apps/angular/permissions/src/app/user.model.ts rename to apps/angular/6-structural-directive/src/app/user.model.ts diff --git a/apps/angular/permissions/src/app/user.store.ts b/apps/angular/6-structural-directive/src/app/user.store.ts similarity index 100% rename from apps/angular/permissions/src/app/user.store.ts rename to apps/angular/6-structural-directive/src/app/user.store.ts diff --git a/apps/angular/router-input/src/assets/.gitkeep b/apps/angular/6-structural-directive/src/assets/.gitkeep similarity index 100% rename from apps/angular/router-input/src/assets/.gitkeep rename to apps/angular/6-structural-directive/src/assets/.gitkeep diff --git a/apps/angular/styling/src/favicon.ico b/apps/angular/6-structural-directive/src/favicon.ico similarity index 100% rename from apps/angular/styling/src/favicon.ico rename to apps/angular/6-structural-directive/src/favicon.ico diff --git a/apps/angular/6-structural-directive/src/index.html b/apps/angular/6-structural-directive/src/index.html new file mode 100644 index 000000000..8a35fb109 --- /dev/null +++ b/apps/angular/6-structural-directive/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-structural-directive + + + + + + + + diff --git a/apps/angular/permissions/src/main.ts b/apps/angular/6-structural-directive/src/main.ts similarity index 100% rename from apps/angular/permissions/src/main.ts rename to apps/angular/6-structural-directive/src/main.ts diff --git a/apps/angular/pipe-easy/src/polyfills.ts b/apps/angular/6-structural-directive/src/polyfills.ts similarity index 100% rename from apps/angular/pipe-easy/src/polyfills.ts rename to apps/angular/6-structural-directive/src/polyfills.ts diff --git a/apps/nx/static-dynamic-import/src/styles.scss b/apps/angular/6-structural-directive/src/styles.scss similarity index 100% rename from apps/nx/static-dynamic-import/src/styles.scss rename to apps/angular/6-structural-directive/src/styles.scss diff --git a/apps/performance/christmas-web-worker/tailwind.config.js b/apps/angular/6-structural-directive/tailwind.config.js similarity index 100% rename from apps/performance/christmas-web-worker/tailwind.config.js rename to apps/angular/6-structural-directive/tailwind.config.js diff --git a/apps/angular/pipe-easy/tsconfig.app.json b/apps/angular/6-structural-directive/tsconfig.app.json similarity index 100% rename from apps/angular/pipe-easy/tsconfig.app.json rename to apps/angular/6-structural-directive/tsconfig.app.json diff --git a/apps/angular/permissions/tsconfig.editor.json b/apps/angular/6-structural-directive/tsconfig.editor.json similarity index 100% rename from apps/angular/permissions/tsconfig.editor.json rename to apps/angular/6-structural-directive/tsconfig.editor.json diff --git a/apps/angular/permissions/tsconfig.json b/apps/angular/6-structural-directive/tsconfig.json similarity index 100% rename from apps/angular/permissions/tsconfig.json rename to apps/angular/6-structural-directive/tsconfig.json diff --git a/apps/forms/control-value-accessor/.eslintrc.json b/apps/angular/8-pure-pipe/.eslintrc.json similarity index 100% rename from apps/forms/control-value-accessor/.eslintrc.json rename to apps/angular/8-pure-pipe/.eslintrc.json diff --git a/apps/angular/8-pure-pipe/README.md b/apps/angular/8-pure-pipe/README.md new file mode 100644 index 000000000..a65a33f85 --- /dev/null +++ b/apps/angular/8-pure-pipe/README.md @@ -0,0 +1,13 @@ +# Pure Pipe + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-pure-pipe +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/8-pipe-pure/). diff --git a/apps/angular/8-pure-pipe/project.json b/apps/angular/8-pure-pipe/project.json new file mode 100644 index 000000000..a684d542f --- /dev/null +++ b/apps/angular/8-pure-pipe/project.json @@ -0,0 +1,72 @@ +{ + "name": "angular-pure-pipe", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/8-pure-pipe/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/8-pure-pipe", + "index": "apps/angular/8-pure-pipe/src/index.html", + "main": "apps/angular/8-pure-pipe/src/main.ts", + "polyfills": "apps/angular/8-pure-pipe/src/polyfills.ts", + "tsConfig": "apps/angular/8-pure-pipe/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/8-pure-pipe/src/favicon.ico", + "apps/angular/8-pure-pipe/src/assets" + ], + "styles": ["apps/angular/8-pure-pipe/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-pure-pipe:build:production" + }, + "development": { + "buildTarget": "angular-pure-pipe:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-pure-pipe:build" + } + } + } +} diff --git a/apps/angular/8-pure-pipe/src/app/app.component.ts b/apps/angular/8-pure-pipe/src/app/app.component.ts new file mode 100644 index 000000000..930fe1313 --- /dev/null +++ b/apps/angular/8-pure-pipe/src/app/app.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + @for (person of persons; track person) { + {{ heavyComputation(person, $index) }} + } + `, +}) +export class AppComponent { + persons = ['toto', 'jack']; + + heavyComputation(name: string, index: number) { + // very heavy computation + return `${name} - ${index}`; + } +} diff --git a/apps/angular/signal-input/src/assets/.gitkeep b/apps/angular/8-pure-pipe/src/assets/.gitkeep similarity index 100% rename from apps/angular/signal-input/src/assets/.gitkeep rename to apps/angular/8-pure-pipe/src/assets/.gitkeep diff --git a/apps/angular/view-transition/src/favicon.ico b/apps/angular/8-pure-pipe/src/favicon.ico similarity index 100% rename from apps/angular/view-transition/src/favicon.ico rename to apps/angular/8-pure-pipe/src/favicon.ico diff --git a/apps/angular/8-pure-pipe/src/index.html b/apps/angular/8-pure-pipe/src/index.html new file mode 100644 index 000000000..c8ee70b97 --- /dev/null +++ b/apps/angular/8-pure-pipe/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-pure-pipe + + + + + + + + diff --git a/apps/angular/pipe-hard/src/main.ts b/apps/angular/8-pure-pipe/src/main.ts similarity index 100% rename from apps/angular/pipe-hard/src/main.ts rename to apps/angular/8-pure-pipe/src/main.ts diff --git a/apps/angular/pipe-hard/src/polyfills.ts b/apps/angular/8-pure-pipe/src/polyfills.ts similarity index 100% rename from apps/angular/pipe-hard/src/polyfills.ts rename to apps/angular/8-pure-pipe/src/polyfills.ts diff --git a/apps/angular/pipe-intermediate/src/styles.scss b/apps/angular/8-pure-pipe/src/styles.scss similarity index 100% rename from apps/angular/pipe-intermediate/src/styles.scss rename to apps/angular/8-pure-pipe/src/styles.scss diff --git a/apps/angular/pipe-hard/tsconfig.app.json b/apps/angular/8-pure-pipe/tsconfig.app.json similarity index 100% rename from apps/angular/pipe-hard/tsconfig.app.json rename to apps/angular/8-pure-pipe/tsconfig.app.json diff --git a/apps/angular/pipe-easy/tsconfig.editor.json b/apps/angular/8-pure-pipe/tsconfig.editor.json similarity index 100% rename from apps/angular/pipe-easy/tsconfig.editor.json rename to apps/angular/8-pure-pipe/tsconfig.editor.json diff --git a/apps/angular/pipe-easy/tsconfig.json b/apps/angular/8-pure-pipe/tsconfig.json similarity index 100% rename from apps/angular/pipe-easy/tsconfig.json rename to apps/angular/8-pure-pipe/tsconfig.json diff --git a/apps/ngrx/effect-selector/.eslintrc.json b/apps/angular/9-wrap-function-pipe/.eslintrc.json similarity index 100% rename from apps/ngrx/effect-selector/.eslintrc.json rename to apps/angular/9-wrap-function-pipe/.eslintrc.json diff --git a/apps/angular/9-wrap-function-pipe/README.md b/apps/angular/9-wrap-function-pipe/README.md new file mode 100644 index 000000000..1491e7dda --- /dev/null +++ b/apps/angular/9-wrap-function-pipe/README.md @@ -0,0 +1,13 @@ +# Wrap Function Pipe + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-wrap-function-pipe +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/9-pipe-wrapFn/). diff --git a/apps/angular/9-wrap-function-pipe/project.json b/apps/angular/9-wrap-function-pipe/project.json new file mode 100644 index 000000000..dc5c4ab6e --- /dev/null +++ b/apps/angular/9-wrap-function-pipe/project.json @@ -0,0 +1,72 @@ +{ + "name": "angular-wrap-function-pipe", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/angular/9-wrap-function-pipe/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/9-wrap-function-pipe", + "index": "apps/angular/9-wrap-function-pipe/src/index.html", + "main": "apps/angular/9-wrap-function-pipe/src/main.ts", + "polyfills": "apps/angular/9-wrap-function-pipe/src/polyfills.ts", + "tsConfig": "apps/angular/9-wrap-function-pipe/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/angular/9-wrap-function-pipe/src/favicon.ico", + "apps/angular/9-wrap-function-pipe/src/assets" + ], + "styles": ["apps/angular/9-wrap-function-pipe/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-wrap-function-pipe:build:production" + }, + "development": { + "buildTarget": "angular-wrap-function-pipe:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-wrap-function-pipe:build" + } + } + } +} diff --git a/apps/angular/9-wrap-function-pipe/src/app/app.component.ts b/apps/angular/9-wrap-function-pipe/src/app/app.component.ts new file mode 100644 index 000000000..af8b6ff73 --- /dev/null +++ b/apps/angular/9-wrap-function-pipe/src/app/app.component.ts @@ -0,0 +1,31 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + @for (person of persons; track person.name) { + {{ showName(person.name, $index) }} + {{ isAllowed(person.age, $first) }} + } + `, +}) +export class AppComponent { + persons = [ + { name: 'Toto', age: 10 }, + { name: 'Jack', age: 15 }, + { name: 'John', age: 30 }, + ]; + + showName(name: string, index: number) { + // very heavy computation + return `${name} - ${index}`; + } + + isAllowed(age: number, isFirst: boolean) { + if (isFirst) { + return 'always allowed'; + } else { + return age > 25 ? 'allowed' : 'declined'; + } + } +} diff --git a/apps/angular/simple-animations/src/assets/.gitkeep b/apps/angular/9-wrap-function-pipe/src/assets/.gitkeep similarity index 100% rename from apps/angular/simple-animations/src/assets/.gitkeep rename to apps/angular/9-wrap-function-pipe/src/assets/.gitkeep diff --git a/apps/forms/control-value-accessor/src/favicon.ico b/apps/angular/9-wrap-function-pipe/src/favicon.ico similarity index 100% rename from apps/forms/control-value-accessor/src/favicon.ico rename to apps/angular/9-wrap-function-pipe/src/favicon.ico diff --git a/apps/angular/9-wrap-function-pipe/src/index.html b/apps/angular/9-wrap-function-pipe/src/index.html new file mode 100644 index 000000000..bc8a0ddbd --- /dev/null +++ b/apps/angular/9-wrap-function-pipe/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-wrap-function-pipe + + + + + + + + diff --git a/apps/angular/pipe-intermediate/src/main.ts b/apps/angular/9-wrap-function-pipe/src/main.ts similarity index 100% rename from apps/angular/pipe-intermediate/src/main.ts rename to apps/angular/9-wrap-function-pipe/src/main.ts diff --git a/apps/angular/pipe-intermediate/src/polyfills.ts b/apps/angular/9-wrap-function-pipe/src/polyfills.ts similarity index 100% rename from apps/angular/pipe-intermediate/src/polyfills.ts rename to apps/angular/9-wrap-function-pipe/src/polyfills.ts diff --git a/apps/angular/router-input/src/styles.scss b/apps/angular/9-wrap-function-pipe/src/styles.scss similarity index 100% rename from apps/angular/router-input/src/styles.scss rename to apps/angular/9-wrap-function-pipe/src/styles.scss diff --git a/apps/angular/pipe-intermediate/tsconfig.app.json b/apps/angular/9-wrap-function-pipe/tsconfig.app.json similarity index 100% rename from apps/angular/pipe-intermediate/tsconfig.app.json rename to apps/angular/9-wrap-function-pipe/tsconfig.app.json diff --git a/apps/angular/pipe-hard/tsconfig.editor.json b/apps/angular/9-wrap-function-pipe/tsconfig.editor.json similarity index 100% rename from apps/angular/pipe-hard/tsconfig.editor.json rename to apps/angular/9-wrap-function-pipe/tsconfig.editor.json diff --git a/apps/angular/pipe-hard/tsconfig.json b/apps/angular/9-wrap-function-pipe/tsconfig.json similarity index 100% rename from apps/angular/pipe-hard/tsconfig.json rename to apps/angular/9-wrap-function-pipe/tsconfig.json diff --git a/apps/angular/anchor-scrolling/README.md b/apps/angular/anchor-scrolling/README.md deleted file mode 100644 index a159fd8b7..000000000 --- a/apps/angular/anchor-scrolling/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Anchor Navigation - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-anchor-scrolling -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/21-achor-scrolling/). diff --git a/apps/angular/anchor-scrolling/jest.config.ts b/apps/angular/anchor-scrolling/jest.config.ts deleted file mode 100644 index 6915111a0..000000000 --- a/apps/angular/anchor-scrolling/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-anchor-scrolling', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/angular/anchor-scrolling', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/anchor-scrolling/project.json b/apps/angular/anchor-scrolling/project.json deleted file mode 100644 index 8db932f32..000000000 --- a/apps/angular/anchor-scrolling/project.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "angular-anchor-scrolling", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/anchor-scrolling/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/anchor-scrolling", - "index": "apps/angular/anchor-scrolling/src/index.html", - "main": "apps/angular/anchor-scrolling/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/anchor-scrolling/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/anchor-scrolling/src/favicon.ico", - "apps/angular/anchor-scrolling/src/assets" - ], - "styles": ["apps/angular/anchor-scrolling/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-anchor-scrolling:build:production" - }, - "development": { - "buildTarget": "angular-anchor-scrolling:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-anchor-scrolling:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/anchor-scrolling/jest.config.ts" - } - } - }, - "tags": [] -} diff --git a/apps/angular/anchor-scrolling/src/app/app.component.ts b/apps/angular/anchor-scrolling/src/app/app.component.ts deleted file mode 100644 index 3fb7c5df0..000000000 --- a/apps/angular/anchor-scrolling/src/app/app.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet], - selector: 'app-root', - template: ` - - `, -}) -export class AppComponent {} diff --git a/apps/angular/anchor-scrolling/src/app/home.component.ts b/apps/angular/anchor-scrolling/src/app/home.component.ts deleted file mode 100644 index 0f24ff6e7..000000000 --- a/apps/angular/anchor-scrolling/src/app/home.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component } from '@angular/core'; -import { NavButtonComponent } from './nav-button.component'; - -@Component({ - standalone: true, - imports: [NavButtonComponent], - selector: 'app-home', - template: ` - Foo Page -
- Empty - Scroll Bottom -
-
- I want to scroll each - Scroll Top -
- `, -}) -export class HomeComponent {} diff --git a/apps/angular/anchor-scrolling/src/index.html b/apps/angular/anchor-scrolling/src/index.html deleted file mode 100644 index 7a2a4b377..000000000 --- a/apps/angular/anchor-scrolling/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - AnchorScrolling - - - - - - - - diff --git a/apps/angular/bug-cd/README.md b/apps/angular/bug-cd/README.md deleted file mode 100644 index cb42e778c..000000000 --- a/apps/angular/bug-cd/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Change Detection Bug - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-bug-cd -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/32-bug-cd/). diff --git a/apps/angular/bug-cd/jest.config.ts b/apps/angular/bug-cd/jest.config.ts deleted file mode 100644 index 1f24a7ce5..000000000 --- a/apps/angular/bug-cd/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-bug-cd', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/angular/bug-cd', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/bug-cd/project.json b/apps/angular/bug-cd/project.json deleted file mode 100644 index 1ba6cdf5a..000000000 --- a/apps/angular/bug-cd/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "angular-bug-cd", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/bug-cd/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/bug-cd", - "index": "apps/angular/bug-cd/src/index.html", - "main": "apps/angular/bug-cd/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/bug-cd/tsconfig.app.json", - "assets": [ - "apps/angular/bug-cd/src/favicon.ico", - "apps/angular/bug-cd/src/assets" - ], - "styles": ["apps/angular/bug-cd/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-bug-cd:build:production" - }, - "development": { - "buildTarget": "angular-bug-cd:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-bug-cd:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/bug-cd/jest.config.ts" - } - } - } -} diff --git a/apps/angular/bug-cd/src/app/app.component.ts b/apps/angular/bug-cd/src/app/app.component.ts deleted file mode 100644 index 046492f9b..000000000 --- a/apps/angular/bug-cd/src/app/app.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet], - selector: 'app-root', - template: ` -

My Application

-
- -
- -
-
- `, - host: { - class: 'flex flex-col gap-2', - }, -}) -export class AppComponent {} diff --git a/apps/angular/bug-cd/src/app/main-navigation.component.ts b/apps/angular/bug-cd/src/app/main-navigation.component.ts deleted file mode 100644 index c8a6f6d22..000000000 --- a/apps/angular/bug-cd/src/app/main-navigation.component.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { AsyncPipe, NgFor, NgIf } from '@angular/common'; -import { Component, Input, inject } from '@angular/core'; -import { RouterLink, RouterLinkActive } from '@angular/router'; -import { FakeServiceService } from './fake.service'; - -interface MenuItem { - path: string; - name: string; -} - -@Component({ - selector: 'app-nav', - standalone: true, - imports: [RouterLink, RouterLinkActive, NgFor], - template: ` - - - {{ menu.name }} - - - `, - styles: [ - ` - a.isSelected { - @apply bg-gray-600 text-white; - } - `, - ], - host: { - class: 'flex flex-col p-2 gap-2', - }, -}) -export class NavigationComponent { - @Input() menus!: MenuItem[]; -} - -@Component({ - standalone: true, - imports: [NavigationComponent, NgIf, AsyncPipe], - template: ` - - - - - - - - - - `, - host: {}, -}) -export class MainNavigationComponent { - private fakeBackend = inject(FakeServiceService); - - readonly info$ = this.fakeBackend.getInfoFromBackend(); - - getMenu(prop: string) { - return [ - { path: '/foo', name: `Foo ${prop}` }, - { path: '/bar', name: `Bar ${prop}` }, - ]; - } -} diff --git a/apps/angular/bug-cd/src/index.html b/apps/angular/bug-cd/src/index.html deleted file mode 100644 index ab925a75c..000000000 --- a/apps/angular/bug-cd/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - test - - - - - - - - diff --git a/apps/angular/context-outlet-type/README.md b/apps/angular/context-outlet-type/README.md deleted file mode 100644 index d195baa04..000000000 --- a/apps/angular/context-outlet-type/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Typed ContextOutlet - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-context-outlet-type -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/4-context-outlet-typed/). diff --git a/apps/angular/context-outlet-type/project.json b/apps/angular/context-outlet-type/project.json deleted file mode 100644 index ee2f03ccf..000000000 --- a/apps/angular/context-outlet-type/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-context-outlet-type", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/context-outlet-type/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/context-outlet-type", - "index": "apps/angular/context-outlet-type/src/index.html", - "main": "apps/angular/context-outlet-type/src/main.ts", - "polyfills": "apps/angular/context-outlet-type/src/polyfills.ts", - "tsConfig": "apps/angular/context-outlet-type/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/context-outlet-type/src/favicon.ico", - "apps/angular/context-outlet-type/src/assets" - ], - "styles": ["apps/angular/context-outlet-type/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-context-outlet-type:build:production" - }, - "development": { - "buildTarget": "angular-context-outlet-type:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-context-outlet-type:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/context-outlet-type/src/app/app.component.ts b/apps/angular/context-outlet-type/src/app/app.component.ts deleted file mode 100644 index 2683ba9d4..000000000 --- a/apps/angular/context-outlet-type/src/app/app.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { NgTemplateOutlet } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ListComponent } from './list.component'; -import { PersonComponent } from './person.component'; - -@Component({ - standalone: true, - imports: [NgTemplateOutlet, PersonComponent, ListComponent], - selector: 'app-root', - template: ` - - - {{ name }}: {{ age }} - - - - - - {{ student.name }}: {{ student.age }} - {{ i }} - - - - - - {{ city.name }}: {{ city.country }} - {{ i }} - - - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AppComponent { - person = { - name: 'toto', - age: 3, - }; - - students = [ - { name: 'toto', age: 3 }, - { name: 'titi', age: 4 }, - ]; - - cities = [ - { name: 'Paris', country: 'France' }, - { name: 'Berlin', country: 'Germany' }, - ]; -} diff --git a/apps/angular/context-outlet-type/src/app/list.component.ts b/apps/angular/context-outlet-type/src/app/list.component.ts deleted file mode 100644 index 5212a425e..000000000 --- a/apps/angular/context-outlet-type/src/app/list.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { - ChangeDetectionStrategy, - Component, - ContentChild, - Input, - TemplateRef, -} from '@angular/core'; - -@Component({ - selector: 'list', - standalone: true, - imports: [CommonModule], - template: ` -
- -
- - No Template - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ListComponent { - @Input() list!: TItem[]; - - @ContentChild('listRef', { read: TemplateRef }) - listTemplateRef!: TemplateRef; -} diff --git a/apps/angular/context-outlet-type/src/index.html b/apps/angular/context-outlet-type/src/index.html deleted file mode 100644 index b3ee6c60d..000000000 --- a/apps/angular/context-outlet-type/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - ContextOutletType - - - - - - - - diff --git a/apps/angular/crud/README.md b/apps/angular/crud/README.md deleted file mode 100644 index 0a6518b44..000000000 --- a/apps/angular/crud/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Crud application - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-crud -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/5-crud/). diff --git a/apps/angular/crud/jest.config.ts b/apps/angular/crud/jest.config.ts deleted file mode 100644 index 74a17f135..000000000 --- a/apps/angular/crud/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-crud', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../../coverage/apps/angular/crud', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/crud/project.json b/apps/angular/crud/project.json deleted file mode 100644 index cb694bbd8..000000000 --- a/apps/angular/crud/project.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "name": "angular-crud", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/crud/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/crud", - "index": "apps/angular/crud/src/index.html", - "main": "apps/angular/crud/src/main.ts", - "polyfills": "apps/angular/crud/src/polyfills.ts", - "tsConfig": "apps/angular/crud/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/crud/src/favicon.ico", - "apps/angular/crud/src/assets" - ], - "styles": [ - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", - "apps/angular/crud/src/styles.scss" - ], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-crud:build:production" - }, - "development": { - "buildTarget": "angular-crud:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-crud:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/crud/jest.config.ts" - } - } - }, - "tags": [] -} diff --git a/apps/angular/crud/src/app/app.component.ts b/apps/angular/crud/src/app/app.component.ts deleted file mode 100644 index 8c3d1b8ae..000000000 --- a/apps/angular/crud/src/app/app.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { HttpClient } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import { randText } from '@ngneat/falso'; - -@Component({ - standalone: true, - imports: [CommonModule], - selector: 'app-root', - template: ` -
- {{ todo.title }} - -
- `, - styles: [], -}) -export class AppComponent implements OnInit { - todos!: any[]; - - constructor(private http: HttpClient) {} - - ngOnInit(): void { - this.http - .get('https://jsonplaceholder.typicode.com/todos') - .subscribe((todos) => { - this.todos = todos; - }); - } - - update(todo: any) { - this.http - .put( - `https://jsonplaceholder.typicode.com/todos/${todo.id}`, - JSON.stringify({ - todo: todo.id, - title: randText(), - body: todo.body, - userId: todo.userId, - }), - { - headers: { - 'Content-type': 'application/json; charset=UTF-8', - }, - }, - ) - .subscribe((todoUpdated: any) => { - this.todos[todoUpdated.id - 1] = todoUpdated; - }); - } -} diff --git a/apps/angular/crud/src/app/app.config.ts b/apps/angular/crud/src/app/app.config.ts deleted file mode 100644 index de0a3ccec..000000000 --- a/apps/angular/crud/src/app/app.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { HttpClientModule } from '@angular/common/http'; -import { ApplicationConfig, importProvidersFrom } from '@angular/core'; -export const appConfig: ApplicationConfig = { - providers: [importProvidersFrom(HttpClientModule)], -}; diff --git a/apps/angular/crud/src/index.html b/apps/angular/crud/src/index.html deleted file mode 100644 index c556437f0..000000000 --- a/apps/angular/crud/src/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Http - - - - - - - - - - - diff --git a/apps/angular/decoupling/README.md b/apps/angular/decoupling/README.md deleted file mode 100644 index 0ba00e70c..000000000 --- a/apps/angular/decoupling/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Decoupling Components - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-decoupling -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/33-decoupling/). diff --git a/apps/angular/decoupling/project.json b/apps/angular/decoupling/project.json deleted file mode 100644 index 5a76f0112..000000000 --- a/apps/angular/decoupling/project.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "angular-decoupling", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/decoupling/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/decoupling", - "index": "apps/angular/decoupling/src/index.html", - "main": "apps/angular/decoupling/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/decoupling/tsconfig.app.json", - "assets": [ - "apps/angular/decoupling/src/favicon.ico", - "apps/angular/decoupling/src/assets" - ], - "styles": ["apps/angular/decoupling/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-decoupling:build:production" - }, - "development": { - "buildTarget": "angular-decoupling:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-decoupling:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/angular/decoupling/src/app/app.component.ts b/apps/angular/decoupling/src/app/app.component.ts deleted file mode 100644 index 34082c331..000000000 --- a/apps/angular/decoupling/src/app/app.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BtnDisabledDirective } from '@angular-challenges/decoupling/brain'; -import { BtnHelmetDirective } from '@angular-challenges/decoupling/helmet'; -import { Component } from '@angular/core'; - -@Component({ - standalone: true, - imports: [BtnDisabledDirective, BtnHelmetDirective], - selector: 'app-root', - template: ` - - `, -}) -export class AppComponent {} diff --git a/apps/angular/decoupling/src/index.html b/apps/angular/decoupling/src/index.html deleted file mode 100644 index 25b5ea13c..000000000 --- a/apps/angular/decoupling/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - decoupling - - - - - - - - diff --git a/apps/angular/di/README.md b/apps/angular/di/README.md deleted file mode 100644 index 032601c1f..000000000 --- a/apps/angular/di/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Master Dependancy Injection - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-di -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/16-di/). diff --git a/apps/angular/di/project.json b/apps/angular/di/project.json deleted file mode 100644 index 600263715..000000000 --- a/apps/angular/di/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-di", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/di/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/di", - "index": "apps/angular/di/src/index.html", - "main": "apps/angular/di/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/di/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/di/src/favicon.ico", - "apps/angular/di/src/assets" - ], - "styles": ["apps/angular/di/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-di:build:production" - }, - "development": { - "buildTarget": "angular-di:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-di:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/di/src/app/app.component.ts b/apps/angular/di/src/app/app.component.ts deleted file mode 100644 index e57a5a5bf..000000000 --- a/apps/angular/di/src/app/app.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { TableComponent } from '@angular-challenges/angular/di'; -import { AsyncPipe, NgFor } from '@angular/common'; -import { Component, Directive } from '@angular/core'; -import { CurrencyPipe } from './currency.pipe'; -import { CurrencyService } from './currency.service'; -import { Product, products } from './product.model'; - -interface ProductContext { - $implicit: Product; -} - -@Directive({ - selector: 'ng-template[product]', - standalone: true, -}) -export class ProductDirective { - static ngTemplateContextGuard( - dir: ProductDirective, - ctx: unknown, - ): ctx is ProductContext { - return true; - } -} - -@Component({ - standalone: true, - imports: [TableComponent, CurrencyPipe, AsyncPipe, NgFor, ProductDirective], - providers: [CurrencyService], - selector: 'app-root', - template: ` - - - - - - - - - - - - - - -
- {{ col }} -
{{ product.name }}{{ product.priceA | currency | async }}{{ product.priceB | currency | async }}{{ product.priceC | currency | async }}
- `, -}) -export class AppComponent { - products = products; - displayedColumns = ['name', 'priceA', 'priceB', 'priceC']; -} diff --git a/apps/angular/di/src/index.html b/apps/angular/di/src/index.html deleted file mode 100644 index c120f5b9c..000000000 --- a/apps/angular/di/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Di - - - - - - - - diff --git a/apps/angular/injection-token/jest.config.ts b/apps/angular/injection-token/jest.config.ts deleted file mode 100644 index d7105f89b..000000000 --- a/apps/angular/injection-token/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-injection-token', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/angular/injection-token', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/injection-token/project.json b/apps/angular/injection-token/project.json deleted file mode 100644 index e800af160..000000000 --- a/apps/angular/injection-token/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "angular-injection-token", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/injection-token/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/injection-token", - "index": "apps/angular/injection-token/src/index.html", - "main": "apps/angular/injection-token/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/injection-token/tsconfig.app.json", - "assets": [ - "apps/angular/injection-token/src/favicon.ico", - "apps/angular/injection-token/src/assets" - ], - "styles": ["apps/angular/injection-token/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-injection-token:build:production" - }, - "development": { - "buildTarget": "angular-injection-token:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-injection-token:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/injection-token/jest.config.ts" - } - } - } -} diff --git a/apps/angular/injection-token/src/app/app.component.ts b/apps/angular/injection-token/src/app/app.component.ts deleted file mode 100644 index 5ac8087cb..000000000 --- a/apps/angular/injection-token/src/app/app.component.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterLink, RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet, RouterLink], - selector: 'app-root', - template: ` -
- - -
- - `, - host: { - class: 'p-10 flex flex-col', - }, -}) -export class AppComponent {} diff --git a/apps/angular/interop-rxjs-signal/README.md b/apps/angular/interop-rxjs-signal/README.md deleted file mode 100644 index 9cf32efa5..000000000 --- a/apps/angular/interop-rxjs-signal/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Interoperability Rxjs/Signal - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-interop-rxjs-signal -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/30-interop-rxjs-signal/). diff --git a/apps/angular/interop-rxjs-signal/jest.config.ts b/apps/angular/interop-rxjs-signal/jest.config.ts deleted file mode 100644 index 23956ebcc..000000000 --- a/apps/angular/interop-rxjs-signal/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-interop-rxjs-signal', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/angular/interop-rxjs-signal', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/interop-rxjs-signal/project.json b/apps/angular/interop-rxjs-signal/project.json deleted file mode 100644 index 5009d474e..000000000 --- a/apps/angular/interop-rxjs-signal/project.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "name": "angular-interop-rxjs-signal", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/interop-rxjs-signal/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/interop-rxjs-signal", - "index": "apps/angular/interop-rxjs-signal/src/index.html", - "main": "apps/angular/interop-rxjs-signal/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/interop-rxjs-signal/tsconfig.app.json", - "assets": [ - "apps/angular/interop-rxjs-signal/src/favicon.ico", - "apps/angular/interop-rxjs-signal/src/assets" - ], - "styles": [ - "apps/angular/interop-rxjs-signal/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-interop-rxjs-signal:build:production" - }, - "development": { - "buildTarget": "angular-interop-rxjs-signal:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-interop-rxjs-signal:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/interop-rxjs-signal/jest.config.ts" - } - } - } -} diff --git a/apps/angular/interop-rxjs-signal/src/app/app.component.ts b/apps/angular/interop-rxjs-signal/src/app/app.component.ts deleted file mode 100644 index 7b83d1470..000000000 --- a/apps/angular/interop-rxjs-signal/src/app/app.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet], - selector: 'app-root', - template: ` - - `, - styles: [''], -}) -export class AppComponent {} diff --git a/apps/angular/interop-rxjs-signal/src/app/detail/detail.component.ts b/apps/angular/interop-rxjs-signal/src/app/detail/detail.component.ts deleted file mode 100644 index 3576db452..000000000 --- a/apps/angular/interop-rxjs-signal/src/app/detail/detail.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { DatePipe } from '@angular/common'; -import { Component, Input as RouterInput } from '@angular/core'; -import { RouterLink } from '@angular/router'; -import { Photo } from '../photo.model'; - -@Component({ - selector: 'app-photos', - standalone: true, - imports: [DatePipe, RouterLink], - template: ` - {{ photo.title }} -

- Title: - {{ photo.title }} -

-

- Owner: - {{ photo.ownername }} -

-

- Date: - {{ photo.datetaken | date }} -

-

- Tags: - {{ photo.tags }} -

- - - `, - host: { - class: 'p-5 block', - }, -}) -export default class DetailComponent { - @RouterInput({ - required: true, - transform: (value: string) => JSON.parse(decodeURIComponent(value)), - }) - photo!: Photo; -} diff --git a/apps/angular/interop-rxjs-signal/src/app/list/photos.component.ts b/apps/angular/interop-rxjs-signal/src/app/list/photos.component.ts deleted file mode 100644 index 29dc0c3f5..000000000 --- a/apps/angular/interop-rxjs-signal/src/app/list/photos.component.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { NgFor, NgIf } from '@angular/common'; -import { Component, OnInit, inject } from '@angular/core'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { RouterLinkWithHref } from '@angular/router'; -import { LetDirective } from '@ngrx/component'; -import { provideComponentStore } from '@ngrx/component-store'; -import { debounceTime, distinctUntilChanged, skipWhile, tap } from 'rxjs'; -import { Photo } from '../photo.model'; -import { PhotoStore } from './photos.store'; - -@Component({ - selector: 'app-photos', - standalone: true, - imports: [ - ReactiveFormsModule, - MatFormFieldModule, - MatProgressBarModule, - NgIf, - NgFor, - MatInputModule, - LetDirective, - RouterLinkWithHref, - ], - template: ` -

Photos

- - - Search - - - - -
-
- - - Page :{{ vm.page }} / {{ vm.pages }} -
- - - -
No Photos found. Type a search word.
-
-
- {{ vm.error }} -
-
-
- `, - providers: [provideComponentStore(PhotoStore)], - host: { - class: 'p-5 block', - }, -}) -export default class PhotosComponent implements OnInit { - store = inject(PhotoStore); - readonly vm$ = this.store.vm$.pipe( - tap(({ search }) => { - if (!this.formInit) { - this.search.setValue(search); - this.formInit = true; - } - }), - ); - - private formInit = false; - search = new FormControl(); - - ngOnInit(): void { - this.store.search( - this.search.valueChanges.pipe( - skipWhile(() => !this.formInit), - debounceTime(300), - distinctUntilChanged(), - ), - ); - } - - trackById(index: number, photo: Photo) { - return photo.id; - } - - encode(photo: Photo) { - return encodeURIComponent(JSON.stringify(photo)); - } -} diff --git a/apps/angular/interop-rxjs-signal/src/index.html b/apps/angular/interop-rxjs-signal/src/index.html deleted file mode 100644 index 1c69fbc7a..000000000 --- a/apps/angular/interop-rxjs-signal/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - rxjs-to-signal - - - - - - - - diff --git a/apps/angular/module-to-standalone/project.json b/apps/angular/module-to-standalone/project.json deleted file mode 100644 index 61fbd270f..000000000 --- a/apps/angular/module-to-standalone/project.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "angular-module-to-standalone", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/module-to-standalone/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/module-to-standalone", - "index": "apps/angular/module-to-standalone/src/index.html", - "main": "apps/angular/module-to-standalone/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/module-to-standalone/tsconfig.app.json", - "assets": [ - "apps/angular/module-to-standalone/src/favicon.ico", - "apps/angular/module-to-standalone/src/assets" - ], - "styles": ["apps/angular/module-to-standalone/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-module-to-standalone:build:production" - }, - "development": { - "buildTarget": "angular-module-to-standalone:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-module-to-standalone:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/angular/module-to-standalone/src/app/app.component.ts b/apps/angular/module-to-standalone/src/app/app.component.ts deleted file mode 100644 index 95eeab5b9..000000000 --- a/apps/angular/module-to-standalone/src/app/app.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - template: ` -
- - - -
- - `, - host: { - class: 'flex flex-col p-4 gap-3', - }, -}) -export class AppComponent {} diff --git a/apps/angular/module-to-standalone/src/index.html b/apps/angular/module-to-standalone/src/index.html deleted file mode 100644 index 4902faa44..000000000 --- a/apps/angular/module-to-standalone/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - module-to-standalone - - - - - - - - diff --git a/apps/angular/ngfor-enhancement/README.md b/apps/angular/ngfor-enhancement/README.md deleted file mode 100644 index 937dfb54e..000000000 --- a/apps/angular/ngfor-enhancement/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Directive Enhancement - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-ngfor-enhancement -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/3-directive-enhancement/). diff --git a/apps/angular/ngfor-enhancement/project.json b/apps/angular/ngfor-enhancement/project.json deleted file mode 100644 index 80c089ca0..000000000 --- a/apps/angular/ngfor-enhancement/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-ngfor-enhancement", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/ngfor-enhancement/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/ngfor-enhancement", - "index": "apps/angular/ngfor-enhancement/src/index.html", - "main": "apps/angular/ngfor-enhancement/src/main.ts", - "polyfills": "apps/angular/ngfor-enhancement/src/polyfills.ts", - "tsConfig": "apps/angular/ngfor-enhancement/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/ngfor-enhancement/src/favicon.ico", - "apps/angular/ngfor-enhancement/src/assets" - ], - "styles": ["apps/angular/ngfor-enhancement/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-ngfor-enhancement:build:production" - }, - "development": { - "buildTarget": "angular-ngfor-enhancement:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-ngfor-enhancement:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/ngfor-enhancement/src/app/app.component.ts b/apps/angular/ngfor-enhancement/src/app/app.component.ts deleted file mode 100644 index cd1d5f23c..000000000 --- a/apps/angular/ngfor-enhancement/src/app/app.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgFor, NgIf } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -interface Person { - name: string; -} - -@Component({ - standalone: true, - imports: [NgFor, NgIf], - selector: 'app-root', - template: ` - -
- {{ person.name }} -
-
- The list is empty !! - `, - styles: [], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AppComponent { - persons: Person[] = []; -} diff --git a/apps/angular/ngfor-enhancement/src/index.html b/apps/angular/ngfor-enhancement/src/index.html deleted file mode 100644 index c38d89dee..000000000 --- a/apps/angular/ngfor-enhancement/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgForEnhance - - - - - - - - diff --git a/apps/angular/permissions/README.md b/apps/angular/permissions/README.md deleted file mode 100644 index 99b2e5582..000000000 --- a/apps/angular/permissions/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Structural Directive - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-permissions -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/6-permissions/). diff --git a/apps/angular/permissions/project.json b/apps/angular/permissions/project.json deleted file mode 100644 index a594781d3..000000000 --- a/apps/angular/permissions/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-permissions", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/permissions/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/permissions", - "index": "apps/angular/permissions/src/index.html", - "main": "apps/angular/permissions/src/main.ts", - "polyfills": "apps/angular/permissions/src/polyfills.ts", - "tsConfig": "apps/angular/permissions/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/permissions/src/favicon.ico", - "apps/angular/permissions/src/assets" - ], - "styles": ["apps/angular/permissions/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-permissions:build:production" - }, - "development": { - "buildTarget": "angular-permissions:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-permissions:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/permissions/src/app/app.component.ts b/apps/angular/permissions/src/app/app.component.ts deleted file mode 100644 index d89a2f579..000000000 --- a/apps/angular/permissions/src/app/app.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet], - selector: 'app-root', - template: ` - - `, - styles: [], -}) -export class AppComponent {} diff --git a/apps/angular/permissions/src/index.html b/apps/angular/permissions/src/index.html deleted file mode 100644 index 14ab31c3b..000000000 --- a/apps/angular/permissions/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Permissions - - - - - - - - diff --git a/apps/angular/pipe-easy/README.md b/apps/angular/pipe-easy/README.md deleted file mode 100644 index 8501be8c7..000000000 --- a/apps/angular/pipe-easy/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Pure Pipe - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-pipe-easy -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/8-pipe-pure/). diff --git a/apps/angular/pipe-easy/project.json b/apps/angular/pipe-easy/project.json deleted file mode 100644 index b1fda113e..000000000 --- a/apps/angular/pipe-easy/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-pipe-easy", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/pipe-easy/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/pipe-easy", - "index": "apps/angular/pipe-easy/src/index.html", - "main": "apps/angular/pipe-easy/src/main.ts", - "polyfills": "apps/angular/pipe-easy/src/polyfills.ts", - "tsConfig": "apps/angular/pipe-easy/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/pipe-easy/src/favicon.ico", - "apps/angular/pipe-easy/src/assets" - ], - "styles": ["apps/angular/pipe-easy/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-pipe-easy:build:production" - }, - "development": { - "buildTarget": "angular-pipe-easy:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-pipe-easy:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/pipe-easy/src/app/app.component.ts b/apps/angular/pipe-easy/src/app/app.component.ts deleted file mode 100644 index 3c19fa169..000000000 --- a/apps/angular/pipe-easy/src/app/app.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgFor } from '@angular/common'; -import { Component } from '@angular/core'; - -@Component({ - standalone: true, - imports: [NgFor], - selector: 'app-root', - template: ` -
- {{ heavyComputation(person, index) }} -
- `, -}) -export class AppComponent { - persons = ['toto', 'jack']; - - heavyComputation(name: string, index: number) { - // very heavy computation - return `${name} - ${index}`; - } -} diff --git a/apps/angular/pipe-easy/src/index.html b/apps/angular/pipe-easy/src/index.html deleted file mode 100644 index eacdd37b7..000000000 --- a/apps/angular/pipe-easy/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - PipeEasy - - - - - - - - diff --git a/apps/angular/pipe-hard/README.md b/apps/angular/pipe-hard/README.md deleted file mode 100644 index ba801bd0c..000000000 --- a/apps/angular/pipe-hard/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Utility Wrapper Pipe - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-pipe-hard -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/10-pipe-utility/). diff --git a/apps/angular/pipe-hard/project.json b/apps/angular/pipe-hard/project.json deleted file mode 100644 index 677edccba..000000000 --- a/apps/angular/pipe-hard/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-pipe-hard", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/pipe-hard/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/pipe-hard", - "index": "apps/angular/pipe-hard/src/index.html", - "main": "apps/angular/pipe-hard/src/main.ts", - "polyfills": "apps/angular/pipe-hard/src/polyfills.ts", - "tsConfig": "apps/angular/pipe-hard/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/pipe-hard/src/favicon.ico", - "apps/angular/pipe-hard/src/assets" - ], - "styles": ["apps/angular/pipe-hard/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-pipe-hard:build:production" - }, - "development": { - "buildTarget": "angular-pipe-hard:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-pipe-hard:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/pipe-hard/src/app/app.component.ts b/apps/angular/pipe-hard/src/app/app.component.ts deleted file mode 100644 index d91fc7436..000000000 --- a/apps/angular/pipe-hard/src/app/app.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { NgFor } from '@angular/common'; -import { Component } from '@angular/core'; -import { PersonUtils } from './person.utils'; - -@Component({ - standalone: true, - imports: [NgFor], - selector: 'app-root', - template: ` -
- {{ activity.name }} : -
- {{ showName(person.name, index) }} - {{ isAllowed(person.age, isFirst, activity.minimumAge) }} -
-
- `, -}) -export class AppComponent { - persons = [ - { name: 'Toto', age: 10 }, - { name: 'Jack', age: 15 }, - { name: 'John', age: 30 }, - ]; - - activities = [ - { name: 'biking', minimumAge: 12 }, - { name: 'hiking', minimumAge: 25 }, - { name: 'dancing', minimumAge: 1 }, - ]; - - showName = PersonUtils.showName; - - isAllowed = PersonUtils.isAllowed; -} diff --git a/apps/angular/pipe-hard/src/index.html b/apps/angular/pipe-hard/src/index.html deleted file mode 100644 index 0b81de34b..000000000 --- a/apps/angular/pipe-hard/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - PipeHard - - - - - - - - diff --git a/apps/angular/pipe-intermediate/README.md b/apps/angular/pipe-intermediate/README.md deleted file mode 100644 index 72809fd19..000000000 --- a/apps/angular/pipe-intermediate/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Wrap Function Pipe - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-pipe-intermediate -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/9-pipe-wrapFn/). diff --git a/apps/angular/pipe-intermediate/project.json b/apps/angular/pipe-intermediate/project.json deleted file mode 100644 index 689731511..000000000 --- a/apps/angular/pipe-intermediate/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-pipe-intermediate", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/pipe-intermediate/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/pipe-intermediate", - "index": "apps/angular/pipe-intermediate/src/index.html", - "main": "apps/angular/pipe-intermediate/src/main.ts", - "polyfills": "apps/angular/pipe-intermediate/src/polyfills.ts", - "tsConfig": "apps/angular/pipe-intermediate/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/pipe-intermediate/src/favicon.ico", - "apps/angular/pipe-intermediate/src/assets" - ], - "styles": ["apps/angular/pipe-intermediate/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-pipe-intermediate:build:production" - }, - "development": { - "buildTarget": "angular-pipe-intermediate:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-pipe-intermediate:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/pipe-intermediate/src/app/app.component.ts b/apps/angular/pipe-intermediate/src/app/app.component.ts deleted file mode 100644 index d9c163c93..000000000 --- a/apps/angular/pipe-intermediate/src/app/app.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NgFor } from '@angular/common'; -import { Component } from '@angular/core'; - -@Component({ - standalone: true, - imports: [NgFor], - selector: 'app-root', - template: ` -
- {{ showName(person.name, index) }} - {{ isAllowed(person.age, isFirst) }} -
- `, -}) -export class AppComponent { - persons = [ - { name: 'Toto', age: 10 }, - { name: 'Jack', age: 15 }, - { name: 'John', age: 30 }, - ]; - - showName(name: string, index: number) { - // very heavy computation - return `${name} - ${index}`; - } - - isAllowed(age: number, isFirst: boolean) { - if (isFirst) { - return 'always allowed'; - } else { - return age > 25 ? 'allowed' : 'declined'; - } - } -} diff --git a/apps/angular/pipe-intermediate/src/index.html b/apps/angular/pipe-intermediate/src/index.html deleted file mode 100644 index 1a8b2ee22..000000000 --- a/apps/angular/pipe-intermediate/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - PipeIntermediate - - - - - - - - diff --git a/apps/angular/pipe-intermediate/tsconfig.json b/apps/angular/pipe-intermediate/tsconfig.json deleted file mode 100644 index b2dbbf22e..000000000 --- a/apps/angular/pipe-intermediate/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], - "compilerOptions": { - "target": "es2020", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/angular/projection/jest.config.ts b/apps/angular/projection/jest.config.ts deleted file mode 100644 index 289a9b0e3..000000000 --- a/apps/angular/projection/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-projection', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../../coverage/apps/angular/projection', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/projection/project.json b/apps/angular/projection/project.json deleted file mode 100644 index 67be2738f..000000000 --- a/apps/angular/projection/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "angular-projection", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/projection/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/projection", - "index": "apps/angular/projection/src/index.html", - "browser": "apps/angular/projection/src/main.ts", - "polyfills": ["apps/angular/projection/src/polyfills.ts"], - "tsConfig": "apps/angular/projection/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/projection/src/favicon.ico", - "apps/angular/projection/src/assets" - ], - "styles": ["apps/angular/projection/src/styles.scss"], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-projection:build:production" - }, - "development": { - "buildTarget": "angular-projection:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-projection:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/projection/jest.config.ts" - } - } - }, - "tags": [] -} diff --git a/apps/angular/projection/src/app/app.component.ts b/apps/angular/projection/src/app/app.component.ts deleted file mode 100644 index b1d076a9f..000000000 --- a/apps/angular/projection/src/app/app.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component } from '@angular/core'; -import { CityCardComponent } from './component/city-card/city-card.component'; -import { StudentCardComponent } from './component/student-card/student-card.component'; -import { TeacherCardComponent } from './component/teacher-card/teacher-card.component'; - -@Component({ - selector: 'app-root', - template: ` -
- - - -
- `, - standalone: true, - imports: [TeacherCardComponent, StudentCardComponent, CityCardComponent], -}) -export class AppComponent {} diff --git a/apps/angular/projection/src/app/component/city-card/city-card.component.ts b/apps/angular/projection/src/app/component/city-card/city-card.component.ts deleted file mode 100644 index 30c8f88ec..000000000 --- a/apps/angular/projection/src/app/component/city-card/city-card.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-city-card', - template: 'TODO City', - standalone: true, - imports: [], -}) -export class CityCardComponent implements OnInit { - constructor() {} - - ngOnInit(): void {} -} diff --git a/apps/angular/projection/src/app/component/student-card/student-card.component.ts b/apps/angular/projection/src/app/component/student-card/student-card.component.ts deleted file mode 100644 index 441cda189..000000000 --- a/apps/angular/projection/src/app/component/student-card/student-card.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; -import { StudentStore } from '../../data-access/student.store'; -import { CardType } from '../../model/card.model'; -import { Student } from '../../model/student.model'; -import { CardComponent } from '../../ui/card/card.component'; - -@Component({ - selector: 'app-student-card', - template: ` - - `, - standalone: true, - styles: [ - ` - ::ng-deep .bg-light-green { - background-color: rgba(0, 250, 0, 0.1); - } - `, - ], - imports: [CardComponent], -}) -export class StudentCardComponent implements OnInit { - students: Student[] = []; - cardType = CardType.STUDENT; - - constructor( - private http: FakeHttpService, - private store: StudentStore, - ) {} - - ngOnInit(): void { - this.http.fetchStudents$.subscribe((s) => this.store.addAll(s)); - - this.store.students$.subscribe((s) => (this.students = s)); - } -} diff --git a/apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts b/apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts deleted file mode 100644 index 995cb7c2f..000000000 --- a/apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; -import { TeacherStore } from '../../data-access/teacher.store'; -import { CardType } from '../../model/card.model'; -import { Teacher } from '../../model/teacher.model'; -import { CardComponent } from '../../ui/card/card.component'; - -@Component({ - selector: 'app-teacher-card', - template: ` - - `, - styles: [ - ` - ::ng-deep .bg-light-red { - background-color: rgba(250, 0, 0, 0.1); - } - `, - ], - standalone: true, - imports: [CardComponent], -}) -export class TeacherCardComponent implements OnInit { - teachers: Teacher[] = []; - cardType = CardType.TEACHER; - - constructor( - private http: FakeHttpService, - private store: TeacherStore, - ) {} - - ngOnInit(): void { - this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t)); - - this.store.teachers$.subscribe((t) => (this.teachers = t)); - } -} diff --git a/apps/angular/projection/src/app/data-access/city.store.ts b/apps/angular/projection/src/app/data-access/city.store.ts deleted file mode 100644 index 711dad1d7..000000000 --- a/apps/angular/projection/src/app/data-access/city.store.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { City } from '../model/city.model'; - -@Injectable({ - providedIn: 'root', -}) -export class CityStore { - private cities = new BehaviorSubject([]); - cities$ = this.cities.asObservable(); - - addAll(cities: City[]) { - this.cities.next(cities); - } - - addOne(student: City) { - this.cities.next([...this.cities.value, student]); - } - - deleteOne(id: number) { - this.cities.next(this.cities.value.filter((s) => s.id !== id)); - } -} diff --git a/apps/angular/projection/src/app/data-access/student.store.ts b/apps/angular/projection/src/app/data-access/student.store.ts deleted file mode 100644 index 7918118c3..000000000 --- a/apps/angular/projection/src/app/data-access/student.store.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { Student } from '../model/student.model'; - -@Injectable({ - providedIn: 'root', -}) -export class StudentStore { - private students = new BehaviorSubject([]); - students$ = this.students.asObservable(); - - addAll(students: Student[]) { - this.students.next(students); - } - - addOne(student: Student) { - this.students.next([...this.students.value, student]); - } - - deleteOne(id: number) { - this.students.next(this.students.value.filter((s) => s.id !== id)); - } -} diff --git a/apps/angular/projection/src/app/data-access/teacher.store.ts b/apps/angular/projection/src/app/data-access/teacher.store.ts deleted file mode 100644 index 93f68c4b1..000000000 --- a/apps/angular/projection/src/app/data-access/teacher.store.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { Teacher } from '../model/teacher.model'; - -@Injectable({ - providedIn: 'root', -}) -export class TeacherStore { - private teachers = new BehaviorSubject([]); - teachers$ = this.teachers.asObservable(); - - addAll(teachers: Teacher[]) { - this.teachers.next(teachers); - } - - addOne(teacher: Teacher) { - this.teachers.next([...this.teachers.value, teacher]); - } - - deleteOne(id: number) { - this.teachers.next(this.teachers.value.filter((t) => t.id !== id)); - } -} diff --git a/apps/angular/projection/src/app/model/city.model.ts b/apps/angular/projection/src/app/model/city.model.ts deleted file mode 100644 index dd26b1957..000000000 --- a/apps/angular/projection/src/app/model/city.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface City { - id: number; - name: string; - country: string; -} diff --git a/apps/angular/projection/src/app/model/student.model.ts b/apps/angular/projection/src/app/model/student.model.ts deleted file mode 100644 index bc18e464a..000000000 --- a/apps/angular/projection/src/app/model/student.model.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Teacher } from './teacher.model'; - -export interface Student { - id: number; - firstName: string; - lastName: string; - mainTeacher: Teacher; - school: string; -} diff --git a/apps/angular/projection/src/app/model/teacher.model.ts b/apps/angular/projection/src/app/model/teacher.model.ts deleted file mode 100644 index 34b4241be..000000000 --- a/apps/angular/projection/src/app/model/teacher.model.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const subject = [ - 'Sciences', - 'History', - 'English', - 'Maths', - 'Sport', -] as const; -export type Subject = (typeof subject)[number]; - -export interface Teacher { - id: number; - firstName: string; - lastName: string; - subject: Subject; -} diff --git a/apps/angular/projection/src/app/ui/card/card.component.ts b/apps/angular/projection/src/app/ui/card/card.component.ts deleted file mode 100644 index f06c9ae00..000000000 --- a/apps/angular/projection/src/app/ui/card/card.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { NgFor, NgIf } from '@angular/common'; -import { Component, Input } from '@angular/core'; -import { randStudent, randTeacher } from '../../data-access/fake-http.service'; -import { StudentStore } from '../../data-access/student.store'; -import { TeacherStore } from '../../data-access/teacher.store'; -import { CardType } from '../../model/card.model'; -import { ListItemComponent } from '../list-item/list-item.component'; - -@Component({ - selector: 'app-card', - template: ` -
- - - -
- -
- - -
- `, - standalone: true, - imports: [NgIf, NgFor, ListItemComponent], -}) -export class CardComponent { - @Input() list: any[] | null = null; - @Input() type!: CardType; - @Input() customClass = ''; - - CardType = CardType; - - constructor( - private teacherStore: TeacherStore, - private studentStore: StudentStore, - ) {} - - addNewItem() { - if (this.type === CardType.TEACHER) { - this.teacherStore.addOne(randTeacher()); - } else if (this.type === CardType.STUDENT) { - this.studentStore.addOne(randStudent()); - } - } -} diff --git a/apps/angular/projection/src/app/ui/list-item/list-item.component.ts b/apps/angular/projection/src/app/ui/list-item/list-item.component.ts deleted file mode 100644 index c0f9cff7f..000000000 --- a/apps/angular/projection/src/app/ui/list-item/list-item.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { StudentStore } from '../../data-access/student.store'; -import { TeacherStore } from '../../data-access/teacher.store'; -import { CardType } from '../../model/card.model'; - -@Component({ - selector: 'app-list-item', - template: ` -
- {{ name }} - -
- `, - standalone: true, -}) -export class ListItemComponent { - @Input() id!: number; - @Input() name!: string; - @Input() type!: CardType; - - constructor( - private teacherStore: TeacherStore, - private studentStore: StudentStore, - ) {} - - delete(id: number) { - if (this.type === CardType.TEACHER) { - this.teacherStore.deleteOne(id); - } else if (this.type === CardType.STUDENT) { - this.studentStore.deleteOne(id); - } - } -} diff --git a/apps/angular/projection/src/index.html b/apps/angular/projection/src/index.html deleted file mode 100644 index 7f19c5dfd..000000000 --- a/apps/angular/projection/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Projection - - - - - - - - diff --git a/apps/angular/projection/src/main.ts b/apps/angular/projection/src/main.ts deleted file mode 100644 index 9cd15da95..000000000 --- a/apps/angular/projection/src/main.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { bootstrapApplication } from '@angular/platform-browser'; -import { AppComponent } from './app/app.component'; - -bootstrapApplication(AppComponent); diff --git a/apps/angular/projection/src/polyfills.ts b/apps/angular/projection/src/polyfills.ts deleted file mode 100644 index e4555ed11..000000000 --- a/apps/angular/projection/src/polyfills.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes recent versions of Safari, Chrome (including - * Opera), Edge on the desktop, and iOS and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/apps/angular/projection/tsconfig.app.json b/apps/angular/projection/tsconfig.app.json deleted file mode 100644 index 7a4dbc47e..000000000 --- a/apps/angular/projection/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "types": [], - "target": "ES2022", - "useDefineForClassFields": false - }, - "files": ["src/main.ts", "src/polyfills.ts"], - "include": ["src/**/*.d.ts"], - "exclude": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts"] -} diff --git a/apps/angular/react-in-angular/jest.config.ts b/apps/angular/react-in-angular/jest.config.ts deleted file mode 100644 index 92d5f2583..000000000 --- a/apps/angular/react-in-angular/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-react-in-angular', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/angular/react-in-angular', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/react-in-angular/project.json b/apps/angular/react-in-angular/project.json deleted file mode 100644 index 8153ec8d3..000000000 --- a/apps/angular/react-in-angular/project.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "name": "angular-react-in-angular", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/react-in-angular/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/react-in-angular", - "index": "apps/angular/react-in-angular/src/index.html", - "browser": "apps/angular/react-in-angular/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/react-in-angular/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/react-in-angular/src/favicon.ico", - "apps/angular/react-in-angular/src/assets" - ], - "styles": ["apps/angular/react-in-angular/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-react-in-angular:build:production" - }, - "development": { - "buildTarget": "angular-react-in-angular:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-react-in-angular:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/react-in-angular/jest.config.ts" - } - } - } -} diff --git a/apps/angular/react-in-angular/src/app/app.component.ts b/apps/angular/react-in-angular/src/app/app.component.ts deleted file mode 100644 index a914cc65f..000000000 --- a/apps/angular/react-in-angular/src/app/app.component.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Component, signal } from '@angular/core'; -import { PostComponent } from './react/post.component'; - -type Post = { title: string; description: string }; - -@Component({ - standalone: true, - imports: [PostComponent], - selector: 'app-root', - template: ` -
-
- @for (post of posts; track post.title) { -
- -
- } -
-
- Selected Post: - - {{ selectedPost()?.title ?? '-' }} - -
-
- `, - styles: [''], -}) -export class AppComponent { - readonly posts = [ - { - title: 'A Deep Dive into Angular', - description: - "Explore Angular's core features, its evolution, and best practices in development for creating dynamic, efficient web applications in our comprehensive guide.", - pictureLink: - 'https://images.unsplash.com/photo-1471958680802-1345a694ba6d', - }, - { - title: 'The Perfect Combination', - description: - 'Unveil the power of combining Angular & React in web development, maximizing efficiency and flexibility for building scalable, sophisticated applications.', - pictureLink: - 'https://images.unsplash.com/photo-1518717202715-9fa9d099f58a', - }, - { - title: 'Taking Angular to the Next Level', - description: - "Discover how integrating React with Angular elevates web development, blending Angular's structure with React's UI prowess for advanced applications.", - pictureLink: - 'https://images.unsplash.com/photo-1532103050105-860af53bc6aa', - }, - ]; - - readonly selectedPost = signal(null); - - selectPost(post: Post) { - this.selectedPost.set(post); - } -} diff --git a/apps/angular/react-in-angular/src/app/react/ReactPost.tsx b/apps/angular/react-in-angular/src/app/react/ReactPost.tsx deleted file mode 100644 index 6ff7b4df4..000000000 --- a/apps/angular/react-in-angular/src/app/react/ReactPost.tsx +++ /dev/null @@ -1,27 +0,0 @@ -// import React from 'react'; - -export default function ReactPost(props: { - title?: string, - description?: string, - pictureLink?: string, - selected?: boolean, - handleClick: () => void -}) { - return ( -
-
- {props.title} -
-
{props.title}
-

- {props.description} -

- -
-
-
- ); -} diff --git a/apps/angular/react-in-angular/tsconfig.json b/apps/angular/react-in-angular/tsconfig.json deleted file mode 100644 index 7a9f4b7a7..000000000 --- a/apps/angular/react-in-angular/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json", - }, - { - "path": "./tsconfig.spec.json", - }, - { - "path": "./tsconfig.editor.json", - }, - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true, - }, -} diff --git a/apps/angular/router-input/project.json b/apps/angular/router-input/project.json deleted file mode 100644 index 523fff02e..000000000 --- a/apps/angular/router-input/project.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "angular-router-input", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/router-input/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/router-input", - "index": "apps/angular/router-input/src/index.html", - "main": "apps/angular/router-input/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/router-input/tsconfig.app.json", - "assets": [ - "apps/angular/router-input/src/favicon.ico", - "apps/angular/router-input/src/assets" - ], - "styles": ["apps/angular/router-input/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-router-input:build:production" - }, - "development": { - "buildTarget": "angular-router-input:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-router-input:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/angular/router-input/src/app/app.component.ts b/apps/angular/router-input/src/app/app.component.ts deleted file mode 100644 index 1ef7e32aa..000000000 --- a/apps/angular/router-input/src/app/app.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component } from '@angular/core'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { RouterLink, RouterModule } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterLink, RouterModule, ReactiveFormsModule], - selector: 'app-root', - template: ` - - - - - - - - `, -}) -export class AppComponent { - userName = new FormControl(); - testId = new FormControl(); -} diff --git a/apps/angular/router-input/src/app/home.component.ts b/apps/angular/router-input/src/app/home.component.ts deleted file mode 100644 index 2ef8c5eb4..000000000 --- a/apps/angular/router-input/src/app/home.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; -@Component({ - selector: 'app-home', - standalone: true, - imports: [], - template: ` -
Home
- `, -}) -export default class HomeComponent {} diff --git a/apps/angular/router-input/src/index.html b/apps/angular/router-input/src/index.html deleted file mode 100644 index daa121537..000000000 --- a/apps/angular/router-input/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - router-input - - - - - - - - diff --git a/apps/angular/signal-input/README.md b/apps/angular/signal-input/README.md deleted file mode 100644 index 78ef74d74..000000000 --- a/apps/angular/signal-input/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Signal Input - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-signal-input -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/43-signal-input/). diff --git a/apps/angular/signal-input/project.json b/apps/angular/signal-input/project.json deleted file mode 100644 index 00f1a6797..000000000 --- a/apps/angular/signal-input/project.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "name": "angular-signal-input", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/signal-input/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/signal-input", - "index": "apps/angular/signal-input/src/index.html", - "browser": "apps/angular/signal-input/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/signal-input/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/signal-input/src/favicon.ico", - "apps/angular/signal-input/src/assets" - ], - "styles": ["apps/angular/signal-input/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-signal-input:build:production" - }, - "development": { - "buildTarget": "angular-signal-input:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-signal-input:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - } - } -} diff --git a/apps/angular/signal-input/src/app/app.component.ts b/apps/angular/signal-input/src/app/app.component.ts deleted file mode 100644 index 22e64386e..000000000 --- a/apps/angular/signal-input/src/app/app.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { JsonPipe } from '@angular/common'; -import { Component } from '@angular/core'; -import { UserComponent } from './user.component'; - -@Component({ - standalone: true, - imports: [UserComponent, JsonPipe], - selector: 'app-root', - template: ` -
-
- Name: - - @if (showUser && !name.value) { -
name required
- } -
-
- LastName: - -
-
- Age: - -
- -
- @if (showUser && !!name.value) { - - } - `, - host: { - class: 'p-10 block flex flex-col gap-10', - }, -}) -export class AppComponent { - showUser = false; -} diff --git a/apps/angular/signal-input/src/index.html b/apps/angular/signal-input/src/index.html deleted file mode 100644 index 4fc2af8ca..000000000 --- a/apps/angular/signal-input/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - angular-signal-input - - - - - - - - diff --git a/apps/angular/simple-animations/jest.config.ts b/apps/angular/simple-animations/jest.config.ts deleted file mode 100644 index 8f5d36cf8..000000000 --- a/apps/angular/simple-animations/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'angular-simple-animations', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/angular/simple-animations', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/angular/simple-animations/project.json b/apps/angular/simple-animations/project.json deleted file mode 100644 index 4ba302da8..000000000 --- a/apps/angular/simple-animations/project.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "name": "angular-simple-animations", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/simple-animations/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/simple-animations", - "index": "apps/angular/simple-animations/src/index.html", - "browser": "apps/angular/simple-animations/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/simple-animations/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/simple-animations/src/favicon.ico", - "apps/angular/simple-animations/src/assets" - ], - "styles": ["apps/angular/simple-animations/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-simple-animations:build:production" - }, - "development": { - "buildTarget": "angular-simple-animations:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-simple-animations:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/angular/simple-animations/jest.config.ts" - } - } - } -} diff --git a/apps/angular/simple-animations/src/app/app.component.ts b/apps/angular/simple-animations/src/app/app.component.ts deleted file mode 100644 index 9f537b3fb..000000000 --- a/apps/angular/simple-animations/src/app/app.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - standalone: true, - imports: [], - selector: 'app-root', - styles: ` - section { - @apply flex flex-1 flex-col gap-5; - } - - .list-item { - @apply flex flex-row border-b px-5 pb-2; - - span { - @apply flex-1; - } - } - `, - template: ` -
-
-
-

2008

-

- Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae - mollitia sequi accusantium, distinctio similique laudantium eveniet - quidem sit placeat possimus tempore dolorum inventore corporis atque - quae ad, nobis explicabo delectus. -

-
- -
-

2010

-

- Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae - mollitia sequi accusantium, distinctio similique laudantium eveniet - quidem sit placeat possimus tempore dolorum inventore corporis atque - quae ad, nobis explicabo delectus. -

-
- -
-

2012

-

- Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae - mollitia sequi accusantium, distinctio similique laudantium eveniet - quidem sit placeat possimus tempore dolorum inventore corporis atque - quae ad, nobis explicabo delectus. -

-
-
- -
-
- Name: - Samuel -
- -
- Age: - 28 -
- -
- Birthdate: - 02.11.1995 -
- -
- City: - Berlin -
- -
- Language: - English -
- -
- Like Pizza: - Hell yeah -
-
-
- `, -}) -export class AppComponent {} diff --git a/apps/angular/simple-animations/tsconfig.json b/apps/angular/simple-animations/tsconfig.json deleted file mode 100644 index 7a9f4b7a7..000000000 --- a/apps/angular/simple-animations/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json", - }, - { - "path": "./tsconfig.spec.json", - }, - { - "path": "./tsconfig.editor.json", - }, - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true, - }, -} diff --git a/apps/angular/styling/README.md b/apps/angular/styling/README.md deleted file mode 100644 index d47efe3e8..000000000 --- a/apps/angular/styling/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Highly Customizable CSS - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve angular-styling -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/13-styling/). diff --git a/apps/angular/styling/project.json b/apps/angular/styling/project.json deleted file mode 100644 index de968463e..000000000 --- a/apps/angular/styling/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "angular-styling", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/styling/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/styling", - "index": "apps/angular/styling/src/index.html", - "main": "apps/angular/styling/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/styling/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/styling/src/favicon.ico", - "apps/angular/styling/src/assets" - ], - "styles": ["apps/angular/styling/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-styling:build:production" - }, - "development": { - "buildTarget": "angular-styling:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-styling:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/angular/styling/src/app/page.component.ts b/apps/angular/styling/src/app/page.component.ts deleted file mode 100644 index 067453294..000000000 --- a/apps/angular/styling/src/app/page.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable @angular-eslint/component-selector */ -import { Component } from '@angular/core'; -import { TextStaticComponent } from './static-text.component'; -import { TextComponent } from './text.component'; - -@Component({ - selector: 'page', - standalone: true, - imports: [TextStaticComponent, TextComponent], - template: ` - - - - This is a blue text - `, -}) -export class PageComponent {} diff --git a/apps/angular/styling/src/index.html b/apps/angular/styling/src/index.html deleted file mode 100644 index 7ea1636bf..000000000 --- a/apps/angular/styling/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Styling - - - - - - - - diff --git a/apps/angular/view-transition/project.json b/apps/angular/view-transition/project.json deleted file mode 100644 index d0a27fd5f..000000000 --- a/apps/angular/view-transition/project.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "name": "angular-view-transition", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/angular/view-transition/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/view-transition", - "index": "apps/angular/view-transition/src/index.html", - "browser": "apps/angular/view-transition/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/angular/view-transition/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/view-transition/src/favicon.ico", - "apps/angular/view-transition/src/assets" - ], - "styles": ["apps/angular/view-transition/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-view-transition:build:production" - }, - "development": { - "buildTarget": "angular-view-transition:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-view-transition:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - } - } -} diff --git a/apps/angular/view-transition/src/app/app.component.ts b/apps/angular/view-transition/src/app/app.component.ts deleted file mode 100644 index da56c04c0..000000000 --- a/apps/angular/view-transition/src/app/app.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet], - selector: 'app-root', - template: ` - - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AppComponent {} diff --git a/apps/angular/view-transition/src/styles.scss b/apps/angular/view-transition/src/styles.scss deleted file mode 100644 index a90f0749c..000000000 --- a/apps/angular/view-transition/src/styles.scss +++ /dev/null @@ -1,4 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - diff --git a/apps/angular/view-transition/tsconfig.json b/apps/angular/view-transition/tsconfig.json deleted file mode 100644 index db0ec0f25..000000000 --- a/apps/angular/view-transition/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json", - }, - { - "path": "./tsconfig.editor.json", - }, - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true, - }, -} diff --git a/apps/ngrx/notification/.eslintrc.json b/apps/forms/41-control-value-accessor/.eslintrc.json similarity index 100% rename from apps/ngrx/notification/.eslintrc.json rename to apps/forms/41-control-value-accessor/.eslintrc.json diff --git a/apps/forms/control-value-accessor/README.md b/apps/forms/41-control-value-accessor/README.md similarity index 100% rename from apps/forms/control-value-accessor/README.md rename to apps/forms/41-control-value-accessor/README.md diff --git a/apps/forms/41-control-value-accessor/jest.config.ts b/apps/forms/41-control-value-accessor/jest.config.ts new file mode 100644 index 000000000..48769acef --- /dev/null +++ b/apps/forms/41-control-value-accessor/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'forms-control-value-accessor', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/forms/41-control-value-accessor', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/forms/41-control-value-accessor/project.json b/apps/forms/41-control-value-accessor/project.json new file mode 100644 index 000000000..a1ec26bac --- /dev/null +++ b/apps/forms/41-control-value-accessor/project.json @@ -0,0 +1,80 @@ +{ + "name": "forms-control-value-accessor", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/forms/41-control-value-accessor/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/forms/41-control-value-accessor", + "index": "apps/forms/41-control-value-accessor/src/index.html", + "browser": "apps/forms/41-control-value-accessor/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/forms/41-control-value-accessor/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/forms/41-control-value-accessor/src/favicon.ico", + "apps/forms/41-control-value-accessor/src/assets" + ], + "styles": ["apps/forms/41-control-value-accessor/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "forms-control-value-accessor:build:production" + }, + "development": { + "buildTarget": "forms-control-value-accessor:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "forms-control-value-accessor:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/forms/41-control-value-accessor/src/app/app.component.ts b/apps/forms/41-control-value-accessor/src/app/app.component.ts new file mode 100644 index 000000000..69134b864 --- /dev/null +++ b/apps/forms/41-control-value-accessor/src/app/app.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { FeedbackFormComponent } from './feedback-form/feedback-form.component'; + +@Component({ + imports: [FeedbackFormComponent], + selector: 'app-root', + template: ` + + `, +}) +export class AppComponent { + apiCall(event: Record): void { + console.log(event); + } +} diff --git a/apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.html b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.html similarity index 100% rename from apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.html rename to apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.html diff --git a/apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.scss b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.scss similarity index 100% rename from apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.scss rename to apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.scss diff --git a/apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.ts b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts similarity index 98% rename from apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.ts rename to apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts index d99700db1..4110d6cf7 100644 --- a/apps/forms/control-value-accessor/src/app/feedback-form/feedback-form.component.ts +++ b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts @@ -8,7 +8,6 @@ import { import { RatingControlComponent } from '../rating-control/rating-control.component'; @Component({ - standalone: true, imports: [RatingControlComponent, ReactiveFormsModule], selector: 'app-feedback-form', templateUrl: 'feedback-form.component.html', diff --git a/apps/forms/control-value-accessor/src/app/rating-control/rating-control.component.html b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.html similarity index 100% rename from apps/forms/control-value-accessor/src/app/rating-control/rating-control.component.html rename to apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.html diff --git a/apps/forms/control-value-accessor/src/app/rating-control/rating-control.component.scss b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.scss similarity index 100% rename from apps/forms/control-value-accessor/src/app/rating-control/rating-control.component.scss rename to apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.scss diff --git a/apps/forms/control-value-accessor/src/app/rating-control/rating-control.component.ts b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts similarity index 100% rename from apps/forms/control-value-accessor/src/app/rating-control/rating-control.component.ts rename to apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts diff --git a/apps/angular/styling/src/assets/.gitkeep b/apps/forms/41-control-value-accessor/src/assets/.gitkeep similarity index 100% rename from apps/angular/styling/src/assets/.gitkeep rename to apps/forms/41-control-value-accessor/src/assets/.gitkeep diff --git a/apps/ngrx/effect-selector/src/favicon.ico b/apps/forms/41-control-value-accessor/src/favicon.ico similarity index 100% rename from apps/ngrx/effect-selector/src/favicon.ico rename to apps/forms/41-control-value-accessor/src/favicon.ico diff --git a/apps/forms/control-value-accessor/src/index.html b/apps/forms/41-control-value-accessor/src/index.html similarity index 100% rename from apps/forms/control-value-accessor/src/index.html rename to apps/forms/41-control-value-accessor/src/index.html diff --git a/apps/forms/control-value-accessor/src/main.ts b/apps/forms/41-control-value-accessor/src/main.ts similarity index 100% rename from apps/forms/control-value-accessor/src/main.ts rename to apps/forms/41-control-value-accessor/src/main.ts diff --git a/apps/performance/christmas-web-worker/src/styles.scss b/apps/forms/41-control-value-accessor/src/styles.scss similarity index 100% rename from apps/performance/christmas-web-worker/src/styles.scss rename to apps/forms/41-control-value-accessor/src/styles.scss diff --git a/apps/performance/ngfor-optimize/src/test-setup.ts b/apps/forms/41-control-value-accessor/src/test-setup.ts similarity index 100% rename from apps/performance/ngfor-optimize/src/test-setup.ts rename to apps/forms/41-control-value-accessor/src/test-setup.ts diff --git a/apps/performance/default-onpush/tailwind.config.js b/apps/forms/41-control-value-accessor/tailwind.config.js similarity index 100% rename from apps/performance/default-onpush/tailwind.config.js rename to apps/forms/41-control-value-accessor/tailwind.config.js diff --git a/apps/performance/default-onpush/tsconfig.app.json b/apps/forms/41-control-value-accessor/tsconfig.app.json similarity index 100% rename from apps/performance/default-onpush/tsconfig.app.json rename to apps/forms/41-control-value-accessor/tsconfig.app.json diff --git a/apps/angular/simple-animations/tsconfig.editor.json b/apps/forms/41-control-value-accessor/tsconfig.editor.json similarity index 100% rename from apps/angular/simple-animations/tsconfig.editor.json rename to apps/forms/41-control-value-accessor/tsconfig.editor.json diff --git a/apps/forms/41-control-value-accessor/tsconfig.json b/apps/forms/41-control-value-accessor/tsconfig.json new file mode 100644 index 000000000..25ca437b4 --- /dev/null +++ b/apps/forms/41-control-value-accessor/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/forms/41-control-value-accessor/tsconfig.spec.json b/apps/forms/41-control-value-accessor/tsconfig.spec.json new file mode 100644 index 000000000..1a4817a7d --- /dev/null +++ b/apps/forms/41-control-value-accessor/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node", "@testing-library/jest-dom"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/nx/static-dynamic-import/.eslintrc.json b/apps/forms/48-avoid-losing-form-data/.eslintrc.json similarity index 100% rename from apps/nx/static-dynamic-import/.eslintrc.json rename to apps/forms/48-avoid-losing-form-data/.eslintrc.json diff --git a/apps/forms/48-avoid-losing-form-data/README.md b/apps/forms/48-avoid-losing-form-data/README.md new file mode 100644 index 000000000..1c0e90b35 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/README.md @@ -0,0 +1,11 @@ +# Avoid losing form data + +> author: [Timothy Alcaide](https://github.com/alcaidio) + +### Run Application + +```bash +npx nx serve forms-avoid-losing-form-data +``` + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/forms/48-forms-avoid-losing-form-data/). diff --git a/apps/forms/48-avoid-losing-form-data/project.json b/apps/forms/48-avoid-losing-form-data/project.json new file mode 100644 index 000000000..9517d3123 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/project.json @@ -0,0 +1,72 @@ +{ + "name": "forms-avoid-losing-form-data", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/forms/48-avoid-losing-form-data/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/forms/48-avoid-losing-form-data", + "index": "apps/forms/48-avoid-losing-form-data/src/index.html", + "browser": "apps/forms/48-avoid-losing-form-data/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/forms/48-avoid-losing-form-data/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/forms/48-avoid-losing-form-data/src/favicon.ico", + "apps/forms/48-avoid-losing-form-data/src/assets" + ], + "styles": [ + "apps/forms/48-avoid-losing-form-data/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "forms-avoid-losing-form-data:build:production" + }, + "development": { + "buildTarget": "forms-avoid-losing-form-data:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "forms-avoid-losing-form-data:build" + } + } + } +} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts new file mode 100644 index 000000000..2b5adc443 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { NavComponent } from './ui/nav.component'; + +@Component({ + imports: [RouterOutlet, NavComponent], + selector: 'app-root', + template: ` +
+ + +
+ +
+
+ `, +}) +export class AppComponent {} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts new file mode 100644 index 000000000..a7c1007b9 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts @@ -0,0 +1,7 @@ +import { ApplicationConfig } from '@angular/core'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { appRoutes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [provideRouter(appRoutes, withComponentInputBinding())], +}; diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts new file mode 100644 index 000000000..84be34b9a --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts @@ -0,0 +1,29 @@ +import { Route } from '@angular/router'; +import { JoinComponent } from './pages/join.component'; +import { PageComponent } from './pages/page.component'; + +export const appRoutes: Route[] = [ + { + path: '', + pathMatch: 'full', + redirectTo: 'form', + }, + { + path: 'form', + loadComponent: () => JoinComponent, + }, + { + path: 'page-1', + data: { + title: 'Page 1', + }, + loadComponent: () => PageComponent, + }, + { + path: 'page-2', + data: { + title: 'Page 2', + }, + loadComponent: () => PageComponent, + }, +]; diff --git a/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts new file mode 100644 index 000000000..51449a7fb --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts @@ -0,0 +1,15 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { FormComponent } from '../ui/form.component'; + +@Component({ + imports: [FormComponent], + template: ` +
+
+ +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class JoinComponent {} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts new file mode 100644 index 000000000..13f4e09c2 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts @@ -0,0 +1,14 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +@Component({ + standalone: true, + template: ` +
+

{{ title() }}

+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PageComponent { + title = input.required(); +} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts new file mode 100644 index 000000000..661da9bcf --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts @@ -0,0 +1,30 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +// NOTE : this is just the dialog content, you need to implement dialog logic + +@Component({ + standalone: true, + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AlertDialogComponent {} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts new file mode 100644 index 000000000..f3190d517 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts @@ -0,0 +1,77 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-form', + imports: [ReactiveFormsModule], + template: ` +
+
+ + +
+ +
+
+ + +
+ +
+ + +
+
+ +
+ + + +
+ +
+ +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FormComponent { + private fb = inject(FormBuilder); + + form = this.fb.nonNullable.group({ + name: ['', { validators: [Validators.required] }], + email: ['', [Validators.required, Validators.email]], // other syntax + phone: '', + message: '', + }); + + onSubmit() { + if (this.form.valid) this.form.reset(); + } +} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/nav.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/nav.component.ts new file mode 100644 index 000000000..269297280 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/nav.component.ts @@ -0,0 +1,32 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink, RouterLinkActive } from '@angular/router'; + +@Component({ + selector: 'app-nav', + imports: [RouterLink, RouterLinkActive], + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NavComponent {} diff --git a/apps/angular/view-transition/src/assets/.gitkeep b/apps/forms/48-avoid-losing-form-data/src/assets/.gitkeep similarity index 100% rename from apps/angular/view-transition/src/assets/.gitkeep rename to apps/forms/48-avoid-losing-form-data/src/assets/.gitkeep diff --git a/apps/ngrx/notification/src/favicon.ico b/apps/forms/48-avoid-losing-form-data/src/favicon.ico similarity index 100% rename from apps/ngrx/notification/src/favicon.ico rename to apps/forms/48-avoid-losing-form-data/src/favicon.ico diff --git a/apps/forms/48-avoid-losing-form-data/src/index.html b/apps/forms/48-avoid-losing-form-data/src/index.html new file mode 100644 index 000000000..ed75bb39d --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/index.html @@ -0,0 +1,13 @@ + + + + + form-avoid-losing-form-data + + + + + + + + diff --git a/apps/performance/memoized/src/main.ts b/apps/forms/48-avoid-losing-form-data/src/main.ts similarity index 100% rename from apps/performance/memoized/src/main.ts rename to apps/forms/48-avoid-losing-form-data/src/main.ts diff --git a/apps/performance/default-onpush/src/styles.scss b/apps/forms/48-avoid-losing-form-data/src/styles.scss similarity index 100% rename from apps/performance/default-onpush/src/styles.scss rename to apps/forms/48-avoid-losing-form-data/src/styles.scss diff --git a/apps/forms/48-avoid-losing-form-data/tailwind.config.js b/apps/forms/48-avoid-losing-form-data/tailwind.config.js new file mode 100644 index 000000000..16f83c7f4 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [require('@tailwindcss/forms')], +}; diff --git a/apps/performance/memoized/tsconfig.app.json b/apps/forms/48-avoid-losing-form-data/tsconfig.app.json similarity index 100% rename from apps/performance/memoized/tsconfig.app.json rename to apps/forms/48-avoid-losing-form-data/tsconfig.app.json diff --git a/apps/forms/48-avoid-losing-form-data/tsconfig.editor.json b/apps/forms/48-avoid-losing-form-data/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/forms/48-avoid-losing-form-data/tsconfig.json b/apps/forms/48-avoid-losing-form-data/tsconfig.json new file mode 100644 index 000000000..3df17b921 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/forms/control-value-accessor/jest.config.ts b/apps/forms/control-value-accessor/jest.config.ts deleted file mode 100644 index 4af08e195..000000000 --- a/apps/forms/control-value-accessor/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'forms-control-value-accessor', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/forms/control-value-accessor', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/forms/control-value-accessor/project.json b/apps/forms/control-value-accessor/project.json deleted file mode 100644 index 70b8025c2..000000000 --- a/apps/forms/control-value-accessor/project.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "name": "forms-control-value-accessor", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/forms/control-value-accessor/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/forms/control-value-accessor", - "index": "apps/forms/control-value-accessor/src/index.html", - "browser": "apps/forms/control-value-accessor/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/forms/control-value-accessor/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/forms/control-value-accessor/src/favicon.ico", - "apps/forms/control-value-accessor/src/assets" - ], - "styles": ["apps/forms/control-value-accessor/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "forms-control-value-accessor:build:production" - }, - "development": { - "buildTarget": "forms-control-value-accessor:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "forms-control-value-accessor:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/forms/control-value-accessor/jest.config.ts" - } - } - } -} diff --git a/apps/forms/control-value-accessor/src/app/app.component.ts b/apps/forms/control-value-accessor/src/app/app.component.ts deleted file mode 100644 index f56b5d7d9..000000000 --- a/apps/forms/control-value-accessor/src/app/app.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { FeedbackFormComponent } from './feedback-form/feedback-form.component'; - -@Component({ - standalone: true, - imports: [FeedbackFormComponent], - selector: 'app-root', - template: ` - - `, -}) -export class AppComponent { - apiCall(event: Record): void { - console.log(event); - } -} diff --git a/apps/ngrx/effect-selector/README.md b/apps/ngrx/effect-selector/README.md deleted file mode 100644 index b12a5341e..000000000 --- a/apps/ngrx/effect-selector/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Effect vs Selector - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve ngrx-effect-selector -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/ngrx/2-effect-selector/). diff --git a/apps/ngrx/effect-selector/jest.config.ts b/apps/ngrx/effect-selector/jest.config.ts deleted file mode 100644 index 86ed38d00..000000000 --- a/apps/ngrx/effect-selector/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'ngrx-effect-selector', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../../coverage/apps/ngrx/effect-selector', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/ngrx/effect-selector/project.json b/apps/ngrx/effect-selector/project.json deleted file mode 100644 index 8cd2d729c..000000000 --- a/apps/ngrx/effect-selector/project.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "name": "ngrx-effect-selector", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/ngrx/effect-selector/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/ngrx/effect-selector", - "index": "apps/ngrx/effect-selector/src/index.html", - "main": "apps/ngrx/effect-selector/src/main.ts", - "polyfills": "apps/ngrx/effect-selector/src/polyfills.ts", - "tsConfig": "apps/ngrx/effect-selector/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/ngrx/effect-selector/src/favicon.ico", - "apps/ngrx/effect-selector/src/assets" - ], - "styles": ["apps/ngrx/effect-selector/src/styles.scss"], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "ngrx-effect-selector:build:production" - }, - "development": { - "buildTarget": "ngrx-effect-selector:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "ngrx-effect-selector:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/ngrx/effect-selector/jest.config.ts" - } - } - }, - "tags": [] -} diff --git a/apps/ngrx/effect-selector/src/app/app.component.ts b/apps/ngrx/effect-selector/src/app/app.component.ts deleted file mode 100644 index 1e4836644..000000000 --- a/apps/ngrx/effect-selector/src/app/app.component.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { AsyncPipe, NgFor } from '@angular/common'; -import { - ChangeDetectionStrategy, - Component, - OnInit, - inject, -} from '@angular/core'; -import { Store } from '@ngrx/store'; -import { loadActivities } from './store/activity/activity.actions'; -import { ActivityType } from './store/activity/activity.model'; -import { selectActivities } from './store/activity/activity.selectors'; -import { loadStatuses } from './store/status/status.actions'; -import { selectAllTeachersByActivityType } from './store/status/status.selectors'; -import { loadUsers } from './store/user/user.actions'; - -@Component({ - selector: 'app-root', - standalone: true, - imports: [NgFor, AsyncPipe], - template: ` -

Activity Board

-
-
-

Activity Name: {{ activity.name }}

-

Main teacher: {{ activity.teacher.name }}

- All teachers available for : {{ activity.type }} are -
    -
  • - {{ teacher.name }} -
  • -
-
-
- `, - styles: [ - ` - section { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 2px; - } - - .card { - display: flex; - flex-direction: column; - border: solid; - border-width: 1px; - border-color: black; - padding: 2px; - } - `, - ], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AppComponent implements OnInit { - private store = inject(Store); - - activities$ = this.store.select(selectActivities); - - ngOnInit(): void { - this.store.dispatch(loadActivities()); - this.store.dispatch(loadUsers()); - this.store.dispatch(loadStatuses()); - } - - getAllTeachersForActivityType$ = (type: ActivityType) => - this.store.select(selectAllTeachersByActivityType(type)); -} diff --git a/apps/ngrx/effect-selector/src/app/app.config.ts b/apps/ngrx/effect-selector/src/app/app.config.ts deleted file mode 100644 index d59c436ed..000000000 --- a/apps/ngrx/effect-selector/src/app/app.config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ApplicationConfig } from '@angular/core'; -import { provideEffects } from '@ngrx/effects'; -import { provideStore } from '@ngrx/store'; -import { ActivityEffects } from './store/activity/activity.effects'; -import { - activityFeatureKey, - activityReducer, -} from './store/activity/activity.reducer'; -import { StatusEffects } from './store/status/status.effects'; -import { UserEffects } from './store/user/user.effects'; - -import { statusFeatureKey, statusReducer } from './store/status/status.reducer'; - -import { userFeatureKey, userReducer } from './store/user/user.reducer'; - -const reducers = { - [statusFeatureKey]: statusReducer, - [activityFeatureKey]: activityReducer, - [userFeatureKey]: userReducer, -}; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStore(reducers), - provideEffects([ActivityEffects, UserEffects, StatusEffects]), - ], -}; diff --git a/apps/ngrx/effect-selector/src/app/store/activity/activity.actions.ts b/apps/ngrx/effect-selector/src/app/store/activity/activity.actions.ts deleted file mode 100644 index c8affff54..000000000 --- a/apps/ngrx/effect-selector/src/app/store/activity/activity.actions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createAction, props } from '@ngrx/store'; -import { Activity } from './activity.model'; - -export const loadActivities = createAction('[Activity Effect] Load Activities'); - -export const loadActivitiesSuccess = createAction( - '[Activity Effect] Load Activities Success', - props<{ activities: Activity[] }>(), -); - -export const loadActivitiesFailure = createAction( - '[Activity Effect] Load Activities Failure', - props<{ error: unknown }>(), -); diff --git a/apps/ngrx/effect-selector/src/app/store/activity/activity.effects.ts b/apps/ngrx/effect-selector/src/app/store/activity/activity.effects.ts deleted file mode 100644 index 88f8b2317..000000000 --- a/apps/ngrx/effect-selector/src/app/store/activity/activity.effects.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { of } from 'rxjs'; -import { catchError, concatMap, map } from 'rxjs/operators'; -import * as ActivityActions from './activity.actions'; -import { ActivityService } from './activity.service'; - -@Injectable() -export class ActivityEffects { - loadActivities$ = createEffect(() => { - return this.actions$.pipe( - ofType(ActivityActions.loadActivities), - concatMap(() => - this.ActivityService.fetchActivities().pipe( - map((activities) => - ActivityActions.loadActivitiesSuccess({ activities }), - ), - catchError((error) => - of(ActivityActions.loadActivitiesFailure({ error })), - ), - ), - ), - ); - }); - - constructor( - private actions$: Actions, - private ActivityService: ActivityService, - ) {} -} diff --git a/apps/ngrx/effect-selector/src/app/store/activity/activity.model.ts b/apps/ngrx/effect-selector/src/app/store/activity/activity.model.ts deleted file mode 100644 index 75f188132..000000000 --- a/apps/ngrx/effect-selector/src/app/store/activity/activity.model.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - incrementalNumber, - rand, - randFirstName, - randText, -} from '@ngneat/falso'; - -export const activityType = [ - 'Sport', - 'Sciences', - 'History', - 'Maths', - 'Physics', -] as const; -export type ActivityType = (typeof activityType)[number]; - -export interface Person { - id: number; - name: string; -} - -export interface Activity { - id: number; - name: string; - type: ActivityType; - teacher: Person; -} - -const factoryPerson = incrementalNumber(); - -export const randPerson = () => ({ - id: factoryPerson(), - name: randFirstName(), -}); - -const factoryActivity = incrementalNumber(); - -export const randActivity = (): Activity => ({ - id: factoryActivity(), - name: randText(), - type: rand(activityType), - teacher: randPerson(), -}); - -export const activities: Activity[] = [ - randActivity(), - randActivity(), - randActivity(), - randActivity(), - randActivity(), - randActivity(), - randActivity(), - randActivity(), - randActivity(), -]; diff --git a/apps/ngrx/effect-selector/src/app/store/activity/activity.reducer.ts b/apps/ngrx/effect-selector/src/app/store/activity/activity.reducer.ts deleted file mode 100644 index b7285e8f6..000000000 --- a/apps/ngrx/effect-selector/src/app/store/activity/activity.reducer.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createReducer, on } from '@ngrx/store'; -import * as ActivityActions from './activity.actions'; -import { Activity } from './activity.model'; - -export const activityFeatureKey = 'Activity'; - -export interface ActivityState { - activities: Activity[]; -} - -export const initialState: ActivityState = { - activities: [], -}; - -export const activityReducer = createReducer( - initialState, - on(ActivityActions.loadActivitiesSuccess, (state, { activities }) => ({ - ...state, - activities, - })), - on(ActivityActions.loadActivitiesFailure, (state) => ({ - state, - activities: [], - })), -); diff --git a/apps/ngrx/effect-selector/src/app/store/activity/activity.selectors.ts b/apps/ngrx/effect-selector/src/app/store/activity/activity.selectors.ts deleted file mode 100644 index 6c6a25a65..000000000 --- a/apps/ngrx/effect-selector/src/app/store/activity/activity.selectors.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { ActivityState, activityFeatureKey } from './activity.reducer'; - -export const selectActivityState = - createFeatureSelector(activityFeatureKey); - -export const selectActivities = createSelector( - selectActivityState, - (state) => state.activities, -); diff --git a/apps/ngrx/effect-selector/src/app/store/activity/activity.service.ts b/apps/ngrx/effect-selector/src/app/store/activity/activity.service.ts deleted file mode 100644 index 07aa8f8ad..000000000 --- a/apps/ngrx/effect-selector/src/app/store/activity/activity.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Injectable } from '@angular/core'; -import { map, timer } from 'rxjs'; -import { activities } from './activity.model'; - -@Injectable({ - providedIn: 'root', -}) -export class ActivityService { - fetchActivities = () => timer(500).pipe(map(() => activities)); -} diff --git a/apps/ngrx/effect-selector/src/app/store/status/status.actions.ts b/apps/ngrx/effect-selector/src/app/store/status/status.actions.ts deleted file mode 100644 index 8fc2ddc49..000000000 --- a/apps/ngrx/effect-selector/src/app/store/status/status.actions.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createAction, props } from '@ngrx/store'; -import { Status } from './status.model'; - -export const loadStatuses = createAction('[Status] Load Statuses'); - -export const loadStatusesSuccess = createAction( - '[Status] Load Statuses Success', - props<{ statuses: Status[] }>(), -); diff --git a/apps/ngrx/effect-selector/src/app/store/status/status.effects.ts b/apps/ngrx/effect-selector/src/app/store/status/status.effects.ts deleted file mode 100644 index 997eb0c1f..000000000 --- a/apps/ngrx/effect-selector/src/app/store/status/status.effects.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { Store } from '@ngrx/store'; -import { combineLatest, concatMap, map } from 'rxjs'; -import { selectActivities } from '../activity/activity.selectors'; -import { selectUser } from '../user/user.selectors'; -import * as StatusActions from './status.actions'; -import { Status } from './status.model'; - -@Injectable() -export class StatusEffects { - loadStatuses$ = createEffect(() => { - return this.actions$.pipe( - ofType(StatusActions.loadStatuses), - concatMap(() => - combineLatest([ - this.store.select(selectUser), - this.store.select(selectActivities), - ]).pipe( - map(([user, activities]): Status[] => { - if (user?.isAdmin) { - return activities.reduce( - (status: Status[], activity): Status[] => { - const index = status.findIndex( - (s) => s.name === activity.type, - ); - if (index === -1) { - return [ - ...status, - { name: activity.type, teachers: [activity.teacher] }, - ]; - } else { - status[index].teachers.push(activity.teacher); - return status; - } - }, - [], - ); - } - return []; - }), - map((statuses) => StatusActions.loadStatusesSuccess({ statuses })), - ), - ), - ); - }); - - constructor( - private actions$: Actions, - private store: Store, - ) {} -} diff --git a/apps/ngrx/effect-selector/src/app/store/status/status.model.ts b/apps/ngrx/effect-selector/src/app/store/status/status.model.ts deleted file mode 100644 index 07733834a..000000000 --- a/apps/ngrx/effect-selector/src/app/store/status/status.model.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ActivityType, Person } from '../activity/activity.model'; - -export interface Status { - name: ActivityType; - teachers: Person[]; -} diff --git a/apps/ngrx/effect-selector/src/app/store/status/status.reducer.ts b/apps/ngrx/effect-selector/src/app/store/status/status.reducer.ts deleted file mode 100644 index b91cbe2fb..000000000 --- a/apps/ngrx/effect-selector/src/app/store/status/status.reducer.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createReducer, on } from '@ngrx/store'; -import { ActivityType, Person } from '../activity/activity.model'; -import * as StatusActions from './status.actions'; -import { Status } from './status.model'; - -export const statusFeatureKey = 'status'; - -export interface StatusState { - statuses: Status[]; - teachersMap: Map; -} - -export const initialState: StatusState = { - statuses: [], - teachersMap: new Map(), -}; - -export const statusReducer = createReducer( - initialState, - on(StatusActions.loadStatusesSuccess, (state, { statuses }): StatusState => { - const map = new Map(); - statuses.forEach((s) => map.set(s.name, s.teachers)); - return { - ...state, - statuses, - teachersMap: map, - }; - }), -); diff --git a/apps/ngrx/effect-selector/src/app/store/status/status.selectors.ts b/apps/ngrx/effect-selector/src/app/store/status/status.selectors.ts deleted file mode 100644 index 80ed059a6..000000000 --- a/apps/ngrx/effect-selector/src/app/store/status/status.selectors.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { ActivityType } from '../activity/activity.model'; -import { StatusState, statusFeatureKey } from './status.reducer'; - -export const selectStatusState = - createFeatureSelector(statusFeatureKey); - -export const selectStatuses = createSelector( - selectStatusState, - (state) => state.statuses, -); - -export const selectAllTeachersByActivityType = (name: ActivityType) => - createSelector( - selectStatusState, - (state) => state.teachersMap.get(name) ?? [], - ); diff --git a/apps/ngrx/effect-selector/src/app/store/user/user.actions.ts b/apps/ngrx/effect-selector/src/app/store/user/user.actions.ts deleted file mode 100644 index a73147dce..000000000 --- a/apps/ngrx/effect-selector/src/app/store/user/user.actions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createAction, props } from '@ngrx/store'; -import { User } from './user.model'; - -export const loadUsers = createAction('[User] Load Users'); - -export const loadUsersSuccess = createAction( - '[User] Load Users Success', - props<{ user: User }>(), -); - -export const loadUsersFailure = createAction( - '[User] Load Users Failure', - props<{ error: unknown }>(), -); diff --git a/apps/ngrx/effect-selector/src/app/store/user/user.effects.ts b/apps/ngrx/effect-selector/src/app/store/user/user.effects.ts deleted file mode 100644 index fff3f6f6c..000000000 --- a/apps/ngrx/effect-selector/src/app/store/user/user.effects.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { of } from 'rxjs'; -import { catchError, concatMap, map } from 'rxjs/operators'; -import * as UserActions from './user.actions'; -import { UserService } from './user.service'; - -@Injectable() -export class UserEffects { - loadUsers$ = createEffect(() => { - return this.actions$.pipe( - ofType(UserActions.loadUsers), - concatMap(() => - this.userService.fetchUser().pipe( - map((user) => UserActions.loadUsersSuccess({ user })), - catchError((error) => of(UserActions.loadUsersFailure({ error }))), - ), - ), - ); - }); - - constructor( - private actions$: Actions, - private userService: UserService, - ) {} -} diff --git a/apps/ngrx/effect-selector/src/app/store/user/user.model.ts b/apps/ngrx/effect-selector/src/app/store/user/user.model.ts deleted file mode 100644 index d7692041c..000000000 --- a/apps/ngrx/effect-selector/src/app/store/user/user.model.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { randFirstName, randLastName, randText } from '@ngneat/falso'; - -export interface User { - firstname: string; - lastname: string; - isAdmin: boolean; - apiKey: string; -} - -export const user: User = { - firstname: randFirstName(), - lastname: randLastName(), - isAdmin: true, - apiKey: randText(), -}; diff --git a/apps/ngrx/effect-selector/src/app/store/user/user.reducer.ts b/apps/ngrx/effect-selector/src/app/store/user/user.reducer.ts deleted file mode 100644 index 3f4768c18..000000000 --- a/apps/ngrx/effect-selector/src/app/store/user/user.reducer.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createReducer, on } from '@ngrx/store'; -import * as UserActions from './user.actions'; -import { User } from './user.model'; - -export const userFeatureKey = 'user'; - -export interface UserState { - user?: User; -} - -export const initialState: UserState = { - user: undefined, -}; - -export const userReducer = createReducer( - initialState, - on(UserActions.loadUsersSuccess, (state, { user }) => ({ ...state, user })), - on(UserActions.loadUsersFailure, (state) => ({ ...state, user: undefined })), -); diff --git a/apps/ngrx/effect-selector/src/app/store/user/user.selectors.ts b/apps/ngrx/effect-selector/src/app/store/user/user.selectors.ts deleted file mode 100644 index 7970b0dfe..000000000 --- a/apps/ngrx/effect-selector/src/app/store/user/user.selectors.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { UserState, userFeatureKey } from './user.reducer'; - -export const selectUserState = createFeatureSelector(userFeatureKey); - -export const selectUser = createSelector( - selectUserState, - (state) => state.user, -); diff --git a/apps/ngrx/effect-selector/src/app/store/user/user.service.ts b/apps/ngrx/effect-selector/src/app/store/user/user.service.ts deleted file mode 100644 index a407b020a..000000000 --- a/apps/ngrx/effect-selector/src/app/store/user/user.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Injectable } from '@angular/core'; -import { map, timer } from 'rxjs'; -import { user } from './user.model'; - -@Injectable({ - providedIn: 'root', -}) -export class UserService { - fetchUser = () => timer(500).pipe(map(() => user)); -} diff --git a/apps/ngrx/effect-selector/src/index.html b/apps/ngrx/effect-selector/src/index.html deleted file mode 100644 index 21d573669..000000000 --- a/apps/ngrx/effect-selector/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Ngrx1 - - - - - - - - diff --git a/apps/ngrx/effect-selector/src/main.ts b/apps/ngrx/effect-selector/src/main.ts deleted file mode 100644 index 31c2a3482..000000000 --- a/apps/ngrx/effect-selector/src/main.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app/app.config'; - -import { AppComponent } from './app/app.component'; - -bootstrapApplication(AppComponent, appConfig); diff --git a/apps/ngrx/effect-selector/src/polyfills.ts b/apps/ngrx/effect-selector/src/polyfills.ts deleted file mode 100644 index e4555ed11..000000000 --- a/apps/ngrx/effect-selector/src/polyfills.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes recent versions of Safari, Chrome (including - * Opera), Edge on the desktop, and iOS and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/apps/ngrx/effect-selector/tsconfig.app.json b/apps/ngrx/effect-selector/tsconfig.app.json deleted file mode 100644 index 7a4dbc47e..000000000 --- a/apps/ngrx/effect-selector/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "types": [], - "target": "ES2022", - "useDefineForClassFields": false - }, - "files": ["src/main.ts", "src/polyfills.ts"], - "include": ["src/**/*.d.ts"], - "exclude": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts"] -} diff --git a/apps/ngrx/effect-selector/tsconfig.editor.json b/apps/ngrx/effect-selector/tsconfig.editor.json deleted file mode 100644 index 20c4afdbf..000000000 --- a/apps/ngrx/effect-selector/tsconfig.editor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.ts"], - "compilerOptions": { - "types": ["jest", "node"] - } -} diff --git a/apps/ngrx/effect-selector/tsconfig.json b/apps/ngrx/effect-selector/tsconfig.json deleted file mode 100644 index 52eb4f718..000000000 --- a/apps/ngrx/effect-selector/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], - "compilerOptions": { - "target": "es2020", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/ngrx/effect-selector/tsconfig.spec.json b/apps/ngrx/effect-selector/tsconfig.spec.json deleted file mode 100644 index 7aa46d88c..000000000 --- a/apps/ngrx/effect-selector/tsconfig.spec.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "files": ["src/test-setup.ts"], - "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] -} diff --git a/apps/ngrx/notification/README.md b/apps/ngrx/notification/README.md deleted file mode 100644 index 36acdab46..000000000 --- a/apps/ngrx/notification/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Power of Effect - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve ngrx-notification -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/ngrx/7-power-effect/). diff --git a/apps/ngrx/notification/project.json b/apps/ngrx/notification/project.json deleted file mode 100644 index 3f8a85179..000000000 --- a/apps/ngrx/notification/project.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "name": "ngrx-notification", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/ngrx/notification/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/ngrx/notification", - "index": "apps/ngrx/notification/src/index.html", - "main": "apps/ngrx/notification/src/main.ts", - "polyfills": "apps/ngrx/notification/src/polyfills.ts", - "tsConfig": "apps/ngrx/notification/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/ngrx/notification/src/favicon.ico", - "apps/ngrx/notification/src/assets" - ], - "styles": [ - "apps/ngrx/notification/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "ngrx-notification:build:production" - }, - "development": { - "buildTarget": "ngrx-notification:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "ngrx-notification:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/ngrx/notification/src/app/app.actions.ts b/apps/ngrx/notification/src/app/app.actions.ts deleted file mode 100644 index 3dcba8134..000000000 --- a/apps/ngrx/notification/src/app/app.actions.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createActionGroup, emptyProps } from '@ngrx/store'; - -// This is the global actions. -export const appActions = createActionGroup({ - source: 'App Component', - events: { - 'Init App': emptyProps(), - }, -}); diff --git a/apps/ngrx/notification/src/app/app.component.ts b/apps/ngrx/notification/src/app/app.component.ts deleted file mode 100644 index dc14376ac..000000000 --- a/apps/ngrx/notification/src/app/app.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, inject, OnInit } from '@angular/core'; -import { RouterLink, RouterOutlet } from '@angular/router'; -import { Store } from '@ngrx/store'; -import { appActions } from './app.actions'; - -@Component({ - standalone: true, - imports: [RouterOutlet, RouterLink], - selector: 'app-root', - template: ` - - - - `, - styles: [ - ` - :host { - display: flex; - flex-direction: column; - gap: 20px; - - nav { - display: flex; - gap: 20px; - } - } - `, - ], -}) -export class AppComponent implements OnInit { - private store = inject(Store); - - ngOnInit(): void { - this.store.dispatch(appActions.initApp()); - } -} diff --git a/apps/ngrx/notification/src/app/app.config.ts b/apps/ngrx/notification/src/app/app.config.ts deleted file mode 100644 index b36822344..000000000 --- a/apps/ngrx/notification/src/app/app.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { FakeBackendService } from '@angular-challenges/ngrx-notification/backend'; -import { APP_INITIALIZER, ApplicationConfig, inject } from '@angular/core'; -import { provideAnimations } from '@angular/platform-browser/animations'; -import { provideRouter } from '@angular/router'; -import { provideEffects } from '@ngrx/effects'; -import { provideStore } from '@ngrx/store'; -import { NotificationService } from './data-access/notification.service'; -import { ROUTES } from './routes'; -import { StudentEffects } from './student/store/student.effects'; -import { - studentReducer, - studentsFeatureKey, -} from './student/store/student.reducer'; -import { TeacherEffects } from './teacher/store/teacher.effects'; -import { - teacherReducer, - teachersFeatureKey, -} from './teacher/store/teacher.reducer'; - -const REDUCERS = { - [teachersFeatureKey]: teacherReducer, - [studentsFeatureKey]: studentReducer, -}; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStore(REDUCERS), - provideEffects([TeacherEffects, StudentEffects]), - provideRouter(ROUTES), - { - provide: APP_INITIALIZER, - multi: true, - useFactory: () => { - const service = inject(FakeBackendService); - return () => service.start(); - }, - }, - { - provide: APP_INITIALIZER, - multi: true, - useFactory: () => { - const service = inject(NotificationService); - return () => service.init(); - }, - }, - provideAnimations(), - ], -}; diff --git a/apps/ngrx/notification/src/app/data-access/http.service.ts b/apps/ngrx/notification/src/app/data-access/http.service.ts deleted file mode 100644 index b5a59aab0..000000000 --- a/apps/ngrx/notification/src/app/data-access/http.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { FakeBackendService } from '@angular-challenges/ngrx-notification/backend'; -import { inject, Injectable } from '@angular/core'; -import { take } from 'rxjs'; - -@Injectable({ providedIn: 'root' }) -export class HttpService { - private fakeBackend = inject(FakeBackendService); - - getAllTeachers = () => this.fakeBackend.getAllTeachers().pipe(take(1)); - - getAllStudents = () => this.fakeBackend.getAllStudents().pipe(take(1)); - - getAllSchools = () => this.fakeBackend.getAllSchools().pipe(take(1)); -} diff --git a/apps/ngrx/notification/src/app/data-access/notification.service.ts b/apps/ngrx/notification/src/app/data-access/notification.service.ts deleted file mode 100644 index b11614054..000000000 --- a/apps/ngrx/notification/src/app/data-access/notification.service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { PushService } from '@angular-challenges/ngrx-notification/backend'; -import { - isSchool, - isStudent, - isTeacher, - Push, -} from '@angular-challenges/ngrx-notification/model'; -import { inject, Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { filter } from 'rxjs'; -import { studentActions } from '../student/store/student.actions'; -import { teacherActions } from '../teacher/store/teacher.actions'; - -@Injectable({ providedIn: 'root' }) -export class NotificationService { - private pushService = inject(PushService); - private store = inject(Store); - - init() { - this.pushService.notification$ - .pipe(filter(Boolean)) - .subscribe((notification: Push) => { - if (isTeacher(notification)) { - this.store.dispatch( - teacherActions.addOneTeacher({ teacher: notification }), - ); - } - if (isStudent(notification)) { - this.store.dispatch( - studentActions.addOneStudent({ student: notification }), - ); - } - if (isSchool(notification)) { - // SchoolStore is a ComponentStore. We can't dispatch a school action here. - // We are stuck. We must have done something wrong and need to refactor... - } - }); - } -} diff --git a/apps/ngrx/notification/src/app/routes.ts b/apps/ngrx/notification/src/app/routes.ts deleted file mode 100644 index 7db44a4dc..000000000 --- a/apps/ngrx/notification/src/app/routes.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Route } from '@angular/router'; -import { TeacherComponent } from './teacher/teacher.component'; - -export const ROUTES: Route[] = [ - { path: '', pathMatch: 'full', redirectTo: 'teacher' }, - { - path: 'teacher', - component: TeacherComponent, - }, - { - path: 'student', - loadComponent: () => - import('./student/student.component').then((m) => m.StudentComponent), - }, - { - path: 'school', - loadComponent: () => - import('./school/school.component').then((m) => m.SchoolComponent), - }, -]; diff --git a/apps/ngrx/notification/src/app/school/school.component.ts b/apps/ngrx/notification/src/app/school/school.component.ts deleted file mode 100644 index af58af9c1..000000000 --- a/apps/ngrx/notification/src/app/school/school.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @angular-eslint/component-selector */ -import { AsyncPipe, NgFor } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { provideComponentStore } from '@ngrx/component-store'; -import { SchoolStore } from './school.store'; - -@Component({ - standalone: true, - imports: [NgFor, AsyncPipe], - providers: [provideComponentStore(SchoolStore)], - selector: 'school', - template: ` -

SCHOOL

-
- {{ school.name }} - {{ school.version }} -
- `, - styles: [ - ` - :host { - display: block; - width: fit-content; - height: fit-content; - border: 1px solid red; - padding: 4px; - } - `, - ], -}) -export class SchoolComponent { - private store = inject(SchoolStore); - school$ = this.store.schools$; -} diff --git a/apps/ngrx/notification/src/app/school/school.store.ts b/apps/ngrx/notification/src/app/school/school.store.ts deleted file mode 100644 index 0fd31fa6d..000000000 --- a/apps/ngrx/notification/src/app/school/school.store.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { School } from '@angular-challenges/ngrx-notification/model'; -import { Injectable } from '@angular/core'; -import { - ComponentStore, - OnStoreInit, - tapResponse, -} from '@ngrx/component-store'; -import { pipe, switchMap } from 'rxjs'; -import { HttpService } from '../data-access/http.service'; - -@Injectable() -export class SchoolStore - extends ComponentStore<{ schools: School[] }> - implements OnStoreInit -{ - readonly schools$ = this.select((state) => state.schools); - - constructor(private httpService: HttpService) { - super({ schools: [] }); - } - - addSchool = this.updater((state, school: School) => ({ - ...state, - schools: [...state.schools, school], - })); - - updateSchool = this.updater((state, school: School) => ({ - ...state, - schools: state.schools.map((t) => (t.id === school.id ? school : t)), - })); - - private readonly loadSchools = this.effect( - pipe( - switchMap(() => - this.httpService.getAllSchools().pipe( - tapResponse( - (schools) => this.patchState({ schools }), - (_) => _, // not handling the error - ), - ), - ), - ), - ); - - ngrxOnStoreInit() { - this.loadSchools(); - } -} diff --git a/apps/ngrx/notification/src/app/student/store/student.actions.ts b/apps/ngrx/notification/src/app/student/store/student.actions.ts deleted file mode 100644 index 52eda8ee0..000000000 --- a/apps/ngrx/notification/src/app/student/store/student.actions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Student } from '@angular-challenges/ngrx-notification/model'; -import { createActionGroup, props } from '@ngrx/store'; - -export const studentActions = createActionGroup({ - source: 'Student API', - events: { - 'Add One Student': props<{ student: Student }>(), - 'Add All Students': props<{ students: Student[] }>(), - }, -}); diff --git a/apps/ngrx/notification/src/app/student/store/student.effects.ts b/apps/ngrx/notification/src/app/student/store/student.effects.ts deleted file mode 100644 index b741bd21d..000000000 --- a/apps/ngrx/notification/src/app/student/store/student.effects.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { inject, Injectable } from '@angular/core'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { map, switchMap } from 'rxjs'; -import { appActions } from '../../app.actions'; -import { HttpService } from '../../data-access/http.service'; -import { studentActions } from './student.actions'; - -@Injectable() -export class StudentEffects { - private actions$ = inject(Actions); - private httpService = inject(HttpService); - - loadStudents$ = createEffect(() => - this.actions$.pipe( - ofType(appActions.initApp), - switchMap(() => - this.httpService - .getAllStudents() - .pipe(map((students) => studentActions.addAllStudents({ students }))), - ), - ), - ); -} diff --git a/apps/ngrx/notification/src/app/student/store/student.reducer.ts b/apps/ngrx/notification/src/app/student/store/student.reducer.ts deleted file mode 100644 index 0b02cd322..000000000 --- a/apps/ngrx/notification/src/app/student/store/student.reducer.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Student } from '@angular-challenges/ngrx-notification/model'; -import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; -import { createReducer, on } from '@ngrx/store'; -import { studentActions } from './student.actions'; - -export const studentsFeatureKey = 'students'; - -export type StudentState = EntityState; - -export const studentAdapter: EntityAdapter = - createEntityAdapter(); - -export const studentReducer = createReducer( - studentAdapter.getInitialState(), - on(studentActions.addOneStudent, (state, { student }) => - studentAdapter.upsertOne(student, state), - ), - on(studentActions.addAllStudents, (state, { students }) => - studentAdapter.setAll(students, state), - ), -); - -export const { selectIds, selectEntities, selectAll, selectTotal } = - studentAdapter.getSelectors(); diff --git a/apps/ngrx/notification/src/app/student/store/student.selectors.ts b/apps/ngrx/notification/src/app/student/store/student.selectors.ts deleted file mode 100644 index b041ff420..000000000 --- a/apps/ngrx/notification/src/app/student/store/student.selectors.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { - StudentState, - studentAdapter, - studentsFeatureKey, -} from './student.reducer'; - -const selectStudentState = - createFeatureSelector(studentsFeatureKey); - -export const { selectAll } = studentAdapter.getSelectors(); - -const selectStudents = createSelector(selectStudentState, selectAll); - -export const StudentSelectors = { - selectStudents, -}; diff --git a/apps/ngrx/notification/src/app/student/student.component.ts b/apps/ngrx/notification/src/app/student/student.component.ts deleted file mode 100644 index bbc45bfd7..000000000 --- a/apps/ngrx/notification/src/app/student/student.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable @angular-eslint/component-selector */ -import { AsyncPipe, NgFor } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { StudentSelectors } from './store/student.selectors'; - -@Component({ - standalone: true, - imports: [NgFor, AsyncPipe], - selector: 'student', - template: ` -

STUDENTS

-
- {{ student.firstname }} {{ student.lastname }} - {{ student.version }} -
- `, - styles: [ - ` - :host { - display: block; - width: fit-content; - height: fit-content; - border: 1px solid red; - padding: 4px; - } - `, - ], -}) -export class StudentComponent { - private store = inject(Store); - students$ = this.store.select(StudentSelectors.selectStudents); -} diff --git a/apps/ngrx/notification/src/app/teacher/store/teacher.actions.ts b/apps/ngrx/notification/src/app/teacher/store/teacher.actions.ts deleted file mode 100644 index ff41af34e..000000000 --- a/apps/ngrx/notification/src/app/teacher/store/teacher.actions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Teacher } from '@angular-challenges/ngrx-notification/model'; -import { createActionGroup, props } from '@ngrx/store'; - -export const teacherActions = createActionGroup({ - source: 'Teacher API', - events: { - 'Add One Teacher': props<{ teacher: Teacher }>(), - 'Add All Teachers': props<{ teachers: Teacher[] }>(), - }, -}); diff --git a/apps/ngrx/notification/src/app/teacher/store/teacher.effects.ts b/apps/ngrx/notification/src/app/teacher/store/teacher.effects.ts deleted file mode 100644 index 43c3fab0b..000000000 --- a/apps/ngrx/notification/src/app/teacher/store/teacher.effects.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { inject, Injectable } from '@angular/core'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { map, switchMap } from 'rxjs'; -import { appActions } from '../../app.actions'; -import { HttpService } from '../../data-access/http.service'; -import { teacherActions } from './teacher.actions'; - -@Injectable() -export class TeacherEffects { - private actions$ = inject(Actions); - private httpService = inject(HttpService); - - loadTeachers$ = createEffect(() => - this.actions$.pipe( - ofType(appActions.initApp), - switchMap(() => - this.httpService - .getAllTeachers() - .pipe(map((teachers) => teacherActions.addAllTeachers({ teachers }))), - ), - ), - ); -} diff --git a/apps/ngrx/notification/src/app/teacher/store/teacher.reducer.ts b/apps/ngrx/notification/src/app/teacher/store/teacher.reducer.ts deleted file mode 100644 index d793b9646..000000000 --- a/apps/ngrx/notification/src/app/teacher/store/teacher.reducer.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Teacher } from '@angular-challenges/ngrx-notification/model'; -import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; -import { createReducer, on } from '@ngrx/store'; -import { teacherActions } from './teacher.actions'; - -export const teachersFeatureKey = 'teachers'; - -export type TeacherState = EntityState; - -export const teacherAdapter: EntityAdapter = - createEntityAdapter(); - -export const teacherReducer = createReducer( - teacherAdapter.getInitialState(), - on(teacherActions.addOneTeacher, (state, { teacher }) => - teacherAdapter.upsertOne(teacher, state), - ), - on(teacherActions.addAllTeachers, (state, { teachers }) => - teacherAdapter.setAll(teachers, state), - ), -); - -export const { selectIds, selectEntities, selectAll, selectTotal } = - teacherAdapter.getSelectors(); diff --git a/apps/ngrx/notification/src/app/teacher/store/teacher.selectors.ts b/apps/ngrx/notification/src/app/teacher/store/teacher.selectors.ts deleted file mode 100644 index a32e82c2b..000000000 --- a/apps/ngrx/notification/src/app/teacher/store/teacher.selectors.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { - TeacherState, - teacherAdapter, - teachersFeatureKey, -} from './teacher.reducer'; - -const selectTeacherState = - createFeatureSelector(teachersFeatureKey); - -export const { selectAll } = teacherAdapter.getSelectors(); - -const selectTeachers = createSelector(selectTeacherState, selectAll); - -export const TeacherSelectors = { - selectTeachers, -}; diff --git a/apps/ngrx/notification/src/app/teacher/teacher.component.ts b/apps/ngrx/notification/src/app/teacher/teacher.component.ts deleted file mode 100644 index 63e812200..000000000 --- a/apps/ngrx/notification/src/app/teacher/teacher.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @angular-eslint/component-selector */ -import { AsyncPipe, NgFor } from '@angular/common'; -import { Component } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { TeacherSelectors } from './store/teacher.selectors'; - -@Component({ - standalone: true, - imports: [NgFor, AsyncPipe], - selector: 'teacher', - template: ` -

TEACHERS

-
- {{ teacher.firstname }} {{ teacher.lastname }} - {{ teacher.version }} -
- `, - styles: [ - ` - :host { - display: block; - width: fit-content; - height: fit-content; - border: 1px solid red; - padding: 4px; - } - `, - ], -}) -export class TeacherComponent { - teacher$ = this.store.select(TeacherSelectors.selectTeachers); - - constructor(private store: Store) {} -} diff --git a/apps/ngrx/notification/src/index.html b/apps/ngrx/notification/src/index.html deleted file mode 100644 index cc46b13b2..000000000 --- a/apps/ngrx/notification/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgrxNotification - - - - - - - - diff --git a/apps/ngrx/notification/src/main.ts b/apps/ngrx/notification/src/main.ts deleted file mode 100644 index 6f91f21a3..000000000 --- a/apps/ngrx/notification/src/main.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { appConfig } from './app/app.config'; - -import { bootstrapApplication } from '@angular/platform-browser'; - -import { AppComponent } from './app/app.component'; - -bootstrapApplication(AppComponent, appConfig).catch((err) => - console.error(err), -); diff --git a/apps/ngrx/notification/src/polyfills.ts b/apps/ngrx/notification/src/polyfills.ts deleted file mode 100644 index e4555ed11..000000000 --- a/apps/ngrx/notification/src/polyfills.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes recent versions of Safari, Chrome (including - * Opera), Edge on the desktop, and iOS and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/apps/ngrx/notification/tsconfig.app.json b/apps/ngrx/notification/tsconfig.app.json deleted file mode 100644 index 7a4dbc47e..000000000 --- a/apps/ngrx/notification/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "types": [], - "target": "ES2022", - "useDefineForClassFields": false - }, - "files": ["src/main.ts", "src/polyfills.ts"], - "include": ["src/**/*.d.ts"], - "exclude": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts"] -} diff --git a/apps/ngrx/notification/tsconfig.editor.json b/apps/ngrx/notification/tsconfig.editor.json deleted file mode 100644 index 3d4f1db8b..000000000 --- a/apps/ngrx/notification/tsconfig.editor.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": [ - "**/*.ts", - "../../../libs/ngrx-notification/backend/src/lib/fake-backend.service.ts" - ], - "compilerOptions": { - "types": [] - } -} diff --git a/apps/ngrx/notification/tsconfig.json b/apps/ngrx/notification/tsconfig.json deleted file mode 100644 index b2dbbf22e..000000000 --- a/apps/ngrx/notification/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], - "compilerOptions": { - "target": "es2020", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/rxjs/pipe-bug/.eslintrc.json b/apps/nx/42-static-vs-dynamic-import/.eslintrc.json similarity index 100% rename from apps/rxjs/pipe-bug/.eslintrc.json rename to apps/nx/42-static-vs-dynamic-import/.eslintrc.json diff --git a/apps/nx/42-static-vs-dynamic-import/README.md b/apps/nx/42-static-vs-dynamic-import/README.md new file mode 100644 index 000000000..a08abaa82 --- /dev/null +++ b/apps/nx/42-static-vs-dynamic-import/README.md @@ -0,0 +1,13 @@ +# Static vs Dynamic Import + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve nx-static-vs-dynamic-import +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/nx/42-static-dynamic-import/). diff --git a/apps/nx/42-static-vs-dynamic-import/project.json b/apps/nx/42-static-vs-dynamic-import/project.json new file mode 100644 index 000000000..7f45fb828 --- /dev/null +++ b/apps/nx/42-static-vs-dynamic-import/project.json @@ -0,0 +1,73 @@ +{ + "name": "nx-static-vs-dynamic-import", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/nx/42-static-vs-dynamic-import/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/nx/42-static-vs-dynamic-import", + "index": "apps/nx/42-static-vs-dynamic-import/src/index.html", + "browser": "apps/nx/42-static-vs-dynamic-import/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/nx/42-static-vs-dynamic-import/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/nx/42-static-vs-dynamic-import/src/favicon.ico", + "apps/nx/42-static-vs-dynamic-import/src/assets" + ], + "styles": [ + "apps/nx/42-static-vs-dynamic-import/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all", + "sourceMap": true + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "nx-static-vs-dynamic-import:build:production" + }, + "development": { + "buildTarget": "nx-static-vs-dynamic-import:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "nx-static-vs-dynamic-import:build" + } + } + } +} diff --git a/apps/nx/42-static-vs-dynamic-import/src/app/app.component.ts b/apps/nx/42-static-vs-dynamic-import/src/app/app.component.ts new file mode 100644 index 000000000..59be617e8 --- /dev/null +++ b/apps/nx/42-static-vs-dynamic-import/src/app/app.component.ts @@ -0,0 +1,26 @@ +import { + UserComponent, + type User, +} from '@angular-challenges/static-dynamic-import/users'; +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [UserComponent, RouterOutlet], + selector: 'app-root', + template: ` + Author: + + + `, + host: { + class: 'flex flex-col', + }, +}) +export class AppComponent { + author: User = { + name: 'Thomas', + lastName: 'Laforge', + country: 'France', + }; +} diff --git a/apps/nx/static-dynamic-import/src/app/app.config.ts b/apps/nx/42-static-vs-dynamic-import/src/app/app.config.ts similarity index 100% rename from apps/nx/static-dynamic-import/src/app/app.config.ts rename to apps/nx/42-static-vs-dynamic-import/src/app/app.config.ts diff --git a/apps/forms/control-value-accessor/src/assets/.gitkeep b/apps/nx/42-static-vs-dynamic-import/src/assets/.gitkeep similarity index 100% rename from apps/forms/control-value-accessor/src/assets/.gitkeep rename to apps/nx/42-static-vs-dynamic-import/src/assets/.gitkeep diff --git a/apps/nx/static-dynamic-import/src/favicon.ico b/apps/nx/42-static-vs-dynamic-import/src/favicon.ico similarity index 100% rename from apps/nx/static-dynamic-import/src/favicon.ico rename to apps/nx/42-static-vs-dynamic-import/src/favicon.ico diff --git a/apps/nx/42-static-vs-dynamic-import/src/index.html b/apps/nx/42-static-vs-dynamic-import/src/index.html new file mode 100644 index 000000000..1b4f2a84b --- /dev/null +++ b/apps/nx/42-static-vs-dynamic-import/src/index.html @@ -0,0 +1,16 @@ + + + + + nx-static-vs-dynamic-import + + + + + + + + + diff --git a/apps/performance/ngfor-biglist/src/main.ts b/apps/nx/42-static-vs-dynamic-import/src/main.ts similarity index 100% rename from apps/performance/ngfor-biglist/src/main.ts rename to apps/nx/42-static-vs-dynamic-import/src/main.ts diff --git a/apps/performance/memoized/src/styles.scss b/apps/nx/42-static-vs-dynamic-import/src/styles.scss similarity index 100% rename from apps/performance/memoized/src/styles.scss rename to apps/nx/42-static-vs-dynamic-import/src/styles.scss diff --git a/apps/performance/memoized/tailwind.config.js b/apps/nx/42-static-vs-dynamic-import/tailwind.config.js similarity index 100% rename from apps/performance/memoized/tailwind.config.js rename to apps/nx/42-static-vs-dynamic-import/tailwind.config.js diff --git a/apps/performance/ngfor-biglist/tsconfig.app.json b/apps/nx/42-static-vs-dynamic-import/tsconfig.app.json similarity index 100% rename from apps/performance/ngfor-biglist/tsconfig.app.json rename to apps/nx/42-static-vs-dynamic-import/tsconfig.app.json diff --git a/apps/angular/view-transition/tsconfig.editor.json b/apps/nx/42-static-vs-dynamic-import/tsconfig.editor.json similarity index 100% rename from apps/angular/view-transition/tsconfig.editor.json rename to apps/nx/42-static-vs-dynamic-import/tsconfig.editor.json diff --git a/apps/nx/static-dynamic-import/tsconfig.json b/apps/nx/42-static-vs-dynamic-import/tsconfig.json similarity index 100% rename from apps/nx/static-dynamic-import/tsconfig.json rename to apps/nx/42-static-vs-dynamic-import/tsconfig.json diff --git a/apps/nx/static-dynamic-import/README.md b/apps/nx/static-dynamic-import/README.md deleted file mode 100644 index 54691c75b..000000000 --- a/apps/nx/static-dynamic-import/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Static vs Dynamic Import - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve nx-static-dynamic-import -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/nx/42-static-dynamic-import/). diff --git a/apps/nx/static-dynamic-import/project.json b/apps/nx/static-dynamic-import/project.json deleted file mode 100644 index c0208b075..000000000 --- a/apps/nx/static-dynamic-import/project.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "name": "nx-static-dynamic-import", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/nx/static-dynamic-import/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/nx/static-dynamic-import", - "index": "apps/nx/static-dynamic-import/src/index.html", - "browser": "apps/nx/static-dynamic-import/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/nx/static-dynamic-import/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/nx/static-dynamic-import/src/favicon.ico", - "apps/nx/static-dynamic-import/src/assets" - ], - "styles": [ - "apps/nx/static-dynamic-import/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all", - "sourceMap": true - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "nx-static-dynamic-import:build:production" - }, - "development": { - "buildTarget": "nx-static-dynamic-import:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "nx-static-dynamic-import:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - } - } -} diff --git a/apps/nx/static-dynamic-import/src/app/app.component.ts b/apps/nx/static-dynamic-import/src/app/app.component.ts deleted file mode 100644 index 9c5bd67d4..000000000 --- a/apps/nx/static-dynamic-import/src/app/app.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - UserComponent, - type User, -} from '@angular-challenges/static-dynamic-import/users'; -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [UserComponent, RouterOutlet], - selector: 'app-root', - template: ` - Author: - - - `, - host: { - class: 'flex flex-col', - }, -}) -export class AppComponent { - author: User = { - name: 'Thomas', - lastName: 'Laforge', - country: 'France', - }; -} diff --git a/apps/nx/static-dynamic-import/src/index.html b/apps/nx/static-dynamic-import/src/index.html deleted file mode 100644 index 77adec4af..000000000 --- a/apps/nx/static-dynamic-import/src/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - nx-static-dynamic-import - - - - - - - - - diff --git a/apps/angular/styling/.eslintrc.json b/apps/performance/12-optimize-change-detection/.eslintrc.json similarity index 100% rename from apps/angular/styling/.eslintrc.json rename to apps/performance/12-optimize-change-detection/.eslintrc.json diff --git a/apps/performance/12-optimize-change-detection/README.md b/apps/performance/12-optimize-change-detection/README.md new file mode 100644 index 000000000..52fe319d7 --- /dev/null +++ b/apps/performance/12-optimize-change-detection/README.md @@ -0,0 +1,13 @@ +# Optimize Change Detection + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve performance-optimize-change-detection +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/12-performance-optimize-change-detection/). diff --git a/apps/performance/12-optimize-change-detection/jest.config.ts b/apps/performance/12-optimize-change-detection/jest.config.ts new file mode 100644 index 000000000..dc22d99ea --- /dev/null +++ b/apps/performance/12-optimize-change-detection/jest.config.ts @@ -0,0 +1,24 @@ +/* eslint-disable */ +export default { + displayName: 'performance-optimize-change-detection', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: + '../../../coverage/apps/performance/12-optimize-change-detection', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/performance/12-optimize-change-detection/project.json b/apps/performance/12-optimize-change-detection/project.json new file mode 100644 index 000000000..188240700 --- /dev/null +++ b/apps/performance/12-optimize-change-detection/project.json @@ -0,0 +1,85 @@ +{ + "name": "performance-optimize-change-detection", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/performance/12-optimize-change-detection/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/performance/12-optimize-change-detection", + "index": "apps/performance/12-optimize-change-detection/src/index.html", + "main": "apps/performance/12-optimize-change-detection/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/performance/12-optimize-change-detection/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/performance/12-optimize-change-detection/src/favicon.ico", + "apps/performance/12-optimize-change-detection/src/assets" + ], + "styles": [ + "apps/performance/12-optimize-change-detection/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "performance-optimize-change-detection:build:production" + }, + "development": { + "buildTarget": "performance-optimize-change-detection:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "performance-optimize-change-detection:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/performance/12-optimize-change-detection/src/app/app.component.ts b/apps/performance/12-optimize-change-detection/src/app/app.component.ts new file mode 100644 index 000000000..b6e8a7c0a --- /dev/null +++ b/apps/performance/12-optimize-change-detection/src/app/app.component.ts @@ -0,0 +1,50 @@ +import { Component, HostListener, signal } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` +
Top
+
Middle
+
Bottom
+ @if (displayButton()) { + + } + `, + styles: [ + ` + :host { + height: 1500px; + display: flex; + flex-direction: column; + justify-content: space-between; + + button { + position: fixed; + bottom: 1rem; + left: 1rem; + z-index: 1; + padding: 1rem; + } + } + `, + ], +}) +export class AppComponent { + title = 'scroll-cd'; + + public displayButton = signal(false); + + @HostListener('window:scroll', ['$event']) + onScroll() { + const pos = window.scrollY; + this.displayButton.set(pos > 50); + } + + goToTop() { + window.scroll({ + top: 0, + left: 0, + behavior: 'smooth', + }); + } +} diff --git a/apps/ngrx/effect-selector/src/assets/.gitkeep b/apps/performance/12-optimize-change-detection/src/assets/.gitkeep similarity index 100% rename from apps/ngrx/effect-selector/src/assets/.gitkeep rename to apps/performance/12-optimize-change-detection/src/assets/.gitkeep diff --git a/apps/performance/christmas-web-worker/src/favicon.ico b/apps/performance/12-optimize-change-detection/src/favicon.ico similarity index 100% rename from apps/performance/christmas-web-worker/src/favicon.ico rename to apps/performance/12-optimize-change-detection/src/favicon.ico diff --git a/apps/performance/12-optimize-change-detection/src/index.html b/apps/performance/12-optimize-change-detection/src/index.html new file mode 100644 index 000000000..c1cb61764 --- /dev/null +++ b/apps/performance/12-optimize-change-detection/src/index.html @@ -0,0 +1,13 @@ + + + + + performance-optimize-change-detection + + + + + + + + diff --git a/apps/performance/christmas-web-worker/src/main.ts b/apps/performance/12-optimize-change-detection/src/main.ts similarity index 100% rename from apps/performance/christmas-web-worker/src/main.ts rename to apps/performance/12-optimize-change-detection/src/main.ts diff --git a/apps/angular/styling/src/styles.scss b/apps/performance/12-optimize-change-detection/src/styles.scss similarity index 100% rename from apps/angular/styling/src/styles.scss rename to apps/performance/12-optimize-change-detection/src/styles.scss diff --git a/apps/ngrx/effect-selector/src/test-setup.ts b/apps/performance/12-optimize-change-detection/src/test-setup.ts similarity index 100% rename from apps/ngrx/effect-selector/src/test-setup.ts rename to apps/performance/12-optimize-change-detection/src/test-setup.ts diff --git a/apps/performance/ngfor-optimize/tsconfig.app.json b/apps/performance/12-optimize-change-detection/tsconfig.app.json similarity index 100% rename from apps/performance/ngfor-optimize/tsconfig.app.json rename to apps/performance/12-optimize-change-detection/tsconfig.app.json diff --git a/apps/forms/control-value-accessor/tsconfig.editor.json b/apps/performance/12-optimize-change-detection/tsconfig.editor.json similarity index 100% rename from apps/forms/control-value-accessor/tsconfig.editor.json rename to apps/performance/12-optimize-change-detection/tsconfig.editor.json diff --git a/apps/angular/interop-rxjs-signal/tsconfig.json b/apps/performance/12-optimize-change-detection/tsconfig.json similarity index 100% rename from apps/angular/interop-rxjs-signal/tsconfig.json rename to apps/performance/12-optimize-change-detection/tsconfig.json diff --git a/apps/performance/scroll-cd/tsconfig.spec.json b/apps/performance/12-optimize-change-detection/tsconfig.spec.json similarity index 100% rename from apps/performance/scroll-cd/tsconfig.spec.json rename to apps/performance/12-optimize-change-detection/tsconfig.spec.json diff --git a/apps/performance/default-onpush/.eslintrc.json b/apps/performance/34-default-vs-onpush/.eslintrc.json similarity index 100% rename from apps/performance/default-onpush/.eslintrc.json rename to apps/performance/34-default-vs-onpush/.eslintrc.json diff --git a/apps/performance/34-default-vs-onpush/README.md b/apps/performance/34-default-vs-onpush/README.md new file mode 100644 index 000000000..ab82704b5 --- /dev/null +++ b/apps/performance/34-default-vs-onpush/README.md @@ -0,0 +1,13 @@ +# Default vs OnPush + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve performance-default-vs-onpush +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/34-default-onpush/). diff --git a/apps/performance/34-default-vs-onpush/project.json b/apps/performance/34-default-vs-onpush/project.json new file mode 100644 index 000000000..f81cb74a3 --- /dev/null +++ b/apps/performance/34-default-vs-onpush/project.json @@ -0,0 +1,75 @@ +{ + "name": "performance-default-vs-onpush", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/performance/34-default-vs-onpush/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/performance/34-default-vs-onpush", + "index": "apps/performance/34-default-vs-onpush/src/index.html", + "main": "apps/performance/34-default-vs-onpush/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/performance/34-default-vs-onpush/tsconfig.app.json", + "assets": [ + "apps/performance/34-default-vs-onpush/src/favicon.ico", + "apps/performance/34-default-vs-onpush/src/assets" + ], + "styles": [ + "apps/performance/34-default-vs-onpush/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "performance-default-vs-onpush:build:production" + }, + "development": { + "buildTarget": "performance-default-vs-onpush:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "performance-default-vs-onpush:build" + } + } + } +} diff --git a/apps/performance/34-default-vs-onpush/src/app/app.component.ts b/apps/performance/34-default-vs-onpush/src/app/app.component.ts new file mode 100644 index 000000000..88b0a6571 --- /dev/null +++ b/apps/performance/34-default-vs-onpush/src/app/app.component.ts @@ -0,0 +1,21 @@ +import { Component } from '@angular/core'; +import { randFirstName } from '@ngneat/falso'; +import { PersonListComponent } from './person-list.component'; +import { RandomComponent } from './random.component'; + +@Component({ + imports: [PersonListComponent, RandomComponent], + selector: 'app-root', + template: ` + + +
+ + +
+ `, +}) +export class AppComponent { + girlList = randFirstName({ gender: 'female', length: 10 }); + boyList = randFirstName({ gender: 'male', length: 10 }); +} diff --git a/apps/performance/default-onpush/src/app/app.config.ts b/apps/performance/34-default-vs-onpush/src/app/app.config.ts similarity index 100% rename from apps/performance/default-onpush/src/app/app.config.ts rename to apps/performance/34-default-vs-onpush/src/app/app.config.ts diff --git a/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts b/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts new file mode 100644 index 000000000..a9c9a75ae --- /dev/null +++ b/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts @@ -0,0 +1,70 @@ +import { Component, Input } from '@angular/core'; + +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { TitleCasePipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; + +@Component({ + selector: 'app-person-list', + imports: [ + FormsModule, + MatListModule, + MatFormFieldModule, + MatInputModule, + MatChipsModule, + CDFlashingDirective, + TitleCasePipe, + ], + template: ` +

+ {{ title | titlecase }} +

+ + + + + + + @if (names?.length === 0) { +
Empty list
+ } + @for (name of names; track name) { + +
+

+ {{ name }} +

+
+
+ } + @if (names?.length !== 0) { + + } +
+ `, + host: { + class: 'w-full flex flex-col items-center', + }, +}) +export class PersonListComponent { + @Input() names: string[] = []; + @Input() title = ''; + + label = ''; + + handleKey(event: KeyboardEvent) { + if (event.key === 'Enter') { + this.names?.unshift(this.label); + this.label = ''; + } + } +} diff --git a/apps/performance/default-onpush/src/app/random.component.ts b/apps/performance/34-default-vs-onpush/src/app/random.component.ts similarity index 93% rename from apps/performance/default-onpush/src/app/random.component.ts rename to apps/performance/34-default-vs-onpush/src/app/random.component.ts index d46cdd053..71479e28d 100644 --- a/apps/performance/default-onpush/src/app/random.component.ts +++ b/apps/performance/34-default-vs-onpush/src/app/random.component.ts @@ -3,7 +3,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-random', - standalone: true, template: `
I do nothing but I'm here
`, diff --git a/apps/ngrx/notification/src/assets/.gitkeep b/apps/performance/34-default-vs-onpush/src/assets/.gitkeep similarity index 100% rename from apps/ngrx/notification/src/assets/.gitkeep rename to apps/performance/34-default-vs-onpush/src/assets/.gitkeep diff --git a/apps/performance/default-onpush/src/favicon.ico b/apps/performance/34-default-vs-onpush/src/favicon.ico similarity index 100% rename from apps/performance/default-onpush/src/favicon.ico rename to apps/performance/34-default-vs-onpush/src/favicon.ico diff --git a/apps/performance/34-default-vs-onpush/src/index.html b/apps/performance/34-default-vs-onpush/src/index.html new file mode 100644 index 000000000..251489ea3 --- /dev/null +++ b/apps/performance/34-default-vs-onpush/src/index.html @@ -0,0 +1,13 @@ + + + + + performance-default-vs-onpush + + + + + + + + diff --git a/apps/performance/ngfor-optimize/src/main.ts b/apps/performance/34-default-vs-onpush/src/main.ts similarity index 100% rename from apps/performance/ngfor-optimize/src/main.ts rename to apps/performance/34-default-vs-onpush/src/main.ts diff --git a/apps/performance/ngfor-biglist/src/styles.scss b/apps/performance/34-default-vs-onpush/src/styles.scss similarity index 100% rename from apps/performance/ngfor-biglist/src/styles.scss rename to apps/performance/34-default-vs-onpush/src/styles.scss diff --git a/apps/performance/ngfor-biglist/tailwind.config.js b/apps/performance/34-default-vs-onpush/tailwind.config.js similarity index 100% rename from apps/performance/ngfor-biglist/tailwind.config.js rename to apps/performance/34-default-vs-onpush/tailwind.config.js diff --git a/apps/performance/scroll-cd/tsconfig.app.json b/apps/performance/34-default-vs-onpush/tsconfig.app.json similarity index 100% rename from apps/performance/scroll-cd/tsconfig.app.json rename to apps/performance/34-default-vs-onpush/tsconfig.app.json diff --git a/apps/performance/default-onpush/tsconfig.editor.json b/apps/performance/34-default-vs-onpush/tsconfig.editor.json similarity index 100% rename from apps/performance/default-onpush/tsconfig.editor.json rename to apps/performance/34-default-vs-onpush/tsconfig.editor.json diff --git a/apps/performance/christmas-web-worker/tsconfig.json b/apps/performance/34-default-vs-onpush/tsconfig.json similarity index 100% rename from apps/performance/christmas-web-worker/tsconfig.json rename to apps/performance/34-default-vs-onpush/tsconfig.json diff --git a/apps/performance/memoized/.eslintrc.json b/apps/performance/35-memoization/.eslintrc.json similarity index 100% rename from apps/performance/memoized/.eslintrc.json rename to apps/performance/35-memoization/.eslintrc.json diff --git a/apps/performance/35-memoization/README.md b/apps/performance/35-memoization/README.md new file mode 100644 index 000000000..a06a0e91d --- /dev/null +++ b/apps/performance/35-memoization/README.md @@ -0,0 +1,13 @@ +# Memoization + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve performance-memoization +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/35-memoize/). diff --git a/apps/performance/35-memoization/project.json b/apps/performance/35-memoization/project.json new file mode 100644 index 000000000..041308626 --- /dev/null +++ b/apps/performance/35-memoization/project.json @@ -0,0 +1,75 @@ +{ + "name": "performance-memoization", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/performance/35-memoization/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/performance/35-memoization", + "index": "apps/performance/35-memoization/src/index.html", + "main": "apps/performance/35-memoization/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/performance/35-memoization/tsconfig.app.json", + "assets": [ + "apps/performance/35-memoization/src/favicon.ico", + "apps/performance/35-memoization/src/assets" + ], + "styles": [ + "apps/performance/35-memoization/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "performance-memoization:build:production" + }, + "development": { + "buildTarget": "performance-memoization:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "performance-memoization:build" + } + } + } +} diff --git a/apps/performance/35-memoization/src/app/app.component.ts b/apps/performance/35-memoization/src/app/app.component.ts new file mode 100644 index 000000000..44e55db4f --- /dev/null +++ b/apps/performance/35-memoization/src/app/app.component.ts @@ -0,0 +1,24 @@ +import { Component } from '@angular/core'; +import { generateList } from './generateList'; +import { PersonListComponent } from './person-list.component'; + +@Component({ + imports: [PersonListComponent], + selector: 'app-root', + template: ` +

Performance is key!!

+ + + @if (loadList) { + + } + `, +}) +export class AppComponent { + persons = generateList(); + loadList = false; +} diff --git a/apps/performance/memoized/src/app/app.config.ts b/apps/performance/35-memoization/src/app/app.config.ts similarity index 100% rename from apps/performance/memoized/src/app/app.config.ts rename to apps/performance/35-memoization/src/app/app.config.ts diff --git a/apps/performance/memoized/src/app/generateList.ts b/apps/performance/35-memoization/src/app/generateList.ts similarity index 100% rename from apps/performance/memoized/src/app/generateList.ts rename to apps/performance/35-memoization/src/app/generateList.ts diff --git a/apps/performance/35-memoization/src/app/person-list.component.ts b/apps/performance/35-memoization/src/app/person-list.component.ts new file mode 100644 index 000000000..dfa4b8311 --- /dev/null +++ b/apps/performance/35-memoization/src/app/person-list.component.ts @@ -0,0 +1,65 @@ +import { Component, Input } from '@angular/core'; + +import { TitleCasePipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { Person } from './person.model'; + +const fibonacci = (num: number): number => { + if (num === 1 || num === 2) { + return 1; + } + return fibonacci(num - 1) + fibonacci(num - 2); +}; + +@Component({ + selector: 'app-person-list', + imports: [ + FormsModule, + MatListModule, + MatFormFieldModule, + MatInputModule, + MatChipsModule, + TitleCasePipe, + ], + template: ` +

+ {{ title | titlecase }} +

+ + + + + + + @for (person of persons; track person.name) { + +
+

{{ person.name }}

+ {{ calculate(person.fib) }} +
+
+ } +
+ `, + host: { + class: 'w-full flex flex-col items-center', + }, +}) +export class PersonListComponent { + @Input() persons: Person[] = []; + @Input() title = ''; + + label = ''; + + calculate(num: number) { + return fibonacci(num); + } +} diff --git a/apps/performance/memoized/src/app/person.model.ts b/apps/performance/35-memoization/src/app/person.model.ts similarity index 100% rename from apps/performance/memoized/src/app/person.model.ts rename to apps/performance/35-memoization/src/app/person.model.ts diff --git a/apps/nx/static-dynamic-import/src/assets/.gitkeep b/apps/performance/35-memoization/src/assets/.gitkeep similarity index 100% rename from apps/nx/static-dynamic-import/src/assets/.gitkeep rename to apps/performance/35-memoization/src/assets/.gitkeep diff --git a/apps/performance/memoized/src/favicon.ico b/apps/performance/35-memoization/src/favicon.ico similarity index 100% rename from apps/performance/memoized/src/favicon.ico rename to apps/performance/35-memoization/src/favicon.ico diff --git a/apps/performance/35-memoization/src/index.html b/apps/performance/35-memoization/src/index.html new file mode 100644 index 000000000..39ddb9045 --- /dev/null +++ b/apps/performance/35-memoization/src/index.html @@ -0,0 +1,13 @@ + + + + + performance-memoization + + + + + + + + diff --git a/apps/rxjs/catch-error/src/main.ts b/apps/performance/35-memoization/src/main.ts similarity index 100% rename from apps/rxjs/catch-error/src/main.ts rename to apps/performance/35-memoization/src/main.ts diff --git a/apps/performance/ngfor-optimize/src/styles.scss b/apps/performance/35-memoization/src/styles.scss similarity index 100% rename from apps/performance/ngfor-optimize/src/styles.scss rename to apps/performance/35-memoization/src/styles.scss diff --git a/apps/performance/ngfor-optimize/tailwind.config.js b/apps/performance/35-memoization/tailwind.config.js similarity index 100% rename from apps/performance/ngfor-optimize/tailwind.config.js rename to apps/performance/35-memoization/tailwind.config.js diff --git a/apps/rxjs/catch-error/tsconfig.app.json b/apps/performance/35-memoization/tsconfig.app.json similarity index 100% rename from apps/rxjs/catch-error/tsconfig.app.json rename to apps/performance/35-memoization/tsconfig.app.json diff --git a/apps/nx/static-dynamic-import/tsconfig.editor.json b/apps/performance/35-memoization/tsconfig.editor.json similarity index 100% rename from apps/nx/static-dynamic-import/tsconfig.editor.json rename to apps/performance/35-memoization/tsconfig.editor.json diff --git a/apps/performance/default-onpush/tsconfig.json b/apps/performance/35-memoization/tsconfig.json similarity index 100% rename from apps/performance/default-onpush/tsconfig.json rename to apps/performance/35-memoization/tsconfig.json diff --git a/apps/performance/ngfor-biglist/.eslintrc.json b/apps/performance/36-ngfor-optimization/.eslintrc.json similarity index 100% rename from apps/performance/ngfor-biglist/.eslintrc.json rename to apps/performance/36-ngfor-optimization/.eslintrc.json diff --git a/apps/performance/36-ngfor-optimization/README.md b/apps/performance/36-ngfor-optimization/README.md new file mode 100644 index 000000000..2ee0bb20e --- /dev/null +++ b/apps/performance/36-ngfor-optimization/README.md @@ -0,0 +1,13 @@ +# NgFor Optimization + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve performance-ngfor-optimization +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/36-ngfor-optimize/). diff --git a/apps/performance/36-ngfor-optimization/jest.config.ts b/apps/performance/36-ngfor-optimization/jest.config.ts new file mode 100644 index 000000000..2e138df90 --- /dev/null +++ b/apps/performance/36-ngfor-optimization/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'performance-ngfor-optimization', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/performance/36-ngfor-optimization', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/performance/36-ngfor-optimization/project.json b/apps/performance/36-ngfor-optimization/project.json new file mode 100644 index 000000000..05dfcd1e2 --- /dev/null +++ b/apps/performance/36-ngfor-optimization/project.json @@ -0,0 +1,86 @@ +{ + "name": "performance-ngfor-optimization", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/performance/36-ngfor-optimization/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/performance/36-ngfor-optimization", + "index": "apps/performance/36-ngfor-optimization/src/index.html", + "main": "apps/performance/36-ngfor-optimization/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/performance/36-ngfor-optimization/tsconfig.app.json", + "assets": [ + "apps/performance/36-ngfor-optimization/src/favicon.ico", + "apps/performance/36-ngfor-optimization/src/assets" + ], + "styles": [ + "apps/performance/36-ngfor-optimization/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "performance-ngfor-optimization:build:production" + }, + "development": { + "buildTarget": "performance-ngfor-optimization:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "performance-ngfor-optimization:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/performance/36-ngfor-optimization/src/app/app.component.ts b/apps/performance/36-ngfor-optimization/src/app/app.component.ts new file mode 100644 index 000000000..f1aa9f133 --- /dev/null +++ b/apps/performance/36-ngfor-optimization/src/app/app.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { PersonService } from './list.service'; +import { PersonListComponent } from './person-list.component'; + +@Component({ + imports: [ + PersonListComponent, + FormsModule, + MatFormFieldModule, + MatInputModule, + ], + providers: [PersonService], + selector: 'app-root', + template: ` +

+ List of Persons +

+ + + + + + + `, + host: { + class: 'flex items-center flex-col gap-5', + }, +}) +export class AppComponent implements OnInit { + readonly personService = inject(PersonService); + readonly persons = this.personService.persons; + + label = ''; + + ngOnInit(): void { + this.personService.loadPersons(); + } + + handleKey(event: any) { + if (event.keyCode === 13) { + this.personService.addPerson(this.label); + this.label = ''; + } + } +} diff --git a/apps/performance/ngfor-biglist/src/app/app.config.ts b/apps/performance/36-ngfor-optimization/src/app/app.config.ts similarity index 100% rename from apps/performance/ngfor-biglist/src/app/app.config.ts rename to apps/performance/36-ngfor-optimization/src/app/app.config.ts diff --git a/apps/performance/ngfor-optimize/src/app/generateList.ts b/apps/performance/36-ngfor-optimization/src/app/generateList.ts similarity index 100% rename from apps/performance/ngfor-optimize/src/app/generateList.ts rename to apps/performance/36-ngfor-optimization/src/app/generateList.ts diff --git a/apps/performance/ngfor-biglist/src/app/list.service.ts b/apps/performance/36-ngfor-optimization/src/app/list.service.ts similarity index 100% rename from apps/performance/ngfor-biglist/src/app/list.service.ts rename to apps/performance/36-ngfor-optimization/src/app/list.service.ts diff --git a/apps/performance/36-ngfor-optimization/src/app/person-list.component.ts b/apps/performance/36-ngfor-optimization/src/app/person-list.component.ts new file mode 100644 index 000000000..701b0521f --- /dev/null +++ b/apps/performance/36-ngfor-optimization/src/app/person-list.component.ts @@ -0,0 +1,33 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Person } from './person.model'; + +@Component({ + selector: 'app-person-list', + template: ` + @for (person of persons; track person.email) { +
+

{{ person.name }}

+
+ + +
+
+ } + `, + host: { + class: 'w-full flex flex-col', + }, +}) +export class PersonListComponent { + @Input() persons: Person[] = []; + @Output() delete = new EventEmitter(); + @Output() update = new EventEmitter(); +} diff --git a/apps/performance/ngfor-biglist/src/app/person.model.ts b/apps/performance/36-ngfor-optimization/src/app/person.model.ts similarity index 100% rename from apps/performance/ngfor-biglist/src/app/person.model.ts rename to apps/performance/36-ngfor-optimization/src/app/person.model.ts diff --git a/apps/performance/christmas-web-worker/src/assets/.gitkeep b/apps/performance/36-ngfor-optimization/src/assets/.gitkeep similarity index 100% rename from apps/performance/christmas-web-worker/src/assets/.gitkeep rename to apps/performance/36-ngfor-optimization/src/assets/.gitkeep diff --git a/apps/performance/ngfor-biglist/src/favicon.ico b/apps/performance/36-ngfor-optimization/src/favicon.ico similarity index 100% rename from apps/performance/ngfor-biglist/src/favicon.ico rename to apps/performance/36-ngfor-optimization/src/favicon.ico diff --git a/apps/performance/36-ngfor-optimization/src/index.html b/apps/performance/36-ngfor-optimization/src/index.html new file mode 100644 index 000000000..07ec600a8 --- /dev/null +++ b/apps/performance/36-ngfor-optimization/src/index.html @@ -0,0 +1,13 @@ + + + + + performance-ngfor-optimization + + + + + + + + diff --git a/apps/testing/create-harness/src/main.ts b/apps/performance/36-ngfor-optimization/src/main.ts similarity index 100% rename from apps/testing/create-harness/src/main.ts rename to apps/performance/36-ngfor-optimization/src/main.ts diff --git a/apps/rxjs/catch-error/src/styles.scss b/apps/performance/36-ngfor-optimization/src/styles.scss similarity index 100% rename from apps/rxjs/catch-error/src/styles.scss rename to apps/performance/36-ngfor-optimization/src/styles.scss diff --git a/apps/rxjs/catch-error/src/test-setup.ts b/apps/performance/36-ngfor-optimization/src/test-setup.ts similarity index 100% rename from apps/rxjs/catch-error/src/test-setup.ts rename to apps/performance/36-ngfor-optimization/src/test-setup.ts diff --git a/apps/rxjs/catch-error/tailwind.config.js b/apps/performance/36-ngfor-optimization/tailwind.config.js similarity index 100% rename from apps/rxjs/catch-error/tailwind.config.js rename to apps/performance/36-ngfor-optimization/tailwind.config.js diff --git a/apps/testing/checkbox/tsconfig.app.json b/apps/performance/36-ngfor-optimization/tsconfig.app.json similarity index 100% rename from apps/testing/checkbox/tsconfig.app.json rename to apps/performance/36-ngfor-optimization/tsconfig.app.json diff --git a/apps/performance/ngfor-optimize/tsconfig.editor.json b/apps/performance/36-ngfor-optimization/tsconfig.editor.json similarity index 100% rename from apps/performance/ngfor-optimize/tsconfig.editor.json rename to apps/performance/36-ngfor-optimization/tsconfig.editor.json diff --git a/apps/performance/ngfor-optimize/tsconfig.json b/apps/performance/36-ngfor-optimization/tsconfig.json similarity index 100% rename from apps/performance/ngfor-optimize/tsconfig.json rename to apps/performance/36-ngfor-optimization/tsconfig.json diff --git a/apps/performance/36-ngfor-optimization/tsconfig.spec.json b/apps/performance/36-ngfor-optimization/tsconfig.spec.json new file mode 100644 index 000000000..1a4817a7d --- /dev/null +++ b/apps/performance/36-ngfor-optimization/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node", "@testing-library/jest-dom"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/performance/ngfor-optimize/.eslintrc.json b/apps/performance/37-optimize-big-list/.eslintrc.json similarity index 100% rename from apps/performance/ngfor-optimize/.eslintrc.json rename to apps/performance/37-optimize-big-list/.eslintrc.json diff --git a/apps/performance/37-optimize-big-list/README.md b/apps/performance/37-optimize-big-list/README.md new file mode 100644 index 000000000..1d8e134c2 --- /dev/null +++ b/apps/performance/37-optimize-big-list/README.md @@ -0,0 +1,13 @@ +# NgFor optimize big list + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve performance-optimize-big-list +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/37-ngfor-biglist/). diff --git a/apps/performance/37-optimize-big-list/project.json b/apps/performance/37-optimize-big-list/project.json new file mode 100644 index 000000000..fbd03771e --- /dev/null +++ b/apps/performance/37-optimize-big-list/project.json @@ -0,0 +1,75 @@ +{ + "name": "performance-optimize-big-list", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/performance/37-optimize-big-list/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/performance/37-optimize-big-list", + "index": "apps/performance/37-optimize-big-list/src/index.html", + "main": "apps/performance/37-optimize-big-list/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/performance/37-optimize-big-list/tsconfig.app.json", + "assets": [ + "apps/performance/37-optimize-big-list/src/favicon.ico", + "apps/performance/37-optimize-big-list/src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", + "apps/performance/37-optimize-big-list/src/styles.scss" + ], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "performance-optimize-big-list:build:production" + }, + "development": { + "buildTarget": "performance-optimize-big-list:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "performance-optimize-big-list:build" + } + } + } +} diff --git a/apps/performance/37-optimize-big-list/src/app/app.component.ts b/apps/performance/37-optimize-big-list/src/app/app.component.ts new file mode 100644 index 000000000..c441134df --- /dev/null +++ b/apps/performance/37-optimize-big-list/src/app/app.component.ts @@ -0,0 +1,41 @@ +import { NgIf } from '@angular/common'; +import { Component, signal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { generateList } from './generateList'; +import { PersonService } from './list.service'; +import { PersonListComponent } from './person-list.component'; + +@Component({ + imports: [ + NgIf, + PersonListComponent, + FormsModule, + MatFormFieldModule, + MatInputModule, + ], + providers: [PersonService], + selector: 'app-root', + template: ` + + + + `, + host: { + class: 'flex items-center flex-col gap-5', + }, +}) +export class AppComponent { + readonly persons = signal(generateList()); + readonly loadList = signal(false); + + label = ''; +} diff --git a/apps/performance/ngfor-optimize/src/app/app.config.ts b/apps/performance/37-optimize-big-list/src/app/app.config.ts similarity index 100% rename from apps/performance/ngfor-optimize/src/app/app.config.ts rename to apps/performance/37-optimize-big-list/src/app/app.config.ts diff --git a/apps/performance/ngfor-biglist/src/app/generateList.ts b/apps/performance/37-optimize-big-list/src/app/generateList.ts similarity index 100% rename from apps/performance/ngfor-biglist/src/app/generateList.ts rename to apps/performance/37-optimize-big-list/src/app/generateList.ts diff --git a/apps/performance/ngfor-optimize/src/app/list.service.ts b/apps/performance/37-optimize-big-list/src/app/list.service.ts similarity index 100% rename from apps/performance/ngfor-optimize/src/app/list.service.ts rename to apps/performance/37-optimize-big-list/src/app/list.service.ts diff --git a/apps/performance/37-optimize-big-list/src/app/person-list.component.ts b/apps/performance/37-optimize-big-list/src/app/person-list.component.ts new file mode 100644 index 000000000..45d95d658 --- /dev/null +++ b/apps/performance/37-optimize-big-list/src/app/person-list.component.ts @@ -0,0 +1,25 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { Person } from './person.model'; + +@Component({ + selector: 'app-person-list', + template: ` +
+
+ @for (person of persons; track person.email) { +
+

{{ person.name }}

+

{{ person.email }}

+
+ } +
+
+ `, + host: { + class: 'w-full flex flex-col', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PersonListComponent { + @Input() persons: Person[] = []; +} diff --git a/apps/performance/ngfor-optimize/src/app/person.model.ts b/apps/performance/37-optimize-big-list/src/app/person.model.ts similarity index 100% rename from apps/performance/ngfor-optimize/src/app/person.model.ts rename to apps/performance/37-optimize-big-list/src/app/person.model.ts diff --git a/apps/performance/default-onpush/src/assets/.gitkeep b/apps/performance/37-optimize-big-list/src/assets/.gitkeep similarity index 100% rename from apps/performance/default-onpush/src/assets/.gitkeep rename to apps/performance/37-optimize-big-list/src/assets/.gitkeep diff --git a/apps/performance/ngfor-optimize/src/favicon.ico b/apps/performance/37-optimize-big-list/src/favicon.ico similarity index 100% rename from apps/performance/ngfor-optimize/src/favicon.ico rename to apps/performance/37-optimize-big-list/src/favicon.ico diff --git a/apps/performance/37-optimize-big-list/src/index.html b/apps/performance/37-optimize-big-list/src/index.html new file mode 100644 index 000000000..f93d2d26f --- /dev/null +++ b/apps/performance/37-optimize-big-list/src/index.html @@ -0,0 +1,13 @@ + + + + + performance-optimize-big-list + + + + + + + + diff --git a/apps/testing/harness/src/main.ts b/apps/performance/37-optimize-big-list/src/main.ts similarity index 100% rename from apps/testing/harness/src/main.ts rename to apps/performance/37-optimize-big-list/src/main.ts diff --git a/apps/testing/checkbox/src/styles.scss b/apps/performance/37-optimize-big-list/src/styles.scss similarity index 100% rename from apps/testing/checkbox/src/styles.scss rename to apps/performance/37-optimize-big-list/src/styles.scss diff --git a/apps/testing/checkbox/tailwind.config.js b/apps/performance/37-optimize-big-list/tailwind.config.js similarity index 100% rename from apps/testing/checkbox/tailwind.config.js rename to apps/performance/37-optimize-big-list/tailwind.config.js diff --git a/apps/testing/create-harness/tsconfig.app.json b/apps/performance/37-optimize-big-list/tsconfig.app.json similarity index 100% rename from apps/testing/create-harness/tsconfig.app.json rename to apps/performance/37-optimize-big-list/tsconfig.app.json diff --git a/apps/performance/christmas-web-worker/tsconfig.editor.json b/apps/performance/37-optimize-big-list/tsconfig.editor.json similarity index 100% rename from apps/performance/christmas-web-worker/tsconfig.editor.json rename to apps/performance/37-optimize-big-list/tsconfig.editor.json diff --git a/apps/performance/memoized/tsconfig.json b/apps/performance/37-optimize-big-list/tsconfig.json similarity index 100% rename from apps/performance/memoized/tsconfig.json rename to apps/performance/37-optimize-big-list/tsconfig.json diff --git a/apps/performance/christmas-web-worker/.eslintrc.json b/apps/performance/40-web-workers/.eslintrc.json similarity index 100% rename from apps/performance/christmas-web-worker/.eslintrc.json rename to apps/performance/40-web-workers/.eslintrc.json diff --git a/apps/performance/40-web-workers/README.md b/apps/performance/40-web-workers/README.md new file mode 100644 index 000000000..067777a8f --- /dev/null +++ b/apps/performance/40-web-workers/README.md @@ -0,0 +1,13 @@ +# Web workers + +> Author: Thomas Laforge + +### Run Application + +```bash +npx nx serve performance-web-workers +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/40-christmas-web-worker/). diff --git a/apps/performance/40-web-workers/project.json b/apps/performance/40-web-workers/project.json new file mode 100644 index 000000000..843a35c60 --- /dev/null +++ b/apps/performance/40-web-workers/project.json @@ -0,0 +1,69 @@ +{ + "name": "performance-web-workers", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/performance/40-web-workers/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/performance/40-web-workers", + "index": "apps/performance/40-web-workers/src/index.html", + "browser": "apps/performance/40-web-workers/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/performance/40-web-workers/tsconfig.app.json", + "assets": [ + "apps/performance/40-web-workers/src/favicon.ico", + "apps/performance/40-web-workers/src/assets" + ], + "styles": ["apps/performance/40-web-workers/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "performance-web-workers:build:production" + }, + "development": { + "buildTarget": "performance-web-workers:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "performance-web-workers:build" + } + } + } +} diff --git a/apps/performance/40-web-workers/src/app/app.component.ts b/apps/performance/40-web-workers/src/app/app.component.ts new file mode 100644 index 000000000..583572ed2 --- /dev/null +++ b/apps/performance/40-web-workers/src/app/app.component.ts @@ -0,0 +1,31 @@ +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { HeavyCalculationService } from './heavy-calculation.service'; +import { UnknownPersonComponent } from './unknown-person/unknown-person.component'; + +@Component({ + imports: [CommonModule, UnknownPersonComponent], + providers: [HeavyCalculationService], + selector: 'app-root', + template: ` + + +
Progress: {{ loadingPercentage() }}%
+ `, + host: { + class: `flex flex-col h-screen w-screen bg-[#1f75c0]`, + }, +}) +export class AppComponent { + private heavyCalculationService = inject(HeavyCalculationService); + + readonly loadingPercentage = this.heavyCalculationService.loadingPercentage; + + discover() { + this.heavyCalculationService.startLoading(); + } +} diff --git a/apps/performance/christmas-web-worker/src/app/heavy-calculation.service.ts b/apps/performance/40-web-workers/src/app/heavy-calculation.service.ts similarity index 100% rename from apps/performance/christmas-web-worker/src/app/heavy-calculation.service.ts rename to apps/performance/40-web-workers/src/app/heavy-calculation.service.ts diff --git a/apps/performance/christmas-web-worker/src/app/unknown-person/unknown-person.component.css b/apps/performance/40-web-workers/src/app/unknown-person/unknown-person.component.css similarity index 100% rename from apps/performance/christmas-web-worker/src/app/unknown-person/unknown-person.component.css rename to apps/performance/40-web-workers/src/app/unknown-person/unknown-person.component.css diff --git a/apps/performance/christmas-web-worker/src/app/unknown-person/unknown-person.component.ts b/apps/performance/40-web-workers/src/app/unknown-person/unknown-person.component.ts similarity index 100% rename from apps/performance/christmas-web-worker/src/app/unknown-person/unknown-person.component.ts rename to apps/performance/40-web-workers/src/app/unknown-person/unknown-person.component.ts diff --git a/apps/performance/memoized/src/assets/.gitkeep b/apps/performance/40-web-workers/src/assets/.gitkeep similarity index 100% rename from apps/performance/memoized/src/assets/.gitkeep rename to apps/performance/40-web-workers/src/assets/.gitkeep diff --git a/apps/performance/scroll-cd/src/favicon.ico b/apps/performance/40-web-workers/src/favicon.ico similarity index 100% rename from apps/performance/scroll-cd/src/favicon.ico rename to apps/performance/40-web-workers/src/favicon.ico diff --git a/apps/performance/40-web-workers/src/index.html b/apps/performance/40-web-workers/src/index.html new file mode 100644 index 000000000..fa0cf99ae --- /dev/null +++ b/apps/performance/40-web-workers/src/index.html @@ -0,0 +1,13 @@ + + + + + performance-web-worker + + + + + + + + diff --git a/apps/performance/scroll-cd/src/main.ts b/apps/performance/40-web-workers/src/main.ts similarity index 100% rename from apps/performance/scroll-cd/src/main.ts rename to apps/performance/40-web-workers/src/main.ts diff --git a/apps/testing/modal/src/styles.scss b/apps/performance/40-web-workers/src/styles.scss similarity index 100% rename from apps/testing/modal/src/styles.scss rename to apps/performance/40-web-workers/src/styles.scss diff --git a/apps/testing/create-harness/tailwind.config.js b/apps/performance/40-web-workers/tailwind.config.js similarity index 100% rename from apps/testing/create-harness/tailwind.config.js rename to apps/performance/40-web-workers/tailwind.config.js diff --git a/apps/testing/harness/tsconfig.app.json b/apps/performance/40-web-workers/tsconfig.app.json similarity index 100% rename from apps/testing/harness/tsconfig.app.json rename to apps/performance/40-web-workers/tsconfig.app.json diff --git a/apps/performance/memoized/tsconfig.editor.json b/apps/performance/40-web-workers/tsconfig.editor.json similarity index 100% rename from apps/performance/memoized/tsconfig.editor.json rename to apps/performance/40-web-workers/tsconfig.editor.json diff --git a/apps/performance/ngfor-biglist/tsconfig.json b/apps/performance/40-web-workers/tsconfig.json similarity index 100% rename from apps/performance/ngfor-biglist/tsconfig.json rename to apps/performance/40-web-workers/tsconfig.json diff --git a/apps/performance/christmas-web-worker/README.md b/apps/performance/christmas-web-worker/README.md deleted file mode 100644 index 6da93949a..000000000 --- a/apps/performance/christmas-web-worker/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Web workers - -> Author: Thomas Laforge - -### Run Application - -```bash -npx nx serve performance-christmas-web-worker -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/40-christmas-web-worker/). diff --git a/apps/performance/christmas-web-worker/project.json b/apps/performance/christmas-web-worker/project.json deleted file mode 100644 index 13b7edd6b..000000000 --- a/apps/performance/christmas-web-worker/project.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "performance-christmas-web-worker", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/performance/christmas-web-worker/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/performance/christmas-web-worker", - "index": "apps/performance/christmas-web-worker/src/index.html", - "browser": "apps/performance/christmas-web-worker/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/performance/christmas-web-worker/tsconfig.app.json", - "assets": [ - "apps/performance/christmas-web-worker/src/favicon.ico", - "apps/performance/christmas-web-worker/src/assets" - ], - "styles": ["apps/performance/christmas-web-worker/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "performance-christmas-web-worker:build:production" - }, - "development": { - "buildTarget": "performance-christmas-web-worker:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "performance-christmas-web-worker:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/performance/christmas-web-worker/src/app/app.component.ts b/apps/performance/christmas-web-worker/src/app/app.component.ts deleted file mode 100644 index 6b9846b13..000000000 --- a/apps/performance/christmas-web-worker/src/app/app.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { HeavyCalculationService } from './heavy-calculation.service'; -import { UnknownPersonComponent } from './unknown-person/unknown-person.component'; - -@Component({ - standalone: true, - imports: [CommonModule, UnknownPersonComponent], - providers: [HeavyCalculationService], - selector: 'app-root', - template: ` - - -
Progress: {{ loadingPercentage() }}%
- `, - host: { - class: `flex flex-col h-screen w-screen bg-[#1f75c0]`, - }, -}) -export class AppComponent { - private heavyCalculationService = inject(HeavyCalculationService); - - readonly loadingPercentage = this.heavyCalculationService.loadingPercentage; - - discover() { - this.heavyCalculationService.startLoading(); - } -} diff --git a/apps/performance/christmas-web-worker/src/index.html b/apps/performance/christmas-web-worker/src/index.html deleted file mode 100644 index 4ca378971..000000000 --- a/apps/performance/christmas-web-worker/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - performance-christmas-web-worker - - - - - - - - diff --git a/apps/performance/default-onpush/README.md b/apps/performance/default-onpush/README.md deleted file mode 100644 index 29d7696ff..000000000 --- a/apps/performance/default-onpush/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Default vs OnPush - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve performance-default-onpush -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/34-default-onpush/). diff --git a/apps/performance/default-onpush/project.json b/apps/performance/default-onpush/project.json deleted file mode 100644 index 14faaabbd..000000000 --- a/apps/performance/default-onpush/project.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "performance-default-onpush", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/performance/default-onpush/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/performance/default-onpush", - "index": "apps/performance/default-onpush/src/index.html", - "main": "apps/performance/default-onpush/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/performance/default-onpush/tsconfig.app.json", - "assets": [ - "apps/performance/default-onpush/src/favicon.ico", - "apps/performance/default-onpush/src/assets" - ], - "styles": [ - "apps/performance/default-onpush/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "performance-default-onpush:build:production" - }, - "development": { - "buildTarget": "performance-default-onpush:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "performance-default-onpush:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/performance/default-onpush/src/app/app.component.ts b/apps/performance/default-onpush/src/app/app.component.ts deleted file mode 100644 index 1eab3c839..000000000 --- a/apps/performance/default-onpush/src/app/app.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component } from '@angular/core'; -import { randFirstName } from '@ngneat/falso'; -import { PersonListComponent } from './person-list.component'; -import { RandomComponent } from './random.component'; - -@Component({ - standalone: true, - imports: [PersonListComponent, RandomComponent], - selector: 'app-root', - template: ` - - -
- - -
- `, -}) -export class AppComponent { - girlList = randFirstName({ gender: 'female', length: 10 }); - boyList = randFirstName({ gender: 'male', length: 10 }); -} diff --git a/apps/performance/default-onpush/src/app/person-list.component.ts b/apps/performance/default-onpush/src/app/person-list.component.ts deleted file mode 100644 index 04574d5c1..000000000 --- a/apps/performance/default-onpush/src/app/person-list.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, Input } from '@angular/core'; - -import { CDFlashingDirective } from '@angular-challenges/shared/directives'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; - -@Component({ - selector: 'app-person-list', - standalone: true, - imports: [ - CommonModule, - FormsModule, - MatListModule, - MatFormFieldModule, - MatInputModule, - MatChipsModule, - CDFlashingDirective, - ], - template: ` -

- {{ title | titlecase }} -

- - - - - - -
Empty list
- -
-

- {{ name }} -

-
-
- -
- `, - host: { - class: 'w-full flex flex-col items-center', - }, -}) -export class PersonListComponent { - @Input() names: string[] = []; - @Input() title = ''; - - label = ''; - - handleKey(event: KeyboardEvent) { - if (event.key === 'Enter') { - this.names?.unshift(this.label); - this.label = ''; - } - } -} diff --git a/apps/performance/default-onpush/src/index.html b/apps/performance/default-onpush/src/index.html deleted file mode 100644 index eecd767f7..000000000 --- a/apps/performance/default-onpush/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Default OnPush - - - - - - - - diff --git a/apps/performance/memoized/README.md b/apps/performance/memoized/README.md deleted file mode 100644 index 520efb1f1..000000000 --- a/apps/performance/memoized/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Memoization - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve performance-memoized -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/35-memoize/). diff --git a/apps/performance/memoized/project.json b/apps/performance/memoized/project.json deleted file mode 100644 index fff792d6a..000000000 --- a/apps/performance/memoized/project.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "performance-memoized", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/performance/memoized/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/performance/memoized", - "index": "apps/performance/memoized/src/index.html", - "main": "apps/performance/memoized/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/performance/memoized/tsconfig.app.json", - "assets": [ - "apps/performance/memoized/src/favicon.ico", - "apps/performance/memoized/src/assets" - ], - "styles": [ - "apps/performance/memoized/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "performance-memoized:build:production" - }, - "development": { - "buildTarget": "performance-memoized:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "performance-memoized:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/performance/memoized/src/app/app.component.ts b/apps/performance/memoized/src/app/app.component.ts deleted file mode 100644 index 4b24fced0..000000000 --- a/apps/performance/memoized/src/app/app.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgIf } from '@angular/common'; -import { Component } from '@angular/core'; -import { generateList } from './generateList'; -import { PersonListComponent } from './person-list.component'; - -@Component({ - standalone: true, - imports: [PersonListComponent, NgIf], - selector: 'app-root', - template: ` -

Performance is key!!

- - - - `, -}) -export class AppComponent { - persons = generateList(); - loadList = false; -} diff --git a/apps/performance/memoized/src/app/person-list.component.ts b/apps/performance/memoized/src/app/person-list.component.ts deleted file mode 100644 index 4a425d4f2..000000000 --- a/apps/performance/memoized/src/app/person-list.component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Component, Input } from '@angular/core'; - -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; -import { Person } from './person.model'; - -const fibonacci = (num: number): number => { - if (num === 1 || num === 2) { - return 1; - } - return fibonacci(num - 1) + fibonacci(num - 2); -}; - -@Component({ - selector: 'app-person-list', - standalone: true, - imports: [ - CommonModule, - FormsModule, - MatListModule, - MatFormFieldModule, - MatInputModule, - MatChipsModule, - ], - template: ` -

- {{ title | titlecase }} -

- - - - - - - -
-

{{ person.name }}

- {{ calculate(person.fib) }} -
-
-
- `, - host: { - class: 'w-full flex flex-col items-center', - }, -}) -export class PersonListComponent { - @Input() persons: Person[] = []; - @Input() title = ''; - - label = ''; - - calculate(num: number) { - return fibonacci(num); - } -} diff --git a/apps/performance/memoized/src/index.html b/apps/performance/memoized/src/index.html deleted file mode 100644 index 1c07d985d..000000000 --- a/apps/performance/memoized/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - performance-memoized - - - - - - - - diff --git a/apps/performance/ngfor-biglist/README.md b/apps/performance/ngfor-biglist/README.md deleted file mode 100644 index 4d164f0c9..000000000 --- a/apps/performance/ngfor-biglist/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# NgFor optimize big list - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve performance-ngfor-biglist -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/37-ngfor-biglist/). diff --git a/apps/performance/ngfor-biglist/project.json b/apps/performance/ngfor-biglist/project.json deleted file mode 100644 index f6701da1b..000000000 --- a/apps/performance/ngfor-biglist/project.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "performance-ngfor-biglist", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/performance/ngfor-biglist/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/performance/ngfor-biglist", - "index": "apps/performance/ngfor-biglist/src/index.html", - "main": "apps/performance/ngfor-biglist/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/performance/ngfor-biglist/tsconfig.app.json", - "assets": [ - "apps/performance/ngfor-biglist/src/favicon.ico", - "apps/performance/ngfor-biglist/src/assets" - ], - "styles": [ - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", - "apps/performance/ngfor-biglist/src/styles.scss" - ], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "performance-ngfor-biglist:build:production" - }, - "development": { - "buildTarget": "performance-ngfor-biglist:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "performance-ngfor-biglist:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/performance/ngfor-biglist/src/app/app.component.ts b/apps/performance/ngfor-biglist/src/app/app.component.ts deleted file mode 100644 index 9c26149b2..000000000 --- a/apps/performance/ngfor-biglist/src/app/app.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NgIf } from '@angular/common'; -import { Component, signal } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { generateList } from './generateList'; -import { PersonService } from './list.service'; -import { PersonListComponent } from './person-list.component'; - -@Component({ - standalone: true, - imports: [ - NgIf, - PersonListComponent, - FormsModule, - MatFormFieldModule, - MatInputModule, - ], - providers: [PersonService], - selector: 'app-root', - template: ` - - - - `, - host: { - class: 'flex items-center flex-col gap-5', - }, -}) -export class AppComponent { - readonly persons = signal(generateList()); - readonly loadList = signal(false); - - label = ''; -} diff --git a/apps/performance/ngfor-biglist/src/app/person-list.component.ts b/apps/performance/ngfor-biglist/src/app/person-list.component.ts deleted file mode 100644 index 4d438e664..000000000 --- a/apps/performance/ngfor-biglist/src/app/person-list.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; - -import { NgForTrackByModule } from '@angular-challenges/shared/directives'; -import { CommonModule } from '@angular/common'; -import { Person } from './person.model'; - -@Component({ - selector: 'app-person-list', - standalone: true, - imports: [CommonModule, NgForTrackByModule], - template: ` -
-
-
-

{{ person.name }}

-

{{ person.email }}

-
-
-
- `, - host: { - class: 'w-full flex flex-col', - }, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class PersonListComponent { - @Input() persons: Person[] = []; -} diff --git a/apps/performance/ngfor-biglist/src/index.html b/apps/performance/ngfor-biglist/src/index.html deleted file mode 100644 index 1b058276a..000000000 --- a/apps/performance/ngfor-biglist/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - performance-ngfor-biglist - - - - - - - - diff --git a/apps/performance/ngfor-optimize/README.md b/apps/performance/ngfor-optimize/README.md deleted file mode 100644 index 1e9b2f833..000000000 --- a/apps/performance/ngfor-optimize/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# NgFor Optimization - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve performance-ngfor-optimize -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/36-ngfor-optimize/). diff --git a/apps/performance/ngfor-optimize/jest.config.ts b/apps/performance/ngfor-optimize/jest.config.ts deleted file mode 100644 index 5b2ccaee0..000000000 --- a/apps/performance/ngfor-optimize/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'performance-ngfor-optimize', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/performance/ngfor-optimize', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/performance/ngfor-optimize/project.json b/apps/performance/ngfor-optimize/project.json deleted file mode 100644 index 20ac3a630..000000000 --- a/apps/performance/ngfor-optimize/project.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "name": "performance-ngfor-optimize", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/performance/ngfor-optimize/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/performance/ngfor-optimize", - "index": "apps/performance/ngfor-optimize/src/index.html", - "main": "apps/performance/ngfor-optimize/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/performance/ngfor-optimize/tsconfig.app.json", - "assets": [ - "apps/performance/ngfor-optimize/src/favicon.ico", - "apps/performance/ngfor-optimize/src/assets" - ], - "styles": [ - "apps/performance/ngfor-optimize/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "performance-ngfor-optimize:build:production" - }, - "development": { - "buildTarget": "performance-ngfor-optimize:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "performance-ngfor-optimize:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/performance/ngfor-optimize/jest.config.ts" - } - } - } -} diff --git a/apps/performance/ngfor-optimize/src/app/app.component.ts b/apps/performance/ngfor-optimize/src/app/app.component.ts deleted file mode 100644 index fb9e98516..000000000 --- a/apps/performance/ngfor-optimize/src/app/app.component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Component, OnInit, inject } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { PersonService } from './list.service'; -import { PersonListComponent } from './person-list.component'; - -@Component({ - standalone: true, - imports: [ - PersonListComponent, - FormsModule, - MatFormFieldModule, - MatInputModule, - ], - providers: [PersonService], - selector: 'app-root', - template: ` -

- List of Persons -

- - - - - - - `, - host: { - class: 'flex items-center flex-col gap-5', - }, -}) -export class AppComponent implements OnInit { - readonly personService = inject(PersonService); - readonly persons = this.personService.persons; - - label = ''; - - ngOnInit(): void { - this.personService.loadPersons(); - } - - handleKey(event: any) { - if (event.keyCode === 13) { - this.personService.addPerson(this.label); - this.label = ''; - } - } -} diff --git a/apps/performance/ngfor-optimize/src/app/person-list.component.ts b/apps/performance/ngfor-optimize/src/app/person-list.component.ts deleted file mode 100644 index ca04956d0..000000000 --- a/apps/performance/ngfor-optimize/src/app/person-list.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; - -import { CommonModule } from '@angular/common'; -import { Person } from './person.model'; - -@Component({ - selector: 'app-person-list', - standalone: true, - imports: [CommonModule], - template: ` -
-

{{ person.name }}

-
- - -
-
- `, - host: { - class: 'w-full flex flex-col', - }, -}) -export class PersonListComponent { - @Input() persons: Person[] = []; - @Output() delete = new EventEmitter(); - @Output() update = new EventEmitter(); -} diff --git a/apps/performance/ngfor-optimize/src/index.html b/apps/performance/ngfor-optimize/src/index.html deleted file mode 100644 index e5b79903e..000000000 --- a/apps/performance/ngfor-optimize/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - performance-ngfor-optimize - - - - - - - - diff --git a/apps/performance/scroll-cd/README.md b/apps/performance/scroll-cd/README.md deleted file mode 100644 index dc0c31301..000000000 --- a/apps/performance/scroll-cd/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Optimize Change Detection - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve performance-scroll-cd -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/performance/12-performance-scroll-cd/). diff --git a/apps/performance/scroll-cd/jest.config.ts b/apps/performance/scroll-cd/jest.config.ts deleted file mode 100644 index 4c9105010..000000000 --- a/apps/performance/scroll-cd/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'performance-scroll-cd', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../../coverage/apps/performance/scroll-cd', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/performance/scroll-cd/project.json b/apps/performance/scroll-cd/project.json deleted file mode 100644 index fb455b73e..000000000 --- a/apps/performance/scroll-cd/project.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "performance-scroll-cd", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/performance/scroll-cd/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/performance/scroll-cd", - "index": "apps/performance/scroll-cd/src/index.html", - "main": "apps/performance/scroll-cd/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/performance/scroll-cd/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/performance/scroll-cd/src/favicon.ico", - "apps/performance/scroll-cd/src/assets" - ], - "styles": ["apps/performance/scroll-cd/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "performance-scroll-cd:build:production" - }, - "development": { - "buildTarget": "performance-scroll-cd:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "performance-scroll-cd:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/performance/scroll-cd/jest.config.ts" - } - } - }, - "tags": [] -} diff --git a/apps/performance/scroll-cd/src/app/app.component.ts b/apps/performance/scroll-cd/src/app/app.component.ts deleted file mode 100644 index e75ef991e..000000000 --- a/apps/performance/scroll-cd/src/app/app.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { AsyncPipe, NgIf } from '@angular/common'; -import { Component, HostListener } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; - -@Component({ - standalone: true, - imports: [NgIf, AsyncPipe], - selector: 'app-root', - template: ` -
Top
-
Middle
-
Bottom
- - `, - styles: [ - ` - :host { - height: 1500px; - display: flex; - flex-direction: column; - justify-content: space-between; - - button { - position: fixed; - bottom: 1rem; - left: 1rem; - z-index: 1; - padding: 1rem; - } - } - `, - ], -}) -export class AppComponent { - title = 'scroll-cd'; - - private displayButtonSubject = new BehaviorSubject(false); - displayButton$ = this.displayButtonSubject.asObservable(); - - @HostListener('window:scroll', ['$event']) - onScroll() { - const pos = window.pageYOffset; - this.displayButtonSubject.next(pos > 50); - } - - goToTop() { - window.scroll({ - top: 0, - left: 0, - behavior: 'smooth', - }); - } -} diff --git a/apps/performance/scroll-cd/src/index.html b/apps/performance/scroll-cd/src/index.html deleted file mode 100644 index 1d22cac27..000000000 --- a/apps/performance/scroll-cd/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - ScrollCd - - - - - - - - diff --git a/apps/typescript/enums-vs-union-types/.eslintrc.json b/apps/rxjs/11-high-order-operator-bug/.eslintrc.json similarity index 100% rename from apps/typescript/enums-vs-union-types/.eslintrc.json rename to apps/rxjs/11-high-order-operator-bug/.eslintrc.json diff --git a/apps/rxjs/11-high-order-operator-bug/README.md b/apps/rxjs/11-high-order-operator-bug/README.md new file mode 100644 index 000000000..5a4fa5512 --- /dev/null +++ b/apps/rxjs/11-high-order-operator-bug/README.md @@ -0,0 +1,13 @@ +# High Order Operator Bug + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve rxjs-high-order-operator-bug +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/rxjs/11-bug-chaining-operator/). diff --git a/apps/rxjs/11-high-order-operator-bug/project.json b/apps/rxjs/11-high-order-operator-bug/project.json new file mode 100644 index 000000000..e8e3f2045 --- /dev/null +++ b/apps/rxjs/11-high-order-operator-bug/project.json @@ -0,0 +1,73 @@ +{ + "name": "rxjs-high-order-operator-bug", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/rxjs/11-high-order-operator-bug/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/rxjs/11-high-order-operator-bug", + "index": "apps/rxjs/11-high-order-operator-bug/src/index.html", + "main": "apps/rxjs/11-high-order-operator-bug/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/rxjs/11-high-order-operator-bug/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/rxjs/11-high-order-operator-bug/src/favicon.ico", + "apps/rxjs/11-high-order-operator-bug/src/assets" + ], + "styles": ["apps/rxjs/11-high-order-operator-bug/src/styles.scss"], + "scripts": [], + "allowedCommonJsDependencies": ["seedrandom"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "rxjs-high-order-operator-bug:build:production" + }, + "development": { + "buildTarget": "rxjs-high-order-operator-bug:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "rxjs-high-order-operator-bug:build" + } + } + } +} diff --git a/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts b/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts new file mode 100644 index 000000000..fb80fb2b6 --- /dev/null +++ b/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts @@ -0,0 +1,52 @@ +/* eslint-disable @angular-eslint/component-selector */ +import { Component, inject, input, signal } from '@angular/core'; +import { take } from 'rxjs'; +import { AppService } from './app.service'; +import { TopicType } from './localDB.service'; + +@Component({ + selector: 'button-delete-topic', + template: ` + +
{{ message() }}
+ `, +}) +export class ButtonDeleteComponent { + readonly topic = input.required(); + + message = signal(''); + + private service = inject(AppService); + + deleteTopic() { + this.service + .deleteOldTopics(this.topic()) + .pipe(take(1)) + .subscribe((result) => + this.message.set( + result + ? `All ${this.topic()} have been deleted` + : `Error: deletion of some ${this.topic()} failed`, + ), + ); + } +} + +@Component({ + imports: [ButtonDeleteComponent], + selector: 'app-root', + template: ` + @for (info of allInfo(); track info.id) { +
{{ info.id }} - {{ info.topic }}
+ } + + Delete Food + Delete Sport + Delete Book + `, +}) +export class AppComponent { + private service = inject(AppService); + + allInfo = this.service.getAllInfo; +} diff --git a/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts b/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts new file mode 100644 index 000000000..df2269a89 --- /dev/null +++ b/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts @@ -0,0 +1,19 @@ +import { inject, Injectable } from '@angular/core'; +import { merge, Observable, of } from 'rxjs'; +import { LocalDBService, TopicType } from './localDB.service'; + +@Injectable({ providedIn: 'root' }) +export class AppService { + private dbService = inject(LocalDBService); + + getAllInfo = this.dbService.infos; + + deleteOldTopics(type: TopicType): Observable { + const infoByType = this.dbService.searchByType(type); + return infoByType.length > 0 + ? infoByType + .map((t) => this.dbService.deleteOneTopic(t.id)) + .reduce((acc, curr) => merge(acc, curr), of(true)) + : of(true); + } +} diff --git a/apps/rxjs/11-high-order-operator-bug/src/app/localDB.service.ts b/apps/rxjs/11-high-order-operator-bug/src/app/localDB.service.ts new file mode 100644 index 000000000..5a035e087 --- /dev/null +++ b/apps/rxjs/11-high-order-operator-bug/src/app/localDB.service.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/member-ordering */ +import { randomError } from '@angular-challenges/shared/utils'; +import { computed, Injectable, signal } from '@angular/core'; +import { of } from 'rxjs'; + +export type TopicType = 'food' | 'book' | 'sport'; + +interface Info { + id: number; + topic: TopicType; +} + +interface DBState { + infos: Info[]; +} + +const initialState: DBState = { + infos: [ + { id: 1, topic: 'book' }, + { id: 2, topic: 'book' }, + { id: 3, topic: 'book' }, + { id: 4, topic: 'book' }, + { id: 5, topic: 'food' }, + { id: 6, topic: 'food' }, + { id: 7, topic: 'book' }, + { id: 8, topic: 'book' }, + { id: 9, topic: 'book' }, + { id: 10, topic: 'sport' }, + ], +}; + +@Injectable({ providedIn: 'root' }) +export class LocalDBService { + private state = signal(initialState); + + infos = computed(() => this.state().infos); + + searchByType = (type: TopicType) => + this.infos().filter((i) => i.topic === type); + + deleteOne = (id: number) => { + this.state.set({ infos: this.state().infos.filter((i) => i.id !== id) }); + }; + + deleteOneTopic = (id: number) => + randomError({ + success: () => { + this.deleteOne(id); + return of(true); + }, + error: () => of(false), + }); +} diff --git a/apps/performance/ngfor-biglist/src/assets/.gitkeep b/apps/rxjs/11-high-order-operator-bug/src/assets/.gitkeep similarity index 100% rename from apps/performance/ngfor-biglist/src/assets/.gitkeep rename to apps/rxjs/11-high-order-operator-bug/src/assets/.gitkeep diff --git a/apps/rxjs/catch-error/src/favicon.ico b/apps/rxjs/11-high-order-operator-bug/src/favicon.ico similarity index 100% rename from apps/rxjs/catch-error/src/favicon.ico rename to apps/rxjs/11-high-order-operator-bug/src/favicon.ico diff --git a/apps/rxjs/11-high-order-operator-bug/src/index.html b/apps/rxjs/11-high-order-operator-bug/src/index.html new file mode 100644 index 000000000..148a16280 --- /dev/null +++ b/apps/rxjs/11-high-order-operator-bug/src/index.html @@ -0,0 +1,13 @@ + + + + + rxjs-high-order-operator-bug + + + + + + + + diff --git a/apps/rxjs/pipe-bug/src/main.ts b/apps/rxjs/11-high-order-operator-bug/src/main.ts similarity index 100% rename from apps/rxjs/pipe-bug/src/main.ts rename to apps/rxjs/11-high-order-operator-bug/src/main.ts diff --git a/apps/ngrx/notification/src/styles.scss b/apps/rxjs/11-high-order-operator-bug/src/styles.scss similarity index 100% rename from apps/ngrx/notification/src/styles.scss rename to apps/rxjs/11-high-order-operator-bug/src/styles.scss diff --git a/apps/rxjs/pipe-bug/tsconfig.app.json b/apps/rxjs/11-high-order-operator-bug/tsconfig.app.json similarity index 100% rename from apps/rxjs/pipe-bug/tsconfig.app.json rename to apps/rxjs/11-high-order-operator-bug/tsconfig.app.json diff --git a/apps/angular/pipe-intermediate/tsconfig.editor.json b/apps/rxjs/11-high-order-operator-bug/tsconfig.editor.json similarity index 100% rename from apps/angular/pipe-intermediate/tsconfig.editor.json rename to apps/rxjs/11-high-order-operator-bug/tsconfig.editor.json diff --git a/apps/rxjs/pipe-bug/tsconfig.json b/apps/rxjs/11-high-order-operator-bug/tsconfig.json similarity index 100% rename from apps/rxjs/pipe-bug/tsconfig.json rename to apps/rxjs/11-high-order-operator-bug/tsconfig.json diff --git a/apps/performance/scroll-cd/.eslintrc.json b/apps/rxjs/14-race-condition/.eslintrc.json similarity index 100% rename from apps/performance/scroll-cd/.eslintrc.json rename to apps/rxjs/14-race-condition/.eslintrc.json diff --git a/apps/rxjs/race-condition/README.md b/apps/rxjs/14-race-condition/README.md similarity index 100% rename from apps/rxjs/race-condition/README.md rename to apps/rxjs/14-race-condition/README.md diff --git a/apps/rxjs/race-condition/cypress.config.ts b/apps/rxjs/14-race-condition/cypress.config.ts similarity index 100% rename from apps/rxjs/race-condition/cypress.config.ts rename to apps/rxjs/14-race-condition/cypress.config.ts diff --git a/apps/rxjs/race-condition/cypress/fixtures/example.json b/apps/rxjs/14-race-condition/cypress/fixtures/example.json similarity index 100% rename from apps/rxjs/race-condition/cypress/fixtures/example.json rename to apps/rxjs/14-race-condition/cypress/fixtures/example.json diff --git a/apps/rxjs/race-condition/cypress/support/commands.ts b/apps/rxjs/14-race-condition/cypress/support/commands.ts similarity index 100% rename from apps/rxjs/race-condition/cypress/support/commands.ts rename to apps/rxjs/14-race-condition/cypress/support/commands.ts diff --git a/apps/rxjs/race-condition/cypress/support/component-index.html b/apps/rxjs/14-race-condition/cypress/support/component-index.html similarity index 100% rename from apps/rxjs/race-condition/cypress/support/component-index.html rename to apps/rxjs/14-race-condition/cypress/support/component-index.html diff --git a/apps/rxjs/race-condition/cypress/support/component.ts b/apps/rxjs/14-race-condition/cypress/support/component.ts similarity index 100% rename from apps/rxjs/race-condition/cypress/support/component.ts rename to apps/rxjs/14-race-condition/cypress/support/component.ts diff --git a/apps/rxjs/race-condition/cypress/tsconfig.json b/apps/rxjs/14-race-condition/cypress/tsconfig.json similarity index 100% rename from apps/rxjs/race-condition/cypress/tsconfig.json rename to apps/rxjs/14-race-condition/cypress/tsconfig.json diff --git a/apps/rxjs/14-race-condition/jest.config.ts b/apps/rxjs/14-race-condition/jest.config.ts new file mode 100644 index 000000000..493fb2452 --- /dev/null +++ b/apps/rxjs/14-race-condition/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'rxjs-race-condition', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/apps/rxjs/14-race-condition', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/rxjs/14-race-condition/project.json b/apps/rxjs/14-race-condition/project.json new file mode 100644 index 000000000..f84ccf155 --- /dev/null +++ b/apps/rxjs/14-race-condition/project.json @@ -0,0 +1,83 @@ +{ + "name": "rxjs-race-condition", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/rxjs/14-race-condition/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/rxjs/14-race-condition", + "index": "apps/rxjs/14-race-condition/src/index.html", + "main": "apps/rxjs/14-race-condition/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/rxjs/14-race-condition/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/rxjs/14-race-condition/src/favicon.ico", + "apps/rxjs/14-race-condition/src/assets" + ], + "styles": ["apps/rxjs/14-race-condition/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "rxjs-race-condition:build:production" + }, + "development": { + "buildTarget": "rxjs-race-condition:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "rxjs-race-condition:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/rxjs/race-condition/src/app/app.component.cy.ts b/apps/rxjs/14-race-condition/src/app/app.component.cy.ts similarity index 100% rename from apps/rxjs/race-condition/src/app/app.component.cy.ts rename to apps/rxjs/14-race-condition/src/app/app.component.cy.ts diff --git a/apps/rxjs/race-condition/src/app/app.component.ts b/apps/rxjs/14-race-condition/src/app/app.component.ts similarity index 100% rename from apps/rxjs/race-condition/src/app/app.component.ts rename to apps/rxjs/14-race-condition/src/app/app.component.ts diff --git a/apps/rxjs/race-condition/src/app/app.config.ts b/apps/rxjs/14-race-condition/src/app/app.config.ts similarity index 100% rename from apps/rxjs/race-condition/src/app/app.config.ts rename to apps/rxjs/14-race-condition/src/app/app.config.ts diff --git a/apps/rxjs/race-condition/src/app/topic-dialog.component.ts b/apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts similarity index 97% rename from apps/rxjs/race-condition/src/app/topic-dialog.component.ts rename to apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts index d63ed4ada..e01a69a01 100644 --- a/apps/rxjs/race-condition/src/app/topic-dialog.component.ts +++ b/apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts @@ -18,7 +18,6 @@ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
`, imports: [MatDialogModule, MatButtonModule, NgFor], - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) export class TopicModalComponent { diff --git a/apps/rxjs/race-condition/src/app/topic.service.ts b/apps/rxjs/14-race-condition/src/app/topic.service.ts similarity index 100% rename from apps/rxjs/race-condition/src/app/topic.service.ts rename to apps/rxjs/14-race-condition/src/app/topic.service.ts diff --git a/apps/performance/ngfor-optimize/src/assets/.gitkeep b/apps/rxjs/14-race-condition/src/assets/.gitkeep similarity index 100% rename from apps/performance/ngfor-optimize/src/assets/.gitkeep rename to apps/rxjs/14-race-condition/src/assets/.gitkeep diff --git a/apps/rxjs/pipe-bug/src/favicon.ico b/apps/rxjs/14-race-condition/src/favicon.ico similarity index 100% rename from apps/rxjs/pipe-bug/src/favicon.ico rename to apps/rxjs/14-race-condition/src/favicon.ico diff --git a/apps/rxjs/14-race-condition/src/index.html b/apps/rxjs/14-race-condition/src/index.html new file mode 100644 index 000000000..629547b5b --- /dev/null +++ b/apps/rxjs/14-race-condition/src/index.html @@ -0,0 +1,13 @@ + + + + + rxjs-race-condition + + + + + + + + diff --git a/apps/rxjs/race-condition/src/main.ts b/apps/rxjs/14-race-condition/src/main.ts similarity index 100% rename from apps/rxjs/race-condition/src/main.ts rename to apps/rxjs/14-race-condition/src/main.ts diff --git a/apps/rxjs/14-race-condition/src/styles.scss b/apps/rxjs/14-race-condition/src/styles.scss new file mode 100644 index 000000000..215c83eb9 --- /dev/null +++ b/apps/rxjs/14-race-condition/src/styles.scss @@ -0,0 +1,25 @@ +/* You can add global styles to this file, and also import other style files */ + +@use '@angular/material' as mat; + +@include mat.elevation-classes(); +@include mat.app-background(); + +$theme-primary: mat.m2-define-palette(mat.$m2-indigo-palette); +$theme-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400); + +$theme-warn: mat.m2-define-palette(mat.$m2-red-palette); + +$theme: mat.m2-define-light-theme( + ( + color: ( + primary: $theme-primary, + accent: $theme-accent, + warn: $theme-warn, + ), + typography: mat.m2-define-typography-config(), + ) +); + +@include mat.dialog-theme($theme); +@include mat.button-theme($theme); diff --git a/apps/performance/scroll-cd/src/test-setup.ts b/apps/rxjs/14-race-condition/src/test-setup.ts similarity index 100% rename from apps/performance/scroll-cd/src/test-setup.ts rename to apps/rxjs/14-race-condition/src/test-setup.ts diff --git a/apps/rxjs/race-condition/tsconfig.app.json b/apps/rxjs/14-race-condition/tsconfig.app.json similarity index 100% rename from apps/rxjs/race-condition/tsconfig.app.json rename to apps/rxjs/14-race-condition/tsconfig.app.json diff --git a/apps/performance/scroll-cd/tsconfig.editor.json b/apps/rxjs/14-race-condition/tsconfig.editor.json similarity index 100% rename from apps/performance/scroll-cd/tsconfig.editor.json rename to apps/rxjs/14-race-condition/tsconfig.editor.json diff --git a/apps/rxjs/14-race-condition/tsconfig.json b/apps/rxjs/14-race-condition/tsconfig.json new file mode 100644 index 000000000..8a0cb05fc --- /dev/null +++ b/apps/rxjs/14-race-condition/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./cypress/tsconfig.base.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/rxjs/race-condition/tsconfig.spec.json b/apps/rxjs/14-race-condition/tsconfig.spec.json similarity index 100% rename from apps/rxjs/race-condition/tsconfig.spec.json rename to apps/rxjs/14-race-condition/tsconfig.spec.json diff --git a/apps/rxjs/catch-error/.eslintrc.json b/apps/rxjs/38-catch-error/.eslintrc.json similarity index 100% rename from apps/rxjs/catch-error/.eslintrc.json rename to apps/rxjs/38-catch-error/.eslintrc.json diff --git a/apps/rxjs/catch-error/README.md b/apps/rxjs/38-catch-error/README.md similarity index 100% rename from apps/rxjs/catch-error/README.md rename to apps/rxjs/38-catch-error/README.md diff --git a/apps/rxjs/38-catch-error/jest.config.ts b/apps/rxjs/38-catch-error/jest.config.ts new file mode 100644 index 000000000..52f04480f --- /dev/null +++ b/apps/rxjs/38-catch-error/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'rxjs-catch-error', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/rxjs/38-catch-error', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/rxjs/38-catch-error/project.json b/apps/rxjs/38-catch-error/project.json new file mode 100644 index 000000000..a655389f3 --- /dev/null +++ b/apps/rxjs/38-catch-error/project.json @@ -0,0 +1,82 @@ +{ + "name": "rxjs-catch-error", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/rxjs/38-catch-error/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/rxjs/38-catch-error", + "index": "apps/rxjs/38-catch-error/src/index.html", + "main": "apps/rxjs/38-catch-error/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/rxjs/38-catch-error/tsconfig.app.json", + "assets": [ + "apps/rxjs/38-catch-error/src/favicon.ico", + "apps/rxjs/38-catch-error/src/assets" + ], + "styles": ["apps/rxjs/38-catch-error/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "rxjs-catch-error:build:production" + }, + "development": { + "buildTarget": "rxjs-catch-error:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "rxjs-catch-error:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/rxjs/catch-error/src/app/app.component.css b/apps/rxjs/38-catch-error/src/app/app.component.css similarity index 100% rename from apps/rxjs/catch-error/src/app/app.component.css rename to apps/rxjs/38-catch-error/src/app/app.component.css diff --git a/apps/rxjs/catch-error/src/app/app.component.spec.ts b/apps/rxjs/38-catch-error/src/app/app.component.spec.ts similarity index 100% rename from apps/rxjs/catch-error/src/app/app.component.spec.ts rename to apps/rxjs/38-catch-error/src/app/app.component.spec.ts diff --git a/apps/rxjs/38-catch-error/src/app/app.component.ts b/apps/rxjs/38-catch-error/src/app/app.component.ts new file mode 100644 index 000000000..65e177567 --- /dev/null +++ b/apps/rxjs/38-catch-error/src/app/app.component.ts @@ -0,0 +1,60 @@ +import { CommonModule } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, DestroyRef, OnInit, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; +import { Subject, concatMap, map } from 'rxjs'; + +@Component({ + imports: [CommonModule, FormsModule], + selector: 'app-root', + template: ` +
+ + possible values: posts, comments, albums, photos, todos, users + +
+
+ + +
+
+ {{ response | json }} +
+ `, + styleUrls: ['./app.component.css'], +}) +export class AppComponent implements OnInit { + submit$ = new Subject(); + input = ''; + response: unknown; + + private destroyRef = inject(DestroyRef); + private http = inject(HttpClient); + + ngOnInit() { + this.submit$ + .pipe( + map(() => this.input), + concatMap((value) => + this.http.get(`https://jsonplaceholder.typicode.com/${value}/1`), + ), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe({ + next: (value) => { + console.log(value); + this.response = value; + }, + error: (error) => { + console.log(error); + this.response = error; + }, + complete: () => console.log('done'), + }); + } +} diff --git a/apps/rxjs/38-catch-error/src/app/app.config.ts b/apps/rxjs/38-catch-error/src/app/app.config.ts new file mode 100644 index 000000000..1c0c9422f --- /dev/null +++ b/apps/rxjs/38-catch-error/src/app/app.config.ts @@ -0,0 +1,6 @@ +import { provideHttpClient } from '@angular/common/http'; +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [provideHttpClient()], +}; diff --git a/apps/performance/scroll-cd/src/assets/.gitkeep b/apps/rxjs/38-catch-error/src/assets/.gitkeep similarity index 100% rename from apps/performance/scroll-cd/src/assets/.gitkeep rename to apps/rxjs/38-catch-error/src/assets/.gitkeep diff --git a/apps/rxjs/race-condition/src/favicon.ico b/apps/rxjs/38-catch-error/src/favicon.ico similarity index 100% rename from apps/rxjs/race-condition/src/favicon.ico rename to apps/rxjs/38-catch-error/src/favicon.ico diff --git a/apps/rxjs/catch-error/src/index.html b/apps/rxjs/38-catch-error/src/index.html similarity index 100% rename from apps/rxjs/catch-error/src/index.html rename to apps/rxjs/38-catch-error/src/index.html diff --git a/apps/typescript/enums-vs-union-types/src/main.ts b/apps/rxjs/38-catch-error/src/main.ts similarity index 100% rename from apps/typescript/enums-vs-union-types/src/main.ts rename to apps/rxjs/38-catch-error/src/main.ts diff --git a/apps/typescript/enums-vs-union-types/src/styles.scss b/apps/rxjs/38-catch-error/src/styles.scss similarity index 100% rename from apps/typescript/enums-vs-union-types/src/styles.scss rename to apps/rxjs/38-catch-error/src/styles.scss diff --git a/apps/testing/checkbox/src/test-setup.ts b/apps/rxjs/38-catch-error/src/test-setup.ts similarity index 100% rename from apps/testing/checkbox/src/test-setup.ts rename to apps/rxjs/38-catch-error/src/test-setup.ts diff --git a/apps/testing/harness/tailwind.config.js b/apps/rxjs/38-catch-error/tailwind.config.js similarity index 100% rename from apps/testing/harness/tailwind.config.js rename to apps/rxjs/38-catch-error/tailwind.config.js diff --git a/apps/typescript/enums-vs-union-types/tsconfig.app.json b/apps/rxjs/38-catch-error/tsconfig.app.json similarity index 100% rename from apps/typescript/enums-vs-union-types/tsconfig.app.json rename to apps/rxjs/38-catch-error/tsconfig.app.json diff --git a/apps/rxjs/catch-error/tsconfig.editor.json b/apps/rxjs/38-catch-error/tsconfig.editor.json similarity index 100% rename from apps/rxjs/catch-error/tsconfig.editor.json rename to apps/rxjs/38-catch-error/tsconfig.editor.json diff --git a/apps/performance/scroll-cd/tsconfig.json b/apps/rxjs/38-catch-error/tsconfig.json similarity index 100% rename from apps/performance/scroll-cd/tsconfig.json rename to apps/rxjs/38-catch-error/tsconfig.json diff --git a/apps/angular/interop-rxjs-signal/tsconfig.spec.json b/apps/rxjs/38-catch-error/tsconfig.spec.json similarity index 100% rename from apps/angular/interop-rxjs-signal/tsconfig.spec.json rename to apps/rxjs/38-catch-error/tsconfig.spec.json diff --git a/apps/rxjs/49-hold-to-save-button/.eslintrc.json b/apps/rxjs/49-hold-to-save-button/.eslintrc.json new file mode 100644 index 000000000..8ebcbfd59 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/rxjs/49-hold-to-save-button/README.md b/apps/rxjs/49-hold-to-save-button/README.md new file mode 100644 index 000000000..e0d4c012b --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/README.md @@ -0,0 +1,13 @@ +# Hold to send button + +> author: alcaidio + +### Run Application + +```bash +npx nx serve rxjs-hold-to-save-button +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/rxjs/49-hold-to-save-btn/). diff --git a/apps/rxjs/49-hold-to-save-button/jest.config.ts b/apps/rxjs/49-hold-to-save-button/jest.config.ts new file mode 100644 index 000000000..1071a5815 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'rxjs-hold-to-save-button', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/rxjs/49-hold-to-save-button', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/rxjs/49-hold-to-save-button/project.json b/apps/rxjs/49-hold-to-save-button/project.json new file mode 100644 index 000000000..7b34db2c9 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/project.json @@ -0,0 +1,80 @@ +{ + "name": "rxjs-hold-to-save-button", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/rxjs/49-hold-to-save-button/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/rxjs/49-hold-to-save-button", + "index": "apps/rxjs/49-hold-to-save-button/src/index.html", + "browser": "apps/rxjs/49-hold-to-save-button/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/rxjs/49-hold-to-save-button/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/rxjs/49-hold-to-save-button/src/favicon.ico", + "apps/rxjs/49-hold-to-save-button/src/assets" + ], + "styles": ["apps/rxjs/49-hold-to-save-button/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "rxjs-hold-to-save-button:build:production" + }, + "development": { + "buildTarget": "rxjs-hold-to-save-button:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "rxjs-hold-to-save-button:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts b/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts new file mode 100644 index 000000000..8f0dbbc70 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts @@ -0,0 +1,25 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + imports: [], + selector: 'app-root', + template: ` +
+
+ + + +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent { + onSend() { + console.log('Save it!'); + } +} diff --git a/apps/angular/simple-animations/src/app/app.config.ts b/apps/rxjs/49-hold-to-save-button/src/app/app.config.ts similarity index 100% rename from apps/angular/simple-animations/src/app/app.config.ts rename to apps/rxjs/49-hold-to-save-button/src/app/app.config.ts diff --git a/apps/rxjs/catch-error/src/assets/.gitkeep b/apps/rxjs/49-hold-to-save-button/src/assets/.gitkeep similarity index 100% rename from apps/rxjs/catch-error/src/assets/.gitkeep rename to apps/rxjs/49-hold-to-save-button/src/assets/.gitkeep diff --git a/apps/testing/checkbox/src/favicon.ico b/apps/rxjs/49-hold-to-save-button/src/favicon.ico similarity index 100% rename from apps/testing/checkbox/src/favicon.ico rename to apps/rxjs/49-hold-to-save-button/src/favicon.ico diff --git a/apps/rxjs/49-hold-to-save-button/src/index.html b/apps/rxjs/49-hold-to-save-button/src/index.html new file mode 100644 index 000000000..3f8e222b1 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/src/index.html @@ -0,0 +1,13 @@ + + + + + rxjs-hold-to-save-button + + + + + + + + diff --git a/apps/rxjs/49-hold-to-save-button/src/main.ts b/apps/rxjs/49-hold-to-save-button/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/rxjs/49-hold-to-save-button/src/styles.scss b/apps/rxjs/49-hold-to-save-button/src/styles.scss new file mode 100644 index 000000000..c98dac907 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/src/styles.scss @@ -0,0 +1,14 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +progress { + @apply h-4 w-full rounded-full bg-gray-200; +} +progress::-webkit-progress-bar { + @apply rounded-full bg-gray-200; +} + +progress::-webkit-progress-value { + @apply rounded-full bg-indigo-600; +} diff --git a/apps/testing/create-harness/src/test-setup.ts b/apps/rxjs/49-hold-to-save-button/src/test-setup.ts similarity index 100% rename from apps/testing/create-harness/src/test-setup.ts rename to apps/rxjs/49-hold-to-save-button/src/test-setup.ts diff --git a/apps/testing/modal/tailwind.config.js b/apps/rxjs/49-hold-to-save-button/tailwind.config.js similarity index 100% rename from apps/testing/modal/tailwind.config.js rename to apps/rxjs/49-hold-to-save-button/tailwind.config.js diff --git a/apps/typescript/overload/tsconfig.app.json b/apps/rxjs/49-hold-to-save-button/tsconfig.app.json similarity index 100% rename from apps/typescript/overload/tsconfig.app.json rename to apps/rxjs/49-hold-to-save-button/tsconfig.app.json diff --git a/apps/rxjs/49-hold-to-save-button/tsconfig.editor.json b/apps/rxjs/49-hold-to-save-button/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/rxjs/49-hold-to-save-button/tsconfig.json b/apps/rxjs/49-hold-to-save-button/tsconfig.json new file mode 100644 index 000000000..4383e7eb8 --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/rxjs/49-hold-to-save-button/tsconfig.spec.json b/apps/rxjs/49-hold-to-save-button/tsconfig.spec.json new file mode 100644 index 000000000..1a4817a7d --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node", "@testing-library/jest-dom"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/rxjs/catch-error/jest.config.ts b/apps/rxjs/catch-error/jest.config.ts deleted file mode 100644 index f82342048..000000000 --- a/apps/rxjs/catch-error/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'rxjs-catch-error', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/rxjs/catch-error', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/rxjs/catch-error/project.json b/apps/rxjs/catch-error/project.json deleted file mode 100644 index 6776c69fb..000000000 --- a/apps/rxjs/catch-error/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "rxjs-catch-error", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/rxjs/catch-error/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/rxjs/catch-error", - "index": "apps/rxjs/catch-error/src/index.html", - "main": "apps/rxjs/catch-error/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/rxjs/catch-error/tsconfig.app.json", - "assets": [ - "apps/rxjs/catch-error/src/favicon.ico", - "apps/rxjs/catch-error/src/assets" - ], - "styles": ["apps/rxjs/catch-error/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "rxjs-catch-error:build:production" - }, - "development": { - "buildTarget": "rxjs-catch-error:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "rxjs-catch-error:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/rxjs/catch-error/jest.config.ts" - } - } - } -} diff --git a/apps/rxjs/catch-error/src/app/app.component.ts b/apps/rxjs/catch-error/src/app/app.component.ts deleted file mode 100644 index b6ac06679..000000000 --- a/apps/rxjs/catch-error/src/app/app.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { HttpClient } from '@angular/common/http'; -import { Component, DestroyRef, OnInit, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormsModule } from '@angular/forms'; -import { Subject, concatMap, map } from 'rxjs'; - -@Component({ - standalone: true, - imports: [CommonModule, FormsModule], - selector: 'app-root', - template: ` -
- - possible values: posts, comments, albums, photos, todos, users - -
-
- - -
-
- {{ response | json }} -
- `, - styleUrls: ['./app.component.css'], -}) -export class AppComponent implements OnInit { - submit$ = new Subject(); - input = ''; - response: unknown; - - private destroyRef = inject(DestroyRef); - private http = inject(HttpClient); - - ngOnInit() { - this.submit$ - .pipe( - map(() => this.input), - concatMap((value) => - this.http.get(`https://jsonplaceholder.typicode.com/${value}/1`), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe({ - next: (value) => { - console.log(value); - this.response = value; - }, - error: (error) => { - console.log(error); - this.response = error; - }, - complete: () => console.log('done'), - }); - } -} diff --git a/apps/rxjs/catch-error/src/app/app.config.ts b/apps/rxjs/catch-error/src/app/app.config.ts deleted file mode 100644 index c5abc2a63..000000000 --- a/apps/rxjs/catch-error/src/app/app.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { HttpClientModule } from '@angular/common/http'; -import { ApplicationConfig, importProvidersFrom } from '@angular/core'; - -export const appConfig: ApplicationConfig = { - providers: [importProvidersFrom(HttpClientModule)], -}; diff --git a/apps/rxjs/pipe-bug/README.md b/apps/rxjs/pipe-bug/README.md deleted file mode 100644 index cad896a79..000000000 --- a/apps/rxjs/pipe-bug/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# High Order Operator Bug - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve rxjs-pipe-bug -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/rxjs/11-bug-chaining-operator/). diff --git a/apps/rxjs/pipe-bug/project.json b/apps/rxjs/pipe-bug/project.json deleted file mode 100644 index 44f1f2999..000000000 --- a/apps/rxjs/pipe-bug/project.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "name": "rxjs-pipe-bug", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/rxjs/pipe-bug/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/rxjs/pipe-bug", - "index": "apps/rxjs/pipe-bug/src/index.html", - "main": "apps/rxjs/pipe-bug/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/rxjs/pipe-bug/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/rxjs/pipe-bug/src/favicon.ico", - "apps/rxjs/pipe-bug/src/assets" - ], - "styles": ["apps/rxjs/pipe-bug/src/styles.scss"], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "rxjs-pipe-bug:build:production" - }, - "development": { - "buildTarget": "rxjs-pipe-bug:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "rxjs-pipe-bug:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/rxjs/pipe-bug/src/app/app.component.ts b/apps/rxjs/pipe-bug/src/app/app.component.ts deleted file mode 100644 index f22b16749..000000000 --- a/apps/rxjs/pipe-bug/src/app/app.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* eslint-disable @angular-eslint/component-selector */ -import { AsyncPipe, NgFor } from '@angular/common'; -import { Component, Input, inject } from '@angular/core'; -import { BehaviorSubject, take } from 'rxjs'; -import { AppService } from './app.service'; -import { TopicType } from './localDB.service'; - -@Component({ - standalone: true, - selector: 'button-delete-topic', - imports: [AsyncPipe], - template: ` - -
{{ message$$ | async }}
- `, -}) -export class ButtonDeleteComponent { - @Input() topic!: TopicType; - - message$$ = new BehaviorSubject(''); - - private service = inject(AppService); - - deleteTopic() { - this.service - .deleteOldTopics(this.topic) - .pipe(take(1)) - .subscribe((result) => - this.message$$.next( - result - ? `All ${this.topic} have been deleted` - : `Error: deletion of some ${this.topic} failed`, - ), - ); - } -} - -@Component({ - standalone: true, - imports: [AsyncPipe, NgFor, ButtonDeleteComponent], - selector: 'app-root', - template: ` -
- {{ item.id }} - {{ item.topic }} -
- - Delete Food - Delete Sport - Delete Book - `, -}) -export class AppComponent { - private service = inject(AppService); - - all$ = this.service.getAll$; -} diff --git a/apps/rxjs/pipe-bug/src/app/app.service.ts b/apps/rxjs/pipe-bug/src/app/app.service.ts deleted file mode 100644 index 7c8173514..000000000 --- a/apps/rxjs/pipe-bug/src/app/app.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { inject, Injectable } from '@angular/core'; -import { merge, mergeMap, Observable, of, take } from 'rxjs'; -import { LocalDBService, TopicType } from './localDB.service'; - -@Injectable({ providedIn: 'root' }) -export class AppService { - private dbService = inject(LocalDBService); - - getAll$ = this.dbService.infos$; - - deleteOldTopics(type: TopicType): Observable { - return this.dbService.searchByType(type).pipe( - take(1), - mergeMap((topicToDelete) => - topicToDelete.length > 0 - ? topicToDelete - .map((t) => this.dbService.deleteOneTopic(t.id)) - .reduce((acc, curr) => merge(acc, curr), of(true)) - : of(true), - ), - ); - } -} diff --git a/apps/rxjs/pipe-bug/src/app/localDB.service.ts b/apps/rxjs/pipe-bug/src/app/localDB.service.ts deleted file mode 100644 index d13d0ed9c..000000000 --- a/apps/rxjs/pipe-bug/src/app/localDB.service.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/member-ordering */ -import { randomError } from '@angular-challenges/shared/utils'; -import { Injectable } from '@angular/core'; -import { ComponentStore } from '@ngrx/component-store'; -import { of } from 'rxjs'; - -export type TopicType = 'food' | 'book' | 'sport'; - -interface Info { - id: number; - topic: TopicType; -} - -interface DBState { - infos: Info[]; -} - -const initialState: DBState = { - infos: [ - { id: 1, topic: 'book' }, - { id: 2, topic: 'book' }, - { id: 3, topic: 'book' }, - { id: 4, topic: 'book' }, - { id: 5, topic: 'food' }, - { id: 6, topic: 'food' }, - { id: 7, topic: 'book' }, - { id: 8, topic: 'book' }, - { id: 9, topic: 'book' }, - { id: 10, topic: 'sport' }, - ], -}; - -@Injectable({ providedIn: 'root' }) -export class LocalDBService extends ComponentStore { - constructor() { - super(initialState); - } - - infos$ = this.select((state) => state.infos); - - searchByType = (type: TopicType) => - this.select((state) => state.infos.filter((i) => i.topic === type)); - - deleteOne = this.updater( - (state, id: number): DBState => ({ - infos: state.infos.filter((i) => i.id !== id), - }), - ); - - deleteOneTopic = (id: number) => - randomError({ - success: () => { - this.deleteOne(id); - return of(true); - }, - error: () => of(false), - }); -} diff --git a/apps/rxjs/pipe-bug/src/index.html b/apps/rxjs/pipe-bug/src/index.html deleted file mode 100644 index 401ddc0d8..000000000 --- a/apps/rxjs/pipe-bug/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - RxjsPipeBug - - - - - - - - diff --git a/apps/rxjs/pipe-bug/tsconfig.editor.json b/apps/rxjs/pipe-bug/tsconfig.editor.json deleted file mode 100644 index 0f9036b03..000000000 --- a/apps/rxjs/pipe-bug/tsconfig.editor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.ts"], - "compilerOptions": { - "types": [] - } -} diff --git a/apps/rxjs/race-condition/jest.config.ts b/apps/rxjs/race-condition/jest.config.ts deleted file mode 100644 index 2e2e8920b..000000000 --- a/apps/rxjs/race-condition/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'rxjs-race-condition', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../../coverage/apps/rxjs/race-condition', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/rxjs/race-condition/project.json b/apps/rxjs/race-condition/project.json deleted file mode 100644 index 2f91c369f..000000000 --- a/apps/rxjs/race-condition/project.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "name": "rxjs-race-condition", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/rxjs/race-condition/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/rxjs/race-condition", - "index": "apps/rxjs/race-condition/src/index.html", - "main": "apps/rxjs/race-condition/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/rxjs/race-condition/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/rxjs/race-condition/src/favicon.ico", - "apps/rxjs/race-condition/src/assets" - ], - "styles": ["apps/rxjs/race-condition/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "rxjs-race-condition:build:production" - }, - "development": { - "buildTarget": "rxjs-race-condition:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "rxjs-race-condition:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/rxjs/race-condition/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/rxjs/race-condition/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "rxjs-race-condition:build" - } - } - }, - "tags": [] -} diff --git a/apps/rxjs/race-condition/src/index.html b/apps/rxjs/race-condition/src/index.html deleted file mode 100644 index af7595012..000000000 --- a/apps/rxjs/race-condition/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - RxjsRaceCondition - - - - - - - - diff --git a/apps/rxjs/race-condition/src/styles.scss b/apps/rxjs/race-condition/src/styles.scss deleted file mode 100644 index 5f2e97eb4..000000000 --- a/apps/rxjs/race-condition/src/styles.scss +++ /dev/null @@ -1,24 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ - -@use '@angular/material' as mat; - -@include mat.core(); - -$theme-primary: mat.define-palette(mat.$indigo-palette); -$theme-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); - -$theme-warn: mat.define-palette(mat.$red-palette); - -$theme: mat.define-light-theme( - ( - color: ( - primary: $theme-primary, - accent: $theme-accent, - warn: $theme-warn, - ), - typography: mat.define-typography-config(), - ) -); - -@include mat.dialog-theme($theme); -@include mat.button-theme($theme); diff --git a/apps/rxjs/race-condition/src/test-setup.ts b/apps/rxjs/race-condition/src/test-setup.ts deleted file mode 100644 index 1100b3e8a..000000000 --- a/apps/rxjs/race-condition/src/test-setup.ts +++ /dev/null @@ -1 +0,0 @@ -import 'jest-preset-angular/setup-jest'; diff --git a/apps/rxjs/race-condition/.eslintrc.json b/apps/signal/30-interop-rxjs-signal/.eslintrc.json similarity index 100% rename from apps/rxjs/race-condition/.eslintrc.json rename to apps/signal/30-interop-rxjs-signal/.eslintrc.json diff --git a/apps/signal/30-interop-rxjs-signal/README.md b/apps/signal/30-interop-rxjs-signal/README.md new file mode 100644 index 000000000..f726d1b0f --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/README.md @@ -0,0 +1,13 @@ +# Interoperability Rxjs/Signal + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-interop-rxjs-signal +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/30-interop-rxjs-signal/). diff --git a/apps/signal/30-interop-rxjs-signal/jest.config.ts b/apps/signal/30-interop-rxjs-signal/jest.config.ts new file mode 100644 index 000000000..9f2001768 --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'signal-interop-rxjs-signal', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/signal/30-interop-rxjs-signal', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/signal/30-interop-rxjs-signal/project.json b/apps/signal/30-interop-rxjs-signal/project.json new file mode 100644 index 000000000..0407655ee --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/project.json @@ -0,0 +1,85 @@ +{ + "name": "signal-interop-rxjs-signal", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/30-interop-rxjs-signal/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/30-interop-rxjs-signal", + "index": "apps/signal/30-interop-rxjs-signal/src/index.html", + "main": "apps/signal/30-interop-rxjs-signal/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/30-interop-rxjs-signal/tsconfig.app.json", + "assets": [ + "apps/signal/30-interop-rxjs-signal/src/favicon.ico", + "apps/signal/30-interop-rxjs-signal/src/assets" + ], + "styles": [ + "apps/signal/30-interop-rxjs-signal/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-interop-rxjs-signal:build:production" + }, + "development": { + "buildTarget": "signal-interop-rxjs-signal:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-interop-rxjs-signal:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/signal/30-interop-rxjs-signal/src/app/app.component.ts b/apps/signal/30-interop-rxjs-signal/src/app/app.component.ts new file mode 100644 index 000000000..54fa2b85d --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/src/app/app.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet], + selector: 'app-root', + template: ` + + `, + styles: [''], +}) +export class AppComponent {} diff --git a/apps/angular/interop-rxjs-signal/src/app/app.config.ts b/apps/signal/30-interop-rxjs-signal/src/app/app.config.ts similarity index 100% rename from apps/angular/interop-rxjs-signal/src/app/app.config.ts rename to apps/signal/30-interop-rxjs-signal/src/app/app.config.ts diff --git a/apps/signal/30-interop-rxjs-signal/src/app/detail/detail.component.ts b/apps/signal/30-interop-rxjs-signal/src/app/detail/detail.component.ts new file mode 100644 index 000000000..070bf7d7c --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/src/app/detail/detail.component.ts @@ -0,0 +1,44 @@ +import { DatePipe } from '@angular/common'; +import { Component, Input as RouterInput } from '@angular/core'; +import { RouterLink } from '@angular/router'; +import { Photo } from '../photo.model'; + +@Component({ + selector: 'app-photos', + imports: [DatePipe, RouterLink], + template: ` + {{ photo.title }} +

+ Title: + {{ photo.title }} +

+

+ Owner: + {{ photo.ownername }} +

+

+ Date: + {{ photo.datetaken | date }} +

+

+ Tags: + {{ photo.tags }} +

+ + + `, + host: { + class: 'p-5 block', + }, +}) +export default class DetailComponent { + @RouterInput({ + required: true, + transform: (value: string) => JSON.parse(decodeURIComponent(value)), + }) + photo!: Photo; +} diff --git a/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts new file mode 100644 index 000000000..7ba115027 --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts @@ -0,0 +1,128 @@ +import { AsyncPipe } from '@angular/common'; +import { Component, inject, OnInit } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { RouterLinkWithHref } from '@angular/router'; +import { provideComponentStore } from '@ngrx/component-store'; +import { + debounceTime, + distinctUntilChanged, + Observable, + skipWhile, + tap, +} from 'rxjs'; +import { Photo } from '../photo.model'; +import { PhotoStore } from './photos.store'; + +@Component({ + selector: 'app-photos', + imports: [ + ReactiveFormsModule, + MatFormFieldModule, + MatProgressBarModule, + MatInputModule, + RouterLinkWithHref, + AsyncPipe, + ], + template: ` +

Photos

+ + + Search + + + + @let vm = vm$ | async; +
+
+ + + Page :{{ vm.page }} / {{ vm.pages }} +
+ @if (vm.loading) { + + } + @if (vm.photos && vm.photos.length > 0) { +
    + @for ( + photo of vm.photos; + track photo.id; + let i = $index + ) { +
  • + + {{ photo.title }} + +
  • + } +
+ } @else { +
No Photos found. Type a search word.
+ } +
+ {{ vm.error }} +
+
+ `, + providers: [provideComponentStore(PhotoStore)], + host: { + class: 'p-5 block', + }, +}) +export default class PhotosComponent implements OnInit { + store = inject(PhotoStore); + readonly vm$: Observable<{ + photos: Photo[]; + search: string; + page: number; + pages: number; + endOfPage: boolean; + loading: boolean; + error: unknown; + }> = this.store.vm$.pipe( + tap(({ search }) => { + if (!this.formInit) { + this.search.setValue(search); + this.formInit = true; + } + }), + ); + + private formInit = false; + search = new FormControl(); + + ngOnInit(): void { + this.store.search( + this.search.valueChanges.pipe( + skipWhile(() => !this.formInit), + debounceTime(300), + distinctUntilChanged(), + ), + ); + } + + encode(photo: Photo) { + return encodeURIComponent(JSON.stringify(photo)); + } +} diff --git a/apps/angular/interop-rxjs-signal/src/app/list/photos.store.ts b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts similarity index 96% rename from apps/angular/interop-rxjs-signal/src/app/list/photos.store.ts rename to apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts index 01de2d4ec..f1315e87e 100644 --- a/apps/angular/interop-rxjs-signal/src/app/list/photos.store.ts +++ b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts @@ -1,10 +1,10 @@ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { ComponentStore, OnStateInit, OnStoreInit, - tapResponse, } from '@ngrx/component-store'; +import { tapResponse } from '@ngrx/operators'; import { pipe } from 'rxjs'; import { filter, mergeMap, tap } from 'rxjs/operators'; import { Photo } from '../photo.model'; diff --git a/apps/angular/interop-rxjs-signal/src/app/photo.model.ts b/apps/signal/30-interop-rxjs-signal/src/app/photo.model.ts similarity index 100% rename from apps/angular/interop-rxjs-signal/src/app/photo.model.ts rename to apps/signal/30-interop-rxjs-signal/src/app/photo.model.ts diff --git a/apps/angular/interop-rxjs-signal/src/app/photos.service.ts b/apps/signal/30-interop-rxjs-signal/src/app/photos.service.ts similarity index 100% rename from apps/angular/interop-rxjs-signal/src/app/photos.service.ts rename to apps/signal/30-interop-rxjs-signal/src/app/photos.service.ts diff --git a/apps/rxjs/pipe-bug/src/assets/.gitkeep b/apps/signal/30-interop-rxjs-signal/src/assets/.gitkeep similarity index 100% rename from apps/rxjs/pipe-bug/src/assets/.gitkeep rename to apps/signal/30-interop-rxjs-signal/src/assets/.gitkeep diff --git a/apps/testing/create-harness/src/favicon.ico b/apps/signal/30-interop-rxjs-signal/src/favicon.ico similarity index 100% rename from apps/testing/create-harness/src/favicon.ico rename to apps/signal/30-interop-rxjs-signal/src/favicon.ico diff --git a/apps/signal/30-interop-rxjs-signal/src/index.html b/apps/signal/30-interop-rxjs-signal/src/index.html new file mode 100644 index 000000000..c8a8e0de5 --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/src/index.html @@ -0,0 +1,13 @@ + + + + + signal-interop-rxjs-signal + + + + + + + + diff --git a/apps/signal/30-interop-rxjs-signal/src/main.ts b/apps/signal/30-interop-rxjs-signal/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/signal/30-interop-rxjs-signal/src/styles.scss b/apps/signal/30-interop-rxjs-signal/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/testing/harness/src/test-setup.ts b/apps/signal/30-interop-rxjs-signal/src/test-setup.ts similarity index 100% rename from apps/testing/harness/src/test-setup.ts rename to apps/signal/30-interop-rxjs-signal/src/test-setup.ts diff --git a/apps/testing/table/tailwind.config.js b/apps/signal/30-interop-rxjs-signal/tailwind.config.js similarity index 100% rename from apps/testing/table/tailwind.config.js rename to apps/signal/30-interop-rxjs-signal/tailwind.config.js diff --git a/apps/signal/30-interop-rxjs-signal/tsconfig.app.json b/apps/signal/30-interop-rxjs-signal/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/30-interop-rxjs-signal/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/rxjs/race-condition/tsconfig.editor.json b/apps/signal/30-interop-rxjs-signal/tsconfig.editor.json similarity index 100% rename from apps/rxjs/race-condition/tsconfig.editor.json rename to apps/signal/30-interop-rxjs-signal/tsconfig.editor.json diff --git a/apps/rxjs/catch-error/tsconfig.json b/apps/signal/30-interop-rxjs-signal/tsconfig.json similarity index 100% rename from apps/rxjs/catch-error/tsconfig.json rename to apps/signal/30-interop-rxjs-signal/tsconfig.json diff --git a/apps/rxjs/catch-error/tsconfig.spec.json b/apps/signal/30-interop-rxjs-signal/tsconfig.spec.json similarity index 100% rename from apps/rxjs/catch-error/tsconfig.spec.json rename to apps/signal/30-interop-rxjs-signal/tsconfig.spec.json diff --git a/apps/signal/43-signal-input/.eslintrc.json b/apps/signal/43-signal-input/.eslintrc.json new file mode 100644 index 000000000..8ebcbfd59 --- /dev/null +++ b/apps/signal/43-signal-input/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/signal/43-signal-input/README.md b/apps/signal/43-signal-input/README.md new file mode 100644 index 000000000..d82216bfd --- /dev/null +++ b/apps/signal/43-signal-input/README.md @@ -0,0 +1,13 @@ +# Signal Input + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-signal-input +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/signal/43-signal-input). diff --git a/apps/signal/43-signal-input/project.json b/apps/signal/43-signal-input/project.json new file mode 100644 index 000000000..674aed1a3 --- /dev/null +++ b/apps/signal/43-signal-input/project.json @@ -0,0 +1,69 @@ +{ + "name": "signal-signal-input", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/43-signal-input/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/43-signal-input", + "index": "apps/signal/43-signal-input/src/index.html", + "browser": "apps/signal/43-signal-input/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/43-signal-input/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/signal/43-signal-input/src/favicon.ico", + "apps/signal/43-signal-input/src/assets" + ], + "styles": ["apps/signal/43-signal-input/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-signal-input:build:production" + }, + "development": { + "buildTarget": "signal-signal-input:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-signal-input:build" + } + } + } +} diff --git a/apps/signal/43-signal-input/src/app/app.component.ts b/apps/signal/43-signal-input/src/app/app.component.ts new file mode 100644 index 000000000..5c2deb684 --- /dev/null +++ b/apps/signal/43-signal-input/src/app/app.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { UserComponent } from './user.component'; + +@Component({ + imports: [UserComponent], + selector: 'app-root', + template: ` +
+
+ Name: + + @if (showUser && !name.value) { +
name required
+ } +
+
+ LastName: + +
+
+ Age: + +
+ +
+ @if (showUser && !!name.value) { + + } + `, + host: { + class: 'p-10 block flex flex-col gap-10', + }, +}) +export class AppComponent { + showUser = false; +} diff --git a/apps/typescript/enums-vs-union-types/src/app/app.config.ts b/apps/signal/43-signal-input/src/app/app.config.ts similarity index 100% rename from apps/typescript/enums-vs-union-types/src/app/app.config.ts rename to apps/signal/43-signal-input/src/app/app.config.ts diff --git a/apps/angular/signal-input/src/app/user.component.ts b/apps/signal/43-signal-input/src/app/user.component.ts similarity index 98% rename from apps/angular/signal-input/src/app/user.component.ts rename to apps/signal/43-signal-input/src/app/user.component.ts index 082638bf6..908f952c3 100644 --- a/apps/angular/signal-input/src/app/user.component.ts +++ b/apps/signal/43-signal-input/src/app/user.component.ts @@ -16,7 +16,6 @@ const ageToCategory = (age: number): Category => { @Component({ selector: 'app-user', - standalone: true, imports: [TitleCasePipe], template: ` {{ fullName | titlecase }} plays tennis in the {{ category }} category!! diff --git a/apps/rxjs/race-condition/src/assets/.gitkeep b/apps/signal/43-signal-input/src/assets/.gitkeep similarity index 100% rename from apps/rxjs/race-condition/src/assets/.gitkeep rename to apps/signal/43-signal-input/src/assets/.gitkeep diff --git a/apps/testing/harness/src/favicon.ico b/apps/signal/43-signal-input/src/favicon.ico similarity index 100% rename from apps/testing/harness/src/favicon.ico rename to apps/signal/43-signal-input/src/favicon.ico diff --git a/apps/signal/43-signal-input/src/index.html b/apps/signal/43-signal-input/src/index.html new file mode 100644 index 000000000..642a346a4 --- /dev/null +++ b/apps/signal/43-signal-input/src/index.html @@ -0,0 +1,13 @@ + + + + + signal-signal-input + + + + + + + + diff --git a/apps/signal/43-signal-input/src/main.ts b/apps/signal/43-signal-input/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/signal/43-signal-input/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/signal/43-signal-input/src/styles.scss b/apps/signal/43-signal-input/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/signal/43-signal-input/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/testing/todos-list/tailwind.config.js b/apps/signal/43-signal-input/tailwind.config.js similarity index 100% rename from apps/testing/todos-list/tailwind.config.js rename to apps/signal/43-signal-input/tailwind.config.js diff --git a/apps/signal/43-signal-input/tsconfig.app.json b/apps/signal/43-signal-input/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/43-signal-input/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/performance/ngfor-biglist/tsconfig.editor.json b/apps/signal/43-signal-input/tsconfig.editor.json similarity index 100% rename from apps/performance/ngfor-biglist/tsconfig.editor.json rename to apps/signal/43-signal-input/tsconfig.editor.json diff --git a/apps/signal/43-signal-input/tsconfig.json b/apps/signal/43-signal-input/tsconfig.json new file mode 100644 index 000000000..b94f8837d --- /dev/null +++ b/apps/signal/43-signal-input/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/signal/50-bug-in-effect/.eslintrc.json b/apps/signal/50-bug-in-effect/.eslintrc.json new file mode 100644 index 000000000..8ebcbfd59 --- /dev/null +++ b/apps/signal/50-bug-in-effect/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/signal/50-bug-in-effect/README.md b/apps/signal/50-bug-in-effect/README.md new file mode 100644 index 000000000..9e55d151a --- /dev/null +++ b/apps/signal/50-bug-in-effect/README.md @@ -0,0 +1,13 @@ +# Bug in Effect ? + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-bug-in-effect +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/50-bug-effect-signal/). diff --git a/apps/signal/50-bug-in-effect/project.json b/apps/signal/50-bug-in-effect/project.json new file mode 100644 index 000000000..12a9bb47a --- /dev/null +++ b/apps/signal/50-bug-in-effect/project.json @@ -0,0 +1,69 @@ +{ + "name": "signal-bug-in-effect", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/50-bug-in-effect/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/50-bug-in-effect", + "index": "apps/signal/50-bug-in-effect/src/index.html", + "browser": "apps/signal/50-bug-in-effect/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/50-bug-in-effect/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/signal/50-bug-in-effect/src/favicon.ico", + "apps/signal/50-bug-in-effect/src/assets" + ], + "styles": ["apps/signal/50-bug-in-effect/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-bug-in-effect:build:production" + }, + "development": { + "buildTarget": "signal-bug-in-effect:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-bug-in-effect:build" + } + } + } +} diff --git a/apps/signal/50-bug-in-effect/src/app/app.component.ts b/apps/signal/50-bug-in-effect/src/app/app.component.ts new file mode 100644 index 000000000..ec6ba09b0 --- /dev/null +++ b/apps/signal/50-bug-in-effect/src/app/app.component.ts @@ -0,0 +1,52 @@ +import { + ChangeDetectionStrategy, + Component, + effect, + model, +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +@Component({ + imports: [FormsModule], + selector: 'app-root', + template: ` +
+

MacBook

+

1999,99 €

+
+ +
+

Extras:

+ +
+ + +500 GB drive-space +
+
+ + +4 GB RAM +
+
+ + Better GPU +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent { + drive = model(false); + ram = model(false); + gpu = model(false); + + constructor() { + /* + Explain for your junior team mate why this bug occurs ... + */ + effect(() => { + if (this.drive() || this.ram() || this.gpu()) { + alert('Price increased!'); + } + }); + } +} diff --git a/apps/signal/50-bug-in-effect/src/app/app.config.ts b/apps/signal/50-bug-in-effect/src/app/app.config.ts new file mode 100644 index 000000000..81a6edde4 --- /dev/null +++ b/apps/signal/50-bug-in-effect/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [], +}; diff --git a/apps/testing/checkbox/src/assets/.gitkeep b/apps/signal/50-bug-in-effect/src/assets/.gitkeep similarity index 100% rename from apps/testing/checkbox/src/assets/.gitkeep rename to apps/signal/50-bug-in-effect/src/assets/.gitkeep diff --git a/apps/testing/input-output/src/favicon.ico b/apps/signal/50-bug-in-effect/src/favicon.ico similarity index 100% rename from apps/testing/input-output/src/favicon.ico rename to apps/signal/50-bug-in-effect/src/favicon.ico diff --git a/apps/signal/50-bug-in-effect/src/index.html b/apps/signal/50-bug-in-effect/src/index.html new file mode 100644 index 000000000..e8f5b7364 --- /dev/null +++ b/apps/signal/50-bug-in-effect/src/index.html @@ -0,0 +1,13 @@ + + + + + signal-bug-in-effect + + + + + + + + diff --git a/apps/signal/50-bug-in-effect/src/main.ts b/apps/signal/50-bug-in-effect/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/signal/50-bug-in-effect/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/signal/50-bug-in-effect/src/styles.scss b/apps/signal/50-bug-in-effect/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/signal/50-bug-in-effect/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/typescript/enums-vs-union-types/tailwind.config.js b/apps/signal/50-bug-in-effect/tailwind.config.js similarity index 100% rename from apps/typescript/enums-vs-union-types/tailwind.config.js rename to apps/signal/50-bug-in-effect/tailwind.config.js diff --git a/apps/signal/50-bug-in-effect/tsconfig.app.json b/apps/signal/50-bug-in-effect/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/50-bug-in-effect/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/50-bug-in-effect/tsconfig.editor.json b/apps/signal/50-bug-in-effect/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/signal/50-bug-in-effect/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/50-bug-in-effect/tsconfig.json b/apps/signal/50-bug-in-effect/tsconfig.json new file mode 100644 index 000000000..3df17b921 --- /dev/null +++ b/apps/signal/50-bug-in-effect/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/signal/51-function-call-effect/.eslintrc.json b/apps/signal/51-function-call-effect/.eslintrc.json new file mode 100644 index 000000000..8ebcbfd59 --- /dev/null +++ b/apps/signal/51-function-call-effect/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/signal/51-function-call-effect/README.md b/apps/signal/51-function-call-effect/README.md new file mode 100644 index 000000000..6d4543361 --- /dev/null +++ b/apps/signal/51-function-call-effect/README.md @@ -0,0 +1,13 @@ +# Function call in effect + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-function-call-effect +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/51-function-call-effect/). diff --git a/apps/signal/51-function-call-effect/project.json b/apps/signal/51-function-call-effect/project.json new file mode 100644 index 000000000..3b3a4f8ae --- /dev/null +++ b/apps/signal/51-function-call-effect/project.json @@ -0,0 +1,69 @@ +{ + "name": "signal-function-call-effect", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/51-function-call-effect/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/51-function-call-effect", + "index": "apps/signal/51-function-call-effect/src/index.html", + "browser": "apps/signal/51-function-call-effect/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/51-function-call-effect/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/signal/51-function-call-effect/src/favicon.ico", + "apps/signal/51-function-call-effect/src/assets" + ], + "styles": ["apps/signal/51-function-call-effect/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-function-call-effect:build:production" + }, + "development": { + "buildTarget": "signal-function-call-effect:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-function-call-effect:build" + } + } + } +} diff --git a/apps/signal/51-function-call-effect/src/app/action.component.ts b/apps/signal/51-function-call-effect/src/app/action.component.ts new file mode 100644 index 000000000..22e0e7a4f --- /dev/null +++ b/apps/signal/51-function-call-effect/src/app/action.component.ts @@ -0,0 +1,44 @@ +import { + ChangeDetectionStrategy, + Component, + effect, + inject, + signal, +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { UserService } from './user.service'; + +@Component({ + imports: [FormsModule], + selector: 'app-actions', + template: ` +
+ + +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ActionsComponent { + private userService = inject(UserService); + protected action = signal(undefined); + + protected actions = ['Create', 'Read', 'Update', 'Delete']; + + constructor() { + effect(() => { + this.userService.log(this.action() ?? 'No action selected'); + }); + } +} diff --git a/apps/signal/51-function-call-effect/src/app/app.component.ts b/apps/signal/51-function-call-effect/src/app/app.component.ts new file mode 100644 index 000000000..5f9342eda --- /dev/null +++ b/apps/signal/51-function-call-effect/src/app/app.component.ts @@ -0,0 +1,33 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ActionsComponent } from './action.component'; +import { UserService } from './user.service'; + +@Component({ + imports: [FormsModule, ActionsComponent], + selector: 'app-root', + template: ` + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent { + protected userService = inject(UserService); + + protected users = ['Thomas', 'John', 'Alice', 'Bob', 'Charlie', 'David']; +} diff --git a/apps/signal/51-function-call-effect/src/app/app.config.ts b/apps/signal/51-function-call-effect/src/app/app.config.ts new file mode 100644 index 000000000..81a6edde4 --- /dev/null +++ b/apps/signal/51-function-call-effect/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [], +}; diff --git a/apps/signal/51-function-call-effect/src/app/user.service.ts b/apps/signal/51-function-call-effect/src/app/user.service.ts new file mode 100644 index 000000000..b43756b9e --- /dev/null +++ b/apps/signal/51-function-call-effect/src/app/user.service.ts @@ -0,0 +1,10 @@ +import { Injectable, signal } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class UserService { + name = signal('Thomas'); + + log(message: string) { + console.log(`${this.name()}: ${message}`); + } +} diff --git a/apps/testing/create-harness/src/assets/.gitkeep b/apps/signal/51-function-call-effect/src/assets/.gitkeep similarity index 100% rename from apps/testing/create-harness/src/assets/.gitkeep rename to apps/signal/51-function-call-effect/src/assets/.gitkeep diff --git a/apps/testing/modal/src/favicon.ico b/apps/signal/51-function-call-effect/src/favicon.ico similarity index 100% rename from apps/testing/modal/src/favicon.ico rename to apps/signal/51-function-call-effect/src/favicon.ico diff --git a/apps/signal/51-function-call-effect/src/index.html b/apps/signal/51-function-call-effect/src/index.html new file mode 100644 index 000000000..523bcaef2 --- /dev/null +++ b/apps/signal/51-function-call-effect/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-function-call-effect + + + + + + + + diff --git a/apps/signal/51-function-call-effect/src/main.ts b/apps/signal/51-function-call-effect/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/signal/51-function-call-effect/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/signal/51-function-call-effect/src/styles.scss b/apps/signal/51-function-call-effect/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/signal/51-function-call-effect/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/signal/51-function-call-effect/tailwind.config.js b/apps/signal/51-function-call-effect/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/signal/51-function-call-effect/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/signal/51-function-call-effect/tsconfig.app.json b/apps/signal/51-function-call-effect/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/51-function-call-effect/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/51-function-call-effect/tsconfig.editor.json b/apps/signal/51-function-call-effect/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/signal/51-function-call-effect/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/51-function-call-effect/tsconfig.json b/apps/signal/51-function-call-effect/tsconfig.json new file mode 100644 index 000000000..3df17b921 --- /dev/null +++ b/apps/signal/51-function-call-effect/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/signal/53-big-signal-performance/.eslintrc.json b/apps/signal/53-big-signal-performance/.eslintrc.json new file mode 100644 index 000000000..d3cd7997a --- /dev/null +++ b/apps/signal/53-big-signal-performance/.eslintrc.json @@ -0,0 +1,19 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": {} + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/signal/53-big-signal-performance/README.md b/apps/signal/53-big-signal-performance/README.md new file mode 100644 index 000000000..2b1b56672 --- /dev/null +++ b/apps/signal/53-big-signal-performance/README.md @@ -0,0 +1,13 @@ +# Big Signal Performance + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-big-signal-performance +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/signal/52-big-signal-performance/). diff --git a/apps/signal/53-big-signal-performance/project.json b/apps/signal/53-big-signal-performance/project.json new file mode 100644 index 000000000..88363564f --- /dev/null +++ b/apps/signal/53-big-signal-performance/project.json @@ -0,0 +1,69 @@ +{ + "name": "signal-big-signal-performance", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/53-big-signal-performance/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/53-big-signal-performance", + "index": "apps/signal/53-big-signal-performance/src/index.html", + "browser": "apps/signal/53-big-signal-performance/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/53-big-signal-performance/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/signal/53-big-signal-performance/src/favicon.ico", + "apps/signal/53-big-signal-performance/src/assets" + ], + "styles": ["apps/signal/53-big-signal-performance/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-big-signal-performance:build:production" + }, + "development": { + "buildTarget": "signal-big-signal-performance:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-big-signal-performance:build" + } + } + } +} diff --git a/apps/signal/53-big-signal-performance/src/app/address.component.ts b/apps/signal/53-big-signal-performance/src/app/address.component.ts new file mode 100644 index 000000000..f894d697e --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/address.component.ts @@ -0,0 +1,20 @@ +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { UserStore } from './user.service'; + +@Component({ + selector: 'address-user', + template: ` +
+ Address: +
Street: {{ userService.user().address.street }}
+
ZipCode: {{ userService.user().address.zipCode }}
+
City: {{ userService.user().address.city }}
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CDFlashingDirective], +}) +export class AddressComponent { + userService = inject(UserStore); +} diff --git a/apps/signal/53-big-signal-performance/src/app/app.component.ts b/apps/signal/53-big-signal-performance/src/app/app.component.ts new file mode 100644 index 000000000..bf15a5dc2 --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/app.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { AddressComponent } from './address.component'; +import { JobComponent } from './job.component'; +import { NameComponent } from './name.component'; +import { NoteComponent } from './note.component'; +import { UserFormComponent } from './user-form.component'; + +@Component({ + selector: 'app-root', + template: ` + + + + + + `, + styles: [''], + imports: [ + JobComponent, + NameComponent, + AddressComponent, + NoteComponent, + UserFormComponent, + ], +}) +export class AppComponent {} diff --git a/apps/signal/53-big-signal-performance/src/app/app.config.ts b/apps/signal/53-big-signal-performance/src/app/app.config.ts new file mode 100644 index 000000000..81a6edde4 --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [], +}; diff --git a/apps/signal/53-big-signal-performance/src/app/job.component.ts b/apps/signal/53-big-signal-performance/src/app/job.component.ts new file mode 100644 index 000000000..d3186fc9f --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/job.component.ts @@ -0,0 +1,19 @@ +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { UserStore } from './user.service'; + +@Component({ + selector: 'job', + template: ` +
+ Job: +
title: {{ userService.user().title }}
+
salary: {{ userService.user().salary }}
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CDFlashingDirective], +}) +export class JobComponent { + userService = inject(UserStore); +} diff --git a/apps/signal/53-big-signal-performance/src/app/name.component.ts b/apps/signal/53-big-signal-performance/src/app/name.component.ts new file mode 100644 index 000000000..f93b5675a --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/name.component.ts @@ -0,0 +1,17 @@ +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { UserStore } from './user.service'; + +@Component({ + selector: 'name', + template: ` +
+ Name: {{ userService.user().name }} +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CDFlashingDirective], +}) +export class NameComponent { + userService = inject(UserStore); +} diff --git a/apps/signal/53-big-signal-performance/src/app/note.component.ts b/apps/signal/53-big-signal-performance/src/app/note.component.ts new file mode 100644 index 000000000..dd0033962 --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/note.component.ts @@ -0,0 +1,17 @@ +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { UserStore } from './user.service'; + +@Component({ + selector: 'note', + template: ` +
+ Note: {{ userService.user().note }} +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CDFlashingDirective], +}) +export class NoteComponent { + userService = inject(UserStore); +} diff --git a/apps/signal/53-big-signal-performance/src/app/user-form.component.ts b/apps/signal/53-big-signal-performance/src/app/user-form.component.ts new file mode 100644 index 000000000..d0f2164ce --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/user-form.component.ts @@ -0,0 +1,99 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { UserStore } from './user.service'; + +@Component({ + selector: 'user-form', + imports: [ReactiveFormsModule], + template: ` +
+
+ Name: + +
+
+ Address: +
+ street: + +
+
+ zipCode: + +
+
+ city: + +
+
+
+ note: + +
+
+ title: + +
+
+ salary: + +
+ +
+ `, + host: { + class: 'block border border-gray-500 p-4 pt-10 m-4', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UserFormComponent { + userStore = inject(UserStore); + + form = new FormGroup({ + name: new FormControl(this.userStore.user().name, { nonNullable: true }), + street: new FormControl(this.userStore.user().address.street, { + nonNullable: true, + }), + zipCode: new FormControl(this.userStore.user().address.zipCode, { + nonNullable: true, + }), + city: new FormControl(this.userStore.user().address.city, { + nonNullable: true, + }), + note: new FormControl(this.userStore.user().note, { nonNullable: true }), + title: new FormControl(this.userStore.user().title, { nonNullable: true }), + salary: new FormControl(this.userStore.user().salary, { + nonNullable: true, + }), + }); + + submit() { + this.userStore.user.update((u) => ({ + ...u, + name: this.form.getRawValue().name, + address: { + ...u.address, + street: this.form.getRawValue().street, + zipCode: this.form.getRawValue().zipCode, + city: this.form.getRawValue().city, + }, + note: this.form.getRawValue().note, + title: this.form.getRawValue().title, + salary: this.form.getRawValue().salary, + })); + } +} diff --git a/apps/signal/53-big-signal-performance/src/app/user.service.ts b/apps/signal/53-big-signal-performance/src/app/user.service.ts new file mode 100644 index 000000000..4b3b7c512 --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/app/user.service.ts @@ -0,0 +1,16 @@ +import { Injectable, signal } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class UserStore { + user = signal({ + name: 'Bob', + address: { + street: '', + zipCode: '', + city: '', + }, + note: '', + title: '', + salary: 0, + }); +} diff --git a/apps/testing/harness/src/assets/.gitkeep b/apps/signal/53-big-signal-performance/src/assets/.gitkeep similarity index 100% rename from apps/testing/harness/src/assets/.gitkeep rename to apps/signal/53-big-signal-performance/src/assets/.gitkeep diff --git a/apps/testing/nested/src/favicon.ico b/apps/signal/53-big-signal-performance/src/favicon.ico similarity index 100% rename from apps/testing/nested/src/favicon.ico rename to apps/signal/53-big-signal-performance/src/favicon.ico diff --git a/apps/signal/53-big-signal-performance/src/index.html b/apps/signal/53-big-signal-performance/src/index.html new file mode 100644 index 000000000..3f038cc7a --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/index.html @@ -0,0 +1,13 @@ + + + + + signal-big-signal-performance + + + + + + + + diff --git a/apps/signal/53-big-signal-performance/src/main.ts b/apps/signal/53-big-signal-performance/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/signal/53-big-signal-performance/src/styles.scss b/apps/signal/53-big-signal-performance/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/signal/53-big-signal-performance/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/signal/53-big-signal-performance/tailwind.config.js b/apps/signal/53-big-signal-performance/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/signal/53-big-signal-performance/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/signal/53-big-signal-performance/tsconfig.app.json b/apps/signal/53-big-signal-performance/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/53-big-signal-performance/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/53-big-signal-performance/tsconfig.editor.json b/apps/signal/53-big-signal-performance/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/signal/53-big-signal-performance/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/53-big-signal-performance/tsconfig.json b/apps/signal/53-big-signal-performance/tsconfig.json new file mode 100644 index 000000000..3df17b921 --- /dev/null +++ b/apps/signal/53-big-signal-performance/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/signal/54-pipe-observable-to-signal/.eslintrc.json b/apps/signal/54-pipe-observable-to-signal/.eslintrc.json new file mode 100644 index 000000000..d3cd7997a --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/.eslintrc.json @@ -0,0 +1,19 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": {} + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/signal/54-pipe-observable-to-signal/README.md b/apps/signal/54-pipe-observable-to-signal/README.md new file mode 100644 index 000000000..495cd3aa9 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/README.md @@ -0,0 +1,13 @@ +# Pipe Observable to Signal + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-pipe-observable-to-signal +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/signal/54-pipe-observable-to-signal/). diff --git a/apps/signal/54-pipe-observable-to-signal/project.json b/apps/signal/54-pipe-observable-to-signal/project.json new file mode 100644 index 000000000..47ab2b9ae --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/project.json @@ -0,0 +1,69 @@ +{ + "name": "signal-pipe-observable-to-signal", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/54-pipe-observable-to-signal/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/54-pipe-observable-to-signal", + "index": "apps/signal/54-pipe-observable-to-signal/src/index.html", + "browser": "apps/signal/54-pipe-observable-to-signal/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/54-pipe-observable-to-signal/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/signal/54-pipe-observable-to-signal/src/favicon.ico", + "apps/signal/54-pipe-observable-to-signal/src/assets" + ], + "styles": ["apps/signal/54-pipe-observable-to-signal/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-pipe-observable-to-signal:build:production" + }, + "development": { + "buildTarget": "signal-pipe-observable-to-signal:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-pipe-observable-to-signal:build" + } + } + } +} diff --git a/apps/signal/54-pipe-observable-to-signal/src/app/app.component.ts b/apps/signal/54-pipe-observable-to-signal/src/app/app.component.ts new file mode 100644 index 000000000..72fe7106e --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/app/app.component.ts @@ -0,0 +1,28 @@ +import { TableComponent } from '@angular-challenges/shared/ui'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ProductRowComponent } from './product-row.component'; +import { products } from './product.model'; + +@Component({ + imports: [TableComponent, ProductRowComponent], + selector: 'app-root', + template: ` + + + + @for (col of displayedColumns; track col) { + + } + + + + + +
{{ col }}
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent { + products = products; + displayedColumns = ['name', 'priceA', 'priceB', 'priceC']; +} diff --git a/apps/signal/54-pipe-observable-to-signal/src/app/currency.pipe.ts b/apps/signal/54-pipe-observable-to-signal/src/app/currency.pipe.ts new file mode 100644 index 000000000..e3cc993f4 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/app/currency.pipe.ts @@ -0,0 +1,15 @@ +import { inject, Pipe, PipeTransform } from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { CurrencyService } from './currency.service'; + +@Pipe({ + name: 'currency', + standalone: true, +}) +export class CurrencyPipe implements PipeTransform { + currencyService = inject(CurrencyService); + + transform(price: number): Observable { + return this.currencyService.symbol$.pipe(map((s) => `${price}${s}`)); + } +} diff --git a/apps/signal/54-pipe-observable-to-signal/src/app/currency.service.ts b/apps/signal/54-pipe-observable-to-signal/src/app/currency.service.ts new file mode 100644 index 000000000..8ddf570bf --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/app/currency.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, map } from 'rxjs'; + +export interface Currency { + name: string; + code: string; + symbol: string; +} + +export const currency: Currency[] = [ + { name: 'Euro', code: 'EUR', symbol: '€' }, + { name: 'Dollar US', code: 'USD', symbol: 'US$' }, + { name: 'Dollar Australian', code: 'AUD', symbol: 'AU$' }, + { name: 'Pound Sterling', code: 'GBP', symbol: '£' }, + { name: 'Dollar Canadian', code: 'CAD', symbol: 'CAD' }, +]; + +@Injectable() +export class CurrencyService { + private code = new BehaviorSubject('EUR'); + + readonly code$ = this.code.asObservable(); + readonly symbol$ = this.code$.pipe( + map((code) => currency.find((c) => c.code === code)?.symbol ?? code), + ); + + public updateCode(code: string) { + this.code.next(code); + } +} diff --git a/apps/signal/54-pipe-observable-to-signal/src/app/product-row.component.ts b/apps/signal/54-pipe-observable-to-signal/src/app/product-row.component.ts new file mode 100644 index 000000000..7cb3d4e03 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/app/product-row.component.ts @@ -0,0 +1,33 @@ +import { AsyncPipe } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + inject, + Input, +} from '@angular/core'; +import { CurrencyPipe } from './currency.pipe'; +import { CurrencyService } from './currency.service'; +import { Product } from './product.model'; + +@Component({ + selector: 'tr[product-row]', + template: ` + {{ productInfo.name }} + {{ productInfo.priceA | currency | async }} + {{ productInfo.priceB | currency | async }} + {{ productInfo.priceC | currency | async }} + `, + imports: [AsyncPipe, CurrencyPipe], + providers: [CurrencyService], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProductRowComponent { + protected productInfo!: Product; + + @Input({ required: true }) set product(product: Product) { + this.currencyService.updateCode(product.currencyCode); + this.productInfo = product; + } + + currencyService = inject(CurrencyService); +} diff --git a/apps/signal/54-pipe-observable-to-signal/src/app/product.model.ts b/apps/signal/54-pipe-observable-to-signal/src/app/product.model.ts new file mode 100644 index 000000000..174e7dc77 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/app/product.model.ts @@ -0,0 +1,55 @@ +export interface Product { + name: string; + priceA: number; + priceB: number; + priceC: number; + currencyCode: string; +} + +export const products: Product[] = [ + { + name: 'bike', + priceA: 1000, + priceB: 2000, + priceC: 2200, + currencyCode: 'USD', + }, + { name: 'tent', priceA: 112, priceB: 120, priceC: 41, currencyCode: 'EUR' }, + { + name: 'sofa', + priceA: 500, + priceB: 422, + priceC: 5000, + currencyCode: 'EUR', + }, + { + name: 'watch', + priceA: 50, + priceB: 130, + priceC: 150, + currencyCode: 'AUD', + }, + { + name: 'computer', + priceA: 1000, + priceB: 2200, + priceC: 3500, + currencyCode: 'GBP', + }, + { name: 'mug', priceA: 10, priceB: 15, priceC: 20, currencyCode: 'EUR' }, + { + name: 'headset', + priceA: 100, + priceB: 150, + priceC: 220, + currencyCode: 'CAD', + }, + { name: 'cable', priceA: 5, priceB: 10, priceC: 15, currencyCode: 'EUR' }, + { + name: 'table', + priceA: 100, + priceB: 20, + priceC: 500, + currencyCode: 'EUR', + }, +]; diff --git a/apps/testing/input-output/src/assets/.gitkeep b/apps/signal/54-pipe-observable-to-signal/src/assets/.gitkeep similarity index 100% rename from apps/testing/input-output/src/assets/.gitkeep rename to apps/signal/54-pipe-observable-to-signal/src/assets/.gitkeep diff --git a/apps/testing/router-outlet/src/favicon.ico b/apps/signal/54-pipe-observable-to-signal/src/favicon.ico similarity index 100% rename from apps/testing/router-outlet/src/favicon.ico rename to apps/signal/54-pipe-observable-to-signal/src/favicon.ico diff --git a/apps/signal/54-pipe-observable-to-signal/src/index.html b/apps/signal/54-pipe-observable-to-signal/src/index.html new file mode 100644 index 000000000..997f94663 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/index.html @@ -0,0 +1,13 @@ + + + + + signal-pipe-observable-to-signal + + + + + + + + diff --git a/apps/testing/checkbox/src/main.ts b/apps/signal/54-pipe-observable-to-signal/src/main.ts similarity index 100% rename from apps/testing/checkbox/src/main.ts rename to apps/signal/54-pipe-observable-to-signal/src/main.ts diff --git a/apps/signal/54-pipe-observable-to-signal/src/styles.scss b/apps/signal/54-pipe-observable-to-signal/src/styles.scss new file mode 100644 index 000000000..552f97df9 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/src/styles.scss @@ -0,0 +1,26 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ +table { + width: 100%; +} + +table thead > tr > th { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #dee2e6; + border-width: 0 0 1px 0; + font-weight: 700; + color: #343a40; + background: #f8f9fa; + transition: box-shadow 0.2s; +} + +table tbody > tr > td { + text-align: left; + border: 1px solid #dee2e6; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} diff --git a/apps/signal/54-pipe-observable-to-signal/tailwind.config.js b/apps/signal/54-pipe-observable-to-signal/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/signal/54-pipe-observable-to-signal/tsconfig.app.json b/apps/signal/54-pipe-observable-to-signal/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/54-pipe-observable-to-signal/tsconfig.editor.json b/apps/signal/54-pipe-observable-to-signal/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/54-pipe-observable-to-signal/tsconfig.json b/apps/signal/54-pipe-observable-to-signal/tsconfig.json new file mode 100644 index 000000000..3df17b921 --- /dev/null +++ b/apps/signal/54-pipe-observable-to-signal/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/signal/56-forms-and-signal/.eslintrc.json b/apps/signal/56-forms-and-signal/.eslintrc.json new file mode 100644 index 000000000..8ebcbfd59 --- /dev/null +++ b/apps/signal/56-forms-and-signal/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/signal/56-forms-and-signal/README.md b/apps/signal/56-forms-and-signal/README.md new file mode 100644 index 000000000..ba5def1f0 --- /dev/null +++ b/apps/signal/56-forms-and-signal/README.md @@ -0,0 +1,13 @@ +# forms and signal + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve signal-forms-and-signal +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/signal/56-forms-and-signal/). diff --git a/apps/signal/56-forms-and-signal/project.json b/apps/signal/56-forms-and-signal/project.json new file mode 100644 index 000000000..d825b063c --- /dev/null +++ b/apps/signal/56-forms-and-signal/project.json @@ -0,0 +1,71 @@ +{ + "name": "signal-forms-and-signal", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/signal/56-forms-and-signal/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/signal/56-forms-and-signal", + "index": "apps/signal/56-forms-and-signal/src/index.html", + "browser": "apps/signal/56-forms-and-signal/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/signal/56-forms-and-signal/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/signal/56-forms-and-signal/public" + } + ], + "styles": ["apps/signal/56-forms-and-signal/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "signal-forms-and-signal:build:production" + }, + "development": { + "buildTarget": "signal-forms-and-signal:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "signal-forms-and-signal:build" + } + } + } +} diff --git a/apps/testing/table/src/favicon.ico b/apps/signal/56-forms-and-signal/public/favicon.ico similarity index 100% rename from apps/testing/table/src/favicon.ico rename to apps/signal/56-forms-and-signal/public/favicon.ico diff --git a/apps/signal/56-forms-and-signal/src/app/app.component.ts b/apps/signal/56-forms-and-signal/src/app/app.component.ts new file mode 100644 index 000000000..d6690ea9a --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/app.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet], + selector: 'app-root', + template: ` +

Shop

+
+ +
+ `, + host: { + class: 'w-full flex justify-center flex-col items-center p-4 gap-10', + }, +}) +export class AppComponent {} diff --git a/apps/signal/56-forms-and-signal/src/app/app.config.ts b/apps/signal/56-forms-and-signal/src/app/app.config.ts new file mode 100644 index 000000000..9cebc385d --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/app.config.ts @@ -0,0 +1,10 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes, withComponentInputBinding()), + ], +}; diff --git a/apps/signal/56-forms-and-signal/src/app/app.routes.ts b/apps/signal/56-forms-and-signal/src/app/app.routes.ts new file mode 100644 index 000000000..091c56d90 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/app.routes.ts @@ -0,0 +1,29 @@ +import { Routes } from '@angular/router'; + +export const routes: Routes = [ + { + path: '', + redirectTo: 'dashboard', + pathMatch: 'full', + }, + { + path: 'dashboard', + loadComponent: () => import('./dashboard.component'), + }, + { + path: 'order', + loadComponent: () => import('./order.component'), + }, + { + path: 'checkout', + loadComponent: () => import('./checkout.component'), + }, + { + path: 'payment', + loadComponent: () => import('./payment.component'), + }, + { + path: '**', + redirectTo: 'dashboard', + }, +]; diff --git a/apps/signal/56-forms-and-signal/src/app/checkout.component.ts b/apps/signal/56-forms-and-signal/src/app/checkout.component.ts new file mode 100644 index 000000000..f9d831088 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/checkout.component.ts @@ -0,0 +1,55 @@ +import { + ChangeDetectionStrategy, + Component, + computed, + input, +} from '@angular/core'; +import { RouterLink } from '@angular/router'; +import { products } from './products'; + +@Component({ + selector: 'app-dashboard', + imports: [RouterLink], + template: ` +

Checkout

+ +
+
Your order:
+
+ {{ quantity() }} x {{ product()?.name }}: {{ product()?.price }}€ +
+
+ +
Billing Information
+
...
+
...
+
...
+
...
+
...
+ + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export default class DashboardComponent { + quantity = input(1); + productId = input('1'); + + product = computed(() => + products.find((product) => product.id === this.productId()), + ); + totalWithVAT = computed( + () => this.quantity() * (this.product()?.price ?? 0) * 1.21, + ); +} diff --git a/apps/signal/56-forms-and-signal/src/app/dashboard.component.ts b/apps/signal/56-forms-and-signal/src/app/dashboard.component.ts new file mode 100644 index 000000000..d96fedfd7 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/dashboard.component.ts @@ -0,0 +1,30 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink } from '@angular/router'; +import { products } from './products'; + +@Component({ + selector: 'app-dashboard', + imports: [RouterLink], + template: ` +

List of Products

+
    + @for (product of products; track product.id) { +
  • +
    + {{ product.name }} ({{ product.price }}€) + +
    +
  • + } +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export default class DashboardComponent { + products = products; +} diff --git a/apps/signal/56-forms-and-signal/src/app/order.component.ts b/apps/signal/56-forms-and-signal/src/app/order.component.ts new file mode 100644 index 000000000..2b03ba814 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/order.component.ts @@ -0,0 +1,71 @@ +import { + ChangeDetectionStrategy, + Component, + computed, + input, +} from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { products } from './products'; + +@Component({ + selector: 'app-order', + imports: [RouterLink, ReactiveFormsModule], + template: ` +

Order

+
+
+ + +
+
+
SubTotal
+
{{ totalWithoutVat() }} €
+
+
+
VAT (21%)
+
{{ vat() }} €
+
+
+
Total
+
{{ total() }} €
+
+ +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export default class OrderComponent { + form = new FormGroup({ + quantity: new FormControl(1, { nonNullable: true }), + }); + + productId = input('1'); + price = computed( + () => products.find((p) => p.id === this.productId())?.price ?? 0, + ); + quantity = toSignal(this.form.controls.quantity.valueChanges, { + initialValue: this.form.getRawValue().quantity, + }); + totalWithoutVat = computed(() => Number(this.price()) * this.quantity()); + vat = computed(() => this.totalWithoutVat() * 0.21); + total = computed(() => this.totalWithoutVat() + this.vat()); +} diff --git a/apps/signal/56-forms-and-signal/src/app/payment.component.ts b/apps/signal/56-forms-and-signal/src/app/payment.component.ts new file mode 100644 index 000000000..800bd6f36 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/payment.component.ts @@ -0,0 +1,18 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink } from '@angular/router'; + +@Component({ + selector: 'app-dashboard', + imports: [RouterLink], + template: ` +

Payment Success

+ + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export default class DashboardComponent {} diff --git a/apps/signal/56-forms-and-signal/src/app/products.ts b/apps/signal/56-forms-and-signal/src/app/products.ts new file mode 100644 index 000000000..a893a5b54 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/app/products.ts @@ -0,0 +1,17 @@ +export const products = [ + { + id: '1', + name: 'Computer', + price: 2000, + }, + { + id: '2', + name: 'Mouse', + price: 40, + }, + { + id: '3', + name: 'Keyboard', + price: 80, + }, +]; diff --git a/apps/signal/56-forms-and-signal/src/index.html b/apps/signal/56-forms-and-signal/src/index.html new file mode 100644 index 000000000..eca231086 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/index.html @@ -0,0 +1,13 @@ + + + + + signal-forms-and-signal + + + + + + + + diff --git a/apps/signal/56-forms-and-signal/src/main.ts b/apps/signal/56-forms-and-signal/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/signal/56-forms-and-signal/src/styles.scss b/apps/signal/56-forms-and-signal/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/signal/56-forms-and-signal/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/signal/56-forms-and-signal/tailwind.config.js b/apps/signal/56-forms-and-signal/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/signal/56-forms-and-signal/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/signal/56-forms-and-signal/tsconfig.app.json b/apps/signal/56-forms-and-signal/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/signal/56-forms-and-signal/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/56-forms-and-signal/tsconfig.editor.json b/apps/signal/56-forms-and-signal/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/signal/56-forms-and-signal/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/signal/56-forms-and-signal/tsconfig.json b/apps/signal/56-forms-and-signal/tsconfig.json new file mode 100644 index 000000000..3df17b921 --- /dev/null +++ b/apps/signal/56-forms-and-signal/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/testing/checkbox/.eslintrc.json b/apps/testing/17-router/.eslintrc.json similarity index 100% rename from apps/testing/checkbox/.eslintrc.json rename to apps/testing/17-router/.eslintrc.json diff --git a/apps/testing/17-router/README.md b/apps/testing/17-router/README.md new file mode 100644 index 000000000..fe9a75448 --- /dev/null +++ b/apps/testing/17-router/README.md @@ -0,0 +1,13 @@ +# Router + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve testing-router +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/17-router/). diff --git a/apps/testing/input-output/cypress.config.ts b/apps/testing/17-router/cypress.config.ts similarity index 100% rename from apps/testing/input-output/cypress.config.ts rename to apps/testing/17-router/cypress.config.ts diff --git a/apps/testing/input-output/cypress/fixtures/example.json b/apps/testing/17-router/cypress/fixtures/example.json similarity index 100% rename from apps/testing/input-output/cypress/fixtures/example.json rename to apps/testing/17-router/cypress/fixtures/example.json diff --git a/apps/testing/17-router/cypress/support/commands.ts b/apps/testing/17-router/cypress/support/commands.ts new file mode 100644 index 000000000..b5d8a9582 --- /dev/null +++ b/apps/testing/17-router/cypress/support/commands.ts @@ -0,0 +1,25 @@ +/// +import { mount } from 'cypress/angular'; + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + mount: typeof mount; + } + } +} + +Cypress.Commands.add('mount', mount); diff --git a/apps/testing/router-outlet/cypress/support/component-index.html b/apps/testing/17-router/cypress/support/component-index.html similarity index 100% rename from apps/testing/router-outlet/cypress/support/component-index.html rename to apps/testing/17-router/cypress/support/component-index.html diff --git a/apps/testing/input-output/cypress/support/component.ts b/apps/testing/17-router/cypress/support/component.ts similarity index 100% rename from apps/testing/input-output/cypress/support/component.ts rename to apps/testing/17-router/cypress/support/component.ts diff --git a/apps/testing/input-output/cypress/tsconfig.json b/apps/testing/17-router/cypress/tsconfig.json similarity index 100% rename from apps/testing/input-output/cypress/tsconfig.json rename to apps/testing/17-router/cypress/tsconfig.json diff --git a/apps/testing/17-router/jest.config.ts b/apps/testing/17-router/jest.config.ts new file mode 100644 index 000000000..997c99f0e --- /dev/null +++ b/apps/testing/17-router/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'testing-router', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/testing/17-router/project.json b/apps/testing/17-router/project.json new file mode 100644 index 000000000..44c7a2b18 --- /dev/null +++ b/apps/testing/17-router/project.json @@ -0,0 +1,96 @@ +{ + "name": "testing-router", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/testing/17-router/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/17-router", + "index": "apps/testing/17-router/src/index.html", + "main": "apps/testing/17-router/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/17-router/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/testing/17-router/src/favicon.ico", + "apps/testing/17-router/src/assets" + ], + "styles": ["apps/testing/17-router/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-router:build:production" + }, + "development": { + "buildTarget": "testing-router:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-router:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + }, + "component-test": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/testing/17-router/cypress.config.ts", + "testingType": "component", + "skipServe": true, + "devServerTarget": "testing-router:build" + } + } + } +} diff --git a/apps/testing/router-outlet/src/app/app.component.cy.ts b/apps/testing/17-router/src/app/app.component.cy.ts similarity index 100% rename from apps/testing/router-outlet/src/app/app.component.cy.ts rename to apps/testing/17-router/src/app/app.component.cy.ts diff --git a/apps/testing/router-outlet/src/app/app.component.spec.ts b/apps/testing/17-router/src/app/app.component.spec.ts similarity index 100% rename from apps/testing/router-outlet/src/app/app.component.spec.ts rename to apps/testing/17-router/src/app/app.component.spec.ts diff --git a/apps/testing/17-router/src/app/app.component.ts b/apps/testing/17-router/src/app/app.component.ts new file mode 100644 index 000000000..cc6298f17 --- /dev/null +++ b/apps/testing/17-router/src/app/app.component.ts @@ -0,0 +1,41 @@ +import { Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet, RouterLink], + selector: 'app-root', + styles: [ + ` + h1 { + margin-bottom: 0; + } + nav a { + padding: 1rem; + text-decoration: none; + margin-top: 10px; + display: inline-block; + background-color: #e8e8e8; + color: #3d3d3d; + border-radius: 4px; + margin-bottom: 10px; + } + nav a:hover { + color: white; + background-color: #42545c; + } + nav a.active { + background-color: black; + } + `, + ], + template: ` +

Library

+ + + + + `, +}) +export class AppComponent {} diff --git a/apps/testing/router-outlet/src/app/app.config.ts b/apps/testing/17-router/src/app/app.config.ts similarity index 100% rename from apps/testing/router-outlet/src/app/app.config.ts rename to apps/testing/17-router/src/app/app.config.ts diff --git a/apps/testing/router-outlet/src/app/app.routes.ts b/apps/testing/17-router/src/app/app.routes.ts similarity index 100% rename from apps/testing/router-outlet/src/app/app.routes.ts rename to apps/testing/17-router/src/app/app.routes.ts diff --git a/apps/testing/router-outlet/src/app/book.guard.ts b/apps/testing/17-router/src/app/book.guard.ts similarity index 100% rename from apps/testing/router-outlet/src/app/book.guard.ts rename to apps/testing/17-router/src/app/book.guard.ts diff --git a/apps/testing/router-outlet/src/app/book.model.ts b/apps/testing/17-router/src/app/book.model.ts similarity index 100% rename from apps/testing/router-outlet/src/app/book.model.ts rename to apps/testing/17-router/src/app/book.model.ts diff --git a/apps/testing/router-outlet/src/app/no-book-search.component.ts b/apps/testing/17-router/src/app/no-book-search.component.ts similarity index 100% rename from apps/testing/router-outlet/src/app/no-book-search.component.ts rename to apps/testing/17-router/src/app/no-book-search.component.ts diff --git a/apps/testing/router-outlet/src/app/search.component.ts b/apps/testing/17-router/src/app/search.component.ts similarity index 98% rename from apps/testing/router-outlet/src/app/search.component.ts rename to apps/testing/17-router/src/app/search.component.ts index b6f8666c3..9027c0cfe 100644 --- a/apps/testing/router-outlet/src/app/search.component.ts +++ b/apps/testing/17-router/src/app/search.component.ts @@ -5,7 +5,6 @@ import { RouterLink } from '@angular/router'; import { availableBooks } from './book.model'; @Component({ - standalone: true, imports: [ReactiveFormsModule, RouterLink, NgFor, NgIf], styles: [ ` diff --git a/apps/testing/router-outlet/src/app/shelf.component.ts b/apps/testing/17-router/src/app/shelf.component.ts similarity index 97% rename from apps/testing/router-outlet/src/app/shelf.component.ts rename to apps/testing/17-router/src/app/shelf.component.ts index a7da5af1a..63cddfab6 100644 --- a/apps/testing/router-outlet/src/app/shelf.component.ts +++ b/apps/testing/17-router/src/app/shelf.component.ts @@ -6,7 +6,6 @@ import { availableBooks } from './book.model'; @Component({ selector: 'app-shelf', - standalone: true, imports: [AsyncPipe, JsonPipe, NgFor], template: `
    diff --git a/apps/testing/modal/src/assets/.gitkeep b/apps/testing/17-router/src/assets/.gitkeep similarity index 100% rename from apps/testing/modal/src/assets/.gitkeep rename to apps/testing/17-router/src/assets/.gitkeep diff --git a/apps/testing/todos-list/src/favicon.ico b/apps/testing/17-router/src/favicon.ico similarity index 100% rename from apps/testing/todos-list/src/favicon.ico rename to apps/testing/17-router/src/favicon.ico diff --git a/apps/testing/17-router/src/index.html b/apps/testing/17-router/src/index.html new file mode 100644 index 000000000..80ec9bf77 --- /dev/null +++ b/apps/testing/17-router/src/index.html @@ -0,0 +1,13 @@ + + + + + testing-router + + + + + + + + diff --git a/apps/testing/modal/src/main.ts b/apps/testing/17-router/src/main.ts similarity index 100% rename from apps/testing/modal/src/main.ts rename to apps/testing/17-router/src/main.ts diff --git a/apps/performance/scroll-cd/src/styles.scss b/apps/testing/17-router/src/styles.scss similarity index 100% rename from apps/performance/scroll-cd/src/styles.scss rename to apps/testing/17-router/src/styles.scss diff --git a/apps/testing/input-output/src/test-setup.ts b/apps/testing/17-router/src/test-setup.ts similarity index 100% rename from apps/testing/input-output/src/test-setup.ts rename to apps/testing/17-router/src/test-setup.ts diff --git a/apps/testing/input-output/tsconfig.app.json b/apps/testing/17-router/tsconfig.app.json similarity index 100% rename from apps/testing/input-output/tsconfig.app.json rename to apps/testing/17-router/tsconfig.app.json diff --git a/apps/testing/checkbox/tsconfig.editor.json b/apps/testing/17-router/tsconfig.editor.json similarity index 100% rename from apps/testing/checkbox/tsconfig.editor.json rename to apps/testing/17-router/tsconfig.editor.json diff --git a/apps/rxjs/race-condition/tsconfig.json b/apps/testing/17-router/tsconfig.json similarity index 100% rename from apps/rxjs/race-condition/tsconfig.json rename to apps/testing/17-router/tsconfig.json diff --git a/apps/testing/checkbox/tsconfig.spec.json b/apps/testing/17-router/tsconfig.spec.json similarity index 100% rename from apps/testing/checkbox/tsconfig.spec.json rename to apps/testing/17-router/tsconfig.spec.json diff --git a/apps/testing/nested/.eslintrc.json b/apps/testing/18-nested-components/.eslintrc.json similarity index 100% rename from apps/testing/nested/.eslintrc.json rename to apps/testing/18-nested-components/.eslintrc.json diff --git a/apps/testing/18-nested-components/README.md b/apps/testing/18-nested-components/README.md new file mode 100644 index 000000000..659b9e365 --- /dev/null +++ b/apps/testing/18-nested-components/README.md @@ -0,0 +1,13 @@ +# Nested Components + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve testing-nested-components +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/18-nested-comp/). diff --git a/apps/testing/modal/cypress.config.ts b/apps/testing/18-nested-components/cypress.config.ts similarity index 100% rename from apps/testing/modal/cypress.config.ts rename to apps/testing/18-nested-components/cypress.config.ts diff --git a/apps/testing/modal/cypress/fixtures/example.json b/apps/testing/18-nested-components/cypress/fixtures/example.json similarity index 100% rename from apps/testing/modal/cypress/fixtures/example.json rename to apps/testing/18-nested-components/cypress/fixtures/example.json diff --git a/apps/testing/18-nested-components/cypress/support/commands.ts b/apps/testing/18-nested-components/cypress/support/commands.ts new file mode 100644 index 000000000..b5d8a9582 --- /dev/null +++ b/apps/testing/18-nested-components/cypress/support/commands.ts @@ -0,0 +1,25 @@ +/// +import { mount } from 'cypress/angular'; + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + mount: typeof mount; + } + } +} + +Cypress.Commands.add('mount', mount); diff --git a/apps/testing/nested/cypress/support/component-index.html b/apps/testing/18-nested-components/cypress/support/component-index.html similarity index 100% rename from apps/testing/nested/cypress/support/component-index.html rename to apps/testing/18-nested-components/cypress/support/component-index.html diff --git a/apps/testing/modal/cypress/support/component.ts b/apps/testing/18-nested-components/cypress/support/component.ts similarity index 100% rename from apps/testing/modal/cypress/support/component.ts rename to apps/testing/18-nested-components/cypress/support/component.ts diff --git a/apps/testing/modal/cypress/tsconfig.json b/apps/testing/18-nested-components/cypress/tsconfig.json similarity index 100% rename from apps/testing/modal/cypress/tsconfig.json rename to apps/testing/18-nested-components/cypress/tsconfig.json diff --git a/apps/testing/18-nested-components/jest.config.ts b/apps/testing/18-nested-components/jest.config.ts new file mode 100644 index 000000000..c77df55f9 --- /dev/null +++ b/apps/testing/18-nested-components/jest.config.ts @@ -0,0 +1,21 @@ +/* eslint-disable */ +export default { + displayName: 'testing-nested-components', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/testing/18-nested-components/project.json b/apps/testing/18-nested-components/project.json new file mode 100644 index 000000000..3ea6467a4 --- /dev/null +++ b/apps/testing/18-nested-components/project.json @@ -0,0 +1,96 @@ +{ + "name": "testing-nested-components", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/testing/18-nested-components/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/18-nested-components", + "index": "apps/testing/18-nested-components/src/index.html", + "main": "apps/testing/18-nested-components/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/18-nested-components/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/testing/18-nested-components/src/favicon.ico", + "apps/testing/18-nested-components/src/assets" + ], + "styles": ["apps/testing/18-nested-components/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-nested-components:build:production" + }, + "development": { + "buildTarget": "testing-nested-components:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-nested-components:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + }, + "component-test": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/testing/18-nested-components/cypress.config.ts", + "testingType": "component", + "skipServe": true, + "devServerTarget": "testing-nested-components:build" + } + } + } +} diff --git a/apps/testing/18-nested-components/src/app/app.component.ts b/apps/testing/18-nested-components/src/app/app.component.ts new file mode 100644 index 000000000..84b0df68a --- /dev/null +++ b/apps/testing/18-nested-components/src/app/app.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { ChildComponent } from './child.component'; + +@Component({ + imports: [ChildComponent], + selector: 'app-root', + template: ` + + `, +}) +export class AppComponent {} diff --git a/apps/testing/nested/src/app/child.component.cy.ts b/apps/testing/18-nested-components/src/app/child.component.cy.ts similarity index 100% rename from apps/testing/nested/src/app/child.component.cy.ts rename to apps/testing/18-nested-components/src/app/child.component.cy.ts diff --git a/apps/testing/nested/src/app/child.component.spec.ts b/apps/testing/18-nested-components/src/app/child.component.spec.ts similarity index 100% rename from apps/testing/nested/src/app/child.component.spec.ts rename to apps/testing/18-nested-components/src/app/child.component.spec.ts diff --git a/apps/testing/18-nested-components/src/app/child.component.ts b/apps/testing/18-nested-components/src/app/child.component.ts new file mode 100644 index 000000000..1aaca5117 --- /dev/null +++ b/apps/testing/18-nested-components/src/app/child.component.ts @@ -0,0 +1,92 @@ +import { NgIf } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, + inject, +} from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { HttpService } from './http.service'; + +@Component({ + selector: 'app-input', + imports: [ReactiveFormsModule], + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class InputComponent { + title = new FormControl('', { nonNullable: true }); +} + +@Component({ + selector: 'result', + standalone: true, + template: ` +

    Title is {{ title }}

    + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ResultComponent { + @Input() title = ''; +} + +@Component({ + selector: 'app-button', + standalone: true, + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ButtonComponent { + @Output() validate = new EventEmitter(); +} + +@Component({ + selector: 'app-error', + standalone: true, + template: ` +

    Title is required !!!

    + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ErrorComponent { + @Output() validate = new EventEmitter(); +} + +@Component({ + selector: 'app-child', + imports: [ + ResultComponent, + ButtonComponent, + InputComponent, + ErrorComponent, + NgIf, + ], + template: ` + + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ChildComponent { + http = inject(HttpService); + + showError = false; + + submit(title: string) { + this.showError = false; + if (title === '') { + this.showError = true; + return; + } + + this.http.sendTitle(title); + } +} diff --git a/apps/testing/nested/src/app/http.service.ts b/apps/testing/18-nested-components/src/app/http.service.ts similarity index 100% rename from apps/testing/nested/src/app/http.service.ts rename to apps/testing/18-nested-components/src/app/http.service.ts diff --git a/apps/testing/nested/src/assets/.gitkeep b/apps/testing/18-nested-components/src/assets/.gitkeep similarity index 100% rename from apps/testing/nested/src/assets/.gitkeep rename to apps/testing/18-nested-components/src/assets/.gitkeep diff --git a/apps/typescript/enums-vs-union-types/src/favicon.ico b/apps/testing/18-nested-components/src/favicon.ico similarity index 100% rename from apps/typescript/enums-vs-union-types/src/favicon.ico rename to apps/testing/18-nested-components/src/favicon.ico diff --git a/apps/testing/18-nested-components/src/index.html b/apps/testing/18-nested-components/src/index.html new file mode 100644 index 000000000..5f7055208 --- /dev/null +++ b/apps/testing/18-nested-components/src/index.html @@ -0,0 +1,13 @@ + + + + + testing-nested-components + + + + + + + + diff --git a/apps/testing/input-output/src/main.ts b/apps/testing/18-nested-components/src/main.ts similarity index 100% rename from apps/testing/input-output/src/main.ts rename to apps/testing/18-nested-components/src/main.ts diff --git a/apps/rxjs/pipe-bug/src/styles.scss b/apps/testing/18-nested-components/src/styles.scss similarity index 100% rename from apps/rxjs/pipe-bug/src/styles.scss rename to apps/testing/18-nested-components/src/styles.scss diff --git a/apps/testing/modal/src/test-setup.ts b/apps/testing/18-nested-components/src/test-setup.ts similarity index 100% rename from apps/testing/modal/src/test-setup.ts rename to apps/testing/18-nested-components/src/test-setup.ts diff --git a/apps/testing/modal/tsconfig.app.json b/apps/testing/18-nested-components/tsconfig.app.json similarity index 100% rename from apps/testing/modal/tsconfig.app.json rename to apps/testing/18-nested-components/tsconfig.app.json diff --git a/apps/testing/input-output/tsconfig.editor.json b/apps/testing/18-nested-components/tsconfig.editor.json similarity index 100% rename from apps/testing/input-output/tsconfig.editor.json rename to apps/testing/18-nested-components/tsconfig.editor.json diff --git a/apps/testing/input-output/tsconfig.json b/apps/testing/18-nested-components/tsconfig.json similarity index 100% rename from apps/testing/input-output/tsconfig.json rename to apps/testing/18-nested-components/tsconfig.json diff --git a/apps/testing/input-output/tsconfig.spec.json b/apps/testing/18-nested-components/tsconfig.spec.json similarity index 100% rename from apps/testing/input-output/tsconfig.spec.json rename to apps/testing/18-nested-components/tsconfig.spec.json diff --git a/apps/testing/create-harness/.eslintrc.json b/apps/testing/19-input-output/.eslintrc.json similarity index 100% rename from apps/testing/create-harness/.eslintrc.json rename to apps/testing/19-input-output/.eslintrc.json diff --git a/apps/testing/input-output/README.md b/apps/testing/19-input-output/README.md similarity index 100% rename from apps/testing/input-output/README.md rename to apps/testing/19-input-output/README.md diff --git a/apps/testing/nested/cypress.config.ts b/apps/testing/19-input-output/cypress.config.ts similarity index 100% rename from apps/testing/nested/cypress.config.ts rename to apps/testing/19-input-output/cypress.config.ts diff --git a/apps/testing/nested/cypress/fixtures/example.json b/apps/testing/19-input-output/cypress/fixtures/example.json similarity index 100% rename from apps/testing/nested/cypress/fixtures/example.json rename to apps/testing/19-input-output/cypress/fixtures/example.json diff --git a/apps/testing/19-input-output/cypress/support/commands.ts b/apps/testing/19-input-output/cypress/support/commands.ts new file mode 100644 index 000000000..b5d8a9582 --- /dev/null +++ b/apps/testing/19-input-output/cypress/support/commands.ts @@ -0,0 +1,25 @@ +/// +import { mount } from 'cypress/angular'; + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + mount: typeof mount; + } + } +} + +Cypress.Commands.add('mount', mount); diff --git a/apps/testing/input-output/cypress/support/component-index.html b/apps/testing/19-input-output/cypress/support/component-index.html similarity index 100% rename from apps/testing/input-output/cypress/support/component-index.html rename to apps/testing/19-input-output/cypress/support/component-index.html diff --git a/apps/testing/nested/cypress/support/component.ts b/apps/testing/19-input-output/cypress/support/component.ts similarity index 100% rename from apps/testing/nested/cypress/support/component.ts rename to apps/testing/19-input-output/cypress/support/component.ts diff --git a/apps/testing/nested/cypress/tsconfig.json b/apps/testing/19-input-output/cypress/tsconfig.json similarity index 100% rename from apps/testing/nested/cypress/tsconfig.json rename to apps/testing/19-input-output/cypress/tsconfig.json diff --git a/apps/testing/input-output/jest.config.ts b/apps/testing/19-input-output/jest.config.ts similarity index 100% rename from apps/testing/input-output/jest.config.ts rename to apps/testing/19-input-output/jest.config.ts diff --git a/apps/testing/19-input-output/project.json b/apps/testing/19-input-output/project.json new file mode 100644 index 000000000..e143e2e02 --- /dev/null +++ b/apps/testing/19-input-output/project.json @@ -0,0 +1,96 @@ +{ + "name": "testing-input-output", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/testing/19-input-output/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/19-input-output", + "index": "apps/testing/19-input-output/src/index.html", + "main": "apps/testing/19-input-output/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/19-input-output/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/testing/19-input-output/src/favicon.ico", + "apps/testing/19-input-output/src/assets" + ], + "styles": ["apps/testing/19-input-output/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-input-output:build:production" + }, + "development": { + "buildTarget": "testing-input-output:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-input-output:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + }, + "component-test": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/testing/19-input-output/cypress.config.ts", + "testingType": "component", + "skipServe": true, + "devServerTarget": "testing-input-output:build" + } + } + } +} diff --git a/apps/testing/19-input-output/src/app/app.component.ts b/apps/testing/19-input-output/src/app/app.component.ts new file mode 100644 index 000000000..7e9e0f78a --- /dev/null +++ b/apps/testing/19-input-output/src/app/app.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { CounterComponent } from './counter.component'; + +@Component({ + imports: [CounterComponent], + selector: 'app-root', + template: ` + + `, +}) +export class AppComponent { + log(counter: number) { + console.log('output log', counter); + } +} diff --git a/apps/testing/input-output/src/app/counter.component.cy.ts b/apps/testing/19-input-output/src/app/counter.component.cy.ts similarity index 100% rename from apps/testing/input-output/src/app/counter.component.cy.ts rename to apps/testing/19-input-output/src/app/counter.component.cy.ts diff --git a/apps/testing/input-output/src/app/counter.component.spec.ts b/apps/testing/19-input-output/src/app/counter.component.spec.ts similarity index 100% rename from apps/testing/input-output/src/app/counter.component.spec.ts rename to apps/testing/19-input-output/src/app/counter.component.spec.ts diff --git a/apps/testing/19-input-output/src/app/counter.component.ts b/apps/testing/19-input-output/src/app/counter.component.ts new file mode 100644 index 000000000..df1eed103 --- /dev/null +++ b/apps/testing/19-input-output/src/app/counter.component.ts @@ -0,0 +1,32 @@ +import { + ChangeDetectionStrategy, + Component, + input, + linkedSignal, + output, +} from '@angular/core'; + +@Component({ + selector: 'app-counter', + template: ` +

    Counter: {{ counter() }}

    + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CounterComponent { + initialValue = input.required(); + public counter = linkedSignal(() => this.initialValue()); + + send = output(); + + public increment = () => { + this.counter.set(this.counter() + 1); + }; + + public decrement = () => { + this.counter.set(this.counter() - 1); + }; +} diff --git a/apps/testing/router-outlet/src/assets/.gitkeep b/apps/testing/19-input-output/src/assets/.gitkeep similarity index 100% rename from apps/testing/router-outlet/src/assets/.gitkeep rename to apps/testing/19-input-output/src/assets/.gitkeep diff --git a/apps/typescript/overload/src/favicon.ico b/apps/testing/19-input-output/src/favicon.ico similarity index 100% rename from apps/typescript/overload/src/favicon.ico rename to apps/testing/19-input-output/src/favicon.ico diff --git a/apps/testing/19-input-output/src/index.html b/apps/testing/19-input-output/src/index.html new file mode 100644 index 000000000..a5cd1c5da --- /dev/null +++ b/apps/testing/19-input-output/src/index.html @@ -0,0 +1,13 @@ + + + + + testing-input-ouput + + + + + + + + diff --git a/apps/testing/nested/src/main.ts b/apps/testing/19-input-output/src/main.ts similarity index 100% rename from apps/testing/nested/src/main.ts rename to apps/testing/19-input-output/src/main.ts diff --git a/apps/testing/input-output/src/styles.scss b/apps/testing/19-input-output/src/styles.scss similarity index 100% rename from apps/testing/input-output/src/styles.scss rename to apps/testing/19-input-output/src/styles.scss diff --git a/apps/testing/nested/src/test-setup.ts b/apps/testing/19-input-output/src/test-setup.ts similarity index 100% rename from apps/testing/nested/src/test-setup.ts rename to apps/testing/19-input-output/src/test-setup.ts diff --git a/apps/testing/nested/tsconfig.app.json b/apps/testing/19-input-output/tsconfig.app.json similarity index 100% rename from apps/testing/nested/tsconfig.app.json rename to apps/testing/19-input-output/tsconfig.app.json diff --git a/apps/testing/modal/tsconfig.editor.json b/apps/testing/19-input-output/tsconfig.editor.json similarity index 100% rename from apps/testing/modal/tsconfig.editor.json rename to apps/testing/19-input-output/tsconfig.editor.json diff --git a/apps/testing/modal/tsconfig.json b/apps/testing/19-input-output/tsconfig.json similarity index 100% rename from apps/testing/modal/tsconfig.json rename to apps/testing/19-input-output/tsconfig.json diff --git a/apps/testing/modal/tsconfig.spec.json b/apps/testing/19-input-output/tsconfig.spec.json similarity index 100% rename from apps/testing/modal/tsconfig.spec.json rename to apps/testing/19-input-output/tsconfig.spec.json diff --git a/apps/testing/harness/.eslintrc.json b/apps/testing/20-modal/.eslintrc.json similarity index 100% rename from apps/testing/harness/.eslintrc.json rename to apps/testing/20-modal/.eslintrc.json diff --git a/apps/testing/modal/README.md b/apps/testing/20-modal/README.md similarity index 100% rename from apps/testing/modal/README.md rename to apps/testing/20-modal/README.md diff --git a/apps/testing/router-outlet/cypress.config.ts b/apps/testing/20-modal/cypress.config.ts similarity index 100% rename from apps/testing/router-outlet/cypress.config.ts rename to apps/testing/20-modal/cypress.config.ts diff --git a/apps/testing/router-outlet/cypress/fixtures/example.json b/apps/testing/20-modal/cypress/fixtures/example.json similarity index 100% rename from apps/testing/router-outlet/cypress/fixtures/example.json rename to apps/testing/20-modal/cypress/fixtures/example.json diff --git a/apps/testing/20-modal/cypress/support/commands.ts b/apps/testing/20-modal/cypress/support/commands.ts new file mode 100644 index 000000000..b5d8a9582 --- /dev/null +++ b/apps/testing/20-modal/cypress/support/commands.ts @@ -0,0 +1,25 @@ +/// +import { mount } from 'cypress/angular'; + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + mount: typeof mount; + } + } +} + +Cypress.Commands.add('mount', mount); diff --git a/apps/testing/modal/cypress/support/component-index.html b/apps/testing/20-modal/cypress/support/component-index.html similarity index 100% rename from apps/testing/modal/cypress/support/component-index.html rename to apps/testing/20-modal/cypress/support/component-index.html diff --git a/apps/testing/router-outlet/cypress/support/component.ts b/apps/testing/20-modal/cypress/support/component.ts similarity index 100% rename from apps/testing/router-outlet/cypress/support/component.ts rename to apps/testing/20-modal/cypress/support/component.ts diff --git a/apps/testing/router-outlet/cypress/tsconfig.json b/apps/testing/20-modal/cypress/tsconfig.json similarity index 100% rename from apps/testing/router-outlet/cypress/tsconfig.json rename to apps/testing/20-modal/cypress/tsconfig.json diff --git a/apps/testing/modal/jest.config.ts b/apps/testing/20-modal/jest.config.ts similarity index 100% rename from apps/testing/modal/jest.config.ts rename to apps/testing/20-modal/jest.config.ts diff --git a/apps/testing/20-modal/project.json b/apps/testing/20-modal/project.json new file mode 100644 index 000000000..28134614f --- /dev/null +++ b/apps/testing/20-modal/project.json @@ -0,0 +1,99 @@ +{ + "name": "testing-modal", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/testing/20-modal/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/20-modal", + "index": "apps/testing/20-modal/src/index.html", + "main": "apps/testing/20-modal/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/20-modal/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/testing/20-modal/src/favicon.ico", + "apps/testing/20-modal/src/assets" + ], + "styles": [ + "apps/testing/20-modal/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-modal:build:production" + }, + "development": { + "buildTarget": "testing-modal:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-modal:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + }, + "component-test": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/testing/20-modal/cypress.config.ts", + "testingType": "component", + "skipServe": true, + "devServerTarget": "testing-modal:build" + } + } + } +} diff --git a/apps/testing/modal/src/app/app.component.cy.ts b/apps/testing/20-modal/src/app/app.component.cy.ts similarity index 100% rename from apps/testing/modal/src/app/app.component.cy.ts rename to apps/testing/20-modal/src/app/app.component.cy.ts diff --git a/apps/testing/modal/src/app/app.component.spec.ts b/apps/testing/20-modal/src/app/app.component.spec.ts similarity index 100% rename from apps/testing/modal/src/app/app.component.spec.ts rename to apps/testing/20-modal/src/app/app.component.spec.ts diff --git a/apps/testing/20-modal/src/app/app.component.ts b/apps/testing/20-modal/src/app/app.component.ts new file mode 100644 index 000000000..d5f35b4bf --- /dev/null +++ b/apps/testing/20-modal/src/app/app.component.ts @@ -0,0 +1,64 @@ +import { AsyncPipe } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { BehaviorSubject } from 'rxjs'; +import { ErrorDialog } from './error.dialog'; +import { ProfilConfirmationDialog } from './profil-confirmation.dialog'; +@Component({ + imports: [ + ReactiveFormsModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + AsyncPipe, + ], + selector: 'app-root', + host: { + class: 'p-4 block flex gap-4 items-center', + }, + template: ` + + Name + + + + +
    {{ result$ | async }}
    + `, +}) +export class AppComponent { + private modal = inject(MatDialog); + private result = new BehaviorSubject(''); + result$ = this.result.asObservable(); + + name = new FormControl('', { nonNullable: true }); + + confirm() { + if (!this.name.value) { + this.modal.open(ErrorDialog); + return; + } + + this.modal + .open(ProfilConfirmationDialog, { + data: { + name: this.name.value, + }, + }) + .afterClosed() + .subscribe((result) => + this.result.next( + result ? 'Name has been submitted' : 'Name is invalid !!', + ), + ); + } +} diff --git a/apps/testing/modal/src/app/app.config.ts b/apps/testing/20-modal/src/app/app.config.ts similarity index 100% rename from apps/testing/modal/src/app/app.config.ts rename to apps/testing/20-modal/src/app/app.config.ts diff --git a/apps/testing/modal/src/app/error.dialog.ts b/apps/testing/20-modal/src/app/error.dialog.ts similarity index 96% rename from apps/testing/modal/src/app/error.dialog.ts rename to apps/testing/20-modal/src/app/error.dialog.ts index 38412ba5e..251d5d513 100644 --- a/apps/testing/modal/src/app/error.dialog.ts +++ b/apps/testing/20-modal/src/app/error.dialog.ts @@ -4,7 +4,6 @@ import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; @Component({ - standalone: true, imports: [MatButtonModule, MatDialogModule], template: `

    Error

    diff --git a/apps/testing/modal/src/app/profil-confirmation.dialog.ts b/apps/testing/20-modal/src/app/profil-confirmation.dialog.ts similarity index 97% rename from apps/testing/modal/src/app/profil-confirmation.dialog.ts rename to apps/testing/20-modal/src/app/profil-confirmation.dialog.ts index 6af2d41dd..e77211e24 100644 --- a/apps/testing/modal/src/app/profil-confirmation.dialog.ts +++ b/apps/testing/20-modal/src/app/profil-confirmation.dialog.ts @@ -4,7 +4,6 @@ import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; @Component({ - standalone: true, imports: [MatButtonModule, MatDialogModule], template: `

    Profil

    diff --git a/apps/testing/table/src/assets/.gitkeep b/apps/testing/20-modal/src/assets/.gitkeep similarity index 100% rename from apps/testing/table/src/assets/.gitkeep rename to apps/testing/20-modal/src/assets/.gitkeep diff --git a/apps/testing/20-modal/src/favicon.ico b/apps/testing/20-modal/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/testing/20-modal/src/favicon.ico differ diff --git a/apps/testing/20-modal/src/index.html b/apps/testing/20-modal/src/index.html new file mode 100644 index 000000000..362733ce1 --- /dev/null +++ b/apps/testing/20-modal/src/index.html @@ -0,0 +1,13 @@ + + + + + testing-modal + + + + + + + + diff --git a/apps/testing/router-outlet/src/main.ts b/apps/testing/20-modal/src/main.ts similarity index 100% rename from apps/testing/router-outlet/src/main.ts rename to apps/testing/20-modal/src/main.ts diff --git a/apps/testing/20-modal/src/styles.scss b/apps/testing/20-modal/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/testing/20-modal/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/testing/router-outlet/src/test-setup.ts b/apps/testing/20-modal/src/test-setup.ts similarity index 100% rename from apps/testing/router-outlet/src/test-setup.ts rename to apps/testing/20-modal/src/test-setup.ts diff --git a/apps/testing/20-modal/tailwind.config.js b/apps/testing/20-modal/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/testing/20-modal/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/testing/router-outlet/tsconfig.app.json b/apps/testing/20-modal/tsconfig.app.json similarity index 100% rename from apps/testing/router-outlet/tsconfig.app.json rename to apps/testing/20-modal/tsconfig.app.json diff --git a/apps/testing/nested/tsconfig.editor.json b/apps/testing/20-modal/tsconfig.editor.json similarity index 100% rename from apps/testing/nested/tsconfig.editor.json rename to apps/testing/20-modal/tsconfig.editor.json diff --git a/apps/testing/nested/tsconfig.json b/apps/testing/20-modal/tsconfig.json similarity index 100% rename from apps/testing/nested/tsconfig.json rename to apps/testing/20-modal/tsconfig.json diff --git a/apps/testing/nested/tsconfig.spec.json b/apps/testing/20-modal/tsconfig.spec.json similarity index 100% rename from apps/testing/nested/tsconfig.spec.json rename to apps/testing/20-modal/tsconfig.spec.json diff --git a/apps/testing/input-output/.eslintrc.json b/apps/testing/23-harness/.eslintrc.json similarity index 100% rename from apps/testing/input-output/.eslintrc.json rename to apps/testing/23-harness/.eslintrc.json diff --git a/apps/testing/harness/README.md b/apps/testing/23-harness/README.md similarity index 100% rename from apps/testing/harness/README.md rename to apps/testing/23-harness/README.md diff --git a/apps/testing/harness/jest.config.ts b/apps/testing/23-harness/jest.config.ts similarity index 100% rename from apps/testing/harness/jest.config.ts rename to apps/testing/23-harness/jest.config.ts diff --git a/apps/testing/23-harness/project.json b/apps/testing/23-harness/project.json new file mode 100644 index 000000000..23e5b04f4 --- /dev/null +++ b/apps/testing/23-harness/project.json @@ -0,0 +1,86 @@ +{ + "name": "testing-harness", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/testing/23-harness/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/23-harness", + "index": "apps/testing/23-harness/src/index.html", + "main": "apps/testing/23-harness/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/23-harness/tsconfig.app.json", + "assets": [ + "apps/testing/23-harness/src/favicon.ico", + "apps/testing/23-harness/src/assets" + ], + "styles": ["apps/testing/23-harness/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-harness:build:production" + }, + "development": { + "buildTarget": "testing-harness:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-harness:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/testing/23-harness/src/app/app.component.ts b/apps/testing/23-harness/src/app/app.component.ts new file mode 100644 index 000000000..7ecf1998d --- /dev/null +++ b/apps/testing/23-harness/src/app/app.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { ChildComponent } from './child.component'; + +@Component({ + imports: [ChildComponent], + selector: 'app-root', + template: ` + + `, + styles: [''], +}) +export class AppComponent {} diff --git a/apps/testing/create-harness/src/app/app.config.ts b/apps/testing/23-harness/src/app/app.config.ts similarity index 100% rename from apps/testing/create-harness/src/app/app.config.ts rename to apps/testing/23-harness/src/app/app.config.ts diff --git a/apps/testing/harness/src/app/child.component.spec.ts b/apps/testing/23-harness/src/app/child.component.spec.ts similarity index 100% rename from apps/testing/harness/src/app/child.component.spec.ts rename to apps/testing/23-harness/src/app/child.component.spec.ts diff --git a/apps/testing/23-harness/src/app/child.component.ts b/apps/testing/23-harness/src/app/child.component.ts new file mode 100644 index 000000000..935f08f89 --- /dev/null +++ b/apps/testing/23-harness/src/app/child.component.ts @@ -0,0 +1,119 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatSliderModule } from '@angular/material/slider'; + +@Component({ + selector: 'app-child', + template: ` + + +

    Slider configuration

    + +
    + + Value + + + + Min value + + + + Max value + + + + Step size + + +
    + +
    + Show ticks +
    + +
    + Show thumb label +
    + +
    + Disabled +
    +
    +
    + + + +
    + + + + + +
    +
    +
    + `, + styles: [ + ` + .mat-mdc-slider { + max-width: 300px; + width: 100%; + } + + .mat-mdc-card + .mat-mdc-card { + margin-top: 8px; + } + `, + ], + imports: [ + MatCardModule, + MatFormFieldModule, + MatInputModule, + FormsModule, + MatCheckboxModule, + MatSliderModule, + MatIconModule, + ], +}) +export class ChildComponent { + disabled = false; + max = 100; + min = 0; + showTicks = false; + step = 1; + thumbLabel = false; + value = 0; + + back() { + if (this.value - this.step >= this.min) { + this.value -= this.step; + } + } + + forward() { + if (this.value + this.step <= this.max) { + this.value += this.step; + } + } +} diff --git a/apps/testing/todos-list/src/assets/.gitkeep b/apps/testing/23-harness/src/assets/.gitkeep similarity index 100% rename from apps/testing/todos-list/src/assets/.gitkeep rename to apps/testing/23-harness/src/assets/.gitkeep diff --git a/apps/testing/23-harness/src/favicon.ico b/apps/testing/23-harness/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/testing/23-harness/src/favicon.ico differ diff --git a/apps/testing/23-harness/src/index.html b/apps/testing/23-harness/src/index.html new file mode 100644 index 000000000..9b55da74f --- /dev/null +++ b/apps/testing/23-harness/src/index.html @@ -0,0 +1,16 @@ + + + + + testing-harness + + + + + + + + + diff --git a/apps/testing/23-harness/src/main.ts b/apps/testing/23-harness/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/testing/23-harness/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/testing/23-harness/src/styles.scss b/apps/testing/23-harness/src/styles.scss new file mode 100644 index 000000000..9a29b71e6 --- /dev/null +++ b/apps/testing/23-harness/src/styles.scss @@ -0,0 +1,29 @@ +@use '@angular/material' as mat; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ + +@include mat.elevation-classes(); +@include mat.app-background(); + +$theme-primary: mat.m2-define-palette(mat.$m2-indigo-palette); +$theme-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400); + +$theme-warn: mat.m2-define-palette(mat.$m2-red-palette); + +$theme: mat.m2-define-light-theme( + ( + color: ( + primary: $theme-primary, + accent: $theme-accent, + warn: $theme-warn, + ), + typography: mat.m2-define-typography-config(), + ) +); + +@include mat.dialog-theme($theme); +@include mat.all-component-themes($theme); diff --git a/apps/testing/table/src/test-setup.ts b/apps/testing/23-harness/src/test-setup.ts similarity index 100% rename from apps/testing/table/src/test-setup.ts rename to apps/testing/23-harness/src/test-setup.ts diff --git a/apps/testing/23-harness/tailwind.config.js b/apps/testing/23-harness/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/testing/23-harness/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/testing/23-harness/tsconfig.app.json b/apps/testing/23-harness/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/testing/23-harness/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/testing/harness/tsconfig.editor.json b/apps/testing/23-harness/tsconfig.editor.json similarity index 100% rename from apps/testing/harness/tsconfig.editor.json rename to apps/testing/23-harness/tsconfig.editor.json diff --git a/apps/testing/create-harness/tsconfig.json b/apps/testing/23-harness/tsconfig.json similarity index 100% rename from apps/testing/create-harness/tsconfig.json rename to apps/testing/23-harness/tsconfig.json diff --git a/apps/testing/create-harness/tsconfig.spec.json b/apps/testing/23-harness/tsconfig.spec.json similarity index 100% rename from apps/testing/create-harness/tsconfig.spec.json rename to apps/testing/23-harness/tsconfig.spec.json diff --git a/apps/testing/modal/.eslintrc.json b/apps/testing/24-harness-creation/.eslintrc.json similarity index 100% rename from apps/testing/modal/.eslintrc.json rename to apps/testing/24-harness-creation/.eslintrc.json diff --git a/apps/testing/24-harness-creation/README.md b/apps/testing/24-harness-creation/README.md new file mode 100644 index 000000000..928bb1aab --- /dev/null +++ b/apps/testing/24-harness-creation/README.md @@ -0,0 +1,13 @@ +# Harness Creation + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve testing-harness-creation +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/24-harness-creation/). diff --git a/apps/testing/24-harness-creation/jest.config.ts b/apps/testing/24-harness-creation/jest.config.ts new file mode 100644 index 000000000..c4b9f08db --- /dev/null +++ b/apps/testing/24-harness-creation/jest.config.ts @@ -0,0 +1,21 @@ +/* eslint-disable */ +export default { + displayName: 'testing-harness-creation', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/testing/24-harness-creation/project.json b/apps/testing/24-harness-creation/project.json new file mode 100644 index 000000000..f65194122 --- /dev/null +++ b/apps/testing/24-harness-creation/project.json @@ -0,0 +1,86 @@ +{ + "name": "testing-harness-creation", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/testing/24-harness-creation/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/24-harness-creation", + "index": "apps/testing/24-harness-creation/src/index.html", + "main": "apps/testing/24-harness-creation/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/24-harness-creation/tsconfig.app.json", + "assets": [ + "apps/testing/24-harness-creation/src/favicon.ico", + "apps/testing/24-harness-creation/src/assets" + ], + "styles": ["apps/testing/24-harness-creation/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-harness-creation:build:production" + }, + "development": { + "buildTarget": "testing-harness-creation:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-harness-creation:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/testing/create-harness/src/app/app.component.spec.ts b/apps/testing/24-harness-creation/src/app/app.component.spec.ts similarity index 100% rename from apps/testing/create-harness/src/app/app.component.spec.ts rename to apps/testing/24-harness-creation/src/app/app.component.spec.ts diff --git a/apps/testing/24-harness-creation/src/app/app.component.ts b/apps/testing/24-harness-creation/src/app/app.component.ts new file mode 100644 index 000000000..b1c16240e --- /dev/null +++ b/apps/testing/24-harness-creation/src/app/app.component.ts @@ -0,0 +1,27 @@ +import { Component, signal } from '@angular/core'; +import { SliderComponent } from './slider.component'; + +@Component({ + imports: [SliderComponent], + selector: 'app-root', + template: ` +

    Slider 1: {{ slider1Value() }}

    + +

    Slider 2: {{ slider2Value() }}

    +

    Enabled only if Slider 1 > 20

    + + `, + styles: [''], +}) +export class AppComponent { + slider1Value = signal(10); + slider2Value = signal(0); +} diff --git a/apps/testing/harness/src/app/app.config.ts b/apps/testing/24-harness-creation/src/app/app.config.ts similarity index 100% rename from apps/testing/harness/src/app/app.config.ts rename to apps/testing/24-harness-creation/src/app/app.config.ts diff --git a/apps/testing/create-harness/src/app/slider.component.ts b/apps/testing/24-harness-creation/src/app/slider.component.ts similarity index 99% rename from apps/testing/create-harness/src/app/slider.component.ts rename to apps/testing/24-harness-creation/src/app/slider.component.ts index bc77ff1f3..4a3e429bb 100644 --- a/apps/testing/create-harness/src/app/slider.component.ts +++ b/apps/testing/24-harness-creation/src/app/slider.component.ts @@ -48,7 +48,6 @@ import { skip } from 'rxjs'; } `, ], - standalone: true, imports: [MatCardModule, MatSliderModule, MatIconModule, FormsModule], }) export class SliderComponent implements OnInit { diff --git a/apps/testing/create-harness/src/app/slider.harness.ts b/apps/testing/24-harness-creation/src/app/slider.harness.ts similarity index 100% rename from apps/testing/create-harness/src/app/slider.harness.ts rename to apps/testing/24-harness-creation/src/app/slider.harness.ts diff --git a/apps/typescript/enums-vs-union-types/src/assets/.gitkeep b/apps/testing/24-harness-creation/src/assets/.gitkeep similarity index 100% rename from apps/typescript/enums-vs-union-types/src/assets/.gitkeep rename to apps/testing/24-harness-creation/src/assets/.gitkeep diff --git a/apps/testing/24-harness-creation/src/favicon.ico b/apps/testing/24-harness-creation/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/testing/24-harness-creation/src/favicon.ico differ diff --git a/apps/testing/24-harness-creation/src/index.html b/apps/testing/24-harness-creation/src/index.html new file mode 100644 index 000000000..c66440e5c --- /dev/null +++ b/apps/testing/24-harness-creation/src/index.html @@ -0,0 +1,16 @@ + + + + + testing-harness-creation + + + + + + + + + diff --git a/apps/testing/24-harness-creation/src/main.ts b/apps/testing/24-harness-creation/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/testing/24-harness-creation/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/testing/24-harness-creation/src/styles.scss b/apps/testing/24-harness-creation/src/styles.scss new file mode 100644 index 000000000..1430d2f9b --- /dev/null +++ b/apps/testing/24-harness-creation/src/styles.scss @@ -0,0 +1,30 @@ +/* You can add global styles to this file, and also import other style files */ +@use '@angular/material' as mat; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ + +@include mat.elevation-classes(); +@include mat.app-background(); + +$theme-primary: mat.m2-define-palette(mat.$m2-indigo-palette); +$theme-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400); + +$theme-warn: mat.m2-define-palette(mat.$m2-red-palette); + +$theme: mat.m2-define-light-theme( + ( + color: ( + primary: $theme-primary, + accent: $theme-accent, + warn: $theme-warn, + ), + typography: mat.m2-define-typography-config(), + ) +); + +@include mat.dialog-theme($theme); +@include mat.all-component-themes($theme); diff --git a/apps/testing/todos-list/src/test-setup.ts b/apps/testing/24-harness-creation/src/test-setup.ts similarity index 100% rename from apps/testing/todos-list/src/test-setup.ts rename to apps/testing/24-harness-creation/src/test-setup.ts diff --git a/apps/testing/24-harness-creation/tailwind.config.js b/apps/testing/24-harness-creation/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/testing/24-harness-creation/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/testing/24-harness-creation/tsconfig.app.json b/apps/testing/24-harness-creation/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/testing/24-harness-creation/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/testing/create-harness/tsconfig.editor.json b/apps/testing/24-harness-creation/tsconfig.editor.json similarity index 100% rename from apps/testing/create-harness/tsconfig.editor.json rename to apps/testing/24-harness-creation/tsconfig.editor.json diff --git a/apps/testing/harness/tsconfig.json b/apps/testing/24-harness-creation/tsconfig.json similarity index 100% rename from apps/testing/harness/tsconfig.json rename to apps/testing/24-harness-creation/tsconfig.json diff --git a/apps/testing/harness/tsconfig.spec.json b/apps/testing/24-harness-creation/tsconfig.spec.json similarity index 100% rename from apps/testing/harness/tsconfig.spec.json rename to apps/testing/24-harness-creation/tsconfig.spec.json diff --git a/apps/testing/router-outlet/.eslintrc.json b/apps/testing/28-checkbox/.eslintrc.json similarity index 100% rename from apps/testing/router-outlet/.eslintrc.json rename to apps/testing/28-checkbox/.eslintrc.json diff --git a/apps/testing/checkbox/README.md b/apps/testing/28-checkbox/README.md similarity index 100% rename from apps/testing/checkbox/README.md rename to apps/testing/28-checkbox/README.md diff --git a/apps/testing/28-checkbox/jest.config.ts b/apps/testing/28-checkbox/jest.config.ts new file mode 100644 index 000000000..c27a02c1e --- /dev/null +++ b/apps/testing/28-checkbox/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'testing-checkbox', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/testing/28-checkbox', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/testing/28-checkbox/project.json b/apps/testing/28-checkbox/project.json new file mode 100644 index 000000000..982e85c2e --- /dev/null +++ b/apps/testing/28-checkbox/project.json @@ -0,0 +1,82 @@ +{ + "name": "testing-checkbox", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/testing/28-checkbox/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/28-checkbox", + "index": "apps/testing/28-checkbox/src/index.html", + "main": "apps/testing/28-checkbox/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/28-checkbox/tsconfig.app.json", + "assets": [ + "apps/testing/28-checkbox/src/favicon.ico", + "apps/testing/28-checkbox/src/assets" + ], + "styles": ["apps/testing/28-checkbox/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-checkbox:build:production" + }, + "development": { + "buildTarget": "testing-checkbox:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-checkbox:build" + } + }, + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/apps/testing/checkbox/src/app/app.component.spec.ts b/apps/testing/28-checkbox/src/app/app.component.spec.ts similarity index 100% rename from apps/testing/checkbox/src/app/app.component.spec.ts rename to apps/testing/28-checkbox/src/app/app.component.spec.ts diff --git a/apps/testing/checkbox/src/app/app.component.ts b/apps/testing/28-checkbox/src/app/app.component.ts similarity index 100% rename from apps/testing/checkbox/src/app/app.component.ts rename to apps/testing/28-checkbox/src/app/app.component.ts diff --git a/apps/typescript/overload/src/assets/.gitkeep b/apps/testing/28-checkbox/src/assets/.gitkeep similarity index 100% rename from apps/typescript/overload/src/assets/.gitkeep rename to apps/testing/28-checkbox/src/assets/.gitkeep diff --git a/apps/testing/28-checkbox/src/favicon.ico b/apps/testing/28-checkbox/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/testing/28-checkbox/src/favicon.ico differ diff --git a/apps/testing/checkbox/src/index.html b/apps/testing/28-checkbox/src/index.html similarity index 100% rename from apps/testing/checkbox/src/index.html rename to apps/testing/28-checkbox/src/index.html diff --git a/apps/typescript/overload/src/main.ts b/apps/testing/28-checkbox/src/main.ts similarity index 100% rename from apps/typescript/overload/src/main.ts rename to apps/testing/28-checkbox/src/main.ts diff --git a/apps/testing/28-checkbox/src/styles.scss b/apps/testing/28-checkbox/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/testing/28-checkbox/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/testing/28-checkbox/src/test-setup.ts b/apps/testing/28-checkbox/src/test-setup.ts new file mode 100644 index 000000000..15de72a3c --- /dev/null +++ b/apps/testing/28-checkbox/src/test-setup.ts @@ -0,0 +1,2 @@ +import '@testing-library/jest-dom'; +import 'jest-preset-angular/setup-jest'; diff --git a/apps/testing/28-checkbox/tailwind.config.js b/apps/testing/28-checkbox/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/testing/28-checkbox/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/testing/28-checkbox/tsconfig.app.json b/apps/testing/28-checkbox/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/testing/28-checkbox/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/testing/router-outlet/tsconfig.editor.json b/apps/testing/28-checkbox/tsconfig.editor.json similarity index 100% rename from apps/testing/router-outlet/tsconfig.editor.json rename to apps/testing/28-checkbox/tsconfig.editor.json diff --git a/apps/testing/checkbox/tsconfig.json b/apps/testing/28-checkbox/tsconfig.json similarity index 100% rename from apps/testing/checkbox/tsconfig.json rename to apps/testing/28-checkbox/tsconfig.json diff --git a/apps/testing/router-outlet/tsconfig.spec.json b/apps/testing/28-checkbox/tsconfig.spec.json similarity index 100% rename from apps/testing/router-outlet/tsconfig.spec.json rename to apps/testing/28-checkbox/tsconfig.spec.json diff --git a/apps/testing/table/.eslintrc.json b/apps/testing/29-real-life-application/.eslintrc.json similarity index 100% rename from apps/testing/table/.eslintrc.json rename to apps/testing/29-real-life-application/.eslintrc.json diff --git a/apps/testing/29-real-life-application/README.md b/apps/testing/29-real-life-application/README.md new file mode 100644 index 000000000..8dcfe21e6 --- /dev/null +++ b/apps/testing/29-real-life-application/README.md @@ -0,0 +1,13 @@ +# Real-life Application + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve testing-real-life-application +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/29-real-application/). diff --git a/apps/testing/table/cypress.config.ts b/apps/testing/29-real-life-application/cypress.config.ts similarity index 100% rename from apps/testing/table/cypress.config.ts rename to apps/testing/29-real-life-application/cypress.config.ts diff --git a/apps/testing/table/cypress/fixtures/example.json b/apps/testing/29-real-life-application/cypress/fixtures/example.json similarity index 100% rename from apps/testing/table/cypress/fixtures/example.json rename to apps/testing/29-real-life-application/cypress/fixtures/example.json diff --git a/apps/testing/29-real-life-application/cypress/support/commands.ts b/apps/testing/29-real-life-application/cypress/support/commands.ts new file mode 100644 index 000000000..b5d8a9582 --- /dev/null +++ b/apps/testing/29-real-life-application/cypress/support/commands.ts @@ -0,0 +1,25 @@ +/// +import { mount } from 'cypress/angular'; + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + mount: typeof mount; + } + } +} + +Cypress.Commands.add('mount', mount); diff --git a/apps/testing/todos-list/cypress/support/component-index.html b/apps/testing/29-real-life-application/cypress/support/component-index.html similarity index 100% rename from apps/testing/todos-list/cypress/support/component-index.html rename to apps/testing/29-real-life-application/cypress/support/component-index.html diff --git a/apps/testing/table/cypress/support/component.ts b/apps/testing/29-real-life-application/cypress/support/component.ts similarity index 100% rename from apps/testing/table/cypress/support/component.ts rename to apps/testing/29-real-life-application/cypress/support/component.ts diff --git a/apps/testing/table/cypress/tsconfig.json b/apps/testing/29-real-life-application/cypress/tsconfig.json similarity index 100% rename from apps/testing/table/cypress/tsconfig.json rename to apps/testing/29-real-life-application/cypress/tsconfig.json diff --git a/apps/testing/29-real-life-application/jest.config.ts b/apps/testing/29-real-life-application/jest.config.ts new file mode 100644 index 000000000..b78a06561 --- /dev/null +++ b/apps/testing/29-real-life-application/jest.config.ts @@ -0,0 +1,21 @@ +/* eslint-disable */ +export default { + displayName: 'testing-real-life-application', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/testing/29-real-life-application/project.json b/apps/testing/29-real-life-application/project.json new file mode 100644 index 000000000..c6f1c12d9 --- /dev/null +++ b/apps/testing/29-real-life-application/project.json @@ -0,0 +1,99 @@ +{ + "name": "testing-real-life-application", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/testing/29-real-life-application/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/testing/29-real-life-application", + "index": "apps/testing/29-real-life-application/src/index.html", + "main": "apps/testing/29-real-life-application/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/testing/29-real-life-application/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/testing/29-real-life-application/src/favicon.ico", + "apps/testing/29-real-life-application/src/assets" + ], + "styles": [ + "apps/testing/29-real-life-application/src/styles.scss", + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "testing-real-life-application:build:production" + }, + "development": { + "buildTarget": "testing-real-life-application:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "testing-real-life-application:build" + } + }, + "test": { + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}", + "{projectRoot}/coverage" + ], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + }, + "component-test": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/testing/29-real-life-application/cypress.config.ts", + "testingType": "component", + "skipServe": true, + "devServerTarget": "testing-real-life-application:build" + } + } + } +} diff --git a/apps/testing/29-real-life-application/src/app/app.component.ts b/apps/testing/29-real-life-application/src/app/app.component.ts new file mode 100644 index 000000000..a4b00aaec --- /dev/null +++ b/apps/testing/29-real-life-application/src/app/app.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + imports: [RouterOutlet], + template: ` + + `, +}) +export class AppComponent {} diff --git a/apps/testing/todos-list/src/app/app.config.ts b/apps/testing/29-real-life-application/src/app/app.config.ts similarity index 100% rename from apps/testing/todos-list/src/app/app.config.ts rename to apps/testing/29-real-life-application/src/app/app.config.ts diff --git a/apps/testing/todos-list/src/app/app.route.ts b/apps/testing/29-real-life-application/src/app/app.route.ts similarity index 100% rename from apps/testing/todos-list/src/app/app.route.ts rename to apps/testing/29-real-life-application/src/app/app.route.ts diff --git a/apps/testing/todos-list/src/app/backend.service.ts b/apps/testing/29-real-life-application/src/app/backend.service.ts similarity index 100% rename from apps/testing/todos-list/src/app/backend.service.ts rename to apps/testing/29-real-life-application/src/app/backend.service.ts diff --git a/apps/testing/29-real-life-application/src/app/detail/detail.component.ts b/apps/testing/29-real-life-application/src/app/detail/detail.component.ts new file mode 100644 index 000000000..4944e0a35 --- /dev/null +++ b/apps/testing/29-real-life-application/src/app/detail/detail.component.ts @@ -0,0 +1,56 @@ +import { Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { RouterLink } from '@angular/router'; +import { LetDirective } from '@ngrx/component'; +import { provideComponentStore } from '@ngrx/component-store'; +import { DetailStore } from './detail.store'; + +@Component({ + selector: 'app-detail', + imports: [MatButtonModule, RouterLink, MatProgressBarModule, LetDirective], + template: ` +

    Ticket Detail:

    + + @if (vm.loading) { + + } + @if (vm.ticket) { +
    +
    + Ticket: + {{ vm.ticket.id }} +
    +
    + Description: + {{ vm.ticket.description }} +
    +
    + AssigneeId: + {{ vm.ticket.assigneeId }} +
    +
    + Is done: + {{ vm.ticket.completed }} +
    +
    + } +
    + + + `, + providers: [provideComponentStore(DetailStore)], + host: { + class: 'p-5 block', + }, +}) +export class DetailComponent { + vm$ = inject(DetailStore).vm$; +} diff --git a/apps/testing/todos-list/src/app/detail/detail.store.ts b/apps/testing/29-real-life-application/src/app/detail/detail.store.ts similarity index 100% rename from apps/testing/todos-list/src/app/detail/detail.store.ts rename to apps/testing/29-real-life-application/src/app/detail/detail.store.ts diff --git a/apps/testing/todos-list/src/app/list/list.component.spec.ts b/apps/testing/29-real-life-application/src/app/list/list.component.spec.ts similarity index 100% rename from apps/testing/todos-list/src/app/list/list.component.spec.ts rename to apps/testing/29-real-life-application/src/app/list/list.component.spec.ts diff --git a/apps/testing/29-real-life-application/src/app/list/list.component.ts b/apps/testing/29-real-life-application/src/app/list/list.component.ts new file mode 100644 index 000000000..513e3242c --- /dev/null +++ b/apps/testing/29-real-life-application/src/app/list/list.component.ts @@ -0,0 +1,71 @@ +import { Component, inject, OnInit } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { LetDirective } from '@ngrx/component'; +import { provideComponentStore } from '@ngrx/component-store'; +import { TicketStore } from './ticket.store'; +import { AddComponent } from './ui/add.component'; +import { RowComponent } from './ui/row.component'; + +@Component({ + selector: 'app-list', + imports: [ + ReactiveFormsModule, + AddComponent, + RowComponent, + MatFormFieldModule, + MatProgressBarModule, + MatInputModule, + LetDirective, + ], + template: ` +

    Tickets

    + + + Search + + + + + + + @if (vm.loading) { + + } +
      + @for (ticket of vm.tickets; track ticket.id) { + + } +
    +
    + {{ vm.error }} +
    +
    + `, + providers: [provideComponentStore(TicketStore)], + host: { + class: 'p-5 block', + }, +}) +export class ListComponent implements OnInit { + ticketStore = inject(TicketStore); + readonly vm$ = this.ticketStore.vm$; + + search = new FormControl(); + + ngOnInit(): void { + this.ticketStore.search(this.search.valueChanges); + } +} diff --git a/apps/testing/todos-list/src/app/list/ticket.store.spec.ts b/apps/testing/29-real-life-application/src/app/list/ticket.store.spec.ts similarity index 100% rename from apps/testing/todos-list/src/app/list/ticket.store.spec.ts rename to apps/testing/29-real-life-application/src/app/list/ticket.store.spec.ts diff --git a/apps/testing/todos-list/src/app/list/ticket.store.ts b/apps/testing/29-real-life-application/src/app/list/ticket.store.ts similarity index 100% rename from apps/testing/todos-list/src/app/list/ticket.store.ts rename to apps/testing/29-real-life-application/src/app/list/ticket.store.ts diff --git a/apps/testing/todos-list/src/app/list/ui/add.component.ts b/apps/testing/29-real-life-application/src/app/list/ui/add.component.ts similarity index 98% rename from apps/testing/todos-list/src/app/list/ui/add.component.ts rename to apps/testing/29-real-life-application/src/app/list/ui/add.component.ts index b3e517125..c48e85a9a 100644 --- a/apps/testing/todos-list/src/app/list/ui/add.component.ts +++ b/apps/testing/29-real-life-application/src/app/list/ui/add.component.ts @@ -12,7 +12,6 @@ import { MatInputModule } from '@angular/material/input'; @Component({ selector: 'app-add', - standalone: true, imports: [ ReactiveFormsModule, MatFormFieldModule, diff --git a/apps/testing/todos-list/src/app/list/ui/row.component.spec.ts b/apps/testing/29-real-life-application/src/app/list/ui/row.component.spec.ts similarity index 100% rename from apps/testing/todos-list/src/app/list/ui/row.component.spec.ts rename to apps/testing/29-real-life-application/src/app/list/ui/row.component.spec.ts diff --git a/apps/testing/todos-list/src/app/list/ui/row.component.ts b/apps/testing/29-real-life-application/src/app/list/ui/row.component.ts similarity index 99% rename from apps/testing/todos-list/src/app/list/ui/row.component.ts rename to apps/testing/29-real-life-application/src/app/list/ui/row.component.ts index ca97d0d65..12c7d5367 100644 --- a/apps/testing/todos-list/src/app/list/ui/row.component.ts +++ b/apps/testing/29-real-life-application/src/app/list/ui/row.component.ts @@ -10,7 +10,6 @@ import { Ticket, TicketUser, User } from '../../backend.service'; @Component({ selector: 'app-row', - standalone: true, imports: [ RouterLink, ReactiveFormsModule, diff --git a/NX b/apps/testing/29-real-life-application/src/assets/.gitkeep similarity index 100% rename from NX rename to apps/testing/29-real-life-application/src/assets/.gitkeep diff --git a/apps/testing/29-real-life-application/src/favicon.ico b/apps/testing/29-real-life-application/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/testing/29-real-life-application/src/favicon.ico differ diff --git a/apps/testing/29-real-life-application/src/index.html b/apps/testing/29-real-life-application/src/index.html new file mode 100644 index 000000000..91b0bf44a --- /dev/null +++ b/apps/testing/29-real-life-application/src/index.html @@ -0,0 +1,13 @@ + + + + + testing-real-life-application + + + + + + + + diff --git a/apps/testing/todos-list/src/main.ts b/apps/testing/29-real-life-application/src/main.ts similarity index 100% rename from apps/testing/todos-list/src/main.ts rename to apps/testing/29-real-life-application/src/main.ts diff --git a/apps/testing/todos-list/src/styles.scss b/apps/testing/29-real-life-application/src/styles.scss similarity index 100% rename from apps/testing/todos-list/src/styles.scss rename to apps/testing/29-real-life-application/src/styles.scss diff --git a/apps/testing/29-real-life-application/src/test-setup.ts b/apps/testing/29-real-life-application/src/test-setup.ts new file mode 100644 index 000000000..15de72a3c --- /dev/null +++ b/apps/testing/29-real-life-application/src/test-setup.ts @@ -0,0 +1,2 @@ +import '@testing-library/jest-dom'; +import 'jest-preset-angular/setup-jest'; diff --git a/apps/testing/29-real-life-application/tailwind.config.js b/apps/testing/29-real-life-application/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/testing/29-real-life-application/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/testing/table/tsconfig.app.json b/apps/testing/29-real-life-application/tsconfig.app.json similarity index 100% rename from apps/testing/table/tsconfig.app.json rename to apps/testing/29-real-life-application/tsconfig.app.json diff --git a/apps/testing/table/tsconfig.editor.json b/apps/testing/29-real-life-application/tsconfig.editor.json similarity index 100% rename from apps/testing/table/tsconfig.editor.json rename to apps/testing/29-real-life-application/tsconfig.editor.json diff --git a/apps/testing/29-real-life-application/tsconfig.json b/apps/testing/29-real-life-application/tsconfig.json new file mode 100644 index 000000000..c0f4e6dd3 --- /dev/null +++ b/apps/testing/29-real-life-application/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./cypress/tsconfig .json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/testing/todos-list/tsconfig.spec.json b/apps/testing/29-real-life-application/tsconfig.spec.json similarity index 100% rename from apps/testing/todos-list/tsconfig.spec.json rename to apps/testing/29-real-life-application/tsconfig.spec.json diff --git a/apps/testing/checkbox/jest.config.ts b/apps/testing/checkbox/jest.config.ts deleted file mode 100644 index 0c5902908..000000000 --- a/apps/testing/checkbox/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'testing-checkbox', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - coverageDirectory: '../../../coverage/apps/testing/checkbox', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/testing/checkbox/project.json b/apps/testing/checkbox/project.json deleted file mode 100644 index 7a7ae0f1b..000000000 --- a/apps/testing/checkbox/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "testing-checkbox", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/testing/checkbox/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/checkbox", - "index": "apps/testing/checkbox/src/index.html", - "main": "apps/testing/checkbox/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/checkbox/tsconfig.app.json", - "assets": [ - "apps/testing/checkbox/src/favicon.ico", - "apps/testing/checkbox/src/assets" - ], - "styles": ["apps/testing/checkbox/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-checkbox:build:production" - }, - "development": { - "buildTarget": "testing-checkbox:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-checkbox:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/checkbox/jest.config.ts" - } - } - } -} diff --git a/apps/testing/create-harness/README.md b/apps/testing/create-harness/README.md deleted file mode 100644 index 600f34055..000000000 --- a/apps/testing/create-harness/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Harness Creation - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve testing-create-harness -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/24-harness-creation/). diff --git a/apps/testing/create-harness/jest.config.ts b/apps/testing/create-harness/jest.config.ts deleted file mode 100644 index 1178b8aaf..000000000 --- a/apps/testing/create-harness/jest.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'testing-create-harness', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/testing/create-harness/project.json b/apps/testing/create-harness/project.json deleted file mode 100644 index 800c4d577..000000000 --- a/apps/testing/create-harness/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "testing-create-harness", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/testing/create-harness/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/create-harness", - "index": "apps/testing/create-harness/src/index.html", - "main": "apps/testing/create-harness/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/create-harness/tsconfig.app.json", - "assets": [ - "apps/testing/create-harness/src/favicon.ico", - "apps/testing/create-harness/src/assets" - ], - "styles": ["apps/testing/create-harness/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-create-harness:build:production" - }, - "development": { - "buildTarget": "testing-create-harness:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-create-harness:build" - } - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/create-harness/jest.config.ts" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/testing/create-harness/src/app/app.component.ts b/apps/testing/create-harness/src/app/app.component.ts deleted file mode 100644 index 06c7eb9af..000000000 --- a/apps/testing/create-harness/src/app/app.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component, signal } from '@angular/core'; -import { SliderComponent } from './slider.component'; - -@Component({ - standalone: true, - imports: [SliderComponent], - selector: 'app-root', - template: ` -

    Slider 1: {{ slider1Value() }}

    - -

    Slider 2: {{ slider2Value() }}

    -

    Enabled only if Slider 1 > 20

    - - `, - styles: [''], -}) -export class AppComponent { - slider1Value = signal(10); - slider2Value = signal(0); -} diff --git a/apps/testing/create-harness/src/index.html b/apps/testing/create-harness/src/index.html deleted file mode 100644 index 03bd39e4f..000000000 --- a/apps/testing/create-harness/src/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - create-harness - - - - - - - - - diff --git a/apps/testing/create-harness/src/styles.scss b/apps/testing/create-harness/src/styles.scss deleted file mode 100644 index 94dbf8ff3..000000000 --- a/apps/testing/create-harness/src/styles.scss +++ /dev/null @@ -1,29 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ -@use '@angular/material' as mat; - -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* You can add global styles to this file, and also import other style files */ - -@include mat.core(); - -$theme-primary: mat.define-palette(mat.$indigo-palette); -$theme-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); - -$theme-warn: mat.define-palette(mat.$red-palette); - -$theme: mat.define-light-theme( - ( - color: ( - primary: $theme-primary, - accent: $theme-accent, - warn: $theme-warn, - ), - typography: mat.define-typography-config(), - ) -); - -@include mat.dialog-theme($theme); -@include mat.all-component-themes($theme); diff --git a/apps/testing/harness/project.json b/apps/testing/harness/project.json deleted file mode 100644 index 6312e83d4..000000000 --- a/apps/testing/harness/project.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "testing-harness", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/testing/harness/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/harness", - "index": "apps/testing/harness/src/index.html", - "main": "apps/testing/harness/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/harness/tsconfig.app.json", - "assets": [ - "apps/testing/harness/src/favicon.ico", - "apps/testing/harness/src/assets" - ], - "styles": ["apps/testing/harness/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-harness:build:production" - }, - "development": { - "buildTarget": "testing-harness:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-harness:build" - } - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/harness/jest.config.ts" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - } -} diff --git a/apps/testing/harness/src/app/app.component.ts b/apps/testing/harness/src/app/app.component.ts deleted file mode 100644 index 1f89505d8..000000000 --- a/apps/testing/harness/src/app/app.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component } from '@angular/core'; -import { ChildComponent } from './child.component'; - -@Component({ - standalone: true, - imports: [ChildComponent], - selector: 'app-root', - template: ` - - `, - styles: [''], -}) -export class AppComponent {} diff --git a/apps/testing/harness/src/app/child.component.ts b/apps/testing/harness/src/app/child.component.ts deleted file mode 100644 index cc9a03e74..000000000 --- a/apps/testing/harness/src/app/child.component.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Component } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { MatCardModule } from '@angular/material/card'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatSliderModule } from '@angular/material/slider'; - -@Component({ - selector: 'app-child', - template: ` - - -

    Slider configuration

    - -
    - - Value - - - - Min value - - - - Max value - - - - Step size - - -
    - -
    - Show ticks -
    - -
    - Show thumb label -
    - -
    - Disabled -
    -
    -
    - - - -
    - - - - - -
    -
    -
    - `, - styles: [ - ` - .mat-mdc-slider { - max-width: 300px; - width: 100%; - } - - .mat-mdc-card + .mat-mdc-card { - margin-top: 8px; - } - `, - ], - standalone: true, - imports: [ - MatCardModule, - MatFormFieldModule, - MatInputModule, - FormsModule, - MatCheckboxModule, - MatSliderModule, - MatIconModule, - ], -}) -export class ChildComponent { - disabled = false; - max = 100; - min = 0; - showTicks = false; - step = 1; - thumbLabel = false; - value = 0; - - back() { - if (this.value - this.step >= this.min) { - this.value -= this.step; - } - } - - forward() { - if (this.value + this.step <= this.max) { - this.value += this.step; - } - } -} diff --git a/apps/testing/harness/src/index.html b/apps/testing/harness/src/index.html deleted file mode 100644 index a4122017e..000000000 --- a/apps/testing/harness/src/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - harness - - - - - - - - - diff --git a/apps/testing/harness/src/styles.scss b/apps/testing/harness/src/styles.scss deleted file mode 100644 index c9c067e5f..000000000 --- a/apps/testing/harness/src/styles.scss +++ /dev/null @@ -1,28 +0,0 @@ -@use '@angular/material' as mat; - -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* You can add global styles to this file, and also import other style files */ - -@include mat.core(); - -$theme-primary: mat.define-palette(mat.$indigo-palette); -$theme-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); - -$theme-warn: mat.define-palette(mat.$red-palette); - -$theme: mat.define-light-theme( - ( - color: ( - primary: $theme-primary, - accent: $theme-accent, - warn: $theme-warn, - ), - typography: mat.define-typography-config(), - ) -); - -@include mat.dialog-theme($theme); -@include mat.all-component-themes($theme); diff --git a/apps/testing/input-output/cypress/support/commands.ts b/apps/testing/input-output/cypress/support/commands.ts deleted file mode 100644 index e6c897603..000000000 --- a/apps/testing/input-output/cypress/support/commands.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -import { mount } from 'cypress/angular'; - -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - mount: typeof mount; - } - } -} - -Cypress.Commands.add('mount', mount); - -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/testing/input-output/project.json b/apps/testing/input-output/project.json deleted file mode 100644 index 4863e5e7b..000000000 --- a/apps/testing/input-output/project.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "name": "testing-input-output", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/testing/input-output/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/input-output", - "index": "apps/testing/input-output/src/index.html", - "main": "apps/testing/input-output/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/input-output/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/testing/input-output/src/favicon.ico", - "apps/testing/input-output/src/assets" - ], - "styles": ["apps/testing/input-output/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-input-output:build:production" - }, - "development": { - "buildTarget": "testing-input-output:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-input-output:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/input-output/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/testing/input-output/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "testing-input-output:build" - } - } - }, - "tags": [] -} diff --git a/apps/testing/input-output/src/app/app.component.ts b/apps/testing/input-output/src/app/app.component.ts deleted file mode 100644 index 6a6392a4b..000000000 --- a/apps/testing/input-output/src/app/app.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { CounterComponent } from './counter.component'; - -@Component({ - standalone: true, - imports: [CounterComponent], - selector: 'app-root', - template: ` - - `, -}) -export class AppComponent { - log(counter: number) { - console.log('output log', counter); - } -} diff --git a/apps/testing/input-output/src/app/counter.component.ts b/apps/testing/input-output/src/app/counter.component.ts deleted file mode 100644 index 1ca95430d..000000000 --- a/apps/testing/input-output/src/app/counter.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { AsyncPipe } from '@angular/common'; -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, -} from '@angular/core'; -import { LetDirective } from '@ngrx/component'; -import { ComponentStore } from '@ngrx/component-store'; - -@Component({ - selector: 'app-counter', - standalone: true, - imports: [AsyncPipe, LetDirective], - template: ` - -

    Counter: {{ counter }}

    - - - -
    - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class CounterComponent extends ComponentStore<{ counter: number }> { - @Input() set initialValue(initialValue: number) { - this.patchState({ counter: initialValue }); - } - - @Output() send = new EventEmitter(); - - readonly counter$ = this.select((state) => state.counter); - - readonly increment = this.updater((state) => ({ - counter: state.counter + 1, - })); - readonly decrement = this.updater((state) => ({ - counter: state.counter - 1, - })); - - constructor() { - super({ counter: 0 }); - } -} diff --git a/apps/testing/input-output/src/index.html b/apps/testing/input-output/src/index.html deleted file mode 100644 index bb1c5edf3..000000000 --- a/apps/testing/input-output/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - TestingInputOutput - - - - - - - - diff --git a/apps/testing/modal/cypress/support/commands.ts b/apps/testing/modal/cypress/support/commands.ts deleted file mode 100644 index e6c897603..000000000 --- a/apps/testing/modal/cypress/support/commands.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -import { mount } from 'cypress/angular'; - -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - mount: typeof mount; - } - } -} - -Cypress.Commands.add('mount', mount); - -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/testing/modal/project.json b/apps/testing/modal/project.json deleted file mode 100644 index 55c019c44..000000000 --- a/apps/testing/modal/project.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "testing-modal", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/testing/modal/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/modal", - "index": "apps/testing/modal/src/index.html", - "main": "apps/testing/modal/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/modal/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/testing/modal/src/favicon.ico", - "apps/testing/modal/src/assets" - ], - "styles": [ - "apps/testing/modal/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-modal:build:production" - }, - "development": { - "buildTarget": "testing-modal:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-modal:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/modal/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/testing/modal/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "testing-modal:build" - } - } - }, - "tags": [] -} diff --git a/apps/testing/modal/src/app/app.component.ts b/apps/testing/modal/src/app/app.component.ts deleted file mode 100644 index afb4b291f..000000000 --- a/apps/testing/modal/src/app/app.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { AsyncPipe } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialog, MatDialogModule } from '@angular/material/dialog'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { BehaviorSubject } from 'rxjs'; -import { ErrorDialog } from './error.dialog'; -import { ProfilConfirmationDialog } from './profil-confirmation.dialog'; -@Component({ - standalone: true, - imports: [ - ReactiveFormsModule, - MatDialogModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - AsyncPipe, - ], - selector: 'app-root', - host: { - class: 'p-4 block flex gap-4 items-center', - }, - template: ` - - Name - - - - -
    {{ result$ | async }}
    - `, -}) -export class AppComponent { - private modal = inject(MatDialog); - private result = new BehaviorSubject(''); - result$ = this.result.asObservable(); - - name = new FormControl('', { nonNullable: true }); - - confirm() { - if (!this.name.value) { - this.modal.open(ErrorDialog); - return; - } - - this.modal - .open(ProfilConfirmationDialog, { - data: { - name: this.name.value, - }, - }) - .afterClosed() - .subscribe((result) => - this.result.next( - result ? 'Name has been submitted' : 'Name is invalid !!', - ), - ); - } -} diff --git a/apps/testing/modal/src/index.html b/apps/testing/modal/src/index.html deleted file mode 100644 index f63ff6890..000000000 --- a/apps/testing/modal/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - TestingModal - - - - - - - - diff --git a/apps/testing/nested/README.md b/apps/testing/nested/README.md deleted file mode 100644 index 8e1876a73..000000000 --- a/apps/testing/nested/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Nested Components - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve testing-nested -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/18-nested-comp/). diff --git a/apps/testing/nested/cypress/support/commands.ts b/apps/testing/nested/cypress/support/commands.ts deleted file mode 100644 index e6c897603..000000000 --- a/apps/testing/nested/cypress/support/commands.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -import { mount } from 'cypress/angular'; - -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - mount: typeof mount; - } - } -} - -Cypress.Commands.add('mount', mount); - -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/testing/nested/jest.config.ts b/apps/testing/nested/jest.config.ts deleted file mode 100644 index a89947bd9..000000000 --- a/apps/testing/nested/jest.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'testing-nested', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/testing/nested/project.json b/apps/testing/nested/project.json deleted file mode 100644 index 2c60724a2..000000000 --- a/apps/testing/nested/project.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "name": "testing-nested", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/testing/nested/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/nested", - "index": "apps/testing/nested/src/index.html", - "main": "apps/testing/nested/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/nested/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/testing/nested/src/favicon.ico", - "apps/testing/nested/src/assets" - ], - "styles": ["apps/testing/nested/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-nested:build:production" - }, - "development": { - "buildTarget": "testing-nested:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-nested:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/nested/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/testing/nested/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "testing-nested:build" - } - } - }, - "tags": [] -} diff --git a/apps/testing/nested/src/app/app.component.ts b/apps/testing/nested/src/app/app.component.ts deleted file mode 100644 index 098836b6f..000000000 --- a/apps/testing/nested/src/app/app.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { ChildComponent } from './child.component'; - -@Component({ - standalone: true, - imports: [ChildComponent], - selector: 'app-root', - template: ` - - `, -}) -export class AppComponent {} diff --git a/apps/testing/nested/src/app/child.component.ts b/apps/testing/nested/src/app/child.component.ts deleted file mode 100644 index bcee84ecd..000000000 --- a/apps/testing/nested/src/app/child.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { NgIf } from '@angular/common'; -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, - inject, -} from '@angular/core'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { HttpService } from './http.service'; - -@Component({ - selector: 'app-input', - standalone: true, - imports: [ReactiveFormsModule], - template: ` - - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class InputComponent { - title = new FormControl('', { nonNullable: true }); -} - -@Component({ - selector: 'result', - standalone: true, - template: ` -

    Title is {{ title }}

    - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ResultComponent { - @Input() title = ''; -} - -@Component({ - selector: 'app-button', - standalone: true, - template: ` - - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ButtonComponent { - @Output() validate = new EventEmitter(); -} - -@Component({ - selector: 'app-error', - standalone: true, - template: ` -

    Title is required !!!

    - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ErrorComponent { - @Output() validate = new EventEmitter(); -} - -@Component({ - selector: 'app-child', - standalone: true, - imports: [ - ResultComponent, - ButtonComponent, - InputComponent, - ErrorComponent, - NgIf, - ], - template: ` - - - - - `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ChildComponent { - http = inject(HttpService); - - showError = false; - - submit(title: string) { - this.showError = false; - if (title === '') { - this.showError = true; - return; - } - - this.http.sendTitle(title); - } -} diff --git a/apps/testing/nested/src/index.html b/apps/testing/nested/src/index.html deleted file mode 100644 index 620e0cd2d..000000000 --- a/apps/testing/nested/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - TestingNested - - - - - - - - diff --git a/apps/testing/router-outlet/README.md b/apps/testing/router-outlet/README.md deleted file mode 100644 index 774915930..000000000 --- a/apps/testing/router-outlet/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Router - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve testing-router-outlet -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/17-router/). diff --git a/apps/testing/router-outlet/cypress/support/commands.ts b/apps/testing/router-outlet/cypress/support/commands.ts deleted file mode 100644 index e6c897603..000000000 --- a/apps/testing/router-outlet/cypress/support/commands.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -import { mount } from 'cypress/angular'; - -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - mount: typeof mount; - } - } -} - -Cypress.Commands.add('mount', mount); - -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/testing/router-outlet/jest.config.ts b/apps/testing/router-outlet/jest.config.ts deleted file mode 100644 index 0bca8aebb..000000000 --- a/apps/testing/router-outlet/jest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'testing-router-outlet', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/testing/router-outlet/project.json b/apps/testing/router-outlet/project.json deleted file mode 100644 index 41697e12a..000000000 --- a/apps/testing/router-outlet/project.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "name": "testing-router-outlet", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/testing/router-outlet/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/router-outlet", - "index": "apps/testing/router-outlet/src/index.html", - "main": "apps/testing/router-outlet/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/router-outlet/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/testing/router-outlet/src/favicon.ico", - "apps/testing/router-outlet/src/assets" - ], - "styles": ["apps/testing/router-outlet/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-router-outlet:build:production" - }, - "development": { - "buildTarget": "testing-router-outlet:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-router-outlet:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/router-outlet/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/testing/router-outlet/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "testing-router-outlet:build" - } - } - }, - "tags": [] -} diff --git a/apps/testing/router-outlet/src/app/app.component.ts b/apps/testing/router-outlet/src/app/app.component.ts deleted file mode 100644 index 3b420a867..000000000 --- a/apps/testing/router-outlet/src/app/app.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterLink, RouterOutlet } from '@angular/router'; - -@Component({ - standalone: true, - imports: [RouterOutlet, RouterLink], - selector: 'app-root', - styles: [ - ` - h1 { - margin-bottom: 0; - } - nav a { - padding: 1rem; - text-decoration: none; - margin-top: 10px; - display: inline-block; - background-color: #e8e8e8; - color: #3d3d3d; - border-radius: 4px; - margin-bottom: 10px; - } - nav a:hover { - color: white; - background-color: #42545c; - } - nav a.active { - background-color: black; - } - `, - ], - template: ` -

    Library

    - - - - - `, -}) -export class AppComponent {} diff --git a/apps/testing/router-outlet/src/index.html b/apps/testing/router-outlet/src/index.html deleted file mode 100644 index b7065804e..000000000 --- a/apps/testing/router-outlet/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - RouterTesting - - - - - - - - diff --git a/apps/testing/router-outlet/src/styles.scss b/apps/testing/router-outlet/src/styles.scss deleted file mode 100644 index 90d4ee007..000000000 --- a/apps/testing/router-outlet/src/styles.scss +++ /dev/null @@ -1 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ diff --git a/apps/testing/router-outlet/tsconfig.json b/apps/testing/router-outlet/tsconfig.json deleted file mode 100644 index 3879b94cd..000000000 --- a/apps/testing/router-outlet/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - }, - { - "path": "./cypress/tsconfig.json" - } - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/testing/table/README.md b/apps/testing/table/README.md deleted file mode 100644 index ff3b4b560..000000000 --- a/apps/testing/table/README.md +++ /dev/null @@ -1,38 +0,0 @@ -

    Table testing

    - -> author: thomas-laforge - -## Statement: - -NOT IMPLEMENTED YET - - - -### Submitting your work - -1. Fork the project -2. clone it -3. npm ci -4. `npx nx serve testing-table` to play with the application -5. `npx nx test testing-table` to test your application with Testing Library -6. `npx nx component-test testing-table --watch` to test your application with Cypress -7. _...work on it_ -8. Commit your work -9. Submit a PR with a title beginning with **Answer:22** that I will review and other dev can review. - -nested testing -nested testing solution author - - - -_You can ask any question on_ twitter diff --git a/apps/testing/table/cypress/support/commands.ts b/apps/testing/table/cypress/support/commands.ts deleted file mode 100644 index e6c897603..000000000 --- a/apps/testing/table/cypress/support/commands.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -import { mount } from 'cypress/angular'; - -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - mount: typeof mount; - } - } -} - -Cypress.Commands.add('mount', mount); - -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/testing/table/cypress/support/component-index.html b/apps/testing/table/cypress/support/component-index.html deleted file mode 100644 index c0a4681dc..000000000 --- a/apps/testing/table/cypress/support/component-index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - testing-table Components App - - -
    - - diff --git a/apps/testing/table/jest.config.ts b/apps/testing/table/jest.config.ts deleted file mode 100644 index 56b0e7df7..000000000 --- a/apps/testing/table/jest.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'testing-table', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/testing/table/project.json b/apps/testing/table/project.json deleted file mode 100644 index db95cb4ea..000000000 --- a/apps/testing/table/project.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "name": "testing-table", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/testing/table/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/table", - "index": "apps/testing/table/src/index.html", - "main": "apps/testing/table/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/table/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/testing/table/src/favicon.ico", - "apps/testing/table/src/assets" - ], - "styles": ["apps/testing/table/src/styles.scss"], - "scripts": [], - "allowedCommonJsDependencies": ["seedrandom"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-table:build:production" - }, - "development": { - "buildTarget": "testing-table:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-table:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/table/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/testing/table/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "testing-table:build" - } - } - }, - "tags": [] -} diff --git a/apps/testing/table/src/app/app.component.ts b/apps/testing/table/src/app/app.component.ts deleted file mode 100644 index 236bc50a6..000000000 --- a/apps/testing/table/src/app/app.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { TableComponent } from './table.component'; - -@Component({ - standalone: true, - imports: [TableComponent], - selector: 'app-root', - template: ` - - `, -}) -export class AppComponent {} diff --git a/apps/testing/table/src/app/table.component.spec.ts b/apps/testing/table/src/app/table.component.spec.ts deleted file mode 100644 index db4015d9f..000000000 --- a/apps/testing/table/src/app/table.component.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; -import { MatTableHarness } from '@angular/material/table/testing'; -import { render } from '@testing-library/angular'; -import userEvent from '@testing-library/user-event'; -import { TableComponent } from './table.component'; - -describe('AppComponent', () => { - // let loader: HarnessLoader; - // let fixture: ComponentFixture; - // let loader: HarnessLoader; - - // beforeEach(async () => { - // const view = await render(TableComponent); - // fixture = view.fixture; - // loader = TestbedHarnessEnvironment.loader(fixture); - // }); - it('error modal is displayed if you click on "Confirm" without inputing a name', async () => { - userEvent.setup(); - const { fixture } = await render(TableComponent); - const loader = TestbedHarnessEnvironment.loader(fixture); - - const tables = await loader.getAllHarnesses(MatTableHarness); - expect(tables.length).toBe(1); - - // const confirmButton = await screen.findByRole('button', { - // name: /confirm/i, - // }); - // await userEvent.click(confirmButton); - - // const dialogControl = await screen.findByRole('dialog'); - // expect(dialogControl).toBeInTheDocument(); - // const errorTitle = await screen.findByRole('heading', { - // name: /error/i, - // }); - // expect(errorTitle).toBeInTheDocument(); - - // const okButton = await screen.findByRole('button', { - // name: /ok/i, - // }); - // await userEvent.click(okButton); - }); - - // subscription('error message is shown if you click "Cancel" in the confirmation modal after submitting a name', async () => { - // userEvent.setup(); - // await render(AppComponent); - - // const inputControl = await screen.getByRole('textbox'); - // await userEvent.type(inputControl, 'toto'); - - // const confirmButton = await screen.findByRole('button', { - // name: /confirm/i, - // }); - // await userEvent.click(confirmButton); - - // const dialogControl = await screen.findByRole('dialog'); - // expect(dialogControl).toBeInTheDocument(); - // const profilTitle = await screen.findByRole('heading', { - // name: /profil/i, - // }); - // expect(profilTitle).toBeInTheDocument(); - - // const cancelButton = await screen.findByRole('button', { - // name: /cancel/i, - // }); - // await userEvent.click(cancelButton); - - // const errorText = await screen.getByText('Name is invalid !!'); - // expect(errorText).toBeInTheDocument(); - // }); - - // subscription('confirm message is shown if you click "Confirm" in the confirmation modal after submitting a name', async () => { - // userEvent.setup(); - // await render(AppComponent); - - // const inputControl = await screen.getByRole('textbox'); - // await userEvent.type(inputControl, 'toto'); - - // const confirmButton = await screen.findByRole('button', { - // name: /confirm/i, - // }); - // await userEvent.click(confirmButton); - - // const dialogControl = await screen.findByRole('dialog'); - // expect(dialogControl).toBeInTheDocument(); - // const profilTitle = await screen.findByRole('heading', { - // name: /profil/i, - // }); - // expect(profilTitle).toBeInTheDocument(); - - // const confirmDialogButton = await screen.findByRole('button', { - // name: /confirm/i, - // }); - // await userEvent.click(confirmDialogButton); - - // const confirmText = await screen.getByText('Name has been submitted'); - // expect(confirmText).toBeInTheDocument(); - // }); -}); diff --git a/apps/testing/table/src/app/table.component.ts b/apps/testing/table/src/app/table.component.ts deleted file mode 100644 index 74df216e6..000000000 --- a/apps/testing/table/src/app/table.component.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { FakeBackendService } from '@angular-challenges/testing-table/backend'; -import { AsyncPipe, DatePipe, NgIf } from '@angular/common'; -import { AfterViewInit, Component, ViewChild, inject } from '@angular/core'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatSort, MatSortModule, SortDirection } from '@angular/material/sort'; -import { MatTableModule } from '@angular/material/table'; -import { User } from '@ngneat/falso'; -import { LetDirective } from '@ngrx/component'; -import { ComponentStore, tapResponse } from '@ngrx/component-store'; -import { map, pipe, startWith, switchMap, tap } from 'rxjs'; - -interface TableState { - loading: boolean; - error?: string; - users: User[]; -} - -@Component({ - selector: 'app-table', - standalone: true, - imports: [ - MatFormFieldModule, - MatInputModule, - MatTableModule, - MatSortModule, - MatPaginatorModule, - MatProgressSpinnerModule, - LetDirective, - NgIf, - AsyncPipe, - DatePipe, - ], - template: ` -
    -
    - - -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    FirstName{{ row.firstName }}LastName{{ row.lastName }}Email{{ row.email }}
    -
    - - -
    - `, -}) -export class TableComponent - extends ComponentStore - implements AfterViewInit -{ - readonly displayedColumns = ['firstName', 'lastName', 'email']; - - private api = inject(FakeBackendService); - - readonly issues$ = this.select((s) => s.users).pipe( - tap((t) => console.log('UserNEw ', t)), - ); - readonly loading$ = this.select((s) => s.loading); - readonly error$ = this.select((s) => s.error); - readonly resultsLength$ = this.select((s) => 100); - - // resultsLength = 0; - // isLoadingResults = true; - // isRateLimitReached = false; - - @ViewChild(MatPaginator) paginator!: MatPaginator; - @ViewChild(MatSort) sort!: MatSort; - - constructor() { - super({ loading: false, users: [] }); - } - - readonly loadData = this.effect<{ - sortActive: keyof User; - sortDir: SortDirection; - pageIndex: number; - }>( - pipe( - tap((t) => console.log('cocou', t)), - tap(() => this.patchState({ loading: true, users: [] })), - switchMap(({ sortActive, sortDir, pageIndex }) => - this.api.getUsers(sortActive, sortDir, pageIndex).pipe( - tap((t) => console.log('user', t)), - tapResponse( - (data) => this.patchState({ users: data, loading: false }), - (err) => this.patchState({ error: err as string, loading: false }), - ), - ), - ), - ), - ); - - ngAfterViewInit(): void { - // due to ExpressionChangedAfterItHasBeenCheckedError - console.log('cocuo', this.sort, this.paginator); - this.loadData( - this.select({ - sortActive: this.sort.sortChange.pipe( - map((s) => s.active as keyof User), - ), - sortDir: this.sort.sortChange.pipe(map((s) => s.direction)), - pageIndex: this.paginator.page.pipe(map((p) => p.pageIndex)), - }).pipe( - startWith({ - sortActive: this.sort.active as keyof User, - sortDir: this.sort.direction, - pageIndex: 1, - }), - ), - ); - } - - // applyFilter(event: Event) { - // const filterValue = (event.target as HTMLInputElement).value; - // this.dataSource.filter = filterValue.trim().toLowerCase(); - - // if (this.dataSource.paginator) { - // this.dataSource.paginator.firstPage(); - // } - // } -} diff --git a/apps/testing/table/src/index.html b/apps/testing/table/src/index.html deleted file mode 100644 index 11da4b083..000000000 --- a/apps/testing/table/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - TestingTable - - - - - - - - diff --git a/apps/testing/table/src/main.ts b/apps/testing/table/src/main.ts deleted file mode 100644 index b0ca80b94..000000000 --- a/apps/testing/table/src/main.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { provideHttpClient } from '@angular/common/http'; -import { bootstrapApplication } from '@angular/platform-browser'; -import { provideAnimations } from '@angular/platform-browser/animations'; -import { AppComponent } from './app/app.component'; - -bootstrapApplication(AppComponent, { - providers: [provideAnimations(), provideHttpClient()], -}).catch((err) => console.error(err)); diff --git a/apps/testing/table/src/styles.scss b/apps/testing/table/src/styles.scss deleted file mode 100644 index ce5105b51..000000000 --- a/apps/testing/table/src/styles.scss +++ /dev/null @@ -1,31 +0,0 @@ -@use '@angular/material' as mat; - -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* You can add global styles to this file, and also import other style files */ - -@include mat.core(); - -$theme-primary: mat.define-palette(mat.$indigo-palette); -$theme-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); - -$theme-warn: mat.define-palette(mat.$red-palette); - -$theme: mat.define-light-theme( - ( - color: ( - primary: $theme-primary, - accent: $theme-accent, - warn: $theme-warn, - ), - typography: mat.define-typography-config(), - ) -); - -@include mat.core-theme($theme); -@include mat.all-component-themes($theme); -// @include mat.form-field-theme($theme); -// @include mat.input-theme($theme); -// @include mat.paginator-theme($theme); diff --git a/apps/testing/table/tsconfig.json b/apps/testing/table/tsconfig.json deleted file mode 100644 index 3879b94cd..000000000 --- a/apps/testing/table/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - }, - { - "path": "./cypress/tsconfig.json" - } - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/testing/table/tsconfig.spec.json b/apps/testing/table/tsconfig.spec.json deleted file mode 100644 index a578b4624..000000000 --- a/apps/testing/table/tsconfig.spec.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node", "@testing-library/jest-dom"], - "target": "ES2016" - }, - "files": ["src/test-setup.ts"], - "include": [ - "jest.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] -} diff --git a/apps/testing/todos-list/README.md b/apps/testing/todos-list/README.md deleted file mode 100644 index 37a8e93be..000000000 --- a/apps/testing/todos-list/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Real-life Application - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve testing-todos-list -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/testing/29-real-application/). diff --git a/apps/testing/todos-list/cypress.config.ts b/apps/testing/todos-list/cypress.config.ts deleted file mode 100644 index 1abef9c0c..000000000 --- a/apps/testing/todos-list/cypress.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { nxComponentTestingPreset } from '@nx/angular/plugins/component-testing'; -import { defineConfig } from 'cypress'; - -export default defineConfig({ - component: nxComponentTestingPreset(__filename), -}); diff --git a/apps/testing/todos-list/cypress/fixtures/example.json b/apps/testing/todos-list/cypress/fixtures/example.json deleted file mode 100644 index 02e425437..000000000 --- a/apps/testing/todos-list/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/apps/testing/todos-list/cypress/support/commands.ts b/apps/testing/todos-list/cypress/support/commands.ts deleted file mode 100644 index e6c897603..000000000 --- a/apps/testing/todos-list/cypress/support/commands.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -import { mount } from 'cypress/angular'; - -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - mount: typeof mount; - } - } -} - -Cypress.Commands.add('mount', mount); - -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/testing/todos-list/cypress/support/component.ts b/apps/testing/todos-list/cypress/support/component.ts deleted file mode 100644 index e7c3e3cbc..000000000 --- a/apps/testing/todos-list/cypress/support/component.ts +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// This example support/component.ts is processed and -// loaded automatically before your subscription files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.ts using ES2015 syntax: -import './commands'; diff --git a/apps/testing/todos-list/cypress/tsconfig.json b/apps/testing/todos-list/cypress/tsconfig.json deleted file mode 100644 index 918d96378..000000000 --- a/apps/testing/todos-list/cypress/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "module": "commonjs", - "types": ["cypress", "node"], - "sourceMap": false - }, - "include": [ - "support/**/*.ts", - "../cypress.config.ts", - "../**/*.cy.ts", - "../**/*.cy.tsx", - "../**/*.cy.js", - "../**/*.cy.jsx", - "../**/*.d.ts" - ] -} diff --git a/apps/testing/todos-list/jest.config.ts b/apps/testing/todos-list/jest.config.ts deleted file mode 100644 index eb61074e5..000000000 --- a/apps/testing/todos-list/jest.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'testing-todos-list', - preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ], -}; diff --git a/apps/testing/todos-list/project.json b/apps/testing/todos-list/project.json deleted file mode 100644 index b20c00d76..000000000 --- a/apps/testing/todos-list/project.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "testing-todos-list", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/testing/todos-list/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/testing/todos-list", - "index": "apps/testing/todos-list/src/index.html", - "main": "apps/testing/todos-list/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/testing/todos-list/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/testing/todos-list/src/favicon.ico", - "apps/testing/todos-list/src/assets" - ], - "styles": [ - "apps/testing/todos-list/src/styles.scss", - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - ], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "testing-todos-list:build:production" - }, - "development": { - "buildTarget": "testing-todos-list:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "testing-todos-list:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/testing/todos-list/jest.config.ts" - } - }, - "component-test": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/testing/todos-list/cypress.config.ts", - "testingType": "component", - "skipServe": true, - "devServerTarget": "testing-todos-list:build" - } - } - }, - "tags": [] -} diff --git a/apps/testing/todos-list/src/app/app.component.ts b/apps/testing/todos-list/src/app/app.component.ts deleted file mode 100644 index 6b61097d4..000000000 --- a/apps/testing/todos-list/src/app/app.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - selector: 'app-root', - standalone: true, - imports: [RouterOutlet], - template: ` - - `, -}) -export class AppComponent {} diff --git a/apps/testing/todos-list/src/app/detail/detail.component.ts b/apps/testing/todos-list/src/app/detail/detail.component.ts deleted file mode 100644 index 7e5eed829..000000000 --- a/apps/testing/todos-list/src/app/detail/detail.component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { AsyncPipe, NgIf } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { RouterLink } from '@angular/router'; -import { LetDirective } from '@ngrx/component'; -import { provideComponentStore } from '@ngrx/component-store'; -import { DetailStore } from './detail.store'; - -@Component({ - selector: 'app-detail', - standalone: true, - imports: [ - MatButtonModule, - RouterLink, - NgIf, - AsyncPipe, - MatProgressBarModule, - LetDirective, - ], - template: ` -

    Ticket Detail:

    - - -
    -
    - Ticket: - {{ ticket.id }} -
    -
    - Description: - {{ ticket.description }} -
    -
    - AssigneeId: - {{ ticket.assigneeId }} -
    -
    - Is done: - {{ ticket.completed }} -
    -
    -
    - - - `, - providers: [provideComponentStore(DetailStore)], - host: { - class: 'p-5 block', - }, -}) -export class DetailComponent { - vm$ = inject(DetailStore).vm$; -} diff --git a/apps/testing/todos-list/src/app/list/list.component.ts b/apps/testing/todos-list/src/app/list/list.component.ts deleted file mode 100644 index 8942f7cc6..000000000 --- a/apps/testing/todos-list/src/app/list/list.component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { NgFor, NgIf } from '@angular/common'; -import { Component, OnInit, inject } from '@angular/core'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { LetDirective } from '@ngrx/component'; -import { provideComponentStore } from '@ngrx/component-store'; -import { TicketStore } from './ticket.store'; -import { AddComponent } from './ui/add.component'; -import { RowComponent } from './ui/row.component'; - -@Component({ - selector: 'app-list', - standalone: true, - imports: [ - ReactiveFormsModule, - AddComponent, - RowComponent, - MatFormFieldModule, - MatProgressBarModule, - NgIf, - NgFor, - MatInputModule, - LetDirective, - ], - template: ` -

    Tickets

    - - - Search - - - - - - - -
      - -
    -
    - {{ vm.error }} -
    -
    - `, - providers: [provideComponentStore(TicketStore)], - host: { - class: 'p-5 block', - }, -}) -export class ListComponent implements OnInit { - ticketStore = inject(TicketStore); - readonly vm$ = this.ticketStore.vm$; - - search = new FormControl(); - - ngOnInit(): void { - this.ticketStore.search(this.search.valueChanges); - } -} diff --git a/apps/testing/todos-list/src/index.html b/apps/testing/todos-list/src/index.html deleted file mode 100644 index 8321d8a0e..000000000 --- a/apps/testing/todos-list/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - TestingTodosList - - - - - - - - diff --git a/apps/testing/todos-list/tsconfig.app.json b/apps/testing/todos-list/tsconfig.app.json deleted file mode 100644 index 01a02ed77..000000000 --- a/apps/testing/todos-list/tsconfig.app.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "types": [] - }, - "files": ["src/main.ts"], - "include": ["src/**/*.d.ts"], - "exclude": [ - "jest.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "cypress/**/*", - "cypress.config.ts", - "**/*.cy.ts", - "**/*.cy.js", - "**/*.cy.tsx", - "**/*.cy.jsx" - ] -} diff --git a/apps/testing/todos-list/tsconfig.editor.json b/apps/testing/todos-list/tsconfig.editor.json deleted file mode 100644 index 8ae117d96..000000000 --- a/apps/testing/todos-list/tsconfig.editor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "compilerOptions": { - "types": ["jest", "node"] - } -} diff --git a/apps/testing/todos-list/tsconfig.json b/apps/testing/todos-list/tsconfig.json deleted file mode 100644 index 3879b94cd..000000000 --- a/apps/testing/todos-list/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - }, - { - "path": "./cypress/tsconfig.json" - } - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/testing/todos-list/.eslintrc.json b/apps/typescript/15-function-overload/.eslintrc.json similarity index 100% rename from apps/testing/todos-list/.eslintrc.json rename to apps/typescript/15-function-overload/.eslintrc.json diff --git a/apps/typescript/15-function-overload/README.md b/apps/typescript/15-function-overload/README.md new file mode 100644 index 000000000..96ce65e9d --- /dev/null +++ b/apps/typescript/15-function-overload/README.md @@ -0,0 +1,13 @@ +# Function Overload + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve typescript-function-overload` +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/typescript/15-typescript-function-overload-fn/). diff --git a/apps/typescript/15-function-overload/project.json b/apps/typescript/15-function-overload/project.json new file mode 100644 index 000000000..95e554c3d --- /dev/null +++ b/apps/typescript/15-function-overload/project.json @@ -0,0 +1,72 @@ +{ + "name": "typescript-function-overload", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/typescript/15-function-overload/src", + "prefix": "app", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/typescript/15-function-overload", + "index": "apps/typescript/15-function-overload/src/index.html", + "main": "apps/typescript/15-function-overload/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/typescript/15-function-overload/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/typescript/15-function-overload/src/favicon.ico", + "apps/typescript/15-function-overload/src/assets" + ], + "styles": ["apps/typescript/15-function-overload/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "typescript-function-overload:build:production" + }, + "development": { + "buildTarget": "typescript-function-overload:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "typescript-function-overload:build" + } + } + } +} diff --git a/apps/typescript/overload/src/app/app.component.ts b/apps/typescript/15-function-overload/src/app/app.component.ts similarity index 100% rename from apps/typescript/overload/src/app/app.component.ts rename to apps/typescript/15-function-overload/src/app/app.component.ts diff --git a/apps/typescript/overload/src/app/vehicle.utils.ts b/apps/typescript/15-function-overload/src/app/vehicle.utils.ts similarity index 100% rename from apps/typescript/overload/src/app/vehicle.utils.ts rename to apps/typescript/15-function-overload/src/app/vehicle.utils.ts diff --git a/apps/ngrx/effect-selector/src/styles.scss b/apps/typescript/15-function-overload/src/assets/.gitkeep similarity index 100% rename from apps/ngrx/effect-selector/src/styles.scss rename to apps/typescript/15-function-overload/src/assets/.gitkeep diff --git a/apps/typescript/15-function-overload/src/favicon.ico b/apps/typescript/15-function-overload/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/typescript/15-function-overload/src/favicon.ico differ diff --git a/apps/typescript/15-function-overload/src/index.html b/apps/typescript/15-function-overload/src/index.html new file mode 100644 index 000000000..37dd3a978 --- /dev/null +++ b/apps/typescript/15-function-overload/src/index.html @@ -0,0 +1,13 @@ + + + + + typescript-function-overload + + + + + + + + diff --git a/apps/typescript/15-function-overload/src/main.ts b/apps/typescript/15-function-overload/src/main.ts new file mode 100644 index 000000000..31c5da482 --- /dev/null +++ b/apps/typescript/15-function-overload/src/main.ts @@ -0,0 +1,4 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent).catch((err) => console.error(err)); diff --git a/apps/testing/nested/src/styles.scss b/apps/typescript/15-function-overload/src/styles.scss similarity index 100% rename from apps/testing/nested/src/styles.scss rename to apps/typescript/15-function-overload/src/styles.scss diff --git a/apps/typescript/15-function-overload/tsconfig.app.json b/apps/typescript/15-function-overload/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/typescript/15-function-overload/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/typescript/enums-vs-union-types/tsconfig.editor.json b/apps/typescript/15-function-overload/tsconfig.editor.json similarity index 100% rename from apps/typescript/enums-vs-union-types/tsconfig.editor.json rename to apps/typescript/15-function-overload/tsconfig.editor.json diff --git a/apps/typescript/overload/tsconfig.json b/apps/typescript/15-function-overload/tsconfig.json similarity index 100% rename from apps/typescript/overload/tsconfig.json rename to apps/typescript/15-function-overload/tsconfig.json diff --git a/apps/typescript/47-enums-vs-union-types/.eslintrc.json b/apps/typescript/47-enums-vs-union-types/.eslintrc.json new file mode 100644 index 000000000..8ebcbfd59 --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/typescript/enums-vs-union-types/README.md b/apps/typescript/47-enums-vs-union-types/README.md similarity index 100% rename from apps/typescript/enums-vs-union-types/README.md rename to apps/typescript/47-enums-vs-union-types/README.md diff --git a/apps/typescript/47-enums-vs-union-types/project.json b/apps/typescript/47-enums-vs-union-types/project.json new file mode 100644 index 000000000..964750ac6 --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/project.json @@ -0,0 +1,69 @@ +{ + "name": "typescript-enums-vs-union-types", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/typescript/47-enums-vs-union-types/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/typescript/47-enums-vs-union-types", + "index": "apps/typescript/47-enums-vs-union-types/src/index.html", + "browser": "apps/typescript/47-enums-vs-union-types/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/typescript/47-enums-vs-union-types/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/typescript/47-enums-vs-union-types/src/favicon.ico", + "apps/typescript/47-enums-vs-union-types/src/assets" + ], + "styles": ["apps/typescript/47-enums-vs-union-types/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "typescript-enums-vs-union-types:build:production" + }, + "development": { + "buildTarget": "typescript-enums-vs-union-types:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "typescript-enums-vs-union-types:build" + } + } + } +} diff --git a/apps/typescript/47-enums-vs-union-types/src/app/app.component.ts b/apps/typescript/47-enums-vs-union-types/src/app/app.component.ts new file mode 100644 index 000000000..05886724f --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/src/app/app.component.ts @@ -0,0 +1,82 @@ +import { Component, computed, signal } from '@angular/core'; + +enum Difficulty { + EASY = 'easy', + NORMAL = 'normal', +} + +enum Direction { + LEFT = 'left', + RIGHT = 'right', +} + +@Component({ + imports: [], + selector: 'app-root', + template: ` +
    +
    + + +
    +

    Selected Difficulty: {{ difficultyLabel() }}

    +
    + +
    +
    + + +
    +

    {{ directionLabel() }}

    +
    + `, + styles: ` + section { + @apply mx-auto my-5 flex w-fit flex-col items-center gap-2; + + > div { + @apply flex w-fit gap-5; + } + } + + button { + @apply rounded-md border px-4 py-2; + } + `, +}) +export class AppComponent { + readonly Difficulty = Difficulty; + readonly difficulty = signal(Difficulty.EASY); + + readonly Direction = Direction; + readonly direction = signal(undefined); + + readonly difficultyLabel = computed(() => { + switch (this.difficulty()) { + case Difficulty.EASY: + return Difficulty.EASY; + case Difficulty.NORMAL: + return Difficulty.NORMAL; + } + }); + + readonly directionLabel = computed(() => { + const prefix = 'You chose to go'; + switch (this.direction()) { + case Direction.LEFT: + return `${prefix} ${Direction.LEFT}`; + case Direction.RIGHT: + return `${prefix} ${Direction.RIGHT}`; + default: + return 'Choose a direction!'; + } + }); +} diff --git a/apps/typescript/47-enums-vs-union-types/src/app/app.config.ts b/apps/typescript/47-enums-vs-union-types/src/app/app.config.ts new file mode 100644 index 000000000..81a6edde4 --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [], +}; diff --git a/apps/typescript/47-enums-vs-union-types/src/assets/.gitkeep b/apps/typescript/47-enums-vs-union-types/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/typescript/47-enums-vs-union-types/src/favicon.ico b/apps/typescript/47-enums-vs-union-types/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/typescript/47-enums-vs-union-types/src/favicon.ico differ diff --git a/apps/typescript/enums-vs-union-types/src/index.html b/apps/typescript/47-enums-vs-union-types/src/index.html similarity index 100% rename from apps/typescript/enums-vs-union-types/src/index.html rename to apps/typescript/47-enums-vs-union-types/src/index.html diff --git a/apps/typescript/47-enums-vs-union-types/src/main.ts b/apps/typescript/47-enums-vs-union-types/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/typescript/47-enums-vs-union-types/src/styles.scss b/apps/typescript/47-enums-vs-union-types/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/typescript/47-enums-vs-union-types/tailwind.config.js b/apps/typescript/47-enums-vs-union-types/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/typescript/47-enums-vs-union-types/tsconfig.app.json b/apps/typescript/47-enums-vs-union-types/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/typescript/overload/tsconfig.editor.json b/apps/typescript/47-enums-vs-union-types/tsconfig.editor.json similarity index 100% rename from apps/typescript/overload/tsconfig.editor.json rename to apps/typescript/47-enums-vs-union-types/tsconfig.editor.json diff --git a/apps/typescript/47-enums-vs-union-types/tsconfig.json b/apps/typescript/47-enums-vs-union-types/tsconfig.json new file mode 100644 index 000000000..b94f8837d --- /dev/null +++ b/apps/typescript/47-enums-vs-union-types/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/typescript/enums-vs-union-types/project.json b/apps/typescript/enums-vs-union-types/project.json deleted file mode 100644 index 63e9a04c0..000000000 --- a/apps/typescript/enums-vs-union-types/project.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "name": "typescript-enums-vs-union-types", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "prefix": "app", - "sourceRoot": "apps/typescript/enums-vs-union-types/src", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:application", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/typescript/enums-vs-union-types", - "index": "apps/typescript/enums-vs-union-types/src/index.html", - "browser": "apps/typescript/enums-vs-union-types/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/typescript/enums-vs-union-types/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/typescript/enums-vs-union-types/src/favicon.ico", - "apps/typescript/enums-vs-union-types/src/assets" - ], - "styles": ["apps/typescript/enums-vs-union-types/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "typescript-enums-vs-union-types:build:production" - }, - "development": { - "buildTarget": "typescript-enums-vs-union-types:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "typescript-enums-vs-union-types:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - } - } -} diff --git a/apps/typescript/enums-vs-union-types/src/app/app.component.ts b/apps/typescript/enums-vs-union-types/src/app/app.component.ts deleted file mode 100644 index d82470519..000000000 --- a/apps/typescript/enums-vs-union-types/src/app/app.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, computed, signal } from '@angular/core'; - -enum Difficulty { - EASY = 'easy', - NORMAL = 'normal', -} - -enum Direction { - LEFT = 'left', - RIGHT = 'right', -} - -@Component({ - standalone: true, - imports: [], - selector: 'app-root', - template: ` -
    -
    - - -
    -

    Selected Difficulty: {{ difficultyLabel() }}

    -
    - -
    -
    - - -
    -

    {{ directionLabel() }}

    -
    - `, - styles: ` - section { - @apply flex flex-col mx-auto my-5 w-fit gap-2 items-center; - - > div { - @apply flex w-fit gap-5; - } - } - - button { - @apply rounded-md border px-4 py-2; - } - `, -}) -export class AppComponent { - readonly Difficulty = Difficulty; - readonly difficulty = signal(Difficulty.EASY); - - readonly Direction = Direction; - readonly direction = signal(undefined); - - readonly difficultyLabel = computed(() => { - switch (this.difficulty()) { - case Difficulty.EASY: - return Difficulty.EASY; - case Difficulty.NORMAL: - return Difficulty.NORMAL; - } - }); - - readonly directionLabel = computed(() => { - const prefix = 'You chose to go'; - switch (this.direction()) { - case Direction.LEFT: - return `${prefix} ${Direction.LEFT}`; - case Direction.RIGHT: - return `${prefix} ${Direction.RIGHT}`; - default: - return 'Choose a direction!'; - } - }); -} diff --git a/apps/typescript/enums-vs-union-types/tsconfig.json b/apps/typescript/enums-vs-union-types/tsconfig.json deleted file mode 100644 index db0ec0f25..000000000 --- a/apps/typescript/enums-vs-union-types/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json", - }, - { - "path": "./tsconfig.editor.json", - }, - ], - "extends": "../../../tsconfig.base.json", - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true, - }, -} diff --git a/apps/typescript/overload/.eslintrc.json b/apps/typescript/overload/.eslintrc.json deleted file mode 100644 index bf8df1428..000000000 --- a/apps/typescript/overload/.eslintrc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "extends": ["../../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts"], - "rules": { - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": "app", - "style": "camelCase" - } - ], - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": "app", - "style": "kebab-case" - } - ] - }, - "extends": [ - "plugin:@nx/angular", - "plugin:@angular-eslint/template/process-inline-templates" - ] - }, - { - "files": ["*.html"], - "extends": ["plugin:@nx/angular-template"], - "rules": {} - } - ] -} diff --git a/apps/typescript/overload/README.md b/apps/typescript/overload/README.md deleted file mode 100644 index b4336af99..000000000 --- a/apps/typescript/overload/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Function Overload - -> author: thomas-laforge - -### Run Application - -```bash -npx nx serve typescript-overload` -``` - -### Documentation and Instruction - -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/typescript/15-typescript-overload-fn/). diff --git a/apps/typescript/overload/project.json b/apps/typescript/overload/project.json deleted file mode 100644 index 397c71bc1..000000000 --- a/apps/typescript/overload/project.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "typescript-overload", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/typescript/overload/src", - "prefix": "app", - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/typescript/overload", - "index": "apps/typescript/overload/src/index.html", - "main": "apps/typescript/overload/src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "apps/typescript/overload/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/typescript/overload/src/favicon.ico", - "apps/typescript/overload/src/assets" - ], - "styles": ["apps/typescript/overload/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "typescript-overload:build:production" - }, - "development": { - "buildTarget": "typescript-overload:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "typescript-overload:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - } - }, - "tags": [] -} diff --git a/apps/typescript/overload/src/index.html b/apps/typescript/overload/src/index.html deleted file mode 100644 index 86561b663..000000000 --- a/apps/typescript/overload/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Overload - - - - - - - - diff --git a/apps/typescript/overload/src/styles.scss b/apps/typescript/overload/src/styles.scss deleted file mode 100644 index 90d4ee007..000000000 --- a/apps/typescript/overload/src/styles.scss +++ /dev/null @@ -1 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ diff --git a/challenge-number.json b/challenge-number.json index fee6a9627..9504fd39d 100644 --- a/challenge-number.json +++ b/challenge-number.json @@ -1,6 +1,6 @@ { - "total": 47, - "🟢": 18, - "🟠": 120, - "🔴": 209 + "total": 59, + "🟢": 22, + "🟠": 124, + "🔴": 212 } diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index bce29de09..953462156 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -23,6 +23,10 @@ export const locales = { ru: { label: 'Русский', lang: 'ru' + }, + 'zh-cn': { + label: '简体中文', + lang: 'zh-CN' } }; @@ -51,7 +55,8 @@ export default defineConfig({ es: 'Guías', fr: 'Guides', pt: 'Guias', - ru: 'Руководство' + ru: 'Руководство', + 'zh-CN': '指南' } }, { @@ -63,8 +68,9 @@ export default defineConfig({ translations: { es: 'Leaderboard', fr: 'Leaderboard', - pt: 'Leaderboard', - ru: 'Leaderboard' + pt: 'Tabela de Classificação', + ru: 'Leaderboard', + 'zh-CN': '排行榜' } }, { @@ -76,7 +82,8 @@ export default defineConfig({ es: 'Desafíos', fr: 'Challenges', pt: 'Desafios', - ru: 'Задачи' + ru: 'Задачи', + 'zh-CN': '挑战' } }], head: [{ diff --git a/docs/src/assets/codespaces.png b/docs/src/assets/codespaces.png new file mode 100644 index 000000000..2b8e8a43c Binary files /dev/null and b/docs/src/assets/codespaces.png differ diff --git a/docs/src/assets/rxjs/49/prototype.gif b/docs/src/assets/rxjs/49/prototype.gif new file mode 100644 index 000000000..32c33528c Binary files /dev/null and b/docs/src/assets/rxjs/49/prototype.gif differ diff --git a/docs/src/components/Author.astro b/docs/src/components/Author.astro index 3ac6d1b0c..43b3af29c 100644 --- a/docs/src/components/Author.astro +++ b/docs/src/components/Author.astro @@ -6,10 +6,11 @@ interface Props { twitter?: string; linkedin?: string; github?: string; + youtube?: string; labels?: Record; } -const { name, twitter, linkedin, github, data } = Astro.props; +const { name, twitter, linkedin, github, youtube, data } = Astro.props; ---

    @@ -23,6 +24,9 @@ const { name, twitter, linkedin, github, data } = Astro.props; {github && } + {youtube && + + }

    diff --git a/docs/src/components/ChallengeFooter.astro b/docs/src/components/ChallengeFooter.astro index 6a714b0d8..ccd4f1a83 100644 --- a/docs/src/components/ChallengeFooter.astro +++ b/docs/src/components/ChallengeFooter.astro @@ -5,7 +5,7 @@ import { getEntry } from 'astro:content'; import AnsweredUser from './github/AnsweredUser.svelte'; const { lang } = Astro.props; -const { author, challengeNumber, title, blogLink, videoLink, command } = Astro.props.entry.data; +const { author, challengeNumber, title, blogLink, videoLinks, command } = Astro.props.entry.data; const { data } = await getEntry('i18n', lang); const authorLink = `https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A${challengeNumber}+label%3A"answer+author"`; @@ -70,8 +70,9 @@ const npxCommand = `npx nx serve ${command}`; {data['challenge.footer.blogPost']} } - {videoLink && - } + {videoLinks && videoLinks.map((videoLink) => ( + + ))}
diff --git a/docs/src/components/CommentSection.astro b/docs/src/components/CommentSection.astro index b8563ae9d..778eba0a2 100644 --- a/docs/src/components/CommentSection.astro +++ b/docs/src/components/CommentSection.astro @@ -3,7 +3,8 @@ import type { Props } from '@astrojs/starlight/props'; const {lang, locale} = Astro.props; -const shortLang = lang.split('-')[0]; +// Chinese needs to `zh-CN` +// const shortLang =lang.split('-')[0]; ---
@@ -19,7 +20,7 @@ const shortLang = lang.split('-')[0]; data-emit-metadata="0" data-input-position="bottom" data-theme="preferred_color_scheme" - data-lang={shortLang} + data-lang={lang} data-loading="lazy" crossorigin="anonymous" async> diff --git a/docs/src/components/Hero.astro b/docs/src/components/Hero.astro index 53d43a41c..8d9500eaa 100644 --- a/docs/src/components/Hero.astro +++ b/docs/src/components/Hero.astro @@ -2,9 +2,9 @@ import Default from '@astrojs/starlight/components/Hero.astro'; import MyIcon from './MyIcon.astro'; import { getEntry } from 'astro:content'; +import SponsorUser from './github/SponsorUser.svelte'; + -const sponsorFetch = await fetch('https://ghs.vercel.app/v2/sponsors/tomalaforge'); -const { sponsors } = await sponsorFetch.json(); const { lang } = Astro.props; const { data } = await getEntry('i18n', lang); @@ -12,22 +12,11 @@ const { data } = await getEntry('i18n', lang); @@ -57,14 +46,6 @@ const { data } = await getEntry('i18n', lang); display: inline-block; } - .avatar { - border-radius: 50%; - width: 30px; - height: auto; - vertical-align: middle; - margin-right: 0.5rem; - } - .action-button { display: flex !important; justify-content: center; diff --git a/docs/src/components/VideoButton.astro b/docs/src/components/VideoButton.astro index a739090b5..723ef30e9 100644 --- a/docs/src/components/VideoButton.astro +++ b/docs/src/components/VideoButton.astro @@ -5,12 +5,13 @@ interface Props { lang: any; link: string; alt: string; - flag?: 'FR'; + flag?: 'FR' | 'ES'; } const { link, alt, flag, lang } = Astro.props; const { data } = await getEntry('i18n', lang); const isFR = flag === 'FR'; +const isES = flag === 'ES'; --- {data['challenge.footer.video']} - {isFR && 🇫🇷} + {isFR && 🇫🇷} + {isES && 🇪🇸} diff --git a/docs/src/components/github/GitHubStats.svelte b/docs/src/components/github/GitHubStats.svelte index 2828fea11..8286a2894 100644 --- a/docs/src/components/github/GitHubStats.svelte +++ b/docs/src/components/github/GitHubStats.svelte @@ -84,6 +84,9 @@ const data = await refresh.json(); token.set(data.token); return; + } else { + token.set('delete'); + throw new Error('Failed to refresh token'); } } else { throw new Error('Failed to fetch data'); diff --git a/docs/src/components/github/SignUp.svelte b/docs/src/components/github/SignUp.svelte index 88f1bbac5..a98a2a943 100644 --- a/docs/src/components/github/SignUp.svelte +++ b/docs/src/components/github/SignUp.svelte @@ -53,6 +53,11 @@ background-color: #218838; } + a :global(svg) { + --sl-icon-color: initial; + } + + @media (width < 450px) { .github-sign-in { display: none; diff --git a/docs/src/components/github/SponsorUser.svelte b/docs/src/components/github/SponsorUser.svelte new file mode 100644 index 000000000..cc29003d7 --- /dev/null +++ b/docs/src/components/github/SponsorUser.svelte @@ -0,0 +1,40 @@ + + +{#each sponsors as { username, avatar }} + + {username} + +{/each} + + diff --git a/docs/src/components/github/github-store.ts b/docs/src/components/github/github-store.ts index 445bd6c3b..48c4ddee4 100644 --- a/docs/src/components/github/github-store.ts +++ b/docs/src/components/github/github-store.ts @@ -31,6 +31,7 @@ token.subscribe((value) => { if (value) { if (value === 'delete') { localStorage.removeItem(TOKEN_KEY); + token.set(null); return; } localStorage.setItem(TOKEN_KEY, JSON.stringify(value)); diff --git a/docs/src/components/leaderboard/LeaderboardAnswer.svelte b/docs/src/components/leaderboard/LeaderboardAnswer.svelte index 4a6872131..0ca776b3c 100644 --- a/docs/src/components/leaderboard/LeaderboardAnswer.svelte +++ b/docs/src/components/leaderboard/LeaderboardAnswer.svelte @@ -7,6 +7,7 @@ let loading = true; let error = null; let isUsernamePresent = false; + let globalCount = 0; token.subscribe(token => { if (token) { @@ -34,6 +35,8 @@ break; } + globalCount = globalCount + items.length; + items.forEach(pr => { const userLogin = pr.user.login; if (prCounts[userLogin]) { @@ -62,7 +65,7 @@ avatar: pr.avatar, count: pr.count, challengeNumber: pr.challengeNumber.sort((a, b) => a - b), - })).filter((r) => r.login !== 'allcontributors[bot]').sort((a, b) => b.count - a.count); + })).filter((r) => r.login !== 'allcontributors[bot]' && r.login !== 'tomalaforge').sort((a, b) => b.count - a.count); } catch (e) { error = e.message; @@ -76,6 +79,7 @@ {#if !$isConnected}
Log in to Github to see the list
{:else} +🔥Total Answers: { globalCount } {#if isUsernamePresent}