feat: challenge 45 React in Angular

Add new challenge: React in Angular.
This commit is contained in:
Wandrille
2024-02-09 09:25:05 +01:00
parent 77ead59094
commit 0f4f14c351
27 changed files with 485 additions and 37 deletions

View File

@@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f
## Challenges ## Challenges
Check [all 44 challenges](https://angular-challenges.vercel.app/) Check [all 45 challenges](https://angular-challenges.vercel.app/)
## Contributors ✨ ## Contributors ✨

View File

@@ -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": {}
}
]
}

View File

@@ -0,0 +1,13 @@
# React in angular
> author: wandrille-guesdon
### Run Application
```bash
npx nx serve angular-react-in-angular
```
### Documentation and Instruction
Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/45-react-in-angular/).

View File

@@ -0,0 +1,22 @@
/* eslint-disable */
export default {
displayName: 'angular-react-in-angular',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/apps/angular/react-in-angular',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/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',
],
};

View File

@@ -0,0 +1,80 @@
{
"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"
}
}
}
}

View File

@@ -0,0 +1,62 @@
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: `
<div class="flex flex-col gap-2 p-12">
<div class="flex gap-2">
@for (post of posts; track post.title) {
<div class="rounded-lg bg-gray-100 p-4">
<app-post
(selectPost)="selectPost(post)"
[post]="post"
[isSelected]="post.title === selectedPost()?.title"></app-post>
</div>
}
</div>
<div class="flex flex-col gap-2 border p-4">
<span class="text-xs font-medium">Selected Post:</span>
<span class="text-lg text-blue-700">
{{ selectedPost()?.title ?? '-' }}
</span>
</div>
</div>
`,
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<Post | null>(null);
selectPost(post: Post) {
this.selectedPost.set(post);
}
}

View File

@@ -0,0 +1,5 @@
import { ApplicationConfig } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [],
};

View File

@@ -0,0 +1,27 @@
// import React from 'react';
export default function ReactPost(props: {
title?: string,
description?: string,
pictureLink?: string,
selected?: boolean,
handleClick: () => void
}) {
return (
<div className={props.selected ? 'bg-blue-100' : 'bg-white'}>
<div className="max-w-sm rounded overflow-hidden shadow-lg">
<img className="w-full h-32 object-cover" src={props.pictureLink} alt={props.title}></img>
<div className="px-6 py-4 flex flex-col gap-2">
<div className="font-bold text-xl mb-2">{props.title}</div>
<p className="text-gray-700 text-base">
{props.description}
</p>
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={props.handleClick}>
Select
</button>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,17 @@
import { Component, EventEmitter, input, Output } from '@angular/core';
type Post = { title: string; description: string; pictureLink: string };
@Component({
standalone: true,
selector: 'app-post',
template: `
<div></div>
`,
styles: [''],
})
export class PostComponent {
post = input<Post | undefined>(undefined);
isSelected = input<boolean>(false);
@Output() selectPost = new EventEmitter<void>();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>angular-react-in-angular</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -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),
);

View File

@@ -0,0 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* You can add global styles to this file, and also import other style files */

View File

@@ -0,0 +1,2 @@
import '@testing-library/jest-dom';
import 'jest-preset-angular/setup-jest';

View File

@@ -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: [],
};

View File

@@ -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"]
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts"],
"compilerOptions": {
"types": ["jest", "node"]
}
}

View File

@@ -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,
},
}

View File

@@ -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"
]
}

View File

@@ -1,6 +1,6 @@
{ {
"total": 44, "total": 45,
"🟢": 16, "🟢": 16,
"🟠": 120, "🟠": 120,
"🔴": 208 "🔴": 209
} }

View File

@@ -0,0 +1,76 @@
---
title: 🔴 React in angular
description: Challenge 45 is about learning how to benefit from the numerous libraries in React
author: Wandrille Guesdon
challengeNumber: 45
command: angular-react-in-angular
sidebar:
order: NaN
badge: New
---
The goal of this challenge is to use a React component inside an Angular application.
Many components are available in React, and it can be interesting to use them in an Angular application. The goal is to create a React component and use it in an Angular application.
## Information
In this challenge, we have a simple application and a react component `ReactPost` in `app/react` to illustrate a react component from a library.
## Statement
- Your task is to display the posts with the React component `ReactPost`.
- When you select a post, the post should be highlighted.
In order to play with the react component, you should start by installing the react dependencies.
```bash
npm i --save react react-dom
npm i --save-dev @types/react @types/react-dom
```
## Constraints
- Do not transform the react component in an angular component. The React component is pretty simple and can be written with ease in Angular. But **the goal is to use the React component**.
### Hint
<details>
<summary>Hint 1 - Configuration</summary>
Allow the React files in tsconfig.json
```
{
...
"compilerOptions": {
...
"jsx": "react"
},
...
}
```
</details>
<details>
<summary>Hint 2 - Initialization</summary>
Create a react root with `createRoot(...)`
</details>
<details>
<summary>Hint 3 - Display</summary>
To render the component, it should look like this:
```
<react root>.render(
<React.StrictMode>
...
</React.StrictMode>
)
```
</details>
<details>
<summary>Hint 4 - Design</summary>
Do not forget to allow the react file in Tailwind.
</details>

View File

@@ -23,16 +23,16 @@ hero:
import { Card, CardGrid } from '@astrojs/starlight/components'; import { Card, CardGrid } from '@astrojs/starlight/components';
import MyIcon from '../../../components/MyIcon.astro'; import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro' import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="44 Desafíos"> <Card title="45 Desafíos">
Este repositorio contiene 44 desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>. Este repositorio contiene 45 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>.
Estos desafíos se resuelven en torno a problemas de la vida real o características específicas para mejorar tus habilidades. Estos desafíos se resuelven en torno a problemas de la vida real o características específicas para mejorar tus habilidades.
</Card> </Card>
<Card title="Suscríbete para ser notificado sobre los últimos retos"> <Card title="Suscríbete para ser notificado sobre los últimos retos">
<SubscriptionForm isNote={false} lang="es"/> <SubscriptionForm isNote={false} lang="es" />
</Card> </Card>
<Card title="Conviértete en un Contribuidor de OSS"> <Card title="Conviértete en un Contribuidor de OSS">
@@ -57,7 +57,8 @@ import SubscriptionForm from '../../../components/SubscriptionForm.astro'
</Card> </Card>
<Card title="Prepárate para Entrevistas"> <Card title="Prepárate para Entrevistas">
Completar estos desafíos te preparará para cualquier desafío técnico que puedas encontrar en un rol de frontend durante las entrevistas. Completar estos desafíos te preparará para cualquier desafío técnico que
puedas encontrar en un rol de frontend durante las entrevistas.
</Card> </Card>
<Card title="Patrocinio"> <Card title="Patrocinio">

View File

@@ -25,15 +25,15 @@ hero:
import { Card, CardGrid } from '@astrojs/starlight/components'; import { Card, CardGrid } from '@astrojs/starlight/components';
import MyIcon from '../../../components/MyIcon.astro'; import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro' import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="44 Défis"> <Card title="45 Défis">
Ce répertoire rassemble 44 défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences. Ce répertoire rassemble 45 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences.
</Card> </Card>
<Card title="Subscribe to get notify of latest challenges"> <Card title="Subscribe to get notify of latest challenges">
<SubscriptionForm isNote={false} lang="fr"/> <SubscriptionForm isNote={false} lang="fr" />
</Card> </Card>
<Card title="Devenir un Mainteneur OSS"> <Card title="Devenir un Mainteneur OSS">

View File

@@ -24,17 +24,16 @@ hero:
import { Card, CardGrid } from '@astrojs/starlight/components'; import { Card, CardGrid } from '@astrojs/starlight/components';
import MyIcon from '../../components/MyIcon.astro'; import MyIcon from '../../components/MyIcon.astro';
import SubscriptionForm from '../../components/SubscriptionForm.astro' import SubscriptionForm from '../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="44 Challenges"> <Card title="45 Challenges">
This repository gathers 44 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>. This repository gathers 45 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
These challenges resolve around real-life issues or specific features to elevate your skills. These challenges resolve around real-life issues or specific features to elevate your skills.
</Card> </Card>
<Card title="Subscribe to get notify of latest challenges"> <Card title="Subscribe to get notify of latest challenges">
<SubscriptionForm isNote={false}/> <SubscriptionForm isNote={false} />
</Card> </Card>
<Card title="Become an OSS Maintainer"> <Card title="Become an OSS Maintainer">

View File

@@ -23,19 +23,20 @@ hero:
import { Card, CardGrid } from '@astrojs/starlight/components'; import { Card, CardGrid } from '@astrojs/starlight/components';
import MyIcon from '../../../components/MyIcon.astro'; import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro' import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="44 Desafios"> <Card title="45 Desafios">
Este repositório possui 44 desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, Este repositório possui 45 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>,
<b>Ngrx</b> e <b>Typescript</b>. <b>Ngrx</b> e <b>Typescript</b>.
Esses desafios são voltados para problemas reais ou funcionalidades específicas afim de Esses desafios são voltados para problemas reais ou funcionalidades específicas afim de
melhorar suas habilidades. melhorar suas habilidades.
</Card> </Card>
<Card title="Inscreva-se para ser notificado do desafio mais recente"> {' '}
<SubscriptionForm isNote={false} lang="pt"/> <Card title="Inscreva-se para ser notificado do desafio mais recente">
</Card> <SubscriptionForm isNote={false} lang="pt" />
</Card>
{' '} {' '}

View File

@@ -23,30 +23,32 @@ hero:
import { Card, CardGrid } from '@astrojs/starlight/components'; import { Card, CardGrid } from '@astrojs/starlight/components';
import MyIcon from '../../../components/MyIcon.astro'; import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro' import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid> <CardGrid>
<Card title="44 Испытания"> <Card title="45 Испытания">
Этот репозиторий содержит 44 испытания, связанных с <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>. Этот репозиторий содержит 45 Испытания, связанных с <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
Испытания основаны на реальных задачах или инструментах для того, чтобы прокачать вас. Испытания основаны на реальных задачах или инструментах для того, чтобы прокачать вас.
</Card> </Card>
<Card title="Subscribe to get notify of latest challenges"> <Card title="Subscribe to get notify of latest challenges">
<SubscriptionForm isNote={false} lang="ru"/> <SubscriptionForm isNote={false} lang="ru" />
</Card> </Card>
<Card title="Станьте мейнтейнером открытого программного обеспечения (OSS)"> <Card title="Станьте мейнтейнером открытого программного обеспечения (OSS)">
Одна из целей этого репозитория <b>снизить барьер</b> для разработки Одна из целей этого репозитория <b>снизить барьер</b> для разработки открытого
открытого программного обеспечения (OSS). Решив эти задачи, вы поймете, программного обеспечения (OSS). Решив эти задачи, вы поймете, как начать
как начать вносить свой вклад в любой другой проект с открытым исходным кодом. вносить свой вклад в любой другой проект с открытым исходным кодом.
</Card> </Card>
<Card title="Учитесь вместе с другими"> <Card title="Учитесь вместе с другими">
Изучение и использование нового фреймворка всегда сопряжено с трудностями. Изучение и использование нового фреймворка всегда сопряжено с трудностями. В
В этом наборе испытаний содержатся реальные примеры задач, чтобы закрепить на практике то, чему вы научились. этом наборе испытаний содержатся реальные примеры задач, чтобы закрепить на
Любой может оставить комментарий или предложить помощь. практике то, чему вы научились. Любой может оставить комментарий или
предложить помощь.
<b> <b>
Учиться одному - здорово, но обучение вместе с другими поможет вам добиться большего. Учиться одному - здорово, но обучение вместе с другими поможет вам добиться
большего.
</b> </b>
</Card> </Card>
@@ -61,8 +63,9 @@ import SubscriptionForm from '../../../components/SubscriptionForm.astro'
</Card> </Card>
<Card title="Поддержка"> <Card title="Поддержка">
Это бесплатный проект, и он будет оставаться таковым как можно дольше. Это бесплатный проект, и он будет оставаться таковым как можно дольше. Однако
Однако вся работа ведется в мое свободное время, включая создание новых испытаний и ревью их решений(PRs). вся работа ведется в мое свободное время, включая создание новых испытаний и
ревью их решений(PRs).
<b>Спонсорство может поддержать меня и способствовать развитию проекта</b>. <b>Спонсорство может поддержать меня и способствовать развитию проекта</b>.
</Card> </Card>