hazo_ui 2.17.0 → 3.0.1

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/CHANGE_LOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v3.0.0 — test-harness sub-export
9
+
10
+ **New sub-export:** `hazo_ui/test-harness`
11
+
12
+ Ships a complete in-app test runner for consuming packages. Import from the dedicated sub-export — it is never included in the main `hazo_ui` bundle.
13
+
14
+ - **`SidebarLayout` + `AppSidebar`** — sidebar shell for test-app pages with `NavItem[]` navigation.
15
+ - **`AutoTestProvider` + `useAutoTest`** — React context that tracks run status, per-case results, and timing across all registered scenarios.
16
+ - **`AutoTestRunner`** — renders all registered scenarios with Run / Run All controls and pass/fail/timing output per case.
17
+ - **`CopyAllFailuresButton`** — copies all failed cases as a structured Claude prompt (8-section format: header, what-went-wrong, expected/actual/diff, test code, code under test, error chain, context, ring buffer).
18
+ - **`registerScenario(scenario)`** — registers a scenario at module load time; scenarios are collected into a global registry.
19
+ - **`assertEqual`, `assertThrows`, `assertResolves`, `assertRejects`, `assertMatch`, `assertIncludes`, `HazoAssertionError`** — assertion helpers for use inside `case.run()`.
20
+ - **`formatAsClaudePrompt(failedCases, options?)`** — formats an array of failed cases into a copy-pasteable Claude prompt.
21
+
22
+ ## v2.18.0 — CelebrationModal
23
+
24
+ **New:** `CelebrationProvider`, `celebrate()`, `CELEBRATION_GRADIENT`
25
+
26
+ - Imperative confetti + modal overlay triggered by `celebrate({ id, title, ... })`.
27
+ - `<CelebrationProvider />` mounts at app root; `celebrate()` works from anywhere in the tree.
28
+ - **Session gating:** each `id` is shown at most once per browser session (sessionStorage). Cross-session gating is the consumer's responsibility.
29
+ - **FIFO queue:** rapid-fire `celebrate()` calls queue up and show one at a time.
30
+ - **Shareable card** (optional): 1080×1080 node rendered at full size, scaled to 400×400 for preview. Download PNG / Share / Copy image buttons (html-to-image, dynamically imported).
31
+ - **`background?: string`:** CSS background for the card. Default: `CELEBRATION_GRADIENT` export.
32
+ - **`caption?: string`:** falls back to `subtitle` when omitted.
33
+ - **Auto-dismiss:** defaults to `true` (8 s). Configure via `autoDismissDelay`.
34
+ - **Audio:** `audioChime: true` plays the bundled success chime (.mp3 inlined as base64).
35
+ - **Dependencies added:** `canvas-confetti`, `html-to-image`.
36
+
8
37
  ## [2.17.0] - 2026-05-17
9
38
 
10
39
  ### Added
package/README.md CHANGED
@@ -3808,6 +3808,176 @@ interface DateRangeOption {
3808
3808
 
3809
3809
  ---
3810
3810
 
3811
+ ## Celebration Modal
3812
+
3813
+ Fire a confetti overlay with an optional 1080×1080 shareable card from anywhere in your app.
3814
+
3815
+ ### Setup
3816
+
3817
+ Mount `<CelebrationProvider />` once at your app root:
3818
+
3819
+ ```tsx
3820
+ // app/layout.tsx
3821
+ import { CelebrationProvider } from "hazo_ui";
3822
+
3823
+ export default function RootLayout({ children }) {
3824
+ return (
3825
+ <html>
3826
+ <body>
3827
+ <CelebrationProvider>
3828
+ {children}
3829
+ </CelebrationProvider>
3830
+ </body>
3831
+ </html>
3832
+ );
3833
+ }
3834
+ ```
3835
+
3836
+ ### Usage
3837
+
3838
+ ```tsx
3839
+ import { celebrate, CELEBRATION_GRADIENT } from "hazo_ui";
3840
+
3841
+ // Basic celebration
3842
+ celebrate({
3843
+ id: "first_login", // sessionStorage dedup key — shown once per session
3844
+ title: "Welcome!",
3845
+ subtitle: "Your account is ready.",
3846
+ });
3847
+
3848
+ // With shareable card
3849
+ celebrate({
3850
+ id: "milestone_100",
3851
+ title: "100 entries!",
3852
+ subtitle: "You just crossed 100 entries.",
3853
+ autoDismissDelay: 10_000,
3854
+ shareableCard: {
3855
+ background: CELEBRATION_GRADIENT, // or any CSS background value
3856
+ foreground: <YourCardContent />,
3857
+ caption: "100 entries logged", // optional; defaults to subtitle
3858
+ },
3859
+ audioChime: true,
3860
+ });
3861
+ ```
3862
+
3863
+ ### Session gating
3864
+
3865
+ `celebrate()` is a no-op if `hazo_ui_celebration_<id>` is already set in sessionStorage.
3866
+ The key is written when the modal opens. Gating resets when the user opens a new tab or browser session.
3867
+
3868
+ **Cross-session dedup is the consumer's responsibility.** Check your server-side milestone state before calling `celebrate()`.
3869
+
3870
+ ### API
3871
+
3872
+ | Prop | Type | Default | Description |
3873
+ |---|---|---|---|
3874
+ | `id` | `string` | required | Dedup key. Used as `hazo_ui_celebration_<id>` in sessionStorage. |
3875
+ | `title` | `string` | required | Modal heading. |
3876
+ | `subtitle` | `string` | — | Subheading; also used as card caption fallback. |
3877
+ | `shareableCard` | `CelebrationShareableCard` | — | Enables card preview + download/share/copy buttons. |
3878
+ | `autoDismiss` | `boolean` | `true` | Auto-close after `autoDismissDelay` ms. |
3879
+ | `autoDismissDelay` | `number` | `8000` | Ms until auto-dismiss. |
3880
+ | `audioChime` | `boolean` | `false` | Play bundled success chime on open. |
3881
+
3882
+ ---
3883
+
3884
+ ## Test Harness (v3.0.0)
3885
+
3886
+ `hazo_ui/test-harness` is a dedicated sub-export that ships a complete in-app test runner for consuming packages. It is **never included in the main `hazo_ui` bundle** — only imported by test-app code.
3887
+
3888
+ ### Setup
3889
+
3890
+ ```tsx
3891
+ // test-app/app/layout.tsx
3892
+ import { SidebarLayout, AppSidebar, AutoTestProvider } from 'hazo_ui/test-harness';
3893
+ import type { NavItem } from 'hazo_ui/test-harness';
3894
+
3895
+ const nav: NavItem[] = [
3896
+ { href: '/', label: 'Overview' },
3897
+ { href: '/my-feature', label: 'My Feature' },
3898
+ ];
3899
+
3900
+ export default function RootLayout({ children }) {
3901
+ return (
3902
+ <html>
3903
+ <body>
3904
+ <AutoTestProvider>
3905
+ <SidebarLayout sidebar={<AppSidebar nav={nav} title="my_pkg" />}>
3906
+ {children}
3907
+ </SidebarLayout>
3908
+ </AutoTestProvider>
3909
+ </body>
3910
+ </html>
3911
+ );
3912
+ }
3913
+ ```
3914
+
3915
+ ### Registering scenarios
3916
+
3917
+ ```ts
3918
+ // test-app/scenarios/my_feature.ts
3919
+ import { registerScenario, assertEqual } from 'hazo_ui/test-harness';
3920
+
3921
+ registerScenario({
3922
+ id: 'my_feature',
3923
+ name: 'My Feature',
3924
+ pkg: 'my_pkg',
3925
+ cases: [
3926
+ {
3927
+ name: 'returns correct value',
3928
+ run: async () => {
3929
+ const result = myFunction(1, 2);
3930
+ assertEqual(result, 3, 'should add two numbers');
3931
+ },
3932
+ },
3933
+ {
3934
+ name: 'throws on invalid input',
3935
+ run: async () => {
3936
+ await assertThrows(() => myFunction(null, 2), 'invalid input');
3937
+ },
3938
+ },
3939
+ ],
3940
+ });
3941
+ ```
3942
+
3943
+ ### Rendering the runner
3944
+
3945
+ ```tsx
3946
+ // test-app/app/my-feature/page.tsx
3947
+ import { AutoTestRunner } from 'hazo_ui/test-harness';
3948
+ import '../scenarios/my_feature'; // registers the scenario
3949
+
3950
+ export default function MyFeaturePage() {
3951
+ return <AutoTestRunner scenarioId="my_feature" />;
3952
+ }
3953
+ ```
3954
+
3955
+ ### Copying failures as a Claude prompt
3956
+
3957
+ `CopyAllFailuresButton` copies all failed cases as a structured prompt with 8 sections (what-went-wrong, expected/actual/diff, test code, code under test, error chain, context, ring buffer). Place it in your sidebar or test page header.
3958
+
3959
+ ```tsx
3960
+ import { CopyAllFailuresButton } from 'hazo_ui/test-harness';
3961
+
3962
+ // Inside your layout or sidebar:
3963
+ <CopyAllFailuresButton />
3964
+ ```
3965
+
3966
+ ### Assertions
3967
+
3968
+ | Function | Description |
3969
+ |---|---|
3970
+ | `assertEqual(actual, expected, msg?)` | Deep-equal assertion |
3971
+ | `assertThrows(fn, msgOrPattern?)` | Sync function must throw |
3972
+ | `assertResolves(promise)` | Promise must resolve |
3973
+ | `assertRejects(promise, msgOrPattern?)` | Promise must reject |
3974
+ | `assertMatch(value, pattern)` | String must match regex or substring |
3975
+ | `assertIncludes(arr, item)` | Array must include item |
3976
+
3977
+ All failures throw `HazoAssertionError` with a structured message.
3978
+
3979
+ ---
3980
+
3811
3981
  ## Troubleshooting
3812
3982
 
3813
3983
  ### Styles not applying (Tailwind v4)
@@ -135,6 +135,8 @@ npm install @radix-ui/react-dialog @radix-ui/react-popover @radix-ui/react-selec
135
135
  - **ProgressiveImage**: No additional dependencies
136
136
  - **HazoUiToaster, successToast, errorToast, rawToast**: `sonner`, `lucide-react`
137
137
  - **useLoadingState, useErrorDisplay** hooks: No additional dependencies
138
+ - **Celebration (v2.18.0)**:
139
+ - **CelebrationProvider, celebrate, CELEBRATION_GRADIENT**: No additional peer deps — `canvas-confetti` and `html-to-image` are bundled direct dependencies
138
140
 
139
141
  ### 4. Configure Tailwind CSS
140
142
 
@@ -252,10 +254,18 @@ import {
252
254
  HazoUiToaster, successToast, errorToast,
253
255
  useLoadingState, useErrorDisplay,
254
256
  } from 'hazo_ui';
257
+
258
+ // Import celebration (v2.18.0)
259
+ import { CelebrationProvider, celebrate, CELEBRATION_GRADIENT } from 'hazo_ui';
260
+
261
+ // Import test harness (v3.0.0) — test-app only, never in production bundles
262
+ import { AutoTestProvider, AutoTestRunner, SidebarLayout, AppSidebar, registerScenario, assertEqual } from 'hazo_ui/test-harness';
255
263
  ```
256
264
 
257
265
  **Toaster setup**: Mount `<HazoUiToaster />` once at the root of your app (e.g., in `layout.tsx`) so `successToast()` / `errorToast()` calls have somewhere to render.
258
266
 
267
+ **Celebration setup**: Mount `<CelebrationProvider />` once at the root of your app (e.g., in `layout.tsx`) so `celebrate({ id, title })` calls have somewhere to render. Session gating (one show per `id` per browser session) runs automatically.
268
+
259
269
  [ ] Use the component in your code (see README.md for detailed usage examples)
260
270
 
261
271
  ### 6. Verify Installation