Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
You are an expert in TypeScript, Angular, and scalable web application development. You write functional, maintainable, performant, and accessible code following Angular and TypeScript best practices.

## TypeScript Best Practices

- Use strict type checking
- Prefer type inference when the type is obvious
- Avoid the `any` type; use `unknown` when type is uncertain

## Angular Best Practices

- Always use standalone components over NgModules
- Must NOT set `standalone: true` inside Angular decorators. It's the default in Angular v20+.
- Use signals for state management
- Implement lazy loading for feature routes
- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead
- Use `NgOptimizedImage` for all static images.
- `NgOptimizedImage` does not work for inline base64 images.

## Accessibility Requirements

- It MUST pass all AXE checks.
- It MUST follow all WCAG AA minimums, including focus management, color contrast, and ARIA attributes.

### Components

- Keep components small and focused on a single responsibility
- Use `input()` and `output()` functions instead of decorators
- Use `computed()` for derived state
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
- Prefer inline templates for small components
- Prefer Reactive forms instead of Template-driven ones
- Do NOT use `ngClass`, use `class` bindings instead
- Do NOT use `ngStyle`, use `style` bindings instead
- When using external templates/styles, use paths relative to the component TS file.

## State Management

- Use signals for local component state
- Use `computed()` for derived state
- Keep state transformations pure and predictable
- Do NOT use `mutate` on signals, use `update` or `set` instead

## Templates

- Keep templates simple and avoid complex logic
- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
- Use the async pipe to handle observables
- Do not assume globals like (`new Date()`) are available.
- Do not write arrow functions in templates (they are not supported).

## Services

- Design services around a single responsibility
- Use the `providedIn: 'root'` option for singleton services
- Use the `inject()` function instead of constructor injection
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CpsIconComponent, ICONS_PATH } from './cps-icon.component';
import { CommonModule } from '@angular/common';

describe('CpsIconComponent', () => {
const createComponent = () => {
fixture = TestBed.createComponent(CpsIconComponent);
component = fixture.componentInstance;
fixture.detectChanges();
};

let component: CpsIconComponent;
let fixture: ComponentFixture<CpsIconComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CommonModule, CpsIconComponent]
}).compileComponents();
});

it('should create', () => {
createComponent();
expect(component).toBeTruthy();
});

describe('Test assets path injection', () => {
it('should use default path when no ICONS_PATH is provided', () => {
createComponent();
expect(component.url).toBe('assets/');
});

it('should use injected ICONS_PATH value', () => {
TestBed.overrideProvider(ICONS_PATH, {
useValue: 'test-assets/'
});
createComponent();
expect(component.url).toBe('test-assets/');
});
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { CommonModule, DOCUMENT } from '@angular/common';
import { Component, Inject, Input, OnChanges } from '@angular/core';
import {
Component,
inject,
Inject,
InjectionToken,
Input,
OnChanges
} from '@angular/core';
import { convertSize } from '../../utils/internal/size-utils';
import { getCSSColor } from '../../utils/colors-utils';

/**
* Injection token that is used to provide the path to the icons.
*/
export const ICONS_PATH = new InjectionToken<string>(
'Icons path for CpsIconComponent'
);

export const iconNames = [
'access',
'access-denied',
Expand Down Expand Up @@ -176,7 +190,7 @@ export class CpsIconComponent implements OnChanges {
@Input() color = 'currentColor';

iconColor = 'currentColor';
url = 'assets/';
url = inject(ICONS_PATH, { optional: true }) ?? 'assets/';
cvtSize = '';

classesList: string[] = ['cps-icon'];
Expand Down
Loading