@keenmate/web-components-devkit 0.0.0-stage → 1.0.0-rc02
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -0
- package/LICENSE +21 -0
- package/README.md +434 -2
- package/components.json +206 -0
- package/package.json +77 -4
- package/src/cli/args.js +16 -0
- package/src/cli/commands/check-release.js +66 -0
- package/src/cli/commands/check-vars.js +116 -0
- package/src/cli/commands/doctor.js +289 -0
- package/src/cli/commands/new.js +323 -0
- package/src/cli/commands/readme.js +111 -0
- package/src/cli/commands/sync.js +48 -0
- package/src/cli/kmwc.js +73 -0
- package/src/cli/sync-plan.js +163 -0
- package/src/config.d.ts +149 -0
- package/src/config.js +279 -0
- package/src/examples/auto.js +20 -0
- package/src/examples/chapter-nav.js +246 -0
- package/src/examples/constants.js +80 -0
- package/src/examples/controls-persistence.js +97 -0
- package/src/examples/copy-link.js +89 -0
- package/src/examples/css/base.css +215 -0
- package/src/examples/css/chapter-nav.css +179 -0
- package/src/examples/css/content.css +251 -0
- package/src/examples/css/controls.css +205 -0
- package/src/examples/css/copy-link.css +82 -0
- package/src/examples/css/demo.css +50 -0
- package/src/examples/css/index-page.css +118 -0
- package/src/examples/css/style.css +30 -0
- package/src/examples/css/theme-switch.css +38 -0
- package/src/examples/css/tokens.css +124 -0
- package/src/examples/css/widgets.css +133 -0
- package/src/examples/demo-code.js +30 -0
- package/src/examples/index-page.js +78 -0
- package/src/examples/index.d.ts +107 -0
- package/src/examples/index.js +18 -0
- package/src/examples/kit.js +22 -0
- package/src/examples/page-config.js +30 -0
- package/src/examples/sections.js +53 -0
- package/src/examples/theme-switch.js +72 -0
- package/src/examples/version-badge.js +23 -0
- package/src/playwright/index.d.ts +15 -0
- package/src/playwright/index.js +66 -0
- package/src/template.js +11 -0
- package/src/vite/index.d.ts +28 -0
- package/src/vite/index.js +243 -0
- package/src/vite/page-composer.js +216 -0
- package/templates/Dockerfile.tmpl +39 -0
- package/templates/Makefile.tmpl +136 -0
- package/templates/claude-block.md +40 -0
- package/templates/dockerignore.tmpl +10 -0
- package/templates/examples-layout.html +13 -0
- package/templates/new/.claude/commands/publish.md +226 -0
- package/templates/new/CHANGELOG.md +13 -0
- package/templates/new/LICENSE +21 -0
- package/templates/new/README.md +71 -0
- package/templates/new/_gitignore +7 -0
- package/templates/new/component-variables.manifest.json +32 -0
- package/templates/new/custom-elements-manifest.config.mjs +23 -0
- package/templates/new/docs/accessibility.md +24 -0
- package/templates/new/docs/examples.md +33 -0
- package/templates/new/docs/theming.md +54 -0
- package/templates/new/docs/usage.md +51 -0
- package/templates/new/e2e/dark-mode.spec.ts +30 -0
- package/templates/new/e2e/smoke.spec.ts +26 -0
- package/templates/new/examples-basic.html +57 -0
- package/templates/new/examples-theming.html +28 -0
- package/templates/new/index.html +16 -0
- package/templates/new/partials/note.html +5 -0
- package/templates/new/playwright.config.ts +4 -0
- package/templates/new/src/__FEATURE__.ts +55 -0
- package/templates/new/src/css/animations.css +1 -0
- package/templates/new/src/css/base.css +43 -0
- package/templates/new/src/css/controls.css +20 -0
- package/templates/new/src/css/dark-mode.css +36 -0
- package/templates/new/src/css/floating.css +1 -0
- package/templates/new/src/css/main.css +27 -0
- package/templates/new/src/css/states.css +22 -0
- package/templates/new/src/css/variables.css +42 -0
- package/templates/new/src/env.d.ts +7 -0
- package/templates/new/src/index.ts +17 -0
- package/templates/new/src/logger.ts +11 -0
- package/templates/new/src/types.ts +25 -0
- package/templates/new/src/web-component.ts +73 -0
- package/templates/new/test/dark-mode.html +18 -0
- package/templates/new/test/fixture.html +11 -0
- package/templates/new/tsconfig.json +15 -0
- package/templates/new/vite.config.ts +5 -0
- package/templates/new-minimal/CHANGELOG.md +13 -0
- package/templates/new-minimal/README.md +71 -0
- package/templates/new-minimal/component-variables.manifest.json +24 -0
- package/templates/new-minimal/docs/accessibility.md +20 -0
- package/templates/new-minimal/docs/examples.md +17 -0
- package/templates/new-minimal/docs/theming.md +53 -0
- package/templates/new-minimal/docs/usage.md +43 -0
- package/templates/new-minimal/e2e/smoke.spec.ts +10 -0
- package/templates/new-minimal/examples-basic.html +23 -0
- package/templates/new-minimal/examples-theming.html +28 -0
- package/templates/new-minimal/src/__FEATURE__.ts +37 -0
- package/templates/new-minimal/src/css/base.css +48 -0
- package/templates/new-minimal/src/css/controls.css +1 -0
- package/templates/new-minimal/src/css/states.css +1 -0
- package/templates/new-minimal/src/css/variables.css +28 -0
- package/templates/new-minimal/src/index.ts +17 -0
- package/templates/new-minimal/src/types.ts +10 -0
- package/templates/new-minimal/src/web-component.ts +48 -0
- package/templates/new-minimal/test/dark-mode.html +18 -0
- package/templates/new-minimal/test/fixture.html +11 -0
- package/templates/nginx.conf.tmpl +43 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prepare {{PACKAGE_NAME}} for npm publish — bump version, finalize CHANGELOG/README, build, test, commit
|
|
3
|
+
argument-hint: rc|release|patch|minor|major
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /publish — prepare an npm release of {{PACKAGE_NAME}}
|
|
7
|
+
|
|
8
|
+
You are preparing this package for `npm publish`. **Do not run `npm publish`** — the user logs in and publishes manually.
|
|
9
|
+
|
|
10
|
+
This command follows the canonical `/publish` structure defined in the BlissFramework component guidelines at
|
|
11
|
+
`web-components/publish-command.md`. Sections marked **[canonical]** are byte-identical across every
|
|
12
|
+
component's `/publish`; sections marked **[per-repo]** are customized for this repo's layout, build, and tests.
|
|
13
|
+
|
|
14
|
+
## Argument [canonical]
|
|
15
|
+
|
|
16
|
+
The release type: **$ARGUMENTS**
|
|
17
|
+
|
|
18
|
+
Must be one of:
|
|
19
|
+
|
|
20
|
+
- `rc` — ship the WIP rc as-is. The topmost CHANGELOG heading (e.g. `## [1.10.0-rc02] - 2026-06-18`) gets ` [PUBLISHED]` appended.
|
|
21
|
+
- `release` — promote a WIP rc to a final release. `X.Y.Z-rcN` → `X.Y.Z`. CHANGELOG heading is renamed to match the new version.
|
|
22
|
+
- `patch` — SemVer patch bump. Drops any `-rc` suffix.
|
|
23
|
+
- `minor` — SemVer minor bump. Drops `-rc`. Resets patch.
|
|
24
|
+
- `major` — SemVer major bump. Drops `-rc`. Resets minor and patch.
|
|
25
|
+
|
|
26
|
+
If missing or invalid, stop and ask the user which one to use (don't guess).
|
|
27
|
+
|
|
28
|
+
## Repo layout [per-repo]
|
|
29
|
+
|
|
30
|
+
Single-package repo, everything at root:
|
|
31
|
+
|
|
32
|
+
- **`./package.json`** — `version` field is the source of truth.
|
|
33
|
+
- **`./CHANGELOG.md`** — at the root. Topmost `## [X.Y.Z] - YYYY-MM-DD` heading **without** the `[PUBLISHED]` marker is the WIP section.
|
|
34
|
+
- **`./README.md`** — at the root. Carries `## What's New in vX.Y.Z` sections near the top (one per release, the **two most recent** retained).
|
|
35
|
+
- **`./dist/`** — gitignored. Produced by `npm run build` (custom-elements-manifest analyze + Vite). Never staged.
|
|
36
|
+
- **`./custom-elements.json`, `./web-types.json`, `./vscode.html-custom-data.json`, `./vscode.css-custom-data.json`** — generated by `npm run analyze` (part of the build) and **committed** (custom-elements-manifest D-CEM-2 = A).
|
|
37
|
+
- **`./component-variables.manifest.json`** — published with the package. Edit if the variable surface changes; `npm run check:vars` validates it.
|
|
38
|
+
- **`./docs/`** — usage / theming / examples / accessibility, published with the package.
|
|
39
|
+
|
|
40
|
+
## CHANGELOG convention in this repo [canonical]
|
|
41
|
+
|
|
42
|
+
There is **no `## [Unreleased]` section**. The WIP section is the topmost `## [X.Y.Z] - YYYY-MM-DD` heading without a `[PUBLISHED]` tag. Already-released sections carry `[PUBLISHED]` at the end of their heading:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
## [1.10.0-rc02] - 2026-06-18 ← WIP, the one you're shipping
|
|
46
|
+
### Added
|
|
47
|
+
- ...
|
|
48
|
+
|
|
49
|
+
## [1.9.0] - 2026-06-09 [PUBLISHED]
|
|
50
|
+
### Added
|
|
51
|
+
- ...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Publishing the WIP section means **appending ` [PUBLISHED]`** to its heading — exact format: `## [X.Y.Z] - YYYY-MM-DD [PUBLISHED]`. The next development cycle creates a fresh `## [next-version] - <date>` heading on its first CHANGELOG edit.
|
|
55
|
+
|
|
56
|
+
## Resolve versions [canonical]
|
|
57
|
+
|
|
58
|
+
Read `./package.json` `version` as `CURRENT_VERSION`.
|
|
59
|
+
Read the topmost `## [X.Y.Z...]` heading from `./CHANGELOG.md` as `WIP_VERSION` (the version the latest WIP section is tagged for).
|
|
60
|
+
|
|
61
|
+
Compute `NEW_VERSION`:
|
|
62
|
+
|
|
63
|
+
| Argument | Logic |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `rc` | If `CURRENT_VERSION` matches `X.Y.Z-rcN`, `NEW_VERSION = CURRENT_VERSION` (no bump — we're shipping what's already in package.json). If `CURRENT_VERSION` is not an rc, stop and ask the user (they probably wanted `release`/`patch`/etc.). |
|
|
66
|
+
| `release` | If `CURRENT_VERSION` matches `X.Y.Z-rcN`, `NEW_VERSION = X.Y.Z`. Otherwise stop. |
|
|
67
|
+
| `patch` | Strip any `-rcN`, then bump patch. |
|
|
68
|
+
| `minor` | Strip any `-rcN`, then bump minor, reset patch. |
|
|
69
|
+
| `major` | Strip any `-rcN`, then bump major, reset minor and patch. |
|
|
70
|
+
|
|
71
|
+
If `WIP_VERSION` ≠ `NEW_VERSION` (e.g. the WIP is `X.Y.Z-rcN` but the user asked for `release`), the CHANGELOG heading rename in step 3 also re-tags the section to `NEW_VERSION` — call this out in the report so the user notices.
|
|
72
|
+
|
|
73
|
+
## Steps (in order)
|
|
74
|
+
|
|
75
|
+
### 1. Sanity checks [canonical]
|
|
76
|
+
|
|
77
|
+
- Run `git status`. The repo intentionally keeps `.claude/`, `test-results/`, `playwright-report/` and `dist-examples/` untracked — those are fine. If there are **other** uncommitted changes that aren't `CHANGELOG.md`, `README.md`, or `package.json`, list them and ask the user before continuing. (Typical case: substantive source changes belonging in this release that haven't been committed yet — confirm they're intended for this version before bumping.)
|
|
78
|
+
- **Verify the new version isn't already on npm.** Run `npm view {{PACKAGE_NAME}}@<NEW_VERSION> version 2>/dev/null` — if it returns the version string, that version is already published and **stop**: bumping over it would fail at publish time and pollute the commit.
|
|
79
|
+
- **Verify the registry hasn't drifted past you.** Run `npm view {{PACKAGE_NAME}} version` to fetch the latest published version on the `latest` tag; if it's higher than `NEW_VERSION` (e.g. someone shipped from another machine, or there's a registry-vs-local mismatch from before the [PUBLISHED] convention landed), warn the user and ask before continuing.
|
|
80
|
+
- Confirm the WIP CHANGELOG section has at least one bullet of substantive content under `### Added`, `### Changed`, `### Removed`, `### Fixed`, or `### Internal`. If empty, stop — there's nothing meaningful to release.
|
|
81
|
+
- Confirm `./README.md` has a `## What's New in vWIP_VERSION` section. If it's missing, draft one from the CHANGELOG and present it to the user for approval before continuing:
|
|
82
|
+
- Read the WIP CHANGELOG section, distill it to 5–8 scannable bullets covering the Added/Changed themes (paraphrase, don't copy CHANGELOG bullets verbatim — those are exhaustive; What's New is the highlight reel). Pure internal refactors and Fixed-only entries don't need coverage, though headline bug fixes worth advertising are worth a bullet.
|
|
83
|
+
- **Follow the canonical "What's New" format** defined in the BlissFramework component guidelines (`web-components/readme-structure.md` → "`## What's New in vX.Y.Z` — canonical format"). Auto-check **C-RS-16** in `readme-structure.checks.sh` enforces it. Concretely:
|
|
84
|
+
- **Heading:** `## What's New in vNEW_VERSION` — lowercase `v`, no backticks around the version, no date.
|
|
85
|
+
- **Each bullet:** `- **<area or component> — <one-line headline>** — <engineer-level prose, 3–8 sentences>`. Bold-wrapped lead phrase, then a true em-dash (` — `, U+2014 with surrounding spaces), then a prose body explaining *what changed*, *why* (regression history / motivation), *what surface is affected* (concrete component / prop / file names listed inline), and *the mechanism* (the technique used). Plain hyphens or en-dashes fail the check.
|
|
86
|
+
- **No `### ` sub-headings** inside a What's New section — no `### Added` / `### Fixed` lifted from the CHANGELOG. It's a flat bullet list.
|
|
87
|
+
- **Reference implementation:** the `## What's New in v1.3.3` / `v1.3.2` sections at the top of `svelte-fluentui/README.md` are the canonical shape — mirror that voice and structure.
|
|
88
|
+
- Show the user the proposed draft as plain markdown in your reply. Ask whether to (a) insert as-is, (b) edit, or (c) abort so they can write it themselves.
|
|
89
|
+
- Only proceed past step 1 once the user approves the draft (or supplies their own). On approval, insert the section directly above the current top `## What's New in vX.Y.Z` heading in `./README.md`, then continue.
|
|
90
|
+
- Do not silently insert the draft without confirmation — release highlights are a writing call and the user owns the voice.
|
|
91
|
+
|
|
92
|
+
### 2. Bump version (if needed) [canonical]
|
|
93
|
+
|
|
94
|
+
If `NEW_VERSION` ≠ `CURRENT_VERSION`, edit `./package.json` and change `"version": "CURRENT_VERSION"` to `"version": "NEW_VERSION"`.
|
|
95
|
+
|
|
96
|
+
For `rc` arg this is normally a no-op — version was bumped earlier in the development cycle.
|
|
97
|
+
|
|
98
|
+
### 3. Finalize CHANGELOG [canonical]
|
|
99
|
+
|
|
100
|
+
In `./CHANGELOG.md`:
|
|
101
|
+
|
|
102
|
+
- If `WIP_VERSION` ≠ `NEW_VERSION` (e.g. promoting `X.Y.Z-rcN` → `X.Y.Z`), rename the WIP heading from `## [WIP_VERSION] - <date>` to `## [NEW_VERSION] - <today>` (today's date from system context).
|
|
103
|
+
- If `WIP_VERSION` == `NEW_VERSION`, leave the bracketed version alone but update the date to today **if** the existing date is stale (more than a few days old). The WIP date is usually whatever day the section was opened; refresh it so the changelog reflects the actual ship date.
|
|
104
|
+
- In either case, **append ` [PUBLISHED]`** to the heading so it reads exactly: `## [NEW_VERSION] - YYYY-MM-DD [PUBLISHED]`.
|
|
105
|
+
- Leave all bullet content untouched.
|
|
106
|
+
- **Do not** create an empty new WIP section — the next dev cycle's first CHANGELOG edit will create one.
|
|
107
|
+
|
|
108
|
+
### 4. Update README "What's New" — only if version changed [canonical]
|
|
109
|
+
|
|
110
|
+
In `./README.md`:
|
|
111
|
+
|
|
112
|
+
- If the existing `## What's New in vWIP_VERSION` section's version differs from `NEW_VERSION` (e.g. promoting `X.Y.Z-rcN` → `X.Y.Z`), rename its heading to `## What's New in vNEW_VERSION`. (No content rewrites — the text was already curated for this release.)
|
|
113
|
+
- Then count the `## What's New in vX.Y.Z` headings. If there are more than **two**, delete the oldest ones so only the **two most recent** remain (the just-finalized one plus the one before it).
|
|
114
|
+
|
|
115
|
+
For `rc` arg this is normally a no-op on the heading itself — only trims if someone left an extra-old section behind.
|
|
116
|
+
|
|
117
|
+
### 5. Validate README reflects the release [canonical]
|
|
118
|
+
|
|
119
|
+
Read both the finalized CHANGELOG section and the matching `What's New in vNEW_VERSION` section. Every **Added** or **Changed** bullet in the CHANGELOG that represents a user-facing feature or behavior change should have a corresponding hit in the What's New section (paraphrased, not verbatim). Pure internal refactors and `Fixed`-only entries don't need coverage, though headline bug fixes worth advertising (e.g. "X used to silently fail; now works") are worth a bullet.
|
|
120
|
+
|
|
121
|
+
If you find a significant CHANGELOG entry that isn't reflected in What's New, add a bullet for it. If the section ends up with more than ~8 bullets after this pass, condense — What's New should be scannable, not exhaustive.
|
|
122
|
+
|
|
123
|
+
### 6. Validate CHANGELOG entries match recent work [canonical]
|
|
124
|
+
|
|
125
|
+
Find the previous `[PUBLISHED]` tag in CHANGELOG (the version just before NEW_VERSION) and locate the commit that bumped to it — usually a commit whose subject starts with `v<previous-version>` or `- v<previous-version>`. Run `git log --oneline <previous-publish-commit>..HEAD` to list commits since.
|
|
126
|
+
|
|
127
|
+
Also check `git diff` (or `git status`) for any uncommitted source/test work outside the files you're editing in this command.
|
|
128
|
+
|
|
129
|
+
For every substantive commit or uncommitted change, verify the WIP CHANGELOG section mentions it. If something significant is missing, **stop and ask the user** before finalizing — don't invent entries on their behalf. Pure example/doc tweaks and trivial typo fixes don't need entries.
|
|
130
|
+
|
|
131
|
+
### 7. Run tests [per-repo]
|
|
132
|
+
|
|
133
|
+
Run `npm run test:e2e` (or equivalently `make test-e2e`). e2e tests via Playwright. All specs must pass.
|
|
134
|
+
|
|
135
|
+
If anything fails, **stop and report**. Do not proceed to build/commit. The user fixes the regression (or decides to fixme the spec) before the publish flow can continue.
|
|
136
|
+
|
|
137
|
+
If Playwright complains that chromium isn't installed, suggest `make test-e2e-install` and stop.
|
|
138
|
+
|
|
139
|
+
### 8. Build the package [per-repo]
|
|
140
|
+
|
|
141
|
+
Run `npm run build` (or `make build`). This:
|
|
142
|
+
- Runs `npm run analyze` — regenerates `custom-elements.json`, `web-types.json` and the VS Code custom-data files.
|
|
143
|
+
- Runs Vite to emit `dist/{{FILE_NAME}}.js`, `dist/{{FILE_NAME}}.umd.js`, `dist/style.css`, and the type declarations.
|
|
144
|
+
|
|
145
|
+
If the build errors, stop and report.
|
|
146
|
+
|
|
147
|
+
After build, do a quick smoke check on the emitted artifacts:
|
|
148
|
+
- `dist/{{FILE_NAME}}.js` and `dist/{{FILE_NAME}}.umd.js` both exist and are non-empty.
|
|
149
|
+
- `dist/style.css` exists.
|
|
150
|
+
- `dist/index.d.ts` (or whatever the `types` field points to) exists.
|
|
151
|
+
- `git status` shows the regenerated CEM files only if the element API changed — stage them in step 10 if so.
|
|
152
|
+
|
|
153
|
+
### 9. Verify the package contents [per-repo]
|
|
154
|
+
|
|
155
|
+
Run `npm pack --dry-run` and confirm the file list includes:
|
|
156
|
+
|
|
157
|
+
- `dist/` (built JS + d.ts files + style.css)
|
|
158
|
+
- `custom-elements.json`, `web-types.json`, `vscode.html-custom-data.json`, `vscode.css-custom-data.json`
|
|
159
|
+
- `component-variables.manifest.json`
|
|
160
|
+
- `docs/`
|
|
161
|
+
- `README.md`
|
|
162
|
+
- `LICENSE`
|
|
163
|
+
- `package.json`
|
|
164
|
+
|
|
165
|
+
If anything user-facing is missing or anything private leaked in (e.g. `test/`, `e2e/`, `examples-*.html`, `*.spec.ts`, `tsconfig.json`, `vite.config.ts`), stop and report — the `files` field in `package.json` controls this and the leak needs fixing before publish.
|
|
166
|
+
|
|
167
|
+
### 10. Commit [canonical]
|
|
168
|
+
|
|
169
|
+
Stage:
|
|
170
|
+
|
|
171
|
+
- `./CHANGELOG.md`
|
|
172
|
+
- `./README.md`
|
|
173
|
+
- `./package.json`
|
|
174
|
+
- `./custom-elements.json`, `./web-types.json`, `./vscode.html-custom-data.json`, `./vscode.css-custom-data.json` (if the build regenerated them)
|
|
175
|
+
|
|
176
|
+
Do **not** stage `dist/` — it's gitignored.
|
|
177
|
+
|
|
178
|
+
Commit message format:
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
vNEW_VERSION - <one-line summary of the headline change>
|
|
182
|
+
|
|
183
|
+
<grouped bullets paraphrased from the CHANGELOG section — split into the same
|
|
184
|
+
groups the CHANGELOG used: Added, Fixed, Changed, Internal, etc. Keep bullets
|
|
185
|
+
terse; full prose lives in the CHANGELOG.>
|
|
186
|
+
|
|
187
|
+
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 11. Report [canonical]
|
|
191
|
+
|
|
192
|
+
Report back with:
|
|
193
|
+
|
|
194
|
+
- The new version number
|
|
195
|
+
- The commit SHA
|
|
196
|
+
- The exact commands to publish. **Pick the right one for the arg type:**
|
|
197
|
+
- For `rc` (publishing a pre-release):
|
|
198
|
+
```
|
|
199
|
+
npm login # if not already logged in
|
|
200
|
+
npm publish --tag rc
|
|
201
|
+
```
|
|
202
|
+
The `--tag rc` is critical — without it npm assigns the `latest` dist-tag, which would make the pre-release the default install for everyone running `npm install {{PACKAGE_NAME}}`. With `--tag rc`, the `latest` tag stays put and consumers opt in via `@rc` or pinning the exact version.
|
|
203
|
+
- For `release` / `patch` / `minor` / `major` (publishing a stable release):
|
|
204
|
+
```
|
|
205
|
+
npm login # if not already logged in
|
|
206
|
+
npm publish
|
|
207
|
+
```
|
|
208
|
+
Or `make publish`. No `--tag` needed — it correctly lands as `latest`.
|
|
209
|
+
- A reminder that the CHANGELOG `[PUBLISHED]` tag is now in place — if `npm publish` fails, the user should revert both the tag (CHANGELOG heading) and the version bump (`package.json`) before retrying, since the registry will refuse to re-publish the same version.
|
|
210
|
+
|
|
211
|
+
## Things not to do [canonical]
|
|
212
|
+
|
|
213
|
+
- **Do not run `npm publish`.** The user publishes manually after `npm login`.
|
|
214
|
+
- **Do not push to git remote.** The commit stays local until the user pushes.
|
|
215
|
+
- **Do not create an empty `[Unreleased]` or new WIP heading** in CHANGELOG after finalizing — the next dev cycle's first edit creates the next heading.
|
|
216
|
+
- **Do not retro-fix older CHANGELOG sections** that are missing the `[PUBLISHED]` tag or carry legacy markers — only finalize the section you're shipping.
|
|
217
|
+
- **Do not silently insert a drafted What's New section.** If you draft one in Step 1 because it's missing, you must present it and wait for explicit approval (or edits) before inserting — the writing voice is the user's call, even when you're handing them a starting point.
|
|
218
|
+
- **Do not keep more than two `## What's New in vX.Y.Z` sections in the README.** Step 4 trims older ones; if you see three or more after Step 4, you missed one.
|
|
219
|
+
- **Do not skip the build step** — without it `dist/` is stale and the publish would ship outdated artifacts (or fail entirely if `dist/` was wiped by `make clean`).
|
|
220
|
+
- **Do not skip the test gate** if the repo has one — the gate is what catches regressions before they ship.
|
|
221
|
+
- **Do not invent CHANGELOG entries** to cover commits you find; ask the user if something's missing.
|
|
222
|
+
- **Do not bump if there's nothing meaningful in the WIP section** — stop and explain.
|
|
223
|
+
|
|
224
|
+
### Repo-specific don'ts
|
|
225
|
+
|
|
226
|
+
- **Do not stage `test/`, `e2e/`, `test-results/`, `playwright-report/` or `dist-examples/`** — they're either ignored or test artifacts.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [{{VERSION}}] - {{TODAY}}
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Initial scaffold (`kmwc new`, `@keenmate/web-components-devkit`): `<{{TAG}}>` element (`{{ELEMENT_CLASS}}`) over the `{{LOGIC_CLASS}}` logic class, `label` / `active` inputs, `change` event with the `onChange` property twin.
|
|
8
|
+
- Canonical CSS set (`src/css/`, prefix `{{PREFIX}}`) with `@layer variables, component, overrides`, `--base-*` fallback chains and Strategy-B dark mode.
|
|
9
|
+
- Custom Elements Manifest (`custom-elements.json`, `web-types.json`, VS Code custom data) and `component-variables.manifest.json`.
|
|
10
|
+
|
|
11
|
+
### Internal
|
|
12
|
+
|
|
13
|
+
- Intake: technology web-component · package `{{PACKAGE_NAME}}` · tag `<{{TAG}}>` · CSS prefix `{{PREFIX}}` (reserve it in BlissFramework `guidelines/web-components/base-variables.md`) · dev port {{DEV_PORT}}.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) {{YEAR}} Keenmate s.r.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# {{PACKAGE_NAME}}
|
|
2
|
+
|
|
3
|
+
> {{TAGLINE}}
|
|
4
|
+
|
|
5
|
+
## What is it
|
|
6
|
+
|
|
7
|
+
`{{PACKAGE_NAME}}` is a custom element (`<{{TAG}}>`) built on
|
|
8
|
+
[`@keenmate/web-components-core`](https://www.npmjs.com/package/@keenmate/web-components-core).
|
|
9
|
+
It works in any HTML page or framework that can render a custom element —
|
|
10
|
+
React, Vue, Svelte, Blazor, plain HTML.
|
|
11
|
+
|
|
12
|
+
It reads `--base-*` variables from the page when `@keenmate/theme-designer`
|
|
13
|
+
is present, falls back to sensible light/dark defaults otherwise, and supports
|
|
14
|
+
per-instance dark mode via `data-theme`.
|
|
15
|
+
|
|
16
|
+
## What's New in v{{VERSION}}
|
|
17
|
+
|
|
18
|
+
- **`<{{TAG}}>` — initial release** — First version, scaffolded with `kmwc new` from `@keenmate/web-components-devkit`. Ships the Element / Logic / Side-layer split, the canonical eight-file CSS set with `@layer variables, component, overrides`, light/dark defaults through `light-dark()` fallbacks, a Custom Elements Manifest for editor tooling, and a `change` event with an `onChange` property twin. See [CHANGELOG.md](./CHANGELOG.md).
|
|
19
|
+
|
|
20
|
+
## Demos & docs
|
|
21
|
+
|
|
22
|
+
- 🚀 [Live demo](https://demos.keenmate.com/{{ID}})
|
|
23
|
+
- 📘 [Usage / API reference](./docs/usage.md)
|
|
24
|
+
- 🎨 [Theming contract](./docs/theming.md)
|
|
25
|
+
- 📚 [Examples / cookbook](./docs/examples.md)
|
|
26
|
+
- ♿ [Accessibility](./docs/accessibility.md)
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install {{PACKAGE_NAME}}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<script type="module">
|
|
38
|
+
import '{{PACKAGE_NAME}}';
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<{{TAG}} label="Hello"></{{TAG}}>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Clicking toggles the `active` state and dispatches a `change` event
|
|
45
|
+
(`event.detail.isActive`). See [usage.md](./docs/usage.md) for the full API.
|
|
46
|
+
|
|
47
|
+
## Browser support
|
|
48
|
+
|
|
49
|
+
Modern evergreen browsers (Chrome, Edge, Firefox, Safari) — anything with
|
|
50
|
+
native `customElements`, CSS `@layer` and `light-dark()` support. No polyfills
|
|
51
|
+
shipped.
|
|
52
|
+
|
|
53
|
+
## About
|
|
54
|
+
|
|
55
|
+
Authored and maintained by [Keenmate s.r.o.](https://keenmate.com/).
|
|
56
|
+
The component ships standalone with sensible light/dark defaults;
|
|
57
|
+
when mounted inside [Pure Admin](https://pureadmin.io/) — or any
|
|
58
|
+
host that publishes the `--base-*` taxonomy via
|
|
59
|
+
[`@keenmate/theme-designer`](https://www.npmjs.com/package/@keenmate/theme-designer) — it adopts the host's colors,
|
|
60
|
+
typography, and sizing automatically. There is no runtime
|
|
61
|
+
dependency on Pure Admin; the integration is opt-in via CSS
|
|
62
|
+
variables.
|
|
63
|
+
|
|
64
|
+
## Built with BlissFramework
|
|
65
|
+
|
|
66
|
+
Follows the [BlissFramework component guidelines](https://blissframework.dev/)
|
|
67
|
+
for structure, theming, color-scheme, and accessibility.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/keenmate/schemas/main/component-variables.schema.json",
|
|
3
|
+
"component": "{{PACKAGE_NAME}}",
|
|
4
|
+
"prefix": "{{PREFIX}}",
|
|
5
|
+
"baseVariables": [
|
|
6
|
+
{ "name": "base-main-bg", "required": false, "usage": "Component surface" },
|
|
7
|
+
{ "name": "base-text-color-1", "required": false, "usage": "Label text" },
|
|
8
|
+
{ "name": "base-border-color", "required": false, "usage": "Button border" },
|
|
9
|
+
{ "name": "base-primary-color", "required": false, "usage": "Active state fill, focus outline" },
|
|
10
|
+
{ "name": "base-on-primary-text-color", "required": false, "usage": "Label text in the active state" },
|
|
11
|
+
{ "name": "base-hover-bg", "required": false, "usage": "Hover background (falls back to the contract formula on --{{PREFIX}}-bg)" },
|
|
12
|
+
{ "name": "base-font-family", "required": false, "usage": "Font family" },
|
|
13
|
+
{ "name": "base-font-size-base", "required": false, "usage": "Font size (multiplier × --{{PREFIX}}-rem)" },
|
|
14
|
+
{ "name": "base-border-radius-md", "required": false, "usage": "Corner radius (multiplier × --{{PREFIX}}-rem)" },
|
|
15
|
+
{ "name": "base-input-size-md-height", "required": false, "usage": "Minimum height (multiplier × --{{PREFIX}}-rem)" }
|
|
16
|
+
],
|
|
17
|
+
"componentVariables": [
|
|
18
|
+
{ "name": "{{PREFIX}}-rem", "category": "spacing", "usage": "Base unit for every size (default 10px; 1rem follows the page font size)" },
|
|
19
|
+
{ "name": "{{PREFIX}}-bg", "category": "surface", "usage": "Component surface" },
|
|
20
|
+
{ "name": "{{PREFIX}}-text-color-1", "category": "text", "usage": "Label text" },
|
|
21
|
+
{ "name": "{{PREFIX}}-border-color", "category": "border", "usage": "Button border" },
|
|
22
|
+
{ "name": "{{PREFIX}}-accent-color", "category": "accent", "usage": "Active state fill, focus outline" },
|
|
23
|
+
{ "name": "{{PREFIX}}-text-color-on-accent", "category": "text", "usage": "Label text in the active state" },
|
|
24
|
+
{ "name": "{{PREFIX}}-hover-bg", "category": "state", "usage": "Hover background" },
|
|
25
|
+
{ "name": "{{PREFIX}}-font-family", "category": "typography", "usage": "Font family" },
|
|
26
|
+
{ "name": "{{PREFIX}}-font-size-base", "category": "typography", "usage": "Font size" },
|
|
27
|
+
{ "name": "{{PREFIX}}-root-border-radius", "category": "radius", "usage": "Corner radius" },
|
|
28
|
+
{ "name": "{{PREFIX}}-root-min-height", "category": "spacing", "usage": "Minimum height" },
|
|
29
|
+
{ "name": "{{PREFIX}}-root-padding-block", "category": "spacing", "usage": "Vertical padding" },
|
|
30
|
+
{ "name": "{{PREFIX}}-root-padding-inline", "category": "spacing", "usage": "Horizontal padding" }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { blissAnalyzerConfig, cssVariablesFromManifestPlugin } from '@keenmate/web-components-core/cem';
|
|
2
|
+
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
|
|
3
|
+
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Custom Elements Manifest (custom-elements-manifest.md). Core's
|
|
7
|
+
* blissAnalyzerConfig() reads the `static inputs` / `static events` tables
|
|
8
|
+
* (ATTRIBUTE_TABLE) straight from the AST, so attributes, properties and events
|
|
9
|
+
* in custom-elements.json come from the same table the runtime uses.
|
|
10
|
+
* cssVariablesFromManifestPlugin() adds the --{{PREFIX}}-* surface from
|
|
11
|
+
* component-variables.manifest.json; the editor plugins then emit
|
|
12
|
+
* vscode.html-custom-data.json / vscode.css-custom-data.json / web-types.json.
|
|
13
|
+
*/
|
|
14
|
+
export default blissAnalyzerConfig({
|
|
15
|
+
globs: ['src/**/*.ts'],
|
|
16
|
+
exclude: ['src/**/*.spec.ts', 'src/**/*.test.ts', 'src/**/*.d.ts', 'src/index.ts'],
|
|
17
|
+
outdir: '.',
|
|
18
|
+
plugins: [
|
|
19
|
+
cssVariablesFromManifestPlugin(),
|
|
20
|
+
customElementVsCodePlugin({ outdir: '.' }),
|
|
21
|
+
customElementJetBrainsPlugin({ outdir: '.', packageJson: false }),
|
|
22
|
+
],
|
|
23
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Accessibility — `<{{TAG}}>`
|
|
2
|
+
|
|
3
|
+
## Keyboard
|
|
4
|
+
|
|
5
|
+
| Key | Action |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `Tab` / `Shift+Tab` | Move focus to / from the toggle button. |
|
|
8
|
+
| `Enter` / `Space` | Toggle the active state (native `<button>` behaviour). |
|
|
9
|
+
|
|
10
|
+
## ARIA
|
|
11
|
+
|
|
12
|
+
The toggle is a native `<button>` with `aria-pressed` reflecting the active
|
|
13
|
+
state, so screen readers announce it as a toggle button. The label text is the
|
|
14
|
+
accessible name.
|
|
15
|
+
|
|
16
|
+
## Focus
|
|
17
|
+
|
|
18
|
+
A visible `:focus-visible` outline uses `--{{PREFIX}}-accent-color`.
|
|
19
|
+
|
|
20
|
+
## Contrast
|
|
21
|
+
|
|
22
|
+
Default colours meet WCAG AA in both light and dark schemes. When overriding
|
|
23
|
+
`--{{PREFIX}}-accent-color`, keep `--{{PREFIX}}-text-color-on-accent` legible
|
|
24
|
+
on it (4.5:1).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Examples — `<{{TAG}}>`
|
|
2
|
+
|
|
3
|
+
Live examples: run `make dev` and open http://localhost:{{DEV_PORT}}.
|
|
4
|
+
|
|
5
|
+
## Common patterns
|
|
6
|
+
|
|
7
|
+
### React to the active state
|
|
8
|
+
|
|
9
|
+
Shows the event API and the `active` attribute reflecting the state.
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<{{TAG}} id="toggle" label="Notifications"></{{TAG}}>
|
|
13
|
+
<script type="module">
|
|
14
|
+
import '{{PACKAGE_NAME}}';
|
|
15
|
+
const toggle = document.getElementById('toggle');
|
|
16
|
+
toggle.addEventListener('change', (event) => {
|
|
17
|
+
console.log('notifications', event.detail.isActive ? 'on' : 'off');
|
|
18
|
+
// the element also reflects the state: toggle.hasAttribute('active')
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### A themed instance
|
|
24
|
+
|
|
25
|
+
Combines a `--base-*` override (all components) with a `--{{PREFIX}}-*` one
|
|
26
|
+
(this instance only), forced dark.
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<style>
|
|
30
|
+
:root { --base-primary-color: #10b981; }
|
|
31
|
+
</style>
|
|
32
|
+
<{{TAG}} label="Dark" data-theme="dark" style="--{{PREFIX}}-root-border-radius: 999px"></{{TAG}}>
|
|
33
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Theming — `<{{TAG}}>`
|
|
2
|
+
|
|
3
|
+
## Container contract
|
|
4
|
+
|
|
5
|
+
The theme container is `:host` — the `<{{TAG}}>` element itself. It paints
|
|
6
|
+
its own surface (`--{{PREFIX}}-bg`), so it is visible standalone. Attach
|
|
7
|
+
`data-theme="dark|light"` to the element (per instance) or to any ancestor; the
|
|
8
|
+
framework conventions `[data-bs-theme]` (Bootstrap) and `.dark` / `.light`
|
|
9
|
+
(Tailwind and friends) are honoured too.
|
|
10
|
+
|
|
11
|
+
## Variable contract
|
|
12
|
+
|
|
13
|
+
Two layers, never three: `--base-*` is the cross-component hook (set by
|
|
14
|
+
`@keenmate/theme-designer` or by you on `:root`), `--{{PREFIX}}-*` is the
|
|
15
|
+
component-local override. The full list, with categories, is in
|
|
16
|
+
[`component-variables.manifest.json`](../component-variables.manifest.json)
|
|
17
|
+
(exported as `{{PACKAGE_NAME}}/manifest`).
|
|
18
|
+
|
|
19
|
+
All sizes multiply through `--{{PREFIX}}-rem` (default `10px`; set `1rem` to
|
|
20
|
+
follow the page font size).
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
/* every Keenmate component on the page */
|
|
24
|
+
:root { --base-primary-color: #10b981; }
|
|
25
|
+
|
|
26
|
+
/* this component only */
|
|
27
|
+
{{TAG}} { --{{PREFIX}}-accent-color: #8b5cf6; }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Color-scheme strategy
|
|
31
|
+
|
|
32
|
+
Strategy B (color-scheme.md): every colour falls back through
|
|
33
|
+
`light-dark(<light>, <dark>)`, and the component sets `color-scheme` on the
|
|
34
|
+
host only when you signal a theme (`data-theme`, `data-bs-theme`, `.dark`).
|
|
35
|
+
Without a signal it inherits the page's `color-scheme` — declare
|
|
36
|
+
`color-scheme: light dark` on your page to follow the OS.
|
|
37
|
+
|
|
38
|
+
## Cascade-layer contract
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
@layer variables, component, overrides;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Any unlayered rule of yours beats every rule of the component; any `--base-*`
|
|
45
|
+
on `:root` beats the `variables` layer. If your page ships a universal reset
|
|
46
|
+
(`* { margin: 0; padding: 0 }`), wrap it in a layer — an unlayered reset beats
|
|
47
|
+
the component's layered defaults.
|
|
48
|
+
|
|
49
|
+
## BEM hooks
|
|
50
|
+
|
|
51
|
+
| Part | Class | `::part()` |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| The toggle button | `.{{PREFIX}}__root` (`.{{PREFIX}}__root--active` when active) | `::part(root)` |
|
|
54
|
+
| The label | `.{{PREFIX}}__label` | `::part(label)` |
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Usage — `<{{TAG}}>`
|
|
2
|
+
|
|
3
|
+
## Attributes / properties
|
|
4
|
+
|
|
5
|
+
Every input works as an HTML attribute **and** a JS property (core input
|
|
6
|
+
table, `ATTRIBUTE_TABLE` in `src/web-component.ts`).
|
|
7
|
+
|
|
8
|
+
| Attribute | Property | Type | Default | Description |
|
|
9
|
+
|---|---|---|---|---|
|
|
10
|
+
| `label` | `label` | `string` | `"Hello from {{TAG}}"` | Text shown inside the component. |
|
|
11
|
+
| `active` | `isActive` | `boolean` (`bool-default-false`: present = true, `"false"` = false) | `false` | Whether the component is in its active (pressed) state. |
|
|
12
|
+
|
|
13
|
+
## Events
|
|
14
|
+
|
|
15
|
+
| Event | `detail` (`{{EVENT_DETAIL_TYPE}}`) | Property twin | When |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| `change` | `{ isActive: boolean }` | `onChange` | The active state was toggled by the user. Bubbles, composed, not cancelable. |
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const el = document.querySelector('{{TAG}}');
|
|
21
|
+
el.addEventListener('change', (event) => console.log(event.detail.isActive));
|
|
22
|
+
el.onChange = (event) => console.log(event.detail.isActive); // same event, property form
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Slots / children
|
|
26
|
+
|
|
27
|
+
None.
|
|
28
|
+
|
|
29
|
+
## Methods
|
|
30
|
+
|
|
31
|
+
None beyond the core `BlissElement` API (`setAttributes()`, `batch()`, `whenSettled()`).
|
|
32
|
+
|
|
33
|
+
## Lifecycle notes
|
|
34
|
+
|
|
35
|
+
The element builds its shadow DOM on first connect; attribute and property
|
|
36
|
+
changes are batched into one update per microtask (`await el.whenSettled()`
|
|
37
|
+
before reading rendered state in tests).
|
|
38
|
+
|
|
39
|
+
## Editor integration
|
|
40
|
+
|
|
41
|
+
The package ships a Custom Elements Manifest plus VS Code custom data. Add to
|
|
42
|
+
`.vscode/settings.json` for attribute autocomplete in HTML:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"html.customData": ["./node_modules/{{PACKAGE_NAME}}/vscode.html-custom-data.json"],
|
|
47
|
+
"css.customData": ["./node_modules/{{PACKAGE_NAME}}/vscode.css-custom-data.json"]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
JetBrains IDEs pick up `web-types.json` automatically.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { test, expect, type Page } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
// Dark-mode signals (rulebook color-scheme.md): page color-scheme, framework
|
|
4
|
+
// class on an ancestor, and the per-instance data-theme attribute.
|
|
5
|
+
const getHostBackground = (page: Page, selector: string) =>
|
|
6
|
+
page.locator(selector).evaluate((element) => getComputedStyle(element).backgroundColor);
|
|
7
|
+
|
|
8
|
+
test.describe('<{{TAG}}> dark mode', () => {
|
|
9
|
+
test('defaults to light, per-instance data-theme="dark" goes dark', async ({ page }) => {
|
|
10
|
+
await page.goto('/test/dark-mode.html');
|
|
11
|
+
expect(await getHostBackground(page, '#light-default')).toBe('rgb(255, 255, 255)');
|
|
12
|
+
expect(await getHostBackground(page, '#dark-instance')).toBe('rgb(30, 41, 59)');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('an ancestor data-theme="dark" reaches the component', async ({ page }) => {
|
|
16
|
+
await page.goto('/test/dark-mode.html');
|
|
17
|
+
expect(await getHostBackground(page, '#dark-ancestor')).toBe('rgb(30, 41, 59)');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('a page color-scheme of dark is inherited', async ({ page }) => {
|
|
21
|
+
await page.goto('/test/dark-mode.html');
|
|
22
|
+
await page.evaluate(() => (document.documentElement.style.colorScheme = 'dark'));
|
|
23
|
+
expect(await getHostBackground(page, '#light-default')).toBe('rgb(30, 41, 59)');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('--base-* themes the component', async ({ page }) => {
|
|
27
|
+
await page.goto('/test/dark-mode.html');
|
|
28
|
+
expect(await getHostBackground(page, '#base-themed')).toBe('rgb(255, 0, 0)');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
test.describe('<{{TAG}}>', () => {
|
|
4
|
+
test('renders the label and reacts to attribute changes', async ({ page }) => {
|
|
5
|
+
await page.goto('/test/fixture.html');
|
|
6
|
+
const label = page.locator('#subject .{{PREFIX}}__label');
|
|
7
|
+
await expect(label).toHaveText('fixture');
|
|
8
|
+
|
|
9
|
+
await page.evaluate(() => document.getElementById('subject')!.setAttribute('label', 'changed'));
|
|
10
|
+
await expect(label).toHaveText('changed');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('click toggles the active state, reflects it and fires change', async ({ page }) => {
|
|
14
|
+
await page.goto('/test/fixture.html');
|
|
15
|
+
const changeDetails = page.evaluate(
|
|
16
|
+
() =>
|
|
17
|
+
new Promise<unknown>((resolve) =>
|
|
18
|
+
document.getElementById('subject')!.addEventListener('change', (event) => resolve((event as CustomEvent).detail), { once: true })
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
await page.locator('#subject .{{PREFIX}}__root').click();
|
|
22
|
+
expect(await changeDetails).toEqual({ isActive: true });
|
|
23
|
+
await expect(page.locator('#subject')).toHaveAttribute('active', '');
|
|
24
|
+
await expect(page.locator('#subject .{{PREFIX}}__root')).toHaveClass(/{{PREFIX}}__root--active/);
|
|
25
|
+
});
|
|
26
|
+
});
|