@oxyhq/bloom 0.5.1 → 0.6.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.
- package/lib/commonjs/error-boundary/ErrorBoundary.js +27 -7
- package/lib/commonjs/error-boundary/ErrorBoundary.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/skeleton/index.js +30 -0
- package/lib/commonjs/skeleton/index.js.map +1 -1
- package/lib/module/error-boundary/ErrorBoundary.js +27 -7
- package/lib/module/error-boundary/ErrorBoundary.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/skeleton/index.js +29 -0
- package/lib/module/skeleton/index.js.map +1 -1
- package/lib/typescript/commonjs/error-boundary/ErrorBoundary.d.ts +3 -1
- package/lib/typescript/commonjs/error-boundary/ErrorBoundary.d.ts.map +1 -1
- package/lib/typescript/commonjs/error-boundary/index.d.ts +1 -1
- package/lib/typescript/commonjs/error-boundary/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/error-boundary/types.d.ts +41 -2
- package/lib/typescript/commonjs/error-boundary/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/skeleton/index.d.ts +24 -1
- package/lib/typescript/commonjs/skeleton/index.d.ts.map +1 -1
- package/lib/typescript/module/error-boundary/ErrorBoundary.d.ts +3 -1
- package/lib/typescript/module/error-boundary/ErrorBoundary.d.ts.map +1 -1
- package/lib/typescript/module/error-boundary/index.d.ts +1 -1
- package/lib/typescript/module/error-boundary/index.d.ts.map +1 -1
- package/lib/typescript/module/error-boundary/types.d.ts +41 -2
- package/lib/typescript/module/error-boundary/types.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -1
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/skeleton/index.d.ts +24 -1
- package/lib/typescript/module/skeleton/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/ErrorBoundary.test.tsx +217 -0
- package/src/__tests__/Skeleton.test.tsx +63 -0
- package/src/error-boundary/ErrorBoundary.tsx +28 -5
- package/src/error-boundary/index.ts +5 -1
- package/src/error-boundary/types.ts +45 -2
- package/src/index.ts +5 -1
- package/src/index.web.ts +5 -1
- package/src/skeleton/index.tsx +54 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/skeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/skeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAC;AAkCtB,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,KAAK,GACN,EAAE;IACD,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,2CA0BA;yBAhCe,IAAI;;;AAmCpB,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAmBA;yBA7Be,MAAM;;;AAgCtB,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,KAAK,EACL,KAAK,GACN,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAkBA;yBA1Be,IAAI;;;AA6BpB;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,EAClB,KAAK,EACL,MAAM,EACN,YAAgB,EAChB,KAAK,EACL,KAAK,GACN,EAAE;IACD,uEAAuE;IACvE,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACzC,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAkBA;yBArCe,GAAG;;;AAwCnB,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAEA;yBARe,GAAG;;;AAWnB,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAEA;yBARe,GAAG"}
|
|
@@ -3,11 +3,13 @@ import type { ErrorBoundaryProps } from './types';
|
|
|
3
3
|
interface ErrorBoundaryState {
|
|
4
4
|
hasError: boolean;
|
|
5
5
|
error: Error | null;
|
|
6
|
+
errorInfo: ErrorInfo | null;
|
|
7
|
+
retryCount: number;
|
|
6
8
|
}
|
|
7
9
|
export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
8
10
|
static displayName: string;
|
|
9
11
|
state: ErrorBoundaryState;
|
|
10
|
-
static getDerivedStateFromError(error: Error): ErrorBoundaryState
|
|
12
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
|
|
11
13
|
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
12
14
|
private handleRetry;
|
|
13
15
|
render(): ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../../../src/error-boundary/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../../../src/error-boundary/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAwCD,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,MAAM,CAAC,WAAW,SAAmB;IAErC,KAAK,EAAE,kBAAkB,CAKvB;IAEF,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI1E,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAK3D,OAAO,CAAC,WAAW,CAOjB;IAEF,MAAM,IAAI,SAAS;CA+BpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/error-boundary/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/error-boundary/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,SAAS,CAAC"}
|
|
@@ -1,8 +1,47 @@
|
|
|
1
1
|
import type { ReactNode, ErrorInfo } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Context passed to a render-prop `fallback` when an ErrorBoundary catches an
|
|
4
|
+
* error. Consumers can use this to render rich fallback UI (stack traces,
|
|
5
|
+
* report buttons, retry counters, i18n strings, etc).
|
|
6
|
+
*/
|
|
7
|
+
export interface ErrorBoundaryFallbackContext {
|
|
8
|
+
/** The error thrown by a descendant during render/commit. */
|
|
9
|
+
error: Error;
|
|
10
|
+
/**
|
|
11
|
+
* The React-supplied error info (`componentStack`). May be `null` if
|
|
12
|
+
* `componentDidCatch` has not run yet for the current crash — in practice it
|
|
13
|
+
* is populated before the fallback is shown.
|
|
14
|
+
*/
|
|
15
|
+
errorInfo: ErrorInfo | null;
|
|
16
|
+
/**
|
|
17
|
+
* Resets the boundary back to its non-error state and re-renders `children`.
|
|
18
|
+
* Increments {@link retryCount} as a side effect.
|
|
19
|
+
*/
|
|
20
|
+
retry: () => void;
|
|
21
|
+
/**
|
|
22
|
+
* Number of times {@link retry} has been invoked for the currently-mounted
|
|
23
|
+
* boundary instance. Starts at 0; increments by 1 on each `retry()` call.
|
|
24
|
+
* Useful for showing "Try again (n/max)" UIs and giving up after N attempts.
|
|
25
|
+
*/
|
|
26
|
+
retryCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The `fallback` may either be a plain ReactNode (rendered as-is, no error
|
|
30
|
+
* context) or a render function that receives the error context and returns
|
|
31
|
+
* a ReactNode.
|
|
32
|
+
*/
|
|
33
|
+
export type ErrorBoundaryFallback = ReactNode | ((context: ErrorBoundaryFallbackContext) => ReactNode);
|
|
2
34
|
export interface ErrorBoundaryProps {
|
|
3
35
|
children: ReactNode;
|
|
4
|
-
/**
|
|
5
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Custom fallback UI to render on error. Accepts either:
|
|
38
|
+
* - a ReactNode (static — same UI on every error), or
|
|
39
|
+
* - a render-prop `(ctx) => ReactNode` receiving `{ error, errorInfo,
|
|
40
|
+
* retry, retryCount }`.
|
|
41
|
+
*
|
|
42
|
+
* Backward-compatible: passing a static node continues to work unchanged.
|
|
43
|
+
*/
|
|
44
|
+
fallback?: ErrorBoundaryFallback;
|
|
6
45
|
/** Error title (defaults to "Something went wrong") */
|
|
7
46
|
title?: string;
|
|
8
47
|
/** Error message (defaults to "An unexpected error occurred") */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/error-boundary/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/error-boundary/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,KAAK,EAAE,KAAK,CAAC;IACb;;;;OAIG;IACH,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,CAAC,CAAC,OAAO,EAAE,4BAA4B,KAAK,SAAS,CAAC,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -17,7 +17,7 @@ export * from './divider';
|
|
|
17
17
|
export * from './radio-indicator';
|
|
18
18
|
export * from './collapsible';
|
|
19
19
|
export { ErrorBoundary } from './error-boundary';
|
|
20
|
-
export type { ErrorBoundaryProps } from './error-boundary';
|
|
20
|
+
export type { ErrorBoundaryProps, ErrorBoundaryFallback, ErrorBoundaryFallbackContext, } from './error-boundary';
|
|
21
21
|
export * from './avatar';
|
|
22
22
|
export * from './loading';
|
|
23
23
|
export * as PromptInput from './prompt-input';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,KAAK,EACL,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,KAAK,EACL,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC;AAC9C,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,KAAK,SAAS,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,gBAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,SAAS,MAAM,aAAa,CAAC;AAGzC,cAAc,iBAAiB,CAAC;AAGhC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAG/B,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC"}
|
|
@@ -17,7 +17,7 @@ export * from './divider';
|
|
|
17
17
|
export * from './radio-indicator';
|
|
18
18
|
export * from './collapsible';
|
|
19
19
|
export { ErrorBoundary } from './error-boundary';
|
|
20
|
-
export type { ErrorBoundaryProps } from './error-boundary';
|
|
20
|
+
export type { ErrorBoundaryProps, ErrorBoundaryFallback, ErrorBoundaryFallbackContext, } from './error-boundary';
|
|
21
21
|
export * from './avatar';
|
|
22
22
|
export * from './loading';
|
|
23
23
|
export * as PromptInput from './prompt-input';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../src/index.web.ts"],"names":[],"mappings":"AAMA,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,KAAK,EACL,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../src/index.web.ts"],"names":[],"mappings":"AAMA,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,KAAK,EACL,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC;AAC9C,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,KAAK,SAAS,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,gBAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,SAAS,MAAM,aAAa,CAAC;AAGzC,cAAc,iBAAiB,CAAC;AAGhC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,IAAI,MAAM,kBAAkB,CAAC;AACzC,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,OAAO,KAAK,WAAW,MAAM,0BAA0B,CAAC;AAGxD,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAG/B,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type ViewStyle, type TextStyle } from 'react-native';
|
|
2
|
+
import { type ViewStyle, type TextStyle, type DimensionValue } from 'react-native';
|
|
3
3
|
export declare function Text({ blend, style, }: {
|
|
4
4
|
style?: TextStyle | TextStyle[];
|
|
5
5
|
blend?: boolean;
|
|
@@ -24,6 +24,29 @@ export declare function Pill({ size, blend, style, }: {
|
|
|
24
24
|
export declare namespace Pill {
|
|
25
25
|
var displayName: string;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Generic rectangular shimmering placeholder. Use for image/card/banner
|
|
29
|
+
* placeholders where neither {@link Pill} (forced borderRadius: 999) nor
|
|
30
|
+
* {@link Circle} fit. Defaults to a small borderRadius (8). Pass `0` for
|
|
31
|
+
* sharp corners or any larger number / percentage string for custom shapes.
|
|
32
|
+
*/
|
|
33
|
+
export declare function Box({ width, height, borderRadius, blend, style, }: {
|
|
34
|
+
/** Width as a number, percentage string, or any valid RN dimension. */
|
|
35
|
+
width?: DimensionValue;
|
|
36
|
+
/** Height as a number, percentage string, or any valid RN dimension. */
|
|
37
|
+
height?: DimensionValue;
|
|
38
|
+
/**
|
|
39
|
+
* Corner radius — number or percentage string. Defaults to 8. Pass 0 for
|
|
40
|
+
* sharp corners.
|
|
41
|
+
*/
|
|
42
|
+
borderRadius?: ViewStyle['borderRadius'];
|
|
43
|
+
/** When true, dampens shimmer opacity (use for nested skeletons). */
|
|
44
|
+
blend?: boolean;
|
|
45
|
+
style?: ViewStyle | ViewStyle[];
|
|
46
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
47
|
+
export declare namespace Box {
|
|
48
|
+
var displayName: string;
|
|
49
|
+
}
|
|
27
50
|
export declare function Col({ children, style, }: {
|
|
28
51
|
children?: React.ReactNode;
|
|
29
52
|
style?: ViewStyle | ViewStyle[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/skeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/skeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAC;AAkCtB,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,KAAK,GACN,EAAE;IACD,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,2CA0BA;yBAhCe,IAAI;;;AAmCpB,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAmBA;yBA7Be,MAAM;;;AAgCtB,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,KAAK,EACL,KAAK,GACN,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAkBA;yBA1Be,IAAI;;;AA6BpB;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,EAClB,KAAK,EACL,MAAM,EACN,YAAgB,EAChB,KAAK,EACL,KAAK,GACN,EAAE;IACD,uEAAuE;IACvE,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACzC,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAkBA;yBArCe,GAAG;;;AAwCnB,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAEA;yBARe,GAAG;;;AAWnB,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAEA;yBARe,GAAG"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import React, { type ErrorInfo, type ReactNode } from 'react';
|
|
2
|
+
import { Text } from 'react-native';
|
|
3
|
+
import { act, render, fireEvent } from '@testing-library/react-native';
|
|
4
|
+
|
|
5
|
+
import { ErrorBoundary } from '../error-boundary';
|
|
6
|
+
import type {
|
|
7
|
+
ErrorBoundaryFallback,
|
|
8
|
+
ErrorBoundaryFallbackContext,
|
|
9
|
+
} from '../error-boundary';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders normally on the first mount, then throws once we flip the
|
|
13
|
+
* controlled flag. Used to drive the boundary into / out of its error state.
|
|
14
|
+
*/
|
|
15
|
+
function MaybeThrow({ shouldThrow, message = 'boom' }: { shouldThrow: boolean; message?: string }) {
|
|
16
|
+
if (shouldThrow) throw new Error(message);
|
|
17
|
+
return <Text>ok</Text>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('ErrorBoundary', () => {
|
|
21
|
+
// React intentionally logs errors caught by boundaries — silence the noise
|
|
22
|
+
// so test output stays useful.
|
|
23
|
+
let errorSpy: jest.SpyInstance;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
errorSpy.mockRestore();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('renders children when no error is thrown', () => {
|
|
32
|
+
const { getByText } = render(
|
|
33
|
+
<ErrorBoundary>
|
|
34
|
+
<MaybeThrow shouldThrow={false} />
|
|
35
|
+
</ErrorBoundary>,
|
|
36
|
+
);
|
|
37
|
+
expect(getByText('ok')).toBeTruthy();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('renders the default fallback when a child throws', () => {
|
|
41
|
+
const { getByText } = render(
|
|
42
|
+
<ErrorBoundary>
|
|
43
|
+
<MaybeThrow shouldThrow />
|
|
44
|
+
</ErrorBoundary>,
|
|
45
|
+
);
|
|
46
|
+
expect(getByText('Something went wrong')).toBeTruthy();
|
|
47
|
+
expect(getByText('An unexpected error occurred')).toBeTruthy();
|
|
48
|
+
expect(getByText('Try Again')).toBeTruthy();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders a static ReactNode fallback (backward-compatible)', () => {
|
|
52
|
+
const { getByText, queryByText } = render(
|
|
53
|
+
<ErrorBoundary fallback={<Text>Static fallback</Text>}>
|
|
54
|
+
<MaybeThrow shouldThrow />
|
|
55
|
+
</ErrorBoundary>,
|
|
56
|
+
);
|
|
57
|
+
expect(getByText('Static fallback')).toBeTruthy();
|
|
58
|
+
// Default fallback must NOT also appear.
|
|
59
|
+
expect(queryByText('Something went wrong')).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('renders a render-prop fallback with error context', () => {
|
|
63
|
+
const { getByText } = render(
|
|
64
|
+
<ErrorBoundary
|
|
65
|
+
fallback={({ error, retryCount }) => (
|
|
66
|
+
<Text>{`err:${error.message} count:${retryCount}`}</Text>
|
|
67
|
+
)}>
|
|
68
|
+
<MaybeThrow shouldThrow message="custom-boom" />
|
|
69
|
+
</ErrorBoundary>,
|
|
70
|
+
);
|
|
71
|
+
expect(getByText('err:custom-boom count:0')).toBeTruthy();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('passes errorInfo to the render-prop after componentDidCatch', () => {
|
|
75
|
+
let captured: ErrorBoundaryFallbackContext | null = null;
|
|
76
|
+
render(
|
|
77
|
+
<ErrorBoundary
|
|
78
|
+
fallback={(ctx) => {
|
|
79
|
+
captured = ctx;
|
|
80
|
+
return <Text>captured</Text>;
|
|
81
|
+
}}>
|
|
82
|
+
<MaybeThrow shouldThrow />
|
|
83
|
+
</ErrorBoundary>,
|
|
84
|
+
);
|
|
85
|
+
expect(captured).not.toBeNull();
|
|
86
|
+
const ctx = captured as unknown as ErrorBoundaryFallbackContext;
|
|
87
|
+
expect(ctx.error.message).toBe('boom');
|
|
88
|
+
// componentDidCatch populates errorInfo with a componentStack string.
|
|
89
|
+
expect(ctx.errorInfo).not.toBeNull();
|
|
90
|
+
expect(typeof ctx.errorInfo?.componentStack).toBe('string');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('invokes onError when a child throws', () => {
|
|
94
|
+
const onError = jest.fn<void, [Error, ErrorInfo]>();
|
|
95
|
+
render(
|
|
96
|
+
<ErrorBoundary onError={onError}>
|
|
97
|
+
<MaybeThrow shouldThrow message="reported" />
|
|
98
|
+
</ErrorBoundary>,
|
|
99
|
+
);
|
|
100
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
101
|
+
const [err, info] = onError.mock.calls[0]!;
|
|
102
|
+
expect(err.message).toBe('reported');
|
|
103
|
+
expect(typeof info.componentStack).toBe('string');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('increments retryCount each time retry() is invoked', () => {
|
|
107
|
+
/**
|
|
108
|
+
* The boundary's `retry()` resets `hasError` and re-renders children. To
|
|
109
|
+
* observe successive `retryCount` values without immediately re-throwing
|
|
110
|
+
* (which would just snap straight back to the fallback), we capture each
|
|
111
|
+
* call's count via the render-prop and trigger retry from the fallback.
|
|
112
|
+
*/
|
|
113
|
+
const seenCounts: number[] = [];
|
|
114
|
+
let triggerRetry: (() => void) | null = null;
|
|
115
|
+
|
|
116
|
+
const fallback: ErrorBoundaryFallback = ({ retry, retryCount }) => {
|
|
117
|
+
seenCounts.push(retryCount);
|
|
118
|
+
triggerRetry = retry;
|
|
119
|
+
return <Text testID="retry-btn">{`count:${retryCount}`}</Text>;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
function AlwaysThrow(): React.ReactElement {
|
|
123
|
+
throw new Error('persistent');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
render(
|
|
127
|
+
<ErrorBoundary fallback={fallback}>
|
|
128
|
+
<AlwaysThrow />
|
|
129
|
+
</ErrorBoundary>,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(seenCounts[0]).toBe(0);
|
|
133
|
+
expect(triggerRetry).not.toBeNull();
|
|
134
|
+
|
|
135
|
+
// First retry — boundary resets, child throws again, fallback re-renders
|
|
136
|
+
// with retryCount === 1. Wrap in act() because retry() triggers React
|
|
137
|
+
// state updates and a synchronous error during the re-render.
|
|
138
|
+
act(() => {
|
|
139
|
+
triggerRetry?.();
|
|
140
|
+
});
|
|
141
|
+
expect(seenCounts).toContain(1);
|
|
142
|
+
|
|
143
|
+
// Second retry → count 2.
|
|
144
|
+
act(() => {
|
|
145
|
+
triggerRetry?.();
|
|
146
|
+
});
|
|
147
|
+
expect(seenCounts).toContain(2);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Type-only compile-time test. These assignments must type-check; they are
|
|
152
|
+
* never executed. If a future change breaks either variant, ts-jest will
|
|
153
|
+
* fail the suite at compile time.
|
|
154
|
+
*/
|
|
155
|
+
it('accepts both ReactNode and render-prop fallback (compile-time)', () => {
|
|
156
|
+
const _staticFallback: ErrorBoundaryFallback = <Text>static</Text>;
|
|
157
|
+
const _undefinedFallback: ErrorBoundaryFallback = undefined as ReactNode;
|
|
158
|
+
const _functionFallback: ErrorBoundaryFallback = ({
|
|
159
|
+
error,
|
|
160
|
+
errorInfo,
|
|
161
|
+
retry,
|
|
162
|
+
retryCount,
|
|
163
|
+
}) => (
|
|
164
|
+
<Text>
|
|
165
|
+
{error.message}
|
|
166
|
+
{errorInfo?.componentStack ?? ''}
|
|
167
|
+
{retryCount}
|
|
168
|
+
{retry.name}
|
|
169
|
+
</Text>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Use the values so TS does not flag them as unused.
|
|
173
|
+
expect(_staticFallback).toBeDefined();
|
|
174
|
+
expect(_undefinedFallback).toBeUndefined();
|
|
175
|
+
expect(typeof _functionFallback).toBe('function');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('also accepts assigning fallback directly onto ErrorBoundary props', () => {
|
|
179
|
+
// Smoke check that the props.fallback union compiles for both variants.
|
|
180
|
+
const _staticProps = (
|
|
181
|
+
<ErrorBoundary fallback={<Text>x</Text>}>
|
|
182
|
+
<Text>y</Text>
|
|
183
|
+
</ErrorBoundary>
|
|
184
|
+
);
|
|
185
|
+
const _renderProps = (
|
|
186
|
+
<ErrorBoundary fallback={({ retry }) => <Text onPress={retry}>z</Text>}>
|
|
187
|
+
<Text>y</Text>
|
|
188
|
+
</ErrorBoundary>
|
|
189
|
+
);
|
|
190
|
+
expect(_staticProps).toBeDefined();
|
|
191
|
+
expect(_renderProps).toBeDefined();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('uses custom default-fallback labels when provided', () => {
|
|
195
|
+
const { getByText } = render(
|
|
196
|
+
<ErrorBoundary title="Oops" message="boom message" retryLabel="Retry!">
|
|
197
|
+
<MaybeThrow shouldThrow />
|
|
198
|
+
</ErrorBoundary>,
|
|
199
|
+
);
|
|
200
|
+
expect(getByText('Oops')).toBeTruthy();
|
|
201
|
+
expect(getByText('boom message')).toBeTruthy();
|
|
202
|
+
expect(getByText('Retry!')).toBeTruthy();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('fires the default-fallback retry button without crashing', () => {
|
|
206
|
+
const { getByText } = render(
|
|
207
|
+
<ErrorBoundary>
|
|
208
|
+
<MaybeThrow shouldThrow />
|
|
209
|
+
</ErrorBoundary>,
|
|
210
|
+
);
|
|
211
|
+
// The button is rendered inside a TouchableOpacity; pressing fires the
|
|
212
|
+
// internal handleRetry which resets state. The child will throw again on
|
|
213
|
+
// the next render, but the test only asserts the press itself does not
|
|
214
|
+
// throw synchronously.
|
|
215
|
+
expect(() => fireEvent.press(getByText('Try Again'))).not.toThrow();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react-native';
|
|
3
|
+
|
|
4
|
+
import { BloomThemeProvider } from '../theme/BloomThemeProvider';
|
|
5
|
+
import * as Skeleton from '../skeleton';
|
|
6
|
+
|
|
7
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
8
|
+
return render(
|
|
9
|
+
<BloomThemeProvider mode="light" colorPreset="teal">
|
|
10
|
+
{ui}
|
|
11
|
+
</BloomThemeProvider>,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('Skeleton.Box', () => {
|
|
16
|
+
it('is exported from the Skeleton namespace', () => {
|
|
17
|
+
expect(typeof Skeleton.Box).toBe('function');
|
|
18
|
+
expect(Skeleton.Box.displayName).toBe('Skeleton.Box');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('renders without crashing with numeric width/height', () => {
|
|
22
|
+
const { UNSAFE_root } = renderWithTheme(
|
|
23
|
+
<Skeleton.Box width={100} height={48} />,
|
|
24
|
+
);
|
|
25
|
+
expect(UNSAFE_root).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders without crashing with percentage width', () => {
|
|
29
|
+
const { UNSAFE_root } = renderWithTheme(
|
|
30
|
+
<Skeleton.Box width="100%" height={200} />,
|
|
31
|
+
);
|
|
32
|
+
expect(UNSAFE_root).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders with sharp corners when borderRadius is 0', () => {
|
|
36
|
+
const { UNSAFE_root } = renderWithTheme(
|
|
37
|
+
<Skeleton.Box width={50} height={50} borderRadius={0} />,
|
|
38
|
+
);
|
|
39
|
+
expect(UNSAFE_root).toBeTruthy();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders with a custom large borderRadius', () => {
|
|
43
|
+
const { UNSAFE_root } = renderWithTheme(
|
|
44
|
+
<Skeleton.Box width={50} height={50} borderRadius={24} />,
|
|
45
|
+
);
|
|
46
|
+
expect(UNSAFE_root).toBeTruthy();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('renders when blend prop is set', () => {
|
|
50
|
+
const { UNSAFE_root } = renderWithTheme(
|
|
51
|
+
<Skeleton.Box width={50} height={50} blend />,
|
|
52
|
+
);
|
|
53
|
+
expect(UNSAFE_root).toBeTruthy();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('still exposes existing primitives (Pill, Circle, Text, Row, Col)', () => {
|
|
57
|
+
expect(typeof Skeleton.Pill).toBe('function');
|
|
58
|
+
expect(typeof Skeleton.Circle).toBe('function');
|
|
59
|
+
expect(typeof Skeleton.Text).toBe('function');
|
|
60
|
+
expect(typeof Skeleton.Row).toBe('function');
|
|
61
|
+
expect(typeof Skeleton.Col).toBe('function');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -6,6 +6,8 @@ import type { ErrorBoundaryProps } from './types';
|
|
|
6
6
|
interface ErrorBoundaryState {
|
|
7
7
|
hasError: boolean;
|
|
8
8
|
error: Error | null;
|
|
9
|
+
errorInfo: ErrorInfo | null;
|
|
10
|
+
retryCount: number;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -52,24 +54,45 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
52
54
|
state: ErrorBoundaryState = {
|
|
53
55
|
hasError: false,
|
|
54
56
|
error: null,
|
|
57
|
+
errorInfo: null,
|
|
58
|
+
retryCount: 0,
|
|
55
59
|
};
|
|
56
60
|
|
|
57
|
-
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
61
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
|
|
58
62
|
return { hasError: true, error };
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
|
66
|
+
this.setState({ errorInfo });
|
|
62
67
|
this.props.onError?.(error, errorInfo);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
private handleRetry = (): void => {
|
|
66
|
-
this.setState(
|
|
71
|
+
this.setState((prev) => ({
|
|
72
|
+
hasError: false,
|
|
73
|
+
error: null,
|
|
74
|
+
errorInfo: null,
|
|
75
|
+
retryCount: prev.retryCount + 1,
|
|
76
|
+
}));
|
|
67
77
|
};
|
|
68
78
|
|
|
69
79
|
render(): ReactNode {
|
|
70
|
-
if (this.state.hasError) {
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
if (this.state.hasError && this.state.error) {
|
|
81
|
+
const { fallback } = this.props;
|
|
82
|
+
|
|
83
|
+
if (typeof fallback === 'function') {
|
|
84
|
+
// Render-prop variant — invoke with error context.
|
|
85
|
+
return fallback({
|
|
86
|
+
error: this.state.error,
|
|
87
|
+
errorInfo: this.state.errorInfo,
|
|
88
|
+
retry: this.handleRetry,
|
|
89
|
+
retryCount: this.state.retryCount,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (fallback !== undefined) {
|
|
94
|
+
// Static ReactNode variant (backward-compatible).
|
|
95
|
+
return fallback;
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
return (
|
|
@@ -1,9 +1,52 @@
|
|
|
1
1
|
import type { ReactNode, ErrorInfo } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Context passed to a render-prop `fallback` when an ErrorBoundary catches an
|
|
5
|
+
* error. Consumers can use this to render rich fallback UI (stack traces,
|
|
6
|
+
* report buttons, retry counters, i18n strings, etc).
|
|
7
|
+
*/
|
|
8
|
+
export interface ErrorBoundaryFallbackContext {
|
|
9
|
+
/** The error thrown by a descendant during render/commit. */
|
|
10
|
+
error: Error;
|
|
11
|
+
/**
|
|
12
|
+
* The React-supplied error info (`componentStack`). May be `null` if
|
|
13
|
+
* `componentDidCatch` has not run yet for the current crash — in practice it
|
|
14
|
+
* is populated before the fallback is shown.
|
|
15
|
+
*/
|
|
16
|
+
errorInfo: ErrorInfo | null;
|
|
17
|
+
/**
|
|
18
|
+
* Resets the boundary back to its non-error state and re-renders `children`.
|
|
19
|
+
* Increments {@link retryCount} as a side effect.
|
|
20
|
+
*/
|
|
21
|
+
retry: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Number of times {@link retry} has been invoked for the currently-mounted
|
|
24
|
+
* boundary instance. Starts at 0; increments by 1 on each `retry()` call.
|
|
25
|
+
* Useful for showing "Try again (n/max)" UIs and giving up after N attempts.
|
|
26
|
+
*/
|
|
27
|
+
retryCount: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The `fallback` may either be a plain ReactNode (rendered as-is, no error
|
|
32
|
+
* context) or a render function that receives the error context and returns
|
|
33
|
+
* a ReactNode.
|
|
34
|
+
*/
|
|
35
|
+
export type ErrorBoundaryFallback =
|
|
36
|
+
| ReactNode
|
|
37
|
+
| ((context: ErrorBoundaryFallbackContext) => ReactNode);
|
|
38
|
+
|
|
3
39
|
export interface ErrorBoundaryProps {
|
|
4
40
|
children: ReactNode;
|
|
5
|
-
/**
|
|
6
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Custom fallback UI to render on error. Accepts either:
|
|
43
|
+
* - a ReactNode (static — same UI on every error), or
|
|
44
|
+
* - a render-prop `(ctx) => ReactNode` receiving `{ error, errorInfo,
|
|
45
|
+
* retry, retryCount }`.
|
|
46
|
+
*
|
|
47
|
+
* Backward-compatible: passing a static node continues to work unchanged.
|
|
48
|
+
*/
|
|
49
|
+
fallback?: ErrorBoundaryFallback;
|
|
7
50
|
/** Error title (defaults to "Something went wrong") */
|
|
8
51
|
title?: string;
|
|
9
52
|
/** Error message (defaults to "An unexpected error occurred") */
|
package/src/index.ts
CHANGED
|
@@ -40,7 +40,11 @@ export * from './divider';
|
|
|
40
40
|
export * from './radio-indicator';
|
|
41
41
|
export * from './collapsible';
|
|
42
42
|
export { ErrorBoundary } from './error-boundary';
|
|
43
|
-
export type {
|
|
43
|
+
export type {
|
|
44
|
+
ErrorBoundaryProps,
|
|
45
|
+
ErrorBoundaryFallback,
|
|
46
|
+
ErrorBoundaryFallbackContext,
|
|
47
|
+
} from './error-boundary';
|
|
44
48
|
export * from './avatar';
|
|
45
49
|
export * from './loading';
|
|
46
50
|
export * as PromptInput from './prompt-input';
|
package/src/index.web.ts
CHANGED
|
@@ -45,7 +45,11 @@ export * from './divider';
|
|
|
45
45
|
export * from './radio-indicator';
|
|
46
46
|
export * from './collapsible';
|
|
47
47
|
export { ErrorBoundary } from './error-boundary';
|
|
48
|
-
export type {
|
|
48
|
+
export type {
|
|
49
|
+
ErrorBoundaryProps,
|
|
50
|
+
ErrorBoundaryFallback,
|
|
51
|
+
ErrorBoundaryFallbackContext,
|
|
52
|
+
} from './error-boundary';
|
|
49
53
|
export * from './avatar';
|
|
50
54
|
export * from './loading';
|
|
51
55
|
export * as PromptInput from './prompt-input';
|