Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
773cc22
Upgrade Angular to version 18.2.13
Copilot Aug 22, 2025
581e1fb
Update Angular CDK and Material to version 18.2.14
Copilot Aug 22, 2025
71454f3
Upgrade Angular to version 19.2.14 (build works, tests need fix)
Copilot Aug 22, 2025
44a1700
Update Angular CDK and Material to v19.2.19
Copilot Aug 22, 2025
07395db
Upgrade Angular to v20.2.1 - latest version
Copilot Aug 22, 2025
a23dc95
Complete Angular 20.2.1 upgrade with CDK and Material 20.2.0
Copilot Aug 22, 2025
fb59dad
Update all dependencies to latest versions and fix jest setup
Copilot Aug 22, 2025
9007534
Modernize ESLint configuration and fix Jest setup for Angular 20
Copilot Aug 22, 2025
d8b3b62
Modernize Angular patterns and update compatibility documentation
Copilot Aug 22, 2025
12ee396
Modernize Angular code to follow latest v20+ standards with input()/o…
Copilot Aug 22, 2025
741d3a6
Replace ngClass with modern class bindings per Angular best practices
Copilot Aug 22, 2025
3553a1c
Replace Cypress with Playwright and Jest with Vitest
Copilot Aug 22, 2025
caa57ab
Implement official Angular Vitest runner replacing standalone Vitest …
Copilot Aug 22, 2025
449ac6a
Fix CI issues: ESLint errors and service initialization circular depe…
Copilot Aug 22, 2025
ff0664e
Fix Vitest DOM matchers and TypeScript configuration for demo tests
Copilot Aug 22, 2025
8c74778
Configure Angular MCP integration with official MCP server setup
Copilot Aug 22, 2025
c16ecbd
Fix Vitest mocking issues in observer-cache.spec.ts and resolve ESLin…
Copilot Aug 22, 2025
c6b4bc9
Fix all failing unit tests by resolving Vitest mocking issues
Copilot Aug 22, 2025
9a49123
Fix CI failures: Update workflow for Playwright and resolve snapshot …
Copilot Aug 22, 2025
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
4 changes: 0 additions & 4 deletions .eslintignore

This file was deleted.

76 changes: 0 additions & 76 deletions .eslintrc.json

This file was deleted.

22 changes: 7 additions & 15 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,15 @@ jobs:
name: artifacts-${{ matrix.project }}
path: dist/${{ matrix.project }}

- name: Run cypress
uses: cypress-io/github-action@v5
with:
project: projects/${{ matrix.project }}-e2e
browser: chrome
headless: true
- name: Install Playwright browsers
run: npx playwright install --with-deps

- name: Upload failed screenshots
uses: actions/upload-artifact@v4
if: failure()
with:
name: failed-e2e-${{ matrix.project }}-screenshots
path: projects/${{ matrix.project }}-e2e/artifacts/screenshots
- name: Run Playwright tests
run: npm run e2e

- name: Upload failed videos
- name: Upload Playwright test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: failed-e2e-${{ matrix.project }}-videos
path: projects/${{ matrix.project }}-e2e/artifacts/videos
name: failed-e2e-${{ matrix.project }}-test-results
path: test-results/
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

## Compatibility matrix 🔢

| **ng-in-viewport** | **Angular** |
| ------------------ | ----------------------- |
| `16.1.x` | `>= 17.x.y \|\| 16.x.y` |
| `16.0.x` | `16.x.y \|\| 15.x.y` |
| `15.0.x` | `15.x.y \|\| 14.x.y` |
| **ng-in-viewport** | **Angular** |
| ------------------ | ----------------------------------------------- |
| `16.1.x` | `>= 17.x.y \|\| 18.x.y \|\| 19.x.y \|\| 20.x.y` |
| `16.0.x` | `16.x.y \|\| 15.x.y` |
| `15.0.x` | `15.x.y \|\| 14.x.y` |

## Support the Project 💖

Expand All @@ -31,6 +31,25 @@ By sponsoring, you'll help to:

Every contribution makes a difference, and even a small gesture goes a long way in keeping `ng-in-viewport` up to date for the community.

## Development 🛠️

### Angular MCP Integration

This project includes Model Context Protocol (MCP) integration for enhanced AI development assistance. The MCP configuration can be found in `mcp-config.json`:

```json
{
"servers": {
"angular-cli": {
"command": "npx",
"args": ["-y", "@angular/cli", "mcp"]
}
}
}
```

This configuration enables AI tools like Claude Desktop to interact with Angular CLI commands through the MCP protocol. For more information about Angular MCP, visit the [official documentation](https://angular.dev/ai/mcp).

## License 📝

[MIT](https://github.com/k3nsei/ng-in-viewport/blob/stable/LICENSE)
Expand Down
27 changes: 27 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "projects/ng-in-viewport/tsconfig.spec.json",
"runner": "vitest",
"buildTarget": "::development",
"setupFiles": ["projects/ng-in-viewport/vitest-setup.ts"]
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
Expand Down Expand Up @@ -99,6 +108,15 @@
"buildTarget": "demo:build"
}
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "projects/demo/tsconfig.spec.json",
"runner": "vitest",
"buildTarget": "::development",
"setupFiles": ["projects/demo/vitest-setup.ts"]
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
Expand Down Expand Up @@ -180,6 +198,15 @@
"buildTarget": "example:build"
}
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "projects/example/tsconfig.spec.json",
"runner": "vitest",
"buildTarget": "::development",
"setupFiles": ["projects/example/vitest-setup.ts"]
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
Expand Down
90 changes: 90 additions & 0 deletions e2e/demo/demo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { test, expect } from '@playwright/test';

test.describe('GIVEN: Demo Application', () => {
test.describe('WHEN page was loaded', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});

test('THEN should render title', async ({ page }) => {
const title = page.locator('.app-header__title');
await expect(title).toBeVisible();
await expect(title).toContainText('ng-in-viewport demo');
});

test('THEN should render 1st column', async ({ page }) => {
const firstColumn = page.locator('.example--first');
await expect(firstColumn).toBeVisible();
await expect(firstColumn).not.toBeEmpty();
});

test('THEN 1st column items from 1st to 9th should be active', async ({ page }) => {
await assertColumnItems(page, 'first', 1, 9);
});

test('THEN should render 2nd column', async ({ page }) => {
const secondColumn = page.locator('.example--second');
await expect(secondColumn).toBeVisible();
await expect(secondColumn).not.toBeEmpty();
});

test('THEN 2nd column items from 1st to 7th should be active', async ({ page }) => {
await assertColumnItems(page, 'second', 1, 7);
});

test.describe('AND scrolled into view 10th item of 1st column', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.locator('.example--first .item:nth-child(10)').scrollIntoViewIfNeeded();
});

test('THEN 1st column items from 10th to 18th should be active', async ({ page }) => {
await assertColumnItems(page, 'first', 10, 18);
});

test('THEN 2nd column items from 1st to 7th should be active', async ({ page }) => {
await assertColumnItems(page, 'second', 1, 7);
});
});

test.describe('AND scrolled 2nd column vertically by 779px', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.locator('.example--second').evaluate((el) => {
el.scrollTo(0, 779);
});
});

test('THEN 1st column items from 1st to 9th should be active', async ({ page }) => {
await assertColumnItems(page, 'first', 1, 9);
});

test('THEN 2nd column items from 11th to 17th should be active', async ({ page }) => {
await assertColumnItems(page, 'second', 11, 17);
});
});
});
});

async function assertColumnItems(page: any, column: 'first' | 'second', start: number, end?: number): Promise<void> {
const items = page.locator(`.example--${column} .item`);
const itemCount = await items.count();

for (let i = 0; i < itemCount; i++) {
const item = items.nth(i);
const number = i + 1;

if (inRange(number, start, end)) {
await expect(item).toHaveClass(/item--active/);
} else {
await expect(item).not.toHaveClass(/item--active/);
}
}
}

function inRange(value: number, start: number, end?: number): boolean {
if (end === undefined) {
return value === start;
}
return value >= start && value <= end;
}
21 changes: 21 additions & 0 deletions e2e/example/page-highlighting.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test, expect } from '@playwright/test';

test.describe('GIVEN: Example Application', () => {
test.describe('WHEN page was loaded', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});

test('THEN title should be rendered', async ({ page }) => {
const title = page.locator('.toolbar-label');
await expect(title).toBeVisible();
await expect(title).toContainText('Example of ng-in-viewport');
});

test('THEN `highlighting` navigation tab should be active', async ({ page }) => {
const activeTab = page.locator('nav a.is-active');
await expect(activeTab).toBeVisible();
await expect(activeTab).toContainText('Highlighting');
});
});
});
Loading
Loading