-
Notifications
You must be signed in to change notification settings - Fork 126
Create a new Component in Fundamental‐NGX
Fundamental-NGX Library is a fairly standard NX workspace, with some customizations.
In NX world a project is an encapsulated unit of code. Projects can be Applications and Libraries.
The Application is the main entry point for a runnable application and it depends on the Libraries. In Fundamental-NGX case, this is the website documentation that runs the components examples. It's located in the apps
folder.
The Libraries are isolated units of code that can be shared code, features, buildable packages for distribution, etc. They are located in the libs
folder. The subfolders cdk
, core
, platform
, fn
, etc. are libraries that are used for the npm packages. The individual components, such as button, input field, avatar, etc., are also NX buildable projects, but they don't end up as a separate npm project.
Let's add a new component called Generic Tag to Fundamental-NGX Core library.
![scheme2](https://private-user-images.githubusercontent.com/39598672/263786191-f48fc27a-e18e-4d7d-ac5a-ed55f31ac00f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUwODc5MjYsIm5iZiI6MTcyNTA4NzYyNiwicGF0aCI6Ii8zOTU5ODY3Mi8yNjM3ODYxOTEtZjQ4ZmMyN2EtZTE4ZS00ZDdkLWFjNWEtZWQ1NWYzMWFjMDBmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MzElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMxVDA3MDAyNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTA0MjM0NGQ3ZTMzNWVkYzJlM2JkYjBkOTNmOWVkYTRlNmE0NGU2MDMyY2NmYjRiOTVhMjUxY2U4Yzg5N2FjYTEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.hU-fjMeOzyABH_BTXuNufegXt2YWGpFfgazEiHh26ws)
index.ts
ng-package.json
Go to the tsconfig.base.json
file of the root project and add the index.ts
to the paths
object of compilerOptions
.
"@fundamental-ngx/core/generic-tag": ["libs/core/src/lib/generic-tag/index.ts"],
In the ng-package.json
file add the following code:
{
"$schema": "../../../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../../../dist/libs/core/generic-tag",
"lib": {
"entryFile": "./index.ts"
}
}
With this step, the new component is included in the Angular eco-system.
project.json
tells NX that this component folder is now an NX project.
{
"name": "core-generic-tag",
"$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"sourceRoot": "libs/core/src/lib/generic-tag",
"prefix": "fd",
"targets": {
"build": {
"executor": "@nx/angular:ng-packagr-lite",
"outputs": ["{workspaceRoot}/dist/libs/core/generic-tag"],
"options": {
"tsConfig": "libs/core/src/lib/generic-tag/tsconfig.lib.json",
"project": "libs/core/src/lib/generic-tag/ng-package.json",
"updateBuildableProjectDepsInPackageJson": false
},
"configurations": {
"production": {
"tsConfig": "libs/core/src/lib/generic-tag/tsconfig.lib.prod.json"
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"options": {
"lintFilePatterns": ["libs/core/src/lib/generic-tag/**/*.ts", "libs/core/src/lib/generic-tag/**/*.html"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/core/src/lib/generic-tag/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"tags": ["type:lib", "scope:fd"]
}
This step includes Generic Tag in the NX eco-system.
The configuration will create 3 targets for NX: build, lint and test.
Each target specifies the executor (the code that executes/runs the target) with a specific set of options.
-
Outputs tell Nx where the target is going to create file artifacts that Nx should cache.
For example,
"outputs": ["{workspaceRoot}/dist/libs/core/generic-tag"]
tells Nx where the build target is going to create file artifacts. - Options is an object that contains any configuration defaults for the executor. These options vary from executor to executor.
- Configurations allows you to create presets of options for different scenarios. All the configurations start with the properties defined in options as a baseline and then overwrite those options.
-
Tags are used for expressing constraints on project dependencies. You can check the base configuration in the
.eslintrc.json
file in thedepConstraints
array.
From another component in the fundamental-ngx/libs/core/src/lib
folder copy the following 4 configuration files:
-
tsconfig.json
- specifies the root files and the compiler options required to compile the project tsconfig.lib.json
tsconfig.lib.prod.json
tsconfig.spec.json
Like for the tsconfig files, copy this file from another projects. It's identical for all components and is used for defining the configuration structure of ESLint. This file will overwrite the parent .eslintrc.json
file
{
"name": "@fundamental-ngx/core/generic-tag",
"version": "VERSION_PLACEHOLDER",
"peerDependencies": {
"@angular/common": "ANGULAR_VER_PLACEHOLDER",
"@angular/core": "ANGULAR_VER_PLACEHOLDER"
},
"dependencies": {
"tslib": "^2.0.0"
}
}
-
generic-tag.component.scss
- the component CSS file. Imports the CSS code from Fundamental-stylesdist
folder
@import 'fundamental-styles/dist/generic-tag';
-
generic-tag.component.html
- the component markup
<div class="fd-generic-tag">Test</div>
-
generic-tag.component.spec.ts
- unit tests file -
generic-tag.component.ts
- component logic file
import { Component, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core';
import { FD_GENERIC_TAG_COMPONENT } from './tokens';
@Component({
selector: 'fd-generic-tag',
templateUrl: './generic-tag.component.html',
styleUrls: ['./generic-tag.component.scss'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: FD_GENERIC_TAG_COMPONENT,
useExisting: GenericTagComponent
}
]
})
export class GenericTagComponent {
}
-
generic-tag.module.ts
- component module file
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GenericTagComponent } from './generic-tag.component';
@NgModule({
imports: [CommonModule],
exports: [GenericTagComponent],
declarations: [GenericTagComponent]
})
export class GenericTagModule {}
-
tokens.ts
- Component Injection Token file
import { InjectionToken } from '@angular/core';
export const FD_GENERIC_TAG_COMPONENT = new InjectionToken('FdGenericTagComponent');
Now in the index.ts
export the component logic, its module and token:
export * from './generic-tag.module';
export * from './generic-tag.component';
export * from './tokens';
Note: please note that this example is very simplified for illustration purposes. Your component folder can contain services, directives, etc.
import baseConfig from '../../../../../jest.config.base';
export default {
...baseConfig,
displayName: 'core-generic-tag',
preset: '../../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '../../../../../jest-extended-matchers.ts'],
coverageDirectory: '../../../../../dist/coverage/core-generic-tag'
};
You can copy the README.md file from another component and modify it per your needs.
Your final file structure should look something like this:
![scheme3](https://private-user-images.githubusercontent.com/39598672/263841534-1c304aaa-0775-4f8c-be09-73bc7eb855ff.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUwODc5MjYsIm5iZiI6MTcyNTA4NzYyNiwicGF0aCI6Ii8zOTU5ODY3Mi8yNjM4NDE1MzQtMWMzMDRhYWEtMDc3NS00ZjhjLWJlMDktNzNiYzdlYjg1NWZmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MzElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMxVDA3MDAyNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTM0MzNmZWNjZGFmMTIyOGQ5NzY3ZDhjODNjNjQ1M2RhMjYyMDIzNTExNjkwOGQ5NzI5NTY2YjAyNDlkZDI3MjQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.07lKpbf_J5ovRudKD2QldTGBN49AVn_acGwQL85_5Tk)
With this step we concluded the implementation of the component in the Core library. Now is time to add it to the documentation website.
in the apps/docs/src/app/core/documentation/core-documentation-data.ts
file add the new component to the components
array.
![Screenshot 2023-08-30 at 1 26 48 PM](https://private-user-images.githubusercontent.com/39598672/264424210-b161b18b-d68c-4c27-a753-0073ba0c6b36.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUwODc5MjYsIm5iZiI6MTcyNTA4NzYyNiwicGF0aCI6Ii8zOTU5ODY3Mi8yNjQ0MjQyMTAtYjE2MWIxOGItZDY4Yy00YzI3LWE3NTMtMDA3M2JhMGM2YjM2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MzElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMxVDA3MDAyNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTE4YTA4YWExMzAwYTY5YzBmYzA0OThhNmQyYzY1YjJjNmViZDBkZDQ1NmNhYWRiNjUyMjE3MzI1ZjdkYjYyMTQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.n8l_3kyjD4Y9-RfupilMosErh-2NrJLEb8HFH4sn0KU)
The newly created component is now in the Side Nav:
![Screenshot 2023-08-30 at 1 30 24 PM](https://private-user-images.githubusercontent.com/39598672/264425273-a860217e-2acb-45f6-91aa-730b468ce2a6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUwODc5MjYsIm5iZiI6MTcyNTA4NzYyNiwicGF0aCI6Ii8zOTU5ODY3Mi8yNjQ0MjUyNzMtYTg2MDIxN2UtMmFjYi00NWY2LTkxYWEtNzMwYjQ2OGNlMmE2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MzElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMxVDA3MDAyNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ0ZWU0Y2VkNWQ2ZDYyMmRjMWQ5NzYyYjk3NTBmMGMwODU4ZjA3OTI0OGU5YmMwZDExMWNjNTU0YmZkMWI1NjAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.xodKcM6QuIgpl-i2306flzrtk-TRZ5Qwt3BsR7JaPZk)
The component documentation, just like the component logic, is a separate NX project. To create it go to libs/docs/core
folder and create a new subfolder called generic-tag
.
11.1. To tell NX that this is a project, create
project.json
file with the following code:
{
"name": "docs-core-generic-tag",
"sourceRoot": "libs/docs/core/generic-tag",
"projectType": "library",
"prefix": "fundamental-ngx",
"targets": {
"e2e": {
"executor": "@fundamental-ngx/nx-plugin:e2e-test",
"options": {
"e2eFiles": ["libs/docs/core/generic-tag/e2e/**/*.e2e-spec.ts"],
"devServerTarget": "docs:serve:e2e"
},
"outputs": ["{workspaceRoot}/allure-results/docs-core-generic-tag"]
}
},
"tags": ["type:lib", "scope:docs"]
}
11.2. from another component copy the
tsconfig.json
file, which is the same for all components.
11.3. create the documentation component
generic-tag-docs.component.html
generic-tag-docs.component.ts
generic-tag-docs.module.ts
11.4. create
index.ts
file for the exports and add it to thepaths
array ofcompilerOptions
intsconfig.base.json
file, where you added the component logicindex.ts
file.
"@fundamental-ngx/docs/core/generic-tag": ["libs/docs/core/generic-tag/index.ts"],
The index.ts
file should export the component module:
export * from './generic-tag-docs.module';
11.4. Create the component route
In the apps/docs/src/app/core/core-documentation.routes.ts
add the following code to the ROUTES
array:
{
path: 'generic-tag',
loadChildren: () => import('@fundamental-ngx/docs/core/generic-tag').then((m) => m.GenericTagDocsModule)
},
11.5. Component header
Each component documentation page has a header containing information related to the use of the component, links, etc.
To create the header add a new folder called generic-tag-header
and inside it place the component logic:
generic-tag-header.component.html
<fd-doc-page>
<header>Generic Tag</header>
<description>
Generic Tag description...
</description>
<import module="GenericTagModule" subPackage="generic-tag"></import>
<fd-header-tabs></fd-header-tabs>
</fd-doc-page>
generic-tag-header.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'fd-generic-tag-header',
templateUrl: './generic-tag-header.component.html'
})
export class GenericTagHeaderComponent {}
11.6. Add the new component to the API Files in the
libs/docs/core/shared/src/lib
folder
export const API_FILES = {
...
genericTag: [
'GenericTagComponent',
],
....
};
11.6. Setup the routes, imports, exports, declarations, providers, etc. in the doc module.
The min setup of the generic-tag-docs.module.ts
should look like this:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ApiComponent, currentComponentProvider, SharedDocumentationPageModule } from '@fundamental-ngx/docs/shared';
import { API_FILES } from '@fundamental-ngx/docs/core/shared';
import { GenericTagModule } from '@fundamental-ngx/core/generic-tag';
import { GenericTagDocsComponent } from './generic-tag-docs.component';
import { GenericTagHeaderComponent } from './generic-tag-header/generic-tag-header.component';
const routes: Routes = [
{
path: '',
component: GenericTagHeaderComponent,
children: [
{ path: '', component: GenericTagDocsComponent },
{ path: 'api', component: ApiComponent, data: { content: API_FILES.genericTag } }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes), SharedDocumentationPageModule, GenericTagModule],
exports: [RouterModule],
declarations: [
GenericTagDocsComponent,
GenericTagHeaderComponent,
],
providers: [currentComponentProvider('generic-tag')]
})
export class GenericTagDocsModule {}
11.7. Create the examples
At this point you are ready to create your component examples in a subfolder called examples
.
Your file structure should look like this:
![Screenshot 2023-08-30 at 2 40 35 PM](https://private-user-images.githubusercontent.com/39598672/264441342-98510320-2b2e-40de-aba9-579ad7e354c7.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUwODc5MjYsIm5iZiI6MTcyNTA4NzYyNiwicGF0aCI6Ii8zOTU5ODY3Mi8yNjQ0NDEzNDItOTg1MTAzMjAtMmIyZS00MGRlLWFiYTktNTc5YWQ3ZTM1NGM3LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MzElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMxVDA3MDAyNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTZjNjFhN2Q0ZTYyZjhiNWYzYmEzNDAzODNkMGZlNjljNWMyOTY0ZTYzZmUxMDU0MTU0YTNhN2I4MmIzNzM3MjImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.oZAhs7iI-QxKlWG5TxT0zhWofyjdumMxN2q_euvojJw)
Check this PR for Generic Tag implementation code.