jattac.libs.web.zest-button 1.2.9 → 1.4.0

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.
@@ -0,0 +1,154 @@
1
+ # AI Knowledge Graph
2
+
3
+ Purpose:
4
+
5
+ Maintain a continuously evolving architectural memory of this repository.
6
+
7
+ This document is not documentation for humans.
8
+
9
+ It is persistent memory for future AI agents.
10
+
11
+ ---
12
+
13
+ # Instructions
14
+
15
+ Every completed task MUST update this file if new knowledge is discovered.
16
+
17
+ This MUST happen BEFORE implementation and AGAIN after implementation.
18
+
19
+ ---
20
+
21
+ # Format
22
+
23
+ Every component entry MUST follow this format:
24
+
25
+ ```
26
+ ## [ComponentName]
27
+
28
+ ### Purpose
29
+ [One sentence]
30
+
31
+ ### Location
32
+ [File path]
33
+
34
+ ### Callers
35
+ [List of callers]
36
+
37
+ ### Callees
38
+ [List of callees]
39
+
40
+ ### Dependencies
41
+ [List of dependencies]
42
+
43
+ ### Publishes
44
+ [List of events]
45
+
46
+ ### Consumes
47
+ [List of events]
48
+
49
+ ### Configuration
50
+ [Configuration sources]
51
+
52
+ ### Database
53
+ [Tables, queries]
54
+
55
+ ### Known Invariants
56
+ [Business rules that MUST NOT change]
57
+
58
+ ### Known Pitfall
59
+ [Things easy to break]
60
+
61
+ ### Thread Safety
62
+ [Concurrency considerations]
63
+
64
+ ### Transaction Boundaries
65
+ [Transaction scope]
66
+
67
+ ### Security Assumption
68
+ [Auth assumptions]
69
+
70
+ ### Performance Characteristics
71
+ [Known traits]
72
+
73
+ ### Test Coverage
74
+ [What is tested, what is not]
75
+ ```
76
+
77
+ ---
78
+
79
+ # Entries
80
+
81
+ <!-- Add component entries below this line -->
82
+
83
+ ## ZestButton dropdown options (split button)
84
+
85
+ ### Purpose
86
+ `zest.dropdownOptions` renders `ZestButton` as a split button: the existing button segment keeps firing its default `onClick` directly, plus a chevron segment that opens a menu of independent secondary actions.
87
+
88
+ ### Location
89
+ `UI/ZestButton.tsx` (additive types + 3 small mandatory edits — see Known Pitfalls), `UI/ZestDropdownMenu.tsx`, `UI/ZestDropdownMenuItem.tsx`, `Styles/ZestButton.module.css` (new rules appended).
90
+
91
+ ### Callers
92
+ Any consumer passing `zest={{ dropdownOptions: [...] }}` to `ZestButton`.
93
+
94
+ ### Callees
95
+ `@radix-ui/react-dropdown-menu` (positioning, dismiss, a11y, keyboard nav — `modal={false}` explicitly, see Known Invariants). Reuses `useBusyState`/`useConfirmation` (one hook instance per menu item, via `ZestDropdownMenuItem`) and `useZestConfig`/`useThemeDetection` (main button, unchanged).
96
+
97
+ ### Dependencies
98
+ `@radix-ui/react-dropdown-menu` (new peer+dev dependency, externalized in `rollup.config.mjs` exactly like `react-icons`).
99
+
100
+ ### Configuration
101
+ `zest.dropdownAriaLabel` (default `"More options"`) — the chevron's accessible name, since it has no visible text.
102
+
103
+ ### Known Invariants
104
+ - `DropdownMenu.Root` **must** stay `modal={false}`. Radix's default (`modal: true`) `aria-hide`s everything outside the menu *including the trigger button itself* while open — wrong for a split button, whose trigger must stay reachable. Discovered via a failing `getByRole` test, not assumed; see decision-log 2026-07-28.
105
+ - The `force-light`/`force-dark` theme class must be applied to **both** the trigger and `DropdownMenu.Content` separately — Radix portals `Content` outside the trigger's DOM subtree, so a theme class scoped only to the trigger never reaches the portaled menu.
106
+ - `ZestButton.tsx`'s render, when `dropdownOptions` is absent/empty, must stay byte-identical to pre-dropdown behavior — enforced by extracting the existing `<button>` JSX into a `buttonElement` const reused in both render branches, and covered by two explicit regression tests in `__tests__/ZestButton.test.tsx`.
107
+
108
+ ### Known Pitfall
109
+ Three lines of *existing* `ZestButton.tsx` logic were deliberately edited (not just added to) for this feature, each BRS-mandated:
110
+ 1. `isDisabled` extended with `anyDropdownItemBusy` (whole control disables while any menu item is busy)
111
+ 2. The `isDefault` Enter-key `useEffect`'s guard/deps gained `dropdownOpen` (Enter must not re-trigger the main action while the menu is open)
112
+ 3. The return statement was restructured (button JSX extracted to a variable) to avoid duplicating markup across the two render paths
113
+
114
+ All three are no-ops when `dropdownOptions` is absent (the new state variables never leave their default `false`), but any future editor touching `isDisabled` or the `isDefault` effect should know these three lines exist and why.
115
+
116
+ ### Test Coverage
117
+ `__tests__/ZestDropdownMenuItem.test.tsx`, `__tests__/ZestDropdownMenu.test.tsx`, and a `describe("dropdownOptions (split button)")` block in `__tests__/ZestButton.test.tsx`. 81 tests total in the suite (up from 52), 97.21% line coverage (up from 95.38%).
118
+
119
+ ### jsdom/Radix testing gotchas (apply to any future test that opens a ZestButton dropdown)
120
+ - jsdom has **no `PointerEvent` constructor at all**. Radix's `DropdownMenu.Trigger` opens on `pointerdown`, not `click` — a plain `.click()` never opens it. `jest.setup.js` polyfills `window.PointerEvent` plus `Element.prototype.{has,set,release}PointerCapture`/`scrollIntoView`. Tests must dispatch `fireEvent.pointerDown` (+`pointerUp`) on the trigger to open it, not `.click()`.
121
+ - jsdom has no `ResizeObserver` either — also polyfilled in `jest.setup.js` (Radix's Popper-based content measurement needs it).
122
+ - `ZestDropdownMenu`'s `open` prop is fully controlled. A `jest.fn()` passed as `onOpenChange` that doesn't feed back into a real `open` state will never actually open the menu, regardless of whether Radix's internal toggle logic fired correctly — tests asserting the open *result* (not just that the callback fired) need a real state-backed wrapper component.
123
+
124
+ ## Jest test infrastructure
125
+
126
+ ### Purpose
127
+ Test tooling for this repo (a React component library: `UI/`, `Styles/`, built via Rollup) — was entirely absent before 2026-07-28 (`package.json`'s `test` script was a no-op stub, no Jest deps/config existed).
128
+
129
+ ### Location
130
+ `jest.config.js`, `jest.setup.js`, `tsconfig.jest.json` (all repo root). Tests live in `__tests__/`, mirroring `UI/`'s structure (e.g. `__tests__/ZestButton.test.tsx` tests `UI/ZestButton.tsx`; `__tests__/hooks/*.test.ts` test `UI/hooks/*.ts`).
131
+
132
+ ### Callers
133
+ `npm test` (`package.json`), `scripts/verify-post.ps1` (runs `npx jest --coverage --coverageReporters=json-summary` directly, not via the npm script), CI (if/when added — none exists yet).
134
+
135
+ ### Callees
136
+ `ts-jest` (via `tsconfig.jest.json`), `jest-environment-jsdom`, `@testing-library/react`, `@testing-library/jest-dom`, `identity-obj-proxy` (CSS Modules stub).
137
+
138
+ ### Dependencies
139
+ All added as devDependencies only: `jest`, `@types/jest`, `ts-jest`, `jest-environment-jsdom`, `@testing-library/react`, `@testing-library/jest-dom`, `identity-obj-proxy`.
140
+
141
+ ### Configuration
142
+ `coverageReporters: ['json-summary', 'text', 'lcov']` — the `json-summary` entry is load-bearing; `scripts/verify-post.ps1` reads `coverage/coverage-summary.json` directly and fails silently (0% coverage) without it. `coverage/` is gitignored; `test-reports/coverage-baseline.json` is not (intentionally committed, per `AI_TEST_CONFIGURATION.md`).
143
+
144
+ ### Known Invariants
145
+ - Test files MUST NOT live inside `UI/`. `tsconfig.json`'s `include` is `UI/**/*.ts(x)`, and `@rollup/plugin-typescript` (used by `rollup.config.mjs` for the production build) type-checks everything matching that glob — not just files reachable from the `UI/index.ts` entry point. A `*.test.tsx` file co-located inside `UI/` will fail type-check against the production `tsconfig.json` (which has no jest/testing-library ambient types) and pollute `npm run build`'s output with TS diagnostics, even though it's never actually bundled into `dist/`. This was hit and fixed during initial setup (see decision-log 2026-07-28).
146
+ - `tsconfig.jest.json` (not `tsconfig.json`) supplies the `types` compiler option (`jest`, `node`, `@testing-library/jest-dom`) needed for jest-dom's ambient matcher types (`toBeInTheDocument`, etc.) to resolve. A triple-slash `/// <reference types="@testing-library/jest-dom" />` in `global.d.ts` was tried first and did NOT reliably work with this project's Jest 30 / `@types/jest` 30 combination — don't reintroduce that approach without re-verifying it actually resolves.
147
+ - `jest.setup.js` stubs `window.matchMedia` globally (jsdom doesn't implement it) because `UI/hooks/useThemeDetection.ts` calls it unconditionally on every render — any test that renders `ZestButton` (directly or indirectly) will throw `TypeError: window.matchMedia is not a function` without this stub. Individual tests (e.g. `__tests__/hooks/useThemeDetection.test.ts`) may still override `window.matchMedia` locally per-test; that's expected and doesn't conflict (each Jest test *file* gets a fresh jsdom global environment).
148
+
149
+ ### Known Pitfall
150
+ `scripts/verify-pre.ps1` / `scripts/verify-post.ps1` hardcode `$projectRoot` — originally pointed at a `lattice-web-light` subfolder that doesn't exist in this repo (copied from a different project's version of these scripts); fixed 2026-07-28 to `$projectRoot = $repoRoot`. If these scripts are ever copied into yet another repo, check this first.
151
+
152
+ ### Test Coverage
153
+ As of 2026-07-28: 52 tests across 9 suites, covering every file currently in `UI/`'s public surface (`ZestButton.tsx` incl. busy/confirm/keyboard flows, all 4 hooks, `ZestButtonConfigContext.ts`, `ZestButtonConfigProvider.tsx`, `SpinnerIcon.tsx`, `semanticTypeButtonConfigDefaults.tsx`). 95.38% lines / 94.76% statements / 97.36% functions / 85.23% branches. Baseline recorded in `test-reports/coverage-baseline.json`. Superseded by the dropdown-options feature's test additions above (81 tests, 97.21% lines as of the same date).
154
+
@@ -0,0 +1,149 @@
1
+ # Decision Log
2
+
3
+ Purpose:
4
+
5
+ Record architectural and implementation decisions for future reference.
6
+
7
+ This document is not documentation for humans.
8
+
9
+ It is persistent memory for future AI agents.
10
+
11
+ ---
12
+
13
+ # Instructions
14
+
15
+ Every task that involves a decision MUST add an entry.
16
+
17
+ Entries MUST be added BEFORE implementation begins.
18
+
19
+ ---
20
+
21
+ # Format
22
+
23
+ ```
24
+ ## [YYYY-MM-DD] Decision Title
25
+
26
+ ### Requested
27
+
28
+ [What the user asked for]
29
+
30
+ ### Options Considered
31
+
32
+ 1. [Option A] — [pros/cons]
33
+ 2. [Option B] — [pros/cons]
34
+ 3. [Option C] — [pros/cons]
35
+
36
+ ### Chosen
37
+
38
+ [Which option was chosen]
39
+
40
+ ### Rationale
41
+
42
+ [Why this option was chosen]
43
+
44
+ ### Trade-offs
45
+
46
+ [What was sacrificed, what was gained]
47
+
48
+ ### Reversible
49
+
50
+ [Yes/No — and what it would take to reverse]
51
+ ```
52
+
53
+ ---
54
+
55
+ # Decisions
56
+
57
+ <!-- Add decision entries below this line -->
58
+
59
+ ## [2026-07-28] Wire up Jest test infrastructure (no prior tests existed)
60
+
61
+ ### Requested
62
+
63
+ "get jest wired up, no half measures" — this component library had zero test tooling (`package.json`'s `test` script was a no-op stub). Preceded by a session establishing/fixing this repo's BRS-gated AI workflow (fixed `scripts/verify-pre.ps1`/`verify-post.ps1`'s hardcoded `lattice-web-light` project path, confirmed throughout that `git remote.origin.url` was never at risk from any of it). Full scope: `BRS.md` (repo root).
64
+
65
+ ### Options Considered
66
+
67
+ 1. `ts-jest` vs `babel-jest` for TS transform — chosen `ts-jest`: this project is TypeScript-first with no Babel config anywhere (Rollup build uses `@rollup/plugin-typescript` directly), so `ts-jest` matches the existing toolchain rather than introducing a second transform pipeline.
68
+ 2. Test file location: co-located next to source (as originally drafted in the BRS) vs. a separate `__tests__/` tree — co-location was tried first and reverted. `tsconfig.json`'s `include: ["UI/**/*.ts", "UI/**/*.tsx", ...]` is read by `@rollup/plugin-typescript` for type-check diagnostics on `npm run build`, not just for what actually gets bundled from the `UI/index.ts` entry graph — a co-located `*.test.tsx` file failed type-check against the production tsconfig (no jest/testing-library types there) and polluted the build output with TS diagnostics. Moved all test files to `__tests__/`, mirroring `UI/`'s structure, rather than modifying `tsconfig.json` (explicitly out of scope per the BRS).
69
+ 3. jest-dom matcher types: triple-slash `/// <reference types="@testing-library/jest-dom" />` in `global.d.ts` vs. a dedicated `tsconfig.jest.json` with an explicit `types` compiler option — the triple-slash approach didn't work reliably against this project's Jest 30 / `@types/jest` 30 combination (TS resolved `expect()`'s return type without ever merging in jest-dom's `Matchers` interface augmentation). Chose the dedicated `tsconfig.jest.json` (extends `tsconfig.json`, adds `types: ["jest","node","@testing-library/jest-dom"]`), used only via `jest.config.js`'s `transform` option — production `tsconfig.json` stays untouched.
70
+ 4. Scope of tests written: minimal smoke tests vs. full behavioral coverage of the current public surface — chosen full coverage (all 4 hooks, config context/provider, `SpinnerIcon`, `semanticTypeButtonConfigDefaults`, and the complete `ZestButton` component including busy/confirm/keyboard-Enter flows), per the user's explicit "no half measures" and the global TDD rule to freeze existing behaviour with real tests, not placeholders.
71
+
72
+ ### Chosen
73
+
74
+ Options 1, 2 (the `__tests__/` variant), 3 (the `tsconfig.jest.json` variant), and 4 (full coverage) — all as amended in `BRS.md`'s "Amendments during implementation" section.
75
+
76
+ ### Rationale
77
+
78
+ Each deviation from the original BRS draft was discovered empirically (by actually running `npm run build` and the test suite, not assumed) and fixed with the smallest change that didn't touch files explicitly marked out-of-scope (`tsconfig.json`).
79
+
80
+ ### Trade-offs
81
+
82
+ `tsconfig.jest.json` is a second TypeScript config file to keep in sync conceptually with `tsconfig.json` (it `extends` it, so most drift risk is limited to the `types` array and `include` list). Test files living in `__tests__/` rather than beside their source is a minor discoverability cost, offset by avoiding any change to the production build's type-checking surface.
83
+
84
+ ### Reversible
85
+
86
+ Yes — every changed/added file is listed in `test-reports/CHANGE_MANIFEST_jest-wiring.md`. No file under `UI/`/`Styles/` (production source) was touched. Revert by removing the new devDependencies, `jest.config.js`, `jest.setup.js`, `tsconfig.jest.json`, `__tests__/`, `BRS.md`, and the `coverage/` line in `.gitignore`, and restoring `package.json`'s `test` script.
87
+
88
+ ## [2026-07-28] ZestButton dropdown options (split button), via @radix-ui/react-dropdown-menu
89
+
90
+ ### Requested
91
+
92
+ "make the button support dropdown options so that in instances where a button may be used for a default action and optional others, then we show in ui that this is possible and provide a hit area to bring up option." Clicking the main segment always fires the default action directly; each dropdown item independent but DRY as possible; mobile-first, desktop-aware; full a11y; leverage a headless menu library if one fits, to avoid hand-rolling popup positioning. Full scope: `docs/features/zest-button-dropdown-options/BRS.md`.
93
+
94
+ ### Options Considered
95
+
96
+ 1. Reuse the sibling repo `jattac.Libs.Web.OverflowMenu` (`D:\work\nyingi\code\systems\jattac-web-libs\jattac.Libs.Web.OverflowMenu`) directly — rejected. Its trigger is a fixed 48×48 circular "kebab" icon-button with hover styling hardcoded via Framer Motion **inline styles** (`whileHover={{ color: '#016a80', ... }}`), which cannot be overridden by external CSS — no way to theme it into ZestButton's variant/dark-mode system without forking. It's also architecturally a standalone floating "more actions" button, not a chevron segment meant to fuse onto another button, and its `src/index.tsx` doesn't export the internals (`MenuRow`, the Radix wiring) separately, so there's no seam to reuse just the mechanics.
97
+ 2. `@floating-ui/react` (generic positioning + a-la-carte interaction hooks) — viable, but a lower-level primitive than needed; would mean hand-assembling menu semantics (roles, type-ahead, keyboard nav) that a menu-specific library already provides.
98
+ 3. `@radix-ui/react-dropdown-menu`, used directly (not via `OverflowMenu`'s wrapper) — chosen. Same package `OverflowMenu` already depends on in production (proven in this org already), but consumed via its `asChild` composition model so ZestButton drives its own trigger element and full styling, inheriting only positioning/dismiss/a11y/keyboard-nav mechanics. No Framer Motion needed — CSS-only transitions match this repo's existing animation approach.
99
+
100
+ ### Chosen
101
+
102
+ Option 3.
103
+
104
+ ### Rationale
105
+
106
+ `asChild` composition is the deciding factor: it gets the exact same battle-tested Radix positioning/a11y engine `OverflowMenu` already uses in this org, without inheriting that component's fixed skin, Framer Motion dependency, or lack of theme/variant props.
107
+
108
+ ### Trade-offs (each found empirically while implementing, not guessed — see `test-reports/CHANGE_MANIFEST_dropdown-options.md` and `docs/guidelines/ai-knowledge.md`'s "ZestButton dropdown options" entry for detail)
109
+
110
+ - Radix's `DropdownMenu.Root` defaults to `modal: true`, which `aria-hide`s the trigger itself while its own menu is open — wrong for a split button; explicitly set `modal={false}`.
111
+ - Radix portals `DropdownMenu.Content` outside the trigger's DOM subtree, so a theme-override class scoped only to the trigger doesn't reach it — applied the theme class to both.
112
+ - jsdom has no `PointerEvent` constructor at all, and Radix's trigger opens on `pointerdown` not `click` — required three jsdom polyfills in `jest.setup.js` (`PointerEvent`, pointer-capture methods, `scrollIntoView`).
113
+ - First full test run showed a real aggregate coverage regression (95.38% → 95.12%) even though all new code was well-tested — a large, decently-but-not-perfectly-covered addition dilutes a weighted-average baseline. Fixed by adding 8 more targeted tests (3 of them for **pre-existing, untouched** `ZestButton.tsx` branches that happened to be uncovered already) rather than treating the dip as acceptable noise, since `AI_TESTING.md` has zero tolerance for any regression.
114
+
115
+ ### Reversible
116
+
117
+ Yes — no file under `UI/`/`Styles/` had an *existing* line rewritten, only 3 small, explicitly BRS-mandated additions to existing conditions (see `ai-knowledge.md`'s "Known Pitfall" entry for exactly which 3 lines and why). Full file list in `test-reports/CHANGE_MANIFEST_dropdown-options.md`.
118
+
119
+ ## [2026-07-28] Fix verify-post.ps1 silently skipping BRS compliance; align verify-pre.ps1's approval regex
120
+
121
+ ### Requested
122
+
123
+ User noticed `verify-post.ps1`'s report looked "ok with there not being a BRS" and asked why that wasn't flagged as an issue.
124
+
125
+ ### Options Considered
126
+
127
+ 1. Leave it — the caller is expected to always pass `-BRSPath` — rejected, since nothing enforces that, and the whole point of this phase is to catch exactly this kind of omission.
128
+ 2. Auto-discover the most-recently-modified `BRS.md` when `-BRSPath` isn't passed (mirroring `verify-pre.ps1`'s existing Phase 1 logic exactly), and make "no BRS found" or "found but not approved" a `FAIL`, never a silent `SKIPPED` — chosen.
129
+
130
+ ### Chosen
131
+
132
+ Option 2, applied to `verify-post.ps1`'s Phase 3.
133
+
134
+ ### Rationale
135
+
136
+ A script whose entire purpose is enforcing this repo's BRS-gated policy must never be able to report `RESULT: COMPLETE` while staying silent about BRS compliance just because an optional parameter was omitted. `verify-pre.ps1` already had the correct auto-discovery pattern; `verify-post.ps1` just never reused it.
137
+
138
+ ### Also found and fixed while testing this change
139
+
140
+ Both scripts' approval-detection regex only recognized `Status: Approved` (or the equivalent markdown-table row), not `Status: Complete` — even though `AI_BRS.md`'s own template defines `Complete` as a later, valid lifecycle stage that can only be reached *after* approval. Running the fixed `verify-post.ps1` against this session's own (by-then-`Complete`) BRS immediately exposed this as a false negative. Fixed both scripts' regexes to accept either value.
141
+
142
+ ### Trade-offs
143
+
144
+ None identified — this only makes both scripts stricter/more accurate, never looser.
145
+
146
+ ### Reversible
147
+
148
+ Yes — confined to `scripts/verify-pre.ps1`'s Phase 1 and `scripts/verify-post.ps1`'s Phase 3 regex/discovery logic.
149
+
package/package.json CHANGED
@@ -1,69 +1,78 @@
1
- {
2
- "name": "jattac.libs.web.zest-button",
3
- "version": "1.2.9",
4
- "description": "A highly customizable and production-ready React button component featuring robust asynchronous handling, rich visual feedback, and built-in confirmation flows for enhanced user experience",
5
- "homepage": "https://github.com/nyingimaina/jattac.libs.web.zest-button#readme",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/nyingimaina/jattac.libs.web.zest-button.git"
9
- },
10
- "bugs": {
11
- "url": "https://github.com/nyingimaina/jattac.libs.web.zest-button/issues"
12
- },
13
- "main": "dist/index.cjs.js",
14
- "module": "dist/index.esm.js",
15
- "types": "dist/index.d.ts",
16
- "files": [
17
- "dist",
18
- "README.md",
19
- "docs"
20
- ],
21
- "scripts": {
22
- "build": "rollup -c rollup.config.mjs",
23
- "dev": "rollup -c rollup.config.mjs -w",
24
- "test": "echo \"No tests specified. See WORKPLAN.md for future plans.\" && exit 0"
25
- },
26
- "keywords": [
27
- "react",
28
- "button",
29
- "ui",
30
- "component",
31
- "interactive",
32
- "react-button",
33
- "form",
34
- "web",
35
- "async",
36
- "loading",
37
- "confirmation",
38
- "feedback",
39
- "animation",
40
- "transitions",
41
- "ux",
42
- "zest",
43
- "jattac"
44
- ],
45
- "author": "Jattac",
46
- "license": "MIT",
47
- "peerDependencies": {
48
- "react": ">=16.8.0",
49
- "react-dom": ">=16.8.0",
50
- "react-icons": "^5.0.1"
51
- },
52
- "devDependencies": {
53
- "@rollup/plugin-commonjs": "^25.0.7",
54
- "@rollup/plugin-node-resolve": "^15.2.3",
55
- "@rollup/plugin-typescript": "^11.1.6",
56
- "@types/node": "^25.1.0",
57
- "@types/react": "^18.2.67",
58
- "@types/react-dom": "^18.2.22",
59
- "postcss": "^8.4.38",
60
- "postcss-modules": "6.0.1",
61
- "react": "^18.2.0",
62
- "react-dom": "^18.2.0",
63
- "rollup": "^4.13.0",
64
- "rollup-plugin-dts": "^6.1.0",
65
- "rollup-plugin-postcss": "^4.0.2",
66
- "tslib": "^2.6.2",
67
- "typescript": "^5.4.2"
68
- }
69
- }
1
+ {
2
+ "name": "jattac.libs.web.zest-button",
3
+ "version": "1.4.0",
4
+ "description": "A highly customizable and production-ready React button component featuring robust asynchronous handling, rich visual feedback, and built-in confirmation flows for enhanced user experience",
5
+ "homepage": "https://github.com/nyingimaina/jattac.libs.web.zest-button#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/nyingimaina/jattac.libs.web.zest-button.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/nyingimaina/jattac.libs.web.zest-button/issues"
12
+ },
13
+ "main": "dist/index.cjs.js",
14
+ "module": "dist/index.esm.js",
15
+ "types": "dist/index.d.ts",
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "docs"
20
+ ],
21
+ "scripts": {
22
+ "build": "rollup -c rollup.config.mjs",
23
+ "dev": "rollup -c rollup.config.mjs -w",
24
+ "test": "jest --coverage"
25
+ },
26
+ "keywords": [
27
+ "react",
28
+ "button",
29
+ "ui",
30
+ "component",
31
+ "interactive",
32
+ "react-button",
33
+ "form",
34
+ "web",
35
+ "async",
36
+ "loading",
37
+ "confirmation",
38
+ "feedback",
39
+ "animation",
40
+ "transitions",
41
+ "ux",
42
+ "zest",
43
+ "jattac"
44
+ ],
45
+ "author": "Jattac",
46
+ "license": "MIT",
47
+ "peerDependencies": {
48
+ "@radix-ui/react-dropdown-menu": "^2.1.24",
49
+ "react": ">=16.8.0",
50
+ "react-dom": ">=16.8.0",
51
+ "react-icons": "^5.0.1"
52
+ },
53
+ "devDependencies": {
54
+ "@radix-ui/react-dropdown-menu": "^2.1.24",
55
+ "@rollup/plugin-commonjs": "^25.0.7",
56
+ "@rollup/plugin-node-resolve": "^15.2.3",
57
+ "@rollup/plugin-typescript": "^11.1.6",
58
+ "@testing-library/jest-dom": "^6.9.1",
59
+ "@testing-library/react": "^16.3.2",
60
+ "@types/jest": "^30.0.0",
61
+ "@types/node": "^25.1.0",
62
+ "@types/react": "^18.2.67",
63
+ "@types/react-dom": "^18.2.22",
64
+ "identity-obj-proxy": "^3.0.0",
65
+ "jest": "^30.4.2",
66
+ "jest-environment-jsdom": "^30.4.1",
67
+ "postcss": "^8.4.38",
68
+ "postcss-modules": "6.0.1",
69
+ "react": "^18.2.0",
70
+ "react-dom": "^18.2.0",
71
+ "rollup": "^4.13.0",
72
+ "rollup-plugin-dts": "^6.1.0",
73
+ "rollup-plugin-postcss": "^4.0.2",
74
+ "ts-jest": "^29.4.12",
75
+ "tslib": "^2.6.2",
76
+ "typescript": "^5.4.2"
77
+ }
78
+ }
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { ZestCustomProps } from './ZestButton';
3
- export interface ZestGlobalConfig {
4
- defaultProps?: ZestCustomProps;
5
- semanticTypeDefaults?: Partial<Record<string, Partial<ZestCustomProps>>>;
6
- }
7
- declare const ZestContext: React.Context<ZestGlobalConfig | undefined>;
8
- export declare const useZest: () => ZestGlobalConfig | undefined;
9
- export default ZestContext;
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- import { ZestGlobalConfig } from './ZestContext';
3
- interface ZestProviderProps {
4
- config: ZestGlobalConfig;
5
- children: React.ReactNode;
6
- }
7
- declare const ZestProvider: React.FC<ZestProviderProps>;
8
- export default ZestProvider;
@@ -1,4 +0,0 @@
1
- import { ZestCustomProps } from './ZestButton';
2
- type SemanticTypeDefaultsMap = Partial<Record<string, Partial<ZestCustomProps>>>;
3
- export declare const semanticTypeDefaults: SemanticTypeDefaultsMap;
4
- export {};
package/docs/api.md DELETED
@@ -1,140 +0,0 @@
1
- ---
2
- [⬅️ Previous: Features Showcase](./features.md)
3
-
4
- # API Reference: The Technical Blueprint
5
-
6
- This document provides an exhaustive reference for all `ZestButton` props and type definitions.
7
-
8
- ---
9
-
10
- ## Table of Contents
11
-
12
- - [ZestButtonProps](#zestbuttonprops)
13
- - [Type Definitions](#type-definitions)
14
- - [ZestCustomProps](#zestcustomprops)
15
- - [ZestGlobalConfig](#zestglobalconfig)
16
- - [VisualOptions](#visualoptions)
17
- - [BusyOptions](#busyoptions)
18
- - [SuccessOptions](#successoptions)
19
- - [ConfirmOptions](#confirmoptions)
20
- - [SemanticType](#semantictype)
21
-
22
- ---
23
-
24
- ### `ZestButtonProps`
25
-
26
- The `ZestButton` component accepts all standard HTML `<button>` attributes (e.g., `disabled`, `type`, `name`, `className`) in addition to its own custom configuration prop, `zest`.
27
-
28
- | Prop Name | Type | Default | Description |
29
- | :--- | :--- | :--- | :--- |
30
- | `zest` | `ZestCustomProps` | `{}` | An object containing all custom configuration for the button's behavior and appearance. See `ZestCustomProps` below for details. |
31
- | `...rest`| `React.ButtonHTMLAttributes` | | All other standard button props are passed directly to the underlying `<button>` element. |
32
-
33
- ---
34
-
35
- ### Type Definitions
36
-
37
- The `zest` prop is a configuration object that follows the `ZestCustomProps` interface. Its properties are detailed below.
38
-
39
- #### `ZestCustomProps`
40
-
41
- This is the main configuration object passed to the `zest` prop.
42
-
43
- | Prop Name | Type | Default | Description |
44
- | :--- | :--- | :--- | :--- |
45
- | `visualOptions` | `VisualOptions` | `{}` | Controls the button's appearance, including variant, size, and icons. |
46
- | `busyOptions` | `BusyOptions` | `{}` | Configures behavior for asynchronous operations. |
47
- | `successOptions` | `SuccessOptions` | `{}` | Configures feedback after a successful or failed operation. |
48
- | `confirmOptions` | `ConfirmOptions` | `undefined` | If provided, enables the "click-to-confirm" workflow. |
49
- | `isDefault` | `boolean` | `false` | If true, the button can be triggered by the `Enter` key. |
50
- | `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Overrides the automatic theme detection. |
51
- | `buttonStyle` | `'solid' \| 'outline' \| 'text' \| 'dashed'`| `'solid'` | Defines the visual style of the button. |
52
- | `semanticType` | `SemanticType` | `undefined` | Defines the semantic type of the button, providing default visuals and behaviors. Extensible via module augmentation. |
53
-
54
- ---
55
-
56
- #### `ZestGlobalConfig`
57
-
58
- This is the configuration object passed to the `config` prop of the `ZestButtonConfigProvider`.
59
-
60
- | Prop Name | Type | Default | Description |
61
- | :--- | :--- | :--- | :--- |
62
- | `defaultProps` | `ZestCustomProps` | `{}` | A set of `ZestCustomProps` that will be applied to all `ZestButton` instances within the provider's scope. |
63
- | `semanticTypeDefaults` | `Partial<Record<string, Partial<ZestCustomProps>>>` | `{}` | A map of semantic types to `ZestCustomProps`. This allows you to define defaults for custom semantic types or override the library's built-in defaults. |
64
-
65
- ---
66
-
67
- #### `VisualOptions`
68
-
69
- Controls the button's appearance.
70
-
71
- | Prop Name | Type | Default | Description |
72
- | :--- | :--- | :--- | :--- |
73
- | `variant` | `'standard' \| 'success' \| 'danger'` | `'standard'`| The color scheme of the button. |
74
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | The size of the button, affecting padding and font size. |
75
- | `stretch` | `boolean` | `false` | If true, the button expands to the full width of its parent. |
76
- | `iconLeft` | `React.ReactNode` | `undefined` | A React node (e.g., an icon component) to display on the left. |
77
- | `iconRight` | `React.ReactNode` | `undefined` | A React node to display on the right. |
78
-
79
- ---
80
-
81
- #### `BusyOptions`
82
-
83
- Configures behavior during asynchronous `onClick` operations.
84
-
85
- | Prop Name | Type | Default | Description |
86
- | :--- | :--- | :--- | :--- |
87
- | `handleInternally` | `boolean` | `true` | If true, automatically manages busy state when `onClick` returns a Promise. |
88
- | `preventRageClick` | `boolean` | `true` | If true, disables the button while it is in a busy, success, or fail state. |
89
- | `minBusyDurationMs`| `number` | `500` | Ensures the spinner is shown for at least this long (in ms) to prevent visual flickering on fast network requests. |
90
-
91
- ---
92
-
93
- #### `SuccessOptions`
94
-
95
- Configures the visual feedback after an operation completes.
96
-
97
- | Prop Name | Type | Default | Description |
98
- | :--- | :--- | :--- | :--- |
99
- | `showCheckmark` | `boolean` | `true` | If true, displays an animated checkmark when the `onClick` Promise resolves successfully. |
100
- | `showFailIcon` | `boolean` | `true` | If true, displays an animated 'X' when the `onClick` Promise rejects or the confirmation timer expires. |
101
- | `autoResetAfterMs`| `number` | `2000` | The duration (in ms) to show the success/fail icon before the button resets to its normal state. |
102
-
103
- ---
104
-
105
- #### `ConfirmOptions`
106
-
107
- If this object is provided, the button will require two clicks to fire the `onClick` event.
108
-
109
- | Prop Name | Type | Default | Description |
110
- | :--- | :--- | :--- | :--- |
111
- | `displayLabel` | `string` | **(Required)**| The text to show during the confirmation phase (e.g., "Confirm?"). The countdown timer is appended automatically. |
112
- | `timeoutSecs` | `number` | **(Required)**| The number of seconds the user has to click the button a second time to confirm the action. |
113
-
114
- ---
115
-
116
- #### `SemanticType`
117
-
118
- The `SemanticType` defines common button actions, allowing `ZestButton` to automatically apply default visuals (e.g., icons, variants) and behaviors (e.g., confirmation prompts). This type is extensible through TypeScript module augmentation.
119
-
120
- **Built-in Semantic Types:**
121
- `'add'`, `'save'`, `'submit'`, `'edit'`, `'update'`, `'delete'`, `'remove'`, `'cancel'`, `'close'`, `'view'`, `'details'`, `'download'`, `'upload'`, `'refresh'`, `'reload'`, `'print'`, `'share'`, `'confirm'`.
122
-
123
- **Extensibility:** Developers can extend this list to include custom semantic types by augmenting the `CustomZestSemanticTypes` interface in their project. For example:
124
-
125
- ```typescript
126
- // your-project/src/typings/zest-button.d.ts
127
- import 'jattac.libs.web.zest-button';
128
-
129
- declare module 'jattac.libs.web.zest-button' {
130
- export interface CustomZestSemanticTypes {
131
- archive: 'archive';
132
- publish: 'publish';
133
- }
134
- }
135
- ```
136
- After augmentation, `'archive'` and `'publish'` would be valid `SemanticType` values, available for autocompletion and type-checking.
137
-
138
- ---
139
-
140
- [⬅️ Previous: Features Showcase](./features.md) | [Next: Configuration Guide ➡️](./configuration.md)