Skip to content
Closed
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
9 changes: 5 additions & 4 deletions .github/workflows/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ jobs:
- name: Check typescript type validity
run: npm run ts-lint

# Individual PR commits are NOT linted: PRs are squash-merged and the
# PR title becomes the commit message, so the title is what must follow
# the conventional-commit format (enforced by pr-title.yml). This
# push-time check remains as a backstop for the squashed commits landing
# on main and for direct pushes.
- name: Validate current commit (last commit) with commitlint
if: github.event_name == 'push'
run: npx commitlint --last --verbose

- name: Validate PR commits with commitlint
if: github.event_name == 'pull_request'
run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose
33 changes: 33 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: PR title

# PRs are squash-merged and the PR title becomes the commit message on main,
# which release-please parses for versioning. Individual commits inside a PR
# are free-form; the PR title is what must follow the conventional-commit
# format. Runs on `edited` so fixing the title re-triggers the check.

on:
pull_request:
types: [opened, edited, synchronize, reopened]
branches-ignore:
- release-please-**

jobs:
lint-pr-title:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: actions/setup-node@v6
with:
node-version: 'lts/*'
cache: 'npm'

# Root-only install: commitlint and its config live in the root
# devDependencies; the workspaces are not needed for this check.
- name: Install commitlint
run: npm ci --workspaces=false --ignore-scripts

- name: Validate PR title with commitlint
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: printf '%s\n' "$PR_TITLE" | npx commitlint --verbose
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
- **Developing a DiracX-Web extension?** Go straight to the [:page_facing_up: Extension README](https://diracx.diracgrid.org/en/latest/developer/manage_extension/)
- **Managing the repository?** Discover tips and tricks in the [:book: Ops Guide](https://diracx.diracgrid.org/en/latest/dev/setup_environment/)

- **Interested in contributing?** Read the [:star: Contributing Document](CONTRIBUTING.md)
- **Interested in contributing?** Read the [:star: Contributing Document](docs/dev/how-to/contribute-to-web.md)



91 changes: 65 additions & 26 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "diracx-web-root",
"version": "0.1.0-a11",
"private": true,
"engines": {
"node": ">=22"
},
"workspaces": [
"packages/diracx-web-components",
"packages/diracx-web",
Expand Down
15 changes: 15 additions & 0 deletions packages/diracx-web-components/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import _import from "eslint-plugin-import";
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import jsxA11y from "eslint-plugin-jsx-a11y";
import tsParser from "@typescript-eslint/parser";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";
Expand All @@ -30,6 +31,7 @@ export default [
"prettier",
),
),
jsxA11y.flatConfigs.recommended,
{
plugins: {
import: fixupPluginRules(_import),
Expand Down Expand Up @@ -79,6 +81,19 @@ export default [
ignoreRestSiblings: true,
},
],
"@typescript-eslint/no-restricted-imports": [
"error",
{
paths: [
{
name: "@mui/icons-material",
message:
"Import icons individually: import Icon from '@mui/icons-material/Icon'",
allowTypeImports: true,
},
],
},
],
"no-restricted-properties": [
"error",
{
Expand Down
10 changes: 10 additions & 0 deletions packages/diracx-web-components/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ const config = {
// The test environment that will be used for testing
testEnvironment: "jest-environment-jsdom",

// Coverage is only collected when jest runs with --coverage
// (see the test:coverage script). Stories, mocks and build output are
// excluded; no thresholds are enforced yet.
collectCoverageFrom: [
"src/**/*.{ts,tsx}",
"!src/**/*.d.ts",
"!src/**/*.stories.{ts,tsx}",
],
coveragePathIgnorePatterns: ["/node_modules/", "/dist/", "/stories/"],

moduleNameMapper: {
"^@axa-fr/react-oidc$": "<rootDir>/stories/mocks/react-oidc.mock.tsx",
"^../../hooks/metadata$": "<rootDir>/stories/mocks/metadata.mock.tsx",
Expand Down
60 changes: 57 additions & 3 deletions packages/diracx-web-components/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import structuredClonePonyfill from "@ungap/structured-clone";
import "@testing-library/jest-dom";

// Polyfill structuredClone for jsdom (used by @mui/x-charts)
// Polyfill structuredClone for jsdom (used by @mui/x-charts).
// jest-environment-jsdom does not expose Node's native structuredClone, and
// Node's v8-serialize trick creates values from the wrong realm (instanceof
// breaks). @ungap/structured-clone implements the structured-clone algorithm
// in the current realm (unlike JSON round-tripping, it preserves Dates,
// Maps, Sets, …).
if (typeof globalThis.structuredClone === "undefined") {
globalThis.structuredClone = <T>(val: T): T =>
JSON.parse(JSON.stringify(val));
globalThis.structuredClone = ((val: unknown) =>
structuredClonePonyfill(val)) as typeof globalThis.structuredClone;
}

// Polyfill PointerEvent for jsdom (used by @mui/x-internal-gestures)
Expand All @@ -20,4 +26,52 @@ if (typeof globalThis.PointerEvent === "undefined") {
};
}

// Mock layout measurements for @tanstack/react-virtual (jsdom has no layout engine).
// Only override offsetHeight/offsetWidth — these are the properties the virtualizer
// uses to determine container size. We do NOT override getBoundingClientRect globally
// because MUI Popover relies on the native implementation.
//
// This is a global override for every test; if a future test needs real layout
// measurements (e.g. to assert that an element is actually visible), call
// restoreRealLayoutMeasurements() in a beforeEach hook.
const originalOffsetHeight = Object.getOwnPropertyDescriptor(
HTMLElement.prototype,
"offsetHeight",
);
const originalOffsetWidth = Object.getOwnPropertyDescriptor(
HTMLElement.prototype,
"offsetWidth",
);

Object.defineProperty(HTMLElement.prototype, "offsetHeight", {
configurable: true,
get() {
return 600;
},
});
Object.defineProperty(HTMLElement.prototype, "offsetWidth", {
configurable: true,
get() {
return 800;
},
});

// Exported so individual tests can opt out of the mocked layout values.
export function restoreRealLayoutMeasurements() {
if (originalOffsetHeight) {
Object.defineProperty(
HTMLElement.prototype,
"offsetHeight",
originalOffsetHeight,
);
}
if (originalOffsetWidth) {
Object.defineProperty(
HTMLElement.prototype,
"offsetWidth",
originalOffsetWidth,
);
}
}

jest.mock("@axa-fr/react-oidc");
Loading
Loading