Address compliance-portal review feedback
Fix the valid issues raised in the scaffold review. UI kit: the Button loading state now replaces only the leading icon instead of dropping the label, Button consumes the `active` variant so it no longer leaks onto the DOM, and every v2 skeleton sets aria-hidden after the prop spread so a consumer cannot override it. @probo/relay: guard the caller-supplied onCompleted/onError callbacks so a throwing callback still settles the awaitable mutation promise instead of leaving it pending. compliance-portal: normalize external website hrefs and read hostname via URL.hostname, add a localized catch-all not-found route, and widen the .gitattributes glob so colocated __generated__ artifacts at any depth are marked generated. Docs: correct the forms guide (Base UI passes plain values, Zod v3 flatten API), spread the child fragment in the permissions example, and drop references to v2 components that do not exist in the ui guide. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -81,11 +81,24 @@ export function createUseMutation(useNotifier: () => MutationNotifier) {
|
||||
);
|
||||
}
|
||||
|
||||
function toError(value: unknown): Error {
|
||||
return value instanceof Error ? value : new Error(String(value));
|
||||
}
|
||||
|
||||
return new Promise<T["response"]>((resolve, reject) => {
|
||||
commit({
|
||||
...config,
|
||||
onCompleted: (response, errors) => {
|
||||
config.onCompleted?.(response, errors);
|
||||
// A throwing caller callback must still settle the wrapper promise,
|
||||
// otherwise `await mutate()` would hang forever.
|
||||
try {
|
||||
config.onCompleted?.(response, errors);
|
||||
} catch (callbackError) {
|
||||
const error = toError(callbackError);
|
||||
notifyError(error);
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (errors && errors.length > 0) {
|
||||
const [payloadError] = errors;
|
||||
notifyError(payloadError);
|
||||
@@ -102,7 +115,13 @@ export function createUseMutation(useNotifier: () => MutationNotifier) {
|
||||
resolve(response);
|
||||
},
|
||||
onError: (error) => {
|
||||
config.onError?.(error);
|
||||
// Swallow a throwing caller callback so the original mutation error
|
||||
// still flows through to the notifier and the rejection.
|
||||
try {
|
||||
config.onError?.(error);
|
||||
} catch {
|
||||
// Intentionally ignored: the mutation error below is authoritative.
|
||||
}
|
||||
notifyError(error);
|
||||
reject(error);
|
||||
},
|
||||
|
||||
@@ -24,5 +24,5 @@ export type AvatarSkeletonProps = Omit<ComponentProps<"span">, "children"> & Var
|
||||
export function AvatarSkeleton(props: AvatarSkeletonProps) {
|
||||
const { size, radius, className, ...rest } = props;
|
||||
|
||||
return <span aria-hidden className={avatarSkeleton({ size, radius, className })} {...rest} />;
|
||||
return <span className={avatarSkeleton({ size, radius, className })} {...rest} aria-hidden />;
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ export type BadgeSkeletonProps = Omit<ComponentProps<"span">, "children"> & Vari
|
||||
export function BadgeSkeleton(props: BadgeSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return <span aria-hidden className={badgeSkeleton({ size, className })} {...rest} />;
|
||||
return <span className={badgeSkeleton({ size, className })} {...rest} aria-hidden />;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export type ButtonProps
|
||||
// or router link (Link) are separate components. See contrib/claude/ui.md.
|
||||
export function Button(props: ButtonProps) {
|
||||
const {
|
||||
size, variant, color, highContrast, className,
|
||||
size, variant, color, highContrast, active, className,
|
||||
iconStart, iconEnd, loading = false, disabled, type = "button", children, ...rest
|
||||
} = props;
|
||||
|
||||
@@ -41,18 +41,12 @@ export function Button(props: ButtonProps) {
|
||||
type={type}
|
||||
disabled={disabled || loading}
|
||||
aria-busy={loading || undefined}
|
||||
className={button({ size, variant, color, highContrast, className })}
|
||||
className={button({ size, variant, color, highContrast, active, className })}
|
||||
{...rest}
|
||||
>
|
||||
{loading
|
||||
? <SpinnerGapIcon className="animate-spin" aria-hidden />
|
||||
: (
|
||||
<>
|
||||
{iconStart}
|
||||
{children}
|
||||
{iconEnd}
|
||||
</>
|
||||
)}
|
||||
{loading ? <SpinnerGapIcon className="animate-spin" aria-hidden /> : iconStart}
|
||||
{children}
|
||||
{iconEnd}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ export type ButtonSkeletonProps = Omit<ComponentProps<"span">, "children"> & Var
|
||||
export function ButtonSkeleton(props: ButtonSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return <span aria-hidden className={buttonSkeleton({ size, className })} {...rest} />;
|
||||
return <span className={buttonSkeleton({ size, className })} {...rest} aria-hidden />;
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ export type CalloutSkeletonProps = Omit<ComponentProps<"div">, "children"> & Var
|
||||
export function CalloutSkeleton(props: CalloutSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return <div aria-hidden className={calloutSkeleton({ size, className })} {...rest} />;
|
||||
return <div className={calloutSkeleton({ size, className })} {...rest} aria-hidden />;
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ export type CardSkeletonProps = Omit<ComponentProps<"div">, "children"> & Varian
|
||||
export function CardSkeleton(props: CardSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return <div aria-hidden className={cardSkeleton({ size, className })} {...rest} />;
|
||||
return <div className={cardSkeleton({ size, className })} {...rest} aria-hidden />;
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ export type IconButtonSkeletonProps = Omit<ComponentProps<"span">, "children"> &
|
||||
export function IconButtonSkeleton(props: IconButtonSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return <span aria-hidden className={iconButtonSkeleton({ size, className })} {...rest} />;
|
||||
return <span className={iconButtonSkeleton({ size, className })} {...rest} aria-hidden />;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function HeadingSkeleton(props: HeadingSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<span aria-hidden className={headingSkeleton({ size, className })} {...rest}>
|
||||
<span className={headingSkeleton({ size, className })} {...rest} aria-hidden>
|
||||
{"\u00A0"}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export function TextSkeleton(props: TextSkeletonProps) {
|
||||
const { size, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<span aria-hidden className={textSkeleton({ size, className })} {...rest}>
|
||||
<span className={textSkeleton({ size, className })} {...rest} aria-hidden>
|
||||
{"\u00A0"}
|
||||
</span>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user