Skip to content

Create a new Component in Fundamental‐NGX

Inna Atanasova edited this page Aug 30, 2023 · 11 revisions

Folder Structure Overview

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.

scheme1

Adding a new component to the library

Let's add a new component called Generic Tag to Fundamental-NGX Core library.

1. Create a new empty folder in fundamental-ngx/libs/core/src/lib called generic-tag

scheme2

2. In the newly created folder add the following new (empty) files:

  • 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.

3. Create a new project.json file.

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 the depConstraints array.

4. Add the tsconfig files.

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

5. Add the .eslintrc.json file

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

6. Create a package.json and add the following code:

{
    "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"
    }
}

7. Create the component files

  • generic-tag.component.scss - the component CSS file. Imports the CSS code from Fundamental-styles dist 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.

8. Create the Jest config file jest.config.ts with the following configuration:

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'
};

9. Add component README.md file

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

With this step we concluded the implementation of the component in the Core library. Now is time to add it to the documentation website.

10. Add the component link to the Side Navigation

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

The newly created component is now in the Side Nav:

Screenshot 2023-08-30 at 1 30 24 PM

11. Create the component examples

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 the paths array of compilerOptions in tsconfig.base.json file, where you added the component logic index.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

Check this PR for Generic Tag implementation code.

Clone this wiki locally