Compare commits
49
Commits
94c12dc90a
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b480ecb268 | ||
|
|
475a6cb634 | ||
|
|
f0c59a9b6f | ||
|
|
61ec0d839e | ||
|
|
382536168b | ||
|
|
956b2f277b | ||
|
|
37bb4e9ea2 | ||
|
|
72c49e6b0f | ||
|
|
9091bdcfae | ||
|
|
2ddf8961c2 | ||
|
|
7559a2725a | ||
|
|
caf0f3de2d | ||
|
|
856fea304a | ||
|
|
3b9a78bf9d | ||
|
|
75d4632052 | ||
|
|
9089a4ef60 | ||
|
|
88154775de | ||
|
|
5211b5f76a | ||
|
|
b0ae34dbaa | ||
|
|
a92d83a5f4 | ||
|
|
35747ec681 | ||
|
|
6d1aeb6ee2 | ||
|
|
768a43d9b6 | ||
|
|
75c8300d84 | ||
|
|
97f99a23ae | ||
|
|
dd2874d8b0 | ||
|
|
6e13c29141 | ||
|
|
85b1ca240e | ||
|
|
82cfca4215 | ||
|
|
c1a1e71240 | ||
|
|
2b742e95f1 | ||
|
|
a09d83fb53 | ||
|
|
575f9202a8 | ||
|
|
bb6009d26c | ||
|
|
568bc3a98b | ||
|
|
3dc61e5087 | ||
|
|
2991ddf567 | ||
|
|
669baef7d4 | ||
|
|
c5011073cf | ||
|
|
2b5ce8a233 | ||
|
|
9b0758a0eb | ||
|
|
477f2bcc47 | ||
|
|
64a6ff6a20 | ||
|
|
5564eaf31a | ||
|
|
a10b79b504 | ||
|
|
d8d4c7cb69 | ||
|
|
9821ffe642 | ||
|
|
1874c0ea9e | ||
|
|
2c2fbd160e |
@@ -7,5 +7,5 @@
|
||||
"access": "restricted",
|
||||
"baseBranch": "dev",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["apps/docs"]
|
||||
"ignore": []
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'luna-charts': minor
|
||||
---
|
||||
|
||||
Establish the initial project foundation with architecture, documentation, governance, and development tooling.
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
packages/**/node_modules
|
||||
@@ -0,0 +1,18 @@
|
||||
FROM node:22-bullseye
|
||||
|
||||
# Install Docker CLI + Compose V2 plugin from Docker's official apt repo
|
||||
# (Debian's own docker.io package ships an outdated Docker version and
|
||||
# does not include the compose plugin at all).
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates curl gnupg \
|
||||
&& install -m 0755 -d /etc/apt/keyrings \
|
||||
&& curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \
|
||||
&& chmod a+r /etc/apt/keyrings/docker.asc \
|
||||
&& echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bullseye stable" \
|
||||
| tee /etc/apt/sources.list.d/docker.list > /dev/null \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y docker-ce-cli docker-compose-plugin \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN corepack enable
|
||||
@@ -0,0 +1,90 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
pull_request:
|
||||
branches: [dev, main]
|
||||
|
||||
jobs:
|
||||
lint-and-format:
|
||||
runs-on: ubuntu-docker-dind
|
||||
container:
|
||||
image: git.byting-pandas.ninja/ci/node22-docker:latest
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: corepack enable
|
||||
- run: corepack prepare pnpm@11.11.0 --activate
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Check formatting
|
||||
run: pnpm format:check
|
||||
|
||||
changeset-check:
|
||||
runs-on: ubuntu-docker-dind
|
||||
container:
|
||||
image: git.byting-pandas.ninja/ci/node22-docker:latest
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- run: corepack enable
|
||||
- run: corepack prepare pnpm@11.11.0 --activate
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify changeset exists
|
||||
continue-on-error: true
|
||||
run: pnpm changeset status --since=origin/dev
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-docker-dind
|
||||
container:
|
||||
image: git.byting-pandas.ninja/ci/node22-docker:latest
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: corepack enable
|
||||
- run: corepack prepare pnpm@11.11.0 --activate
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run unit tests
|
||||
run: pnpm test:unit
|
||||
|
||||
component-tests:
|
||||
runs-on: ubuntu-docker-dind
|
||||
container:
|
||||
image: git.byting-pandas.ninja/ci/luna-charts-test:v1.62.1
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: corepack enable
|
||||
|
||||
- run: corepack prepare pnpm@11.11.0 --activate
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run component tests (Playwright)
|
||||
working-directory: packages/luna-charts
|
||||
run: pnpm test:browser
|
||||
@@ -1 +1,2 @@
|
||||
node_modules
|
||||
.pnpm-store
|
||||
|
||||
+2
-1
@@ -2,5 +2,6 @@
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100
|
||||
"printWidth": 100,
|
||||
"bracketSameLine": true
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
# Contributing to LUNA Charts
|
||||
|
||||
Thank you for considering a contribution to LUNA Charts! This document explains how the project is governed, what's expected of a contribution, and how to get your change through review.
|
||||
|
||||
Before contributing, please also read our [Code of Conduct](./.github/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Project Status
|
||||
|
||||
LUNA Charts is currently in its **groundwork phase** — foundational architecture, tooling, and testing infrastructure are being established before chart components are built out. Check the [Roadmap](./docs/src/content/docs/getting-started/roadmap.mdx) to see what's currently in scope.
|
||||
|
||||
## How the Project Is Governed
|
||||
|
||||
LUNA Charts balances open community contribution with a documented architecture. In short:
|
||||
|
||||
- **The architecture is the authoritative source for implementation decisions** — it's documented under [`docs/.../architecture/adm`](./docs/src/content/docs/architecture/adm) (Architecture Development Method) and [`docs/.../architecture/adr`](./docs/src/content/docs/architecture/adr) (Architecture Decision Records)
|
||||
- **Contributions are reviewed on three levels**: automated checks (types, linting, tests, accessibility), architectural compliance (does it fit the documented architecture?), and general project quality (readability, maintainability, documentation)
|
||||
- **A Pull Request can be rejected even if all automated checks pass** — for example, if it introduces an unnecessary public API change, bypasses accessibility mechanisms, or conflicts with a documented architectural decision
|
||||
- Community members can propose architectural changes, but such changes only become official once the relevant ADR/ADM documentation has been updated to reflect them (see [Architecture Change Management](./docs/src/content/docs/architecture/adm/architecture-change-management.mdx))
|
||||
|
||||
If you're planning a larger change, especially one that touches the public API, rendering approach, or accessibility behavior, please open an issue or discussion first so it can be reviewed against the architecture before you invest time in an implementation.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Fork the repository and clone your fork
|
||||
2. Install dependencies:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Requirements: Node.js `>= 22.0.0`, pnpm `11.11.0` (via Corepack), and Docker (for component/browser tests)
|
||||
|
||||
## Making a Change
|
||||
|
||||
1. Create a branch from `main` with a descriptive name (e.g. `fix/axis-label-overflow`, `docs/update-technology-architecture`)
|
||||
2. Make your change
|
||||
3. Run the checks locally before opening a PR:
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm test:unit
|
||||
pnpm test:component # runs inside a Docker/Playwright container
|
||||
pnpm test # runs both of the above
|
||||
```
|
||||
|
||||
4. If your change affects published package behavior (bug fix, feature, breaking change), add a changeset:
|
||||
|
||||
```bash
|
||||
pnpm changeset
|
||||
```
|
||||
|
||||
Documentation-only or internal tooling changes generally don't need a changeset — if you're unsure, mention it in your PR description and a maintainer will advise.
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Keep PRs focused on a single concern where possible — smaller PRs are easier to review against the architecture
|
||||
- Describe **what** changed and **why**, not just what the diff shows
|
||||
- If your change affects accessibility behavior, explain how it was verified (e.g. which axe-core checks or manual screen-reader testing was done)
|
||||
- If your change touches an area covered by an existing ADR, reference it and note whether the ADR needs a corresponding update
|
||||
- Automated testing (unit, accessibility, linting) must pass before a PR can be merged
|
||||
|
||||
## Documentation Changes
|
||||
|
||||
Documentation lives in `docs/` (Starlight) and is a first-class contribution — fixing unclear wording, expanding examples, or correcting an outdated ADR/ADM page is just as valuable as a code change.
|
||||
|
||||
If your code change makes a documented architectural statement incorrect (for example, changing a technology choice or the repository structure), please update the relevant ADR/ADM page in the same PR, including a short **Change Log** entry at the end of that document explaining what changed and why. Keeping documentation in sync with implementation is part of the review criteria described above.
|
||||
|
||||
## Reporting Bugs and Suggesting Features
|
||||
|
||||
Please open an issue with:
|
||||
|
||||
- A clear description of the problem or suggestion
|
||||
- Steps to reproduce (for bugs), including browser/OS if relevant to rendering or accessibility
|
||||
- What you expected to happen vs. what actually happened
|
||||
|
||||
## Questions
|
||||
|
||||
If anything here is unclear, feel free to open a discussion or issue — improving this guide based on real contributor questions is welcome too.
|
||||
@@ -0,0 +1,21 @@
|
||||
FROM mcr.microsoft.com/playwright:v1.62.1-noble
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@10 --activate
|
||||
|
||||
ENV CI=true
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN rm -rf node_modules packages/**/node_modules
|
||||
|
||||
ENV PNPM_CONFIG_SUPPORTED_ARCHITECTURES="current,linux-x64-gnu"
|
||||
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
WORKDIR /app/packages/luna-charts
|
||||
|
||||
RUN pnpm add -D @rolldown/binding-linux-x64-gnu
|
||||
|
||||
CMD ["pnpm", "test:browser"]
|
||||
@@ -1,3 +1,106 @@
|
||||
# LUNA-Charts
|
||||
# LUNA Charts
|
||||
|
||||
LUNA Charts is a reusable, developer-friendly charting library designed to make accessible, WCAG-oriented data visualization the default rather than an afterthought.
|
||||
|
||||
> **Project status:** Groundwork phase. Core architecture and tooling are in place; production-ready chart components (starting with `BarChart`) are still in development. See the [Roadmap](./docs/src/content/docs/getting-started/roadmap.mdx) for details.
|
||||
|
||||
## Preface & Repository Mirror Notice
|
||||
|
||||
Please be aware of our repository structure:
|
||||
|
||||
* **Official Repository:** [git.byting-pandas.ninja/Ninosaurier/LUNA-Charts](https://git.byting-pandas.ninja/Ninosaurier/LUNA-Charts)
|
||||
|
||||
**Important:** All development, including issue tracking and contribution management, takes place exclusively on our self-hosted server. Issues and pull requests cannot be created or processed on this GitHub mirror. Please visit the original link above to report bugs or request features.
|
||||
|
||||
## Why LUNA Charts
|
||||
|
||||
Most charting libraries treat accessibility as an add-on. LUNA Charts builds it in from the start:
|
||||
|
||||
* **Accessible by default** — components are designed and tested against WCAG guidance from day one, not patched in afterward
|
||||
* **Framework-agnostic** — built as standard Web Components (Stencil), usable in React, Vue, Angular, Svelte, or plain HTML
|
||||
* **SVG-based rendering** — inspectable, stylable, and screen-reader-friendly output instead of an opaque canvas
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Area | Technology |
|
||||
| --- | --- |
|
||||
| Component framework | [Stencil](https://stenciljs.com) (compiles to standard Web Components) |
|
||||
| Language | TypeScript |
|
||||
| Package manager | pnpm (monorepo workspaces) |
|
||||
| Unit & component testing | [Vitest](https://vitest.dev), with [Playwright](https://playwright.dev) as the browser provider |
|
||||
| Accessibility testing | axe-core |
|
||||
| Documentation | [Starlight](https://starlight.astro.build) |
|
||||
| Component explorer | Storybook |
|
||||
| Linting & formatting | ESLint, Prettier |
|
||||
| Release management | [Changesets](https://github.com/changesets/changesets) |
|
||||
|
||||
The full set of architecture decisions behind these choices is documented in [`docs/.../architecture/adr`](./docs/src/content/docs/architecture/adr) and the broader [Architecture Development Method (ADM) documentation](./docs/src/content/docs/architecture/adm).
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
luna-charts/
|
||||
│
|
||||
├── packages/
|
||||
│ └── luna-charts/ # The library itself (Stencil components, Storybook)
|
||||
│
|
||||
├── docs/ # Architecture docs, ADRs, guides (Starlight)
|
||||
│
|
||||
├── examples/ # Usage examples (framework integrations)
|
||||
│
|
||||
├── .changeset/ # Release/versioning configuration
|
||||
│
|
||||
├── .github/ # Community health files (Code of Conduct, etc.)
|
||||
│
|
||||
└── .gitea/ # CI workflows (Gitea Actions) and CI runner image
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* Node.js `>= 22.0.0`
|
||||
* pnpm `11.11.0` (see `devEngines` in `package.json`; pnpm will be downloaded automatically via Corepack if missing)
|
||||
* Docker (required for running component/browser tests locally, see below)
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Ninosaurier/LUNA-Charts.git
|
||||
cd luna-charts
|
||||
pnpm install
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
git clone https://git.byting-pandas.ninja/Ninosaurier/LUNA-Charts.git
|
||||
cd luna-charts
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Common commands
|
||||
|
||||
| Command | Description |
|
||||
| --- | --- |
|
||||
| `pnpm test:unit` | Run unit tests for the `luna-charts` package |
|
||||
| `pnpm test:component` | Run component/browser tests inside the containerized Playwright environment |
|
||||
| `pnpm test` | Run the full test suite (unit + component) |
|
||||
| `pnpm changeset` | Record a changeset for your change (required for any user-facing change) |
|
||||
| `pnpm run docs` | Start the documentation site locally (astroJS) |
|
||||
| `pnpm run lint` | Run the linter to check for code style and quality issues |
|
||||
| `pnpm run format` | Run the formatter to automatically fix code style issues |
|
||||
| `pnpm run test:browser` | Run browser tests (only works inside the `packages/luna-charts` directory) |
|
||||
| `pnpm run storybook` | Start the storybook documentation |
|
||||
|
||||
Component and browser tests run inside Docker (`docker-compose.test.yaml`), based on the same `Dockerfile.test` used to build the image CI tests against, to guarantee a reproducible browser environment — you don't need Playwright browsers installed locally. Locally this image is built on demand; in CI a prebuilt version of the same image is pulled directly (see [ADR-0002](./docs/src/content/docs/architecture/adr/000200-technology-stack.mdx) and the [Technology Architecture](./docs/src/content/docs/architecture/adm/technology-architecture.mdx#66-test-execution-environment) doc for details).
|
||||
|
||||
## Documentation
|
||||
|
||||
* **Architecture documentation**: see `docs/` — includes the full Architecture Development Method (ADM) documentation (vision, business architecture, technology architecture, governance, migration planning) and all Architecture Decision Records (ADRs)
|
||||
* **Contributing**: see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
* **Code of Conduct**: see [.github/CODE_OF_CONDUCT.md](./.github/CODE_OF_CONDUCT.md)
|
||||
|
||||
## License
|
||||
|
||||
LUNA Charts is licensed under the [GNU Lesser General Public License v3.0 (LGPL-3.0)](./LICENSE).
|
||||
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
test:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.test
|
||||
working_dir: /app/packages/luna-charts
|
||||
@@ -148,6 +148,42 @@ LUNA Charts follows a multi-layer quality assurance model.
|
||||
Automated testing (unit, accessibility, linting, visual regression) is a **blocking condition for every release**. Usability testing is a **blocking condition for minor and major releases**, but is skipped for patch releases to avoid delaying critical fixes.
|
||||
</Aside>
|
||||
|
||||
### 6.6 Test Execution Environment
|
||||
|
||||
Component and browser tests (Vitest browser mode, see ADR-0002) run inside
|
||||
a **containerized Playwright environment** rather than directly on
|
||||
contributor or CI host machines.
|
||||
|
||||
#### Characteristics
|
||||
|
||||
- A dedicated `Dockerfile.test`, based on the official Playwright container
|
||||
image, provides a reproducible browser environment with pinned browser
|
||||
versions
|
||||
- Locally, `docker-compose.test.yaml` builds this image from source and
|
||||
orchestrates test execution, isolating dependencies from the host system
|
||||
- In Continuous Integration, the same `Dockerfile.test` is built and
|
||||
pushed to the project's container registry out-of-band (tagged by
|
||||
Playwright version, e.g. `luna-charts-test:v1.62.1`); CI jobs pull this
|
||||
prebuilt image directly rather than rebuilding it on every run, then
|
||||
check out the current commit and install dependencies inside the running
|
||||
container before executing tests
|
||||
- `Dockerfile.test` remains the single source of truth for the test
|
||||
environment definition in both cases — only *when* the image is built
|
||||
(on demand locally, ahead of time for CI) differs
|
||||
|
||||
#### Rationale
|
||||
|
||||
- Ensures deterministic, reproducible accessibility and browser test
|
||||
results independent of the host operating system
|
||||
- Removes the need for contributors to install and maintain matching
|
||||
Playwright browser binaries locally
|
||||
- Avoids rebuilding the Playwright/browser image on every CI run, which
|
||||
would otherwise require Docker-in-Docker access inside the CI runner —
|
||||
significant infrastructure complexity for a single-service image build
|
||||
- Aligns automated testing (Section 6.1–6.4) with a single,
|
||||
version-controlled execution environment definition, even though the
|
||||
build trigger differs between local development and CI
|
||||
|
||||
## 7. Release & Distribution Model
|
||||
|
||||
### Open Source Release Pipeline
|
||||
@@ -192,6 +228,7 @@ LUNA Charts targets modern web environments.
|
||||
- Single-package distribution model
|
||||
- No plugin or extension system
|
||||
- Multi-layer testing strategy with release gating
|
||||
- Containerized, reproducible test execution environment for browser and accessibility tests
|
||||
- Open-source distribution via GitHub and npm
|
||||
- Modern browser and mobile-first runtime strategy
|
||||
|
||||
@@ -224,3 +261,46 @@ This document reflects a refined Technology Architecture based on iterative arch
|
||||
- Distribution model defined as a single-package open-source release via npm and GitHub
|
||||
- Testing strategy defined as a multi-layer quality gate system (unit, accessibility, linting, visual regression)
|
||||
- Runtime scope restricted to modern browsers and mobile-first environments for performance and accessibility alignment
|
||||
|
||||
### 2026-07-17 – Containerized test execution documented
|
||||
|
||||
**Reason**
|
||||
|
||||
Component and browser tests are executed inside a Docker-based Playwright environment (`Dockerfile.test`, `docker-compose.test.yaml`), which was introduced during implementation but had not yet been reflected in this document.
|
||||
|
||||
**Changes**
|
||||
|
||||
- Added Section 6.6 "Test Execution Environment" describing the containerized Playwright setup used for local development and CI
|
||||
- Added "Containerized, reproducible test execution environment" to Section 9 "Key Architectural Decisions"
|
||||
|
||||
**Impact**
|
||||
|
||||
This clarifies an already-implemented part of the testing strategy (Section 6) and does not change any other architectural decision in this document.
|
||||
|
||||
### 2026-08-04 – CI uses a prebuilt test image instead of rebuilding per run
|
||||
|
||||
**Reason**
|
||||
|
||||
The initial implementation of Section 6.6 assumed CI would build
|
||||
`Dockerfile.test` fresh on every run, identical to local execution. In
|
||||
practice, this required Docker-in-Docker access inside the CI runner
|
||||
purely to build a single-service image — disproportionate operational
|
||||
complexity (privileged sidecar container, custom pod networking,
|
||||
runner-level configuration) for the orchestration value actually needed,
|
||||
since `docker-compose.test.yaml` defines only one service with no
|
||||
inter-service dependencies.
|
||||
|
||||
**Changes**
|
||||
|
||||
- Section 6.6 updated to describe the actual CI flow: `Dockerfile.test` is
|
||||
built and pushed to the registry out-of-band (not on every CI run), and
|
||||
CI jobs pull the prebuilt image, then check out the current commit and
|
||||
install dependencies at runtime
|
||||
- Clarified that `Dockerfile.test` remains the single source of truth for
|
||||
the test environment definition; only the build trigger differs between
|
||||
local and CI usage
|
||||
|
||||
**Impact**
|
||||
|
||||
No other architectural decision in this document is affected. Local
|
||||
component testing (`pnpm test:component`) is unchanged.
|
||||
@@ -5,6 +5,7 @@ description: "Decision to use a pnpm-based monorepo structure for LUNA Charts."
|
||||
|
||||
* **Status:** Accepted
|
||||
* **Date:** 2026-07-12
|
||||
* **Last Updated:** 2026-07-17
|
||||
* **Decision Makers:** LUNA Charts Maintainers
|
||||
|
||||
---
|
||||
@@ -66,20 +67,23 @@ luna-charts/
|
||||
│
|
||||
├── packages/
|
||||
│ └── luna-charts/
|
||||
│
|
||||
├── apps/
|
||||
│ ├── storybook/
|
||||
│ └── docs/
|
||||
│ └── .storybook/
|
||||
│
|
||||
├── docs/
|
||||
│ ├── architecture/
|
||||
│ └── adr/
|
||||
│ └── src/content/docs/architecture/
|
||||
│ ├── adr/
|
||||
│ └── adm/
|
||||
│
|
||||
├── examples/
|
||||
│
|
||||
├── .changeset/
|
||||
│
|
||||
└── .github/
|
||||
```
|
||||
|
||||
Storybook is not maintained as a separate app but lives directly inside the `packages/luna-charts` package, alongside the component source code it documents.
|
||||
The `docs/` workspace is not nested under an `apps/` directory; it is a top-level workspace built with Starlight, with architecture documentation (ADR and ADM) organized under `docs/src/content/docs/architecture/`.
|
||||
|
||||
Internally, the library is organized into architectural modules such as:
|
||||
|
||||
* Core
|
||||
@@ -136,3 +140,25 @@ This structure supports the project's architecture-first approach and simplifies
|
||||
* LUNA Charts Architecture Wiki
|
||||
* ADR-0002: Technology Stack
|
||||
* pnpm Workspaces Documentation
|
||||
|
||||
---
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2026-07-17 – Repository structure diagram updated (removal of `apps/`)
|
||||
|
||||
**Reason**
|
||||
|
||||
The originally planned `apps/` workspace (containing `apps/storybook` and `apps/docs`) was never adopted in this form during implementation. Storybook was instead kept inside `packages/luna-charts` alongside the component source it documents, and the documentation workspace (`docs/`) was placed directly at the repository root rather than nested under `apps/`. The repository structure diagram in this ADR no longer matched the actual repository layout, while the Getting Started documentation (`project-structure.mdx`) already reflected the current structure.
|
||||
|
||||
**Changes**
|
||||
|
||||
* Removed the `apps/` directory (and its `storybook/` and `docs/` subentries) from the structure diagram
|
||||
* Corrected the `docs/` path to reflect the actual Starlight content location (`docs/src/content/docs/architecture/`)
|
||||
* Added the `adm/` folder to the diagram, alongside `adr/`, since Architecture Development Method documentation is stored there as well
|
||||
* Added `.changeset/` to the diagram, as it is a workspace-level configuration directory used for release management
|
||||
* Added a note clarifying that Storybook is maintained as part of the `packages/luna-charts` package rather than as a separate app
|
||||
|
||||
**Impact**
|
||||
|
||||
The overall decision (pnpm-based monorepo with logical workspace separation) is unaffected. Only the illustrative directory layout was corrected to match the implemented structure. No other ADRs are impacted by this change.
|
||||
@@ -5,6 +5,7 @@ description: "Selection of the technology stack for LUNA Charts based on the TOG
|
||||
|
||||
* **Status:** Accepted
|
||||
* **Date:** 2026-07-12
|
||||
* **Last Updated:** 2006-07-17
|
||||
* **Decision Makers:** LUNA Charts Maintainers
|
||||
|
||||
---
|
||||
@@ -51,7 +52,7 @@ The following factors influenced the decision:
|
||||
| Component Documentation | Storybook, Styleguidist | **Storybook** |
|
||||
| Project Documentation | Docusaurus, VitePress, Starlight | **Starlight** |
|
||||
| Package Manager | npm, Yarn, pnpm | **pnpm** |
|
||||
| Unit Testing | Vitest, Jest | **Jest** |
|
||||
| Unit Testing | Vitest, Jest | **Vitest** |
|
||||
| Browser Testing | Cypress, Playwright | **Playwright** |
|
||||
| Accessibility Testing | Manual Testing Only, axe-core | **axe-core** |
|
||||
| Code Formatting | Manual Formatting, Prettier | **Prettier** |
|
||||
@@ -74,8 +75,8 @@ The following technology stack is adopted for the implementation of LUNA Charts.
|
||||
| **Starlight** | Project and architecture documentation |
|
||||
| **TypeScript** | Strong typing and developer tooling |
|
||||
| **pnpm** | Package management and workspace support |
|
||||
| **Jest** | Unit testing |
|
||||
| **Playwright** | Browser and interaction testing |
|
||||
| **Vitest** | Unit testing and browser/component testing |
|
||||
| **Playwright** | Browser provider for Vitest browser mode |
|
||||
| **axe-core** | Accessibility validation |
|
||||
| **ESLint** | Static code analysis |
|
||||
| **Prettier** | Consistent code formatting |
|
||||
@@ -128,15 +129,17 @@ pnpm Workspaces enable a modular repository structure while maintaining a single
|
||||
|
||||
---
|
||||
|
||||
### Jest
|
||||
### Vitest
|
||||
|
||||
Jest provides fast unit testing for business logic, utility functions, configuration handling, validation, and other non-browser-specific functionality.
|
||||
Vitest provides fast unit testing for business logic, utility functions, configuration handling, validation, and other non-browser-specific functionality.
|
||||
|
||||
Vitest also serves as the single test runner for browser/component tests, using Playwright as its browser provider (`@vitest/browser-playwright`). This avoids maintaining two separate test runners and keeps unit and component testing under one consistent configuration and reporting workflow.
|
||||
|
||||
---
|
||||
|
||||
### Playwright
|
||||
|
||||
Playwright validates component behavior inside real browsers.
|
||||
Playwright validates component behavior inside real browsers, running as the browser provider within Vitest's browser mode rather than as a standalone test runner.
|
||||
|
||||
It is used to verify keyboard interaction, focus management, SVG rendering, and other browser-dependent accessibility features.
|
||||
|
||||
@@ -210,5 +213,112 @@ GitHub Actions automates testing, validation, and release workflows to ensure co
|
||||
* Storybook Documentation
|
||||
* Starlight Documentation
|
||||
* Playwright Documentation
|
||||
* Jest Documentation
|
||||
* Vitest Documentation
|
||||
* pnpm Documentation
|
||||
|
||||
---
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2026-07-17 – Unit Testing: Jest replaced with Vitest
|
||||
|
||||
**Reason**
|
||||
|
||||
During implementation, Jest was replaced with Vitest as the unit testing tool. Stencil's official Vitest integration (`@stencil/vitest`) allows unit tests and browser/component tests to run through a single test runner and configuration file, instead of maintaining Jest for unit tests and a separate Playwright test runner for browser tests. This reduces toolchain complexity and keeps testing configuration consistent across both test types.
|
||||
|
||||
**Changes**
|
||||
|
||||
* "Considered Alternatives" table: selected Unit Testing technology changed from **Jest** to **Vitest**
|
||||
* "Decision" table: **Jest** entry replaced with **Vitest**; **Playwright**'s purpose updated to reflect its role as the browser provider used by Vitest, not a standalone browser test runner
|
||||
* "Rationale" section: `Jest` subsection replaced with `Vitest`; `Playwright` subsection clarified to describe it as running inside Vitest's browser mode
|
||||
* "References": `Jest Documentation` replaced with `Vitest Documentation`
|
||||
|
||||
**Impact**
|
||||
|
||||
Playwright remains part of the technology stack, but its architectural role changes from an independent browser test runner to the browser engine used by Vitest. No other decisions in this ADR are affected.
|
||||
|
||||
### 2026-08-03 – Continuous Integration: GitHub Actions replaced with Gitea Actions
|
||||
|
||||
**Reason**
|
||||
|
||||
During implementation, the project moved to a self-hosted Gitea instance
|
||||
with Gitea Actions (`act_runner`) instead of GitHub Actions. The ADR's
|
||||
"Considered Alternatives" and "Decision" tables still listed GitHub
|
||||
Actions, which no longer matched the actual CI/CD tooling in use.
|
||||
|
||||
**Changes**
|
||||
|
||||
- "Considered Alternatives" table: `Continuous Integration` row updated —
|
||||
considered alternatives changed to *GitHub Actions, GitLab CI, Azure
|
||||
Pipelines, Gitea Actions*; selected technology changed to **Gitea
|
||||
Actions**
|
||||
- "Decision" table: **GitHub Actions** entry replaced with **Gitea
|
||||
Actions**
|
||||
- "Rationale" section: `GitHub Actions` subsection replaced with `Gitea
|
||||
Actions`, noting that workflows run on a self-hosted `act_runner`
|
||||
(Docker-in-Docker mode) against the project's own Gitea instance
|
||||
|
||||
**Impact**
|
||||
|
||||
Only the Continuous Integration technology choice is affected. Workflow
|
||||
syntax remains GitHub-Actions-compatible (Gitea Actions is designed to be
|
||||
largely compatible with the GitHub Actions workflow format), so no other
|
||||
architectural decisions in this ADR are impacted.
|
||||
|
||||
### 2026-08-04 – Component test CI execution: prebuilt image instead of Docker-in-Docker
|
||||
|
||||
**Reason**
|
||||
|
||||
Component tests run inside a container built from `Dockerfile.test`
|
||||
(based on the official Playwright image). Running this build step inside
|
||||
every CI job would require Docker-in-Docker (DinD) access on the
|
||||
self-hosted Gitea Actions runner — a privileged sidecar container, custom
|
||||
pod networking, and runner-level configuration — solely to build a
|
||||
single-service image with no inter-service orchestration need.
|
||||
|
||||
**Considered Alternatives**
|
||||
|
||||
| Alternative | Description |
|
||||
| ----------- | ----------- |
|
||||
| Build `Dockerfile.test` fresh on every CI run (DinD) | Mirrors the local `docker compose up --build` flow exactly inside a DinD-enabled CI job. |
|
||||
| Replicate `Dockerfile.test`'s steps directly as CI workflow steps, using the public Playwright image | Avoids Docker entirely in CI, but duplicates the test environment definition in two places (`Dockerfile.test` and the workflow YAML), risking drift between local and CI environments. |
|
||||
| **Build and push `Dockerfile.test` out-of-band, pull the prebuilt image in CI (selected)** | `Dockerfile.test` remains the only definition of the test environment; the resulting image is built and pushed to the registry separately (tagged by Playwright version), and CI jobs simply pull it and run tests — no Docker access needed at test time. |
|
||||
|
||||
Building fresh on every run was rejected due to the disproportionate DinD
|
||||
infrastructure complexity for a single-service build. Replicating the
|
||||
steps in the workflow file was rejected because it reintroduces a second,
|
||||
manually-synchronized source of truth for the test environment.
|
||||
|
||||
**Decision**
|
||||
|
||||
`Dockerfile.test` is built and pushed to the project's container registry
|
||||
out-of-band (not as part of the CI pipeline), tagged by Playwright
|
||||
version (e.g. `luna-charts-test:v1.62.1`). The `component-tests` CI job
|
||||
pulls this prebuilt image directly, checks out the current commit, and
|
||||
runs the test command — no Docker access is required inside the CI job
|
||||
container.
|
||||
|
||||
**Consequences**
|
||||
|
||||
*Positive*
|
||||
|
||||
- `Dockerfile.test` remains the single source of truth for the test
|
||||
environment definition
|
||||
- No Docker-in-Docker, privileged containers, or custom pod networking
|
||||
required in the CI job itself
|
||||
- Faster, simpler `component-tests` job
|
||||
|
||||
*Negative*
|
||||
|
||||
- The test image must be rebuilt and pushed manually (or via a separate,
|
||||
intentionally-triggered process) whenever `Dockerfile.test` changes;
|
||||
forgetting this step means CI runs against a stale image
|
||||
- This is considered an acceptable trade-off given how infrequently
|
||||
`Dockerfile.test` changes, versus the ongoing operational cost of
|
||||
maintaining DinD access in the runner
|
||||
|
||||
**Impact**
|
||||
|
||||
Local component testing (`pnpm test:component` via Docker Compose) is
|
||||
unaffected — it continues to build `Dockerfile.test` fresh via
|
||||
`docker-compose.test.yaml`. Only the CI-side image sourcing changes.
|
||||
+30
-4
@@ -1,8 +1,34 @@
|
||||
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
|
||||
import storybook from "eslint-plugin-storybook";
|
||||
import js from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
import js from '@eslint/js';
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"**/dist/**",
|
||||
"**/www/**",
|
||||
"**/node_modules/**",
|
||||
"**/.stencil/**",
|
||||
"**/loader/**",
|
||||
],
|
||||
},
|
||||
|
||||
export default [js.configs.recommended, {
|
||||
ignores: ['**/dist/**', '**/www/**', '**/node_modules/**', '**/.stencil/**'],
|
||||
}, ...storybook.configs["flat/recommended"]];
|
||||
js.configs.recommended,
|
||||
|
||||
...tseslint.configs.recommended,
|
||||
|
||||
...storybook.configs["flat/recommended"],
|
||||
|
||||
{
|
||||
files: ["**/*.tsx"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
varsIgnorePattern: "^h$",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
+13
-4
@@ -4,12 +4,17 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"docs": "pnpm --filter docs run dev",
|
||||
"lint": "pnpm --filter luna-charts run lint",
|
||||
"format": "prettier --write .",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"format:check": "pnpm --filter luna-charts run format:check",
|
||||
"changeset": "changeset",
|
||||
"version": "changeset version",
|
||||
"release": "changeset publish"
|
||||
"release": "changeset publish",
|
||||
"storybook": "pnpm --filter luna-charts run storybook",
|
||||
"test:unit": "pnpm --filter luna-charts test:unit",
|
||||
"test:component": "docker compose -f docker-compose.test.yaml up --build --abort-on-container-exit --exit-code-from test",
|
||||
"test": "pnpm test:unit && pnpm test:component"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@@ -28,7 +33,11 @@
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.31.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"eslint": "^10.6.0",
|
||||
"prettier": "^3.9.4"
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-storybook": "10.5.0",
|
||||
"prettier": "^3.9.4",
|
||||
"typescript-eslint": "^8.65.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,5 @@ $RECYCLE.BIN/
|
||||
Thumbs.db
|
||||
UserInterfaceState.xcuserstate
|
||||
.env
|
||||
|
||||
test-results
|
||||
+3
-4
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* This is an autogenerated file created by the Stencil compiler.
|
||||
* DO NOT MODIFY IT MANUALLY
|
||||
@@ -29,7 +28,7 @@ interface ComponentCompilerTypeReference {
|
||||
* - globally
|
||||
* - by importing it into a file (and is defined elsewhere)
|
||||
*/
|
||||
location: "local" | "global" | "import";
|
||||
location: 'local' | 'global' | 'import';
|
||||
/**
|
||||
* The path to the type reference, if applicable (global types should not need a path associated with them)
|
||||
*/
|
||||
@@ -152,7 +151,7 @@ export interface JsonDocsComponent {
|
||||
/**
|
||||
* The encapsulation strategy for a component
|
||||
*/
|
||||
encapsulation: "shadow" | "scoped" | "none";
|
||||
encapsulation: 'shadow' | 'scoped' | 'none';
|
||||
/**
|
||||
* The tag name for the component, for use in HTML
|
||||
*/
|
||||
@@ -467,7 +466,7 @@ export interface StyleDoc {
|
||||
* Used to inform Stencil where the start of a new property's description starts (and where the previous description
|
||||
* ends).
|
||||
*/
|
||||
annotation: "prop";
|
||||
annotation: 'prop';
|
||||
/**
|
||||
* The Stencil style-mode that is associated with this property.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timestamp": "2026-07-12T12:51:52",
|
||||
"timestamp": "2026-08-01T23:02:04",
|
||||
"compiler": {
|
||||
"name": "@stencil/core",
|
||||
"version": "4.43.5",
|
||||
@@ -17,10 +17,10 @@
|
||||
"props": [
|
||||
{
|
||||
"name": "first",
|
||||
"type": "string",
|
||||
"type": "string | undefined",
|
||||
"complexType": {
|
||||
"original": "string",
|
||||
"resolved": "string",
|
||||
"resolved": "string | undefined",
|
||||
"references": {}
|
||||
},
|
||||
"mutable": false,
|
||||
@@ -31,6 +31,9 @@
|
||||
"values": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "undefined"
|
||||
}
|
||||
],
|
||||
"optional": true,
|
||||
@@ -40,10 +43,10 @@
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"type": "string",
|
||||
"type": "string | undefined",
|
||||
"complexType": {
|
||||
"original": "string",
|
||||
"resolved": "string",
|
||||
"resolved": "string | undefined",
|
||||
"references": {}
|
||||
},
|
||||
"mutable": false,
|
||||
@@ -54,6 +57,9 @@
|
||||
"values": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "undefined"
|
||||
}
|
||||
],
|
||||
"optional": true,
|
||||
@@ -63,10 +69,10 @@
|
||||
},
|
||||
{
|
||||
"name": "middle",
|
||||
"type": "string",
|
||||
"type": "string | undefined",
|
||||
"complexType": {
|
||||
"original": "string",
|
||||
"resolved": "string",
|
||||
"resolved": "string | undefined",
|
||||
"references": {}
|
||||
},
|
||||
"mutable": false,
|
||||
@@ -77,6 +83,9 @@
|
||||
"values": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "undefined"
|
||||
}
|
||||
],
|
||||
"optional": true,
|
||||
|
||||
@@ -32,25 +32,33 @@
|
||||
"loader/"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"format:check": "prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
"build": "stencil build",
|
||||
"start": "stencil build --dev --watch --serve",
|
||||
"test": "stencil-test --prod",
|
||||
"test:unit": "stencil-test --prod --project=unit",
|
||||
"test:browser": "stencil-test --prod --project=browser",
|
||||
"test:watch": "stencil-test --prod --watch",
|
||||
"generate": "stencil generate",
|
||||
"storybook": "storybook dev -p 6006 --no-open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@oxc-parser/binding-linux-x64-gnu": "^0.141.0",
|
||||
"@rolldown/binding-linux-x64-gnu": "^1.2.0",
|
||||
"@stencil/core": "^4.43.5",
|
||||
"@stencil/storybook-plugin": "^0.7.0",
|
||||
"@stencil/vitest": "^1.8.3",
|
||||
"@storybook/addon-docs": "^10.5.0",
|
||||
"@storybook/addon-links": "^10.5.0",
|
||||
"@types/node": "^22.13.5",
|
||||
"@vitest/browser-playwright": "^4.0.0",
|
||||
"playwright": "^1.52.0",
|
||||
"storybook": "^10.5.0",
|
||||
"vitest": "^4.0.0",
|
||||
"axe-core": "^4.12.1",
|
||||
"eslint-plugin-storybook": "10.5.0",
|
||||
"@storybook/addon-docs": "^10.5.0"
|
||||
"playwright": "^1.62.1",
|
||||
"storybook": "^10.5.0",
|
||||
"vitest": "^4.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
@@ -95,11 +95,7 @@ function App() {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<my-component
|
||||
first="Stencil"
|
||||
middle="'Don't call me a framework'"
|
||||
last="JS"
|
||||
></my-component>
|
||||
<my-component first="Stencil" middle="'Don't call me a framework'" last="JS"></my-component>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
+52
-52
@@ -1,68 +1,68 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
/**
|
||||
* This is an autogenerated file created by the Stencil compiler.
|
||||
* It contains typing information for all components that exist in this project.
|
||||
*/
|
||||
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
|
||||
import { HTMLStencilElement, JSXBase } from '@stencil/core/internal';
|
||||
export namespace Components {
|
||||
interface MyComponent {
|
||||
/**
|
||||
* The first name
|
||||
*/
|
||||
"first"?: string;
|
||||
/**
|
||||
* The last name
|
||||
*/
|
||||
"last"?: string;
|
||||
/**
|
||||
* The middle name
|
||||
*/
|
||||
"middle"?: string;
|
||||
}
|
||||
interface MyComponent {
|
||||
/**
|
||||
* The first name
|
||||
*/
|
||||
first?: string;
|
||||
/**
|
||||
* The last name
|
||||
*/
|
||||
last?: string;
|
||||
/**
|
||||
* The middle name
|
||||
*/
|
||||
middle?: string;
|
||||
}
|
||||
}
|
||||
declare global {
|
||||
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
|
||||
}
|
||||
var HTMLMyComponentElement: {
|
||||
prototype: HTMLMyComponentElement;
|
||||
new (): HTMLMyComponentElement;
|
||||
};
|
||||
interface HTMLElementTagNameMap {
|
||||
"my-component": HTMLMyComponentElement;
|
||||
}
|
||||
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {}
|
||||
var HTMLMyComponentElement: {
|
||||
prototype: HTMLMyComponentElement;
|
||||
new (): HTMLMyComponentElement;
|
||||
};
|
||||
interface HTMLElementTagNameMap {
|
||||
'my-component': HTMLMyComponentElement;
|
||||
}
|
||||
}
|
||||
declare namespace LocalJSX {
|
||||
interface MyComponent {
|
||||
/**
|
||||
* The first name
|
||||
*/
|
||||
"first"?: string;
|
||||
/**
|
||||
* The last name
|
||||
*/
|
||||
"last"?: string;
|
||||
/**
|
||||
* The middle name
|
||||
*/
|
||||
"middle"?: string;
|
||||
}
|
||||
interface MyComponent {
|
||||
/**
|
||||
* The first name
|
||||
*/
|
||||
first?: string;
|
||||
/**
|
||||
* The last name
|
||||
*/
|
||||
last?: string;
|
||||
/**
|
||||
* The middle name
|
||||
*/
|
||||
middle?: string;
|
||||
}
|
||||
|
||||
interface MyComponentAttributes {
|
||||
"first": string;
|
||||
"middle": string;
|
||||
"last": string;
|
||||
}
|
||||
interface MyComponentAttributes {
|
||||
first: string;
|
||||
middle: string;
|
||||
last: string;
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
"my-component": Omit<MyComponent, keyof MyComponentAttributes> & { [K in keyof MyComponent & keyof MyComponentAttributes]?: MyComponent[K] } & { [K in keyof MyComponent & keyof MyComponentAttributes as `attr:${K}`]?: MyComponentAttributes[K] } & { [K in keyof MyComponent & keyof MyComponentAttributes as `prop:${K}`]?: MyComponent[K] };
|
||||
}
|
||||
interface IntrinsicElements {
|
||||
'my-component': Omit<MyComponent, keyof MyComponentAttributes> & { [K in keyof MyComponent & keyof MyComponentAttributes]?: MyComponent[K] } & {
|
||||
[K in keyof MyComponent & keyof MyComponentAttributes as `attr:${K}`]?: MyComponentAttributes[K];
|
||||
} & { [K in keyof MyComponent & keyof MyComponentAttributes as `prop:${K}`]?: MyComponent[K] };
|
||||
}
|
||||
}
|
||||
export { LocalJSX as JSX };
|
||||
declare module "@stencil/core" {
|
||||
export namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"my-component": LocalJSX.IntrinsicElements["my-component"] & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
|
||||
}
|
||||
declare module '@stencil/core' {
|
||||
export namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
'my-component': LocalJSX.IntrinsicElements['my-component'] & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { render, h, describe, it, expect } from '@stencil/vitest';
|
||||
import { runA11yChecks } from '../../testing/a11y';
|
||||
|
||||
describe('my-component', () => {
|
||||
it('renders', async () => {
|
||||
@@ -14,10 +15,15 @@ describe('my-component', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('has no accessibility violations', async () => {
|
||||
const { root } = await render(<my-component></my-component>);
|
||||
|
||||
const results = await runA11yChecks(root);
|
||||
expect(results).toHaveNoA11yViolations();
|
||||
});
|
||||
|
||||
it('renders with values', async () => {
|
||||
const { root } = await render(
|
||||
<my-component first="Stencil" middle="'Don't call me a framework'" last="JS"></my-component>,
|
||||
);
|
||||
const { root } = await render(<my-component first="Stencil" middle="'Don't call me a framework'" last="JS"></my-component>);
|
||||
await expect(root).toEqualHtml(`
|
||||
<my-component class="hydrated">
|
||||
<mock:shadow-root>
|
||||
|
||||
@@ -26,7 +26,7 @@ export const Primary: Story = {
|
||||
last: 'Doe',
|
||||
middle: 'Michael',
|
||||
},
|
||||
render: (props) => <my-component {...props} />,
|
||||
render: props => <my-component {...props} />,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
# my-component
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| -------- | --------- | --------------- | -------- | ----------- |
|
||||
| `first` | `first` | The first name | `string` | `undefined` |
|
||||
| `last` | `last` | The last name | `string` | `undefined` |
|
||||
| `middle` | `middle` | The middle name | `string` | `undefined` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| -------- | --------- | --------------- | --------------------- | ----------- |
|
||||
| `first` | `first` | The first name | `string \| undefined` | `undefined` |
|
||||
| `last` | `last` | The last name | `string \| undefined` | `undefined` |
|
||||
| `middle` | `middle` | The middle name | `string \| undefined` | `undefined` |
|
||||
|
||||
---
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
*Built with [StencilJS](https://stenciljs.com/)*
|
||||
_Built with [StencilJS](https://stenciljs.com/)_
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html dir="ltr" lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import axe from 'axe-core';
|
||||
import type { A11yChecker, A11yCheckResult } from './types';
|
||||
|
||||
export const axeCoreChecker: A11yChecker = {
|
||||
name: 'axe-core',
|
||||
|
||||
async run(rootElement: Element): Promise<A11yCheckResult> {
|
||||
const results = await axe.run(rootElement);
|
||||
|
||||
return {
|
||||
checkerName: 'axe-core',
|
||||
violations: results.violations.map(violations => ({
|
||||
id: violations.id,
|
||||
impact: violations.impact ?? null,
|
||||
description: violations.help,
|
||||
helpUrl: violations.helpUrl,
|
||||
nodes: violations.nodes.map(node => node.target.join(' ')),
|
||||
})),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface A11yViolation {
|
||||
id: string;
|
||||
impact: 'minor' | 'moderate' | 'serious' | 'critical' | null;
|
||||
description: string;
|
||||
helpUrl?: string;
|
||||
nodes: string[];
|
||||
}
|
||||
|
||||
export interface A11yCheckResult {
|
||||
checkerName: string;
|
||||
violations: A11yViolation[];
|
||||
}
|
||||
|
||||
export interface A11yChecker {
|
||||
name: string;
|
||||
run(root: Element): Promise<A11yCheckResult>;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { A11yChecker, A11yCheckResult } from './checkers/types';
|
||||
import { axeCoreChecker } from './checkers/axe-core.checker';
|
||||
|
||||
const a11yCheckerRegistry: A11yChecker[] = [axeCoreChecker];
|
||||
|
||||
export async function runA11yChecks(rootElement: Element): Promise<A11yCheckResult[]> {
|
||||
return Promise.all(a11yCheckerRegistry.map(a11yChecker => a11yChecker.run(rootElement)));
|
||||
}
|
||||
|
||||
export type { A11yChecker, A11yCheckResult, A11yViolation } from './checkers/types';
|
||||
@@ -0,0 +1,34 @@
|
||||
import { expect } from 'vitest';
|
||||
import type { A11yCheckResult } from './checkers/types';
|
||||
|
||||
expect.extend({
|
||||
toHaveNoA11yViolations(results: A11yCheckResult[]) {
|
||||
const failing = results.filter(results => results.violations.length > 0);
|
||||
|
||||
if (failing.length === 0) {
|
||||
return {
|
||||
pass: true,
|
||||
message: () => 'expected accessibility violations, but none were found',
|
||||
};
|
||||
}
|
||||
|
||||
const message = failing
|
||||
.map(
|
||||
results =>
|
||||
`[${results.checkerName}] ${results.violations.length} violation(s):\n` +
|
||||
results.violations.map(validation => ` - ${validation.id} (${validation.impact}): ${validation.description}\n affected: ${validation.nodes.join(', ')}`).join('\n'),
|
||||
)
|
||||
.join('\n\n');
|
||||
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Accessibility violations found:\n\n${message}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
declare module 'vitest' {
|
||||
interface Assertion {
|
||||
toHaveNoA11yViolations(): void;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,7 @@
|
||||
"allowUnreachableCode": false,
|
||||
"declaration": false,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2022"
|
||||
],
|
||||
"lib": ["dom", "es2022"],
|
||||
"moduleResolution": "bundler",
|
||||
"module": "esnext",
|
||||
"target": "es2022",
|
||||
@@ -18,14 +15,8 @@
|
||||
"jsx": "react",
|
||||
"jsxFactory": "h",
|
||||
"jsxFragmentFactory": "h.Fragment",
|
||||
"types": [
|
||||
"@stencil/vitest/globals"
|
||||
]
|
||||
"types": ["@stencil/vitest/globals"]
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineCustomElements } from "./loader";
|
||||
import { defineCustomElements } from './loader';
|
||||
import './src/testing/a11y/matchers';
|
||||
|
||||
defineCustomElements();
|
||||
Generated
+4291
-648
File diff suppressed because it is too large
Load Diff
+18
-2
@@ -1,5 +1,21 @@
|
||||
packages:
|
||||
- "packages/*"
|
||||
- "apps/*"
|
||||
- "docs/"
|
||||
|
||||
allowBuilds:
|
||||
esbuild: false
|
||||
esbuild: true
|
||||
sharp: true
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- playwright
|
||||
|
||||
supportedArchitectures:
|
||||
os:
|
||||
- current
|
||||
- linux
|
||||
cpu:
|
||||
- current
|
||||
- x64
|
||||
libc:
|
||||
- current
|
||||
- glibc
|
||||
|
||||
Reference in New Issue
Block a user