refactor: harden tests

This commit is contained in:
zhom
2026-07-21 23:13:17 +04:00
parent f7daf68b52
commit 9624ec846d
81 changed files with 11771 additions and 983 deletions
-51
View File
@@ -1,54 +1,3 @@
/**
* Unified Toast System
*
* This module provides a comprehensive toast system that solves styling issues
* and provides a single, flexible toast component for all use cases.
*
* Features:
* - Proper background styling (no transparency issues)
* - Loading states with spinners
* - Progress bars for downloads/updates
* - Success/error states
* - Customizable icons and content
* - Auto-update notifications
*
* Usage Examples:
*
* Simple loading toast:
* ```
* import { showToast } from "./custom-toast";
* showToast({
* type: "loading",
* title: "Loading...",
* description: "Please wait..."
* });
* ```
*
* Auto-update toast:
* ```
* showAutoUpdateToast("Wayfern", "149.0.7827.116");
* ```
*
* Download progress toast:
* ```
* showToast({
* type: "download",
* title: "Downloading Wayfern 149.0.7827.116",
* progress: { percentage: 45, speed: "2.5", eta: "30s" }
* });
* ```
*
* Version update progress:
* ```
* showToast({
* type: "version-update",
* title: "Updating browser versions",
* progress: { current: 3, total: 5, found: 12 }
* });
* ```
*/
/** biome-ignore-all lint/suspicious/noExplicitAny: TODO */
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { useTranslation } from "react-i18next";
import {
+13 -15
View File
@@ -1,5 +1,3 @@
/** biome-ignore-all lint/a11y/noStaticElementInteractions: temporary suppress until in active use */
/** biome-ignore-all lint/a11y/useKeyWithClickEvents: temporary suppress until in active use */
"use client";
import { Command as CommandPrimitive, useCommandState } from "cmdk";
@@ -14,9 +12,9 @@ export interface Option {
value: string;
label?: string;
disable?: boolean;
/** fixed option that can't be removed. */
/** An option that cannot be removed. */
fixed?: boolean;
/** Group the options by providing key. */
/** Group options by this property. */
[key: string]: string | boolean | undefined;
}
type GroupOption = Record<string, Option[]>;
@@ -24,21 +22,20 @@ type GroupOption = Record<string, Option[]>;
interface MultipleSelectorProps {
value?: Option[];
defaultOptions?: Option[];
/** manually controlled options */
/** Manually controlled options. */
options?: Option[];
placeholder?: string;
/** Loading component. */
loadingIndicator?: React.ReactNode;
/** Empty component. */
emptyIndicator?: React.ReactNode;
/** Debounce time for async search. Only work with `onSearch`. */
/** Debounce time for async search. Used only with `onSearch`. */
delay?: number;
/**
* Only work with `onSearch` prop. Trigger search when `onFocus`.
* For example, when user click on the input, it will trigger the search to get initial options.
* Trigger `onSearch` when the input receives focus.
**/
triggerSearchOnFocus?: boolean;
/** async search */
/** Asynchronous search. */
onSearch?: (value: string) => Promise<Option[]>;
onChange?: (options: Option[]) => void;
/** Limit the maximum number of selected options. */
@@ -48,13 +45,12 @@ interface MultipleSelectorProps {
/** Hide the placeholder when there are options selected. */
hidePlaceholderWhenSelected?: boolean;
disabled?: boolean;
/** Group the options base on provided key. */
/** Group options by the provided key. */
groupBy?: string;
className?: string;
badgeClassName?: string;
/**
* First item selected is a default behavior by cmdk. That is why the default is true.
* This is a workaround solution by add a dummy item.
* Prevent cmdk from selecting the first item by inserting a dummy item.
*
* @reference: https://github.com/pacocoursey/cmdk/issues/171
*/
@@ -375,7 +371,7 @@ const MultipleSelector = React.forwardRef<
return Object.values(selectables).some((group) => group.length > 0);
}, [selectables]);
/** Avoid Creatable Selector freezing or lagging when paste a long string. */
// Skip fuzzy matching for creatable inputs to keep long pasted values responsive.
const commandFilter = React.useCallback(() => {
if (commandProps?.filter) {
return commandProps.filter;
@@ -401,13 +397,15 @@ const MultipleSelector = React.forwardRef<
"relative h-auto overflow-visible bg-transparent",
commandProps?.className,
)}
// Consumers may override filtering even when search is asynchronous.
shouldFilter={
commandProps?.shouldFilter !== undefined
? commandProps.shouldFilter
: !onSearch
} // When onSearch is provided, we don't want to filter the options. You can still override it.
}
filter={commandFilter()}
>
{/* biome-ignore lint/a11y/noStaticElementInteractions: pointer focus is forwarded to the nested input; keyboard users focus nested controls directly */}
<div
className={cn(
"min-h-10 rounded-md border border-input text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
@@ -417,7 +415,7 @@ const MultipleSelector = React.forwardRef<
},
className,
)}
onClick={() => {
onMouseDown={() => {
if (disabled) return;
inputRef.current?.focus();
}}
+24 -27
View File
@@ -1,6 +1,6 @@
"use client";
import { AnimatePresence, type HTMLMotionProps, motion } from "motion/react";
import { type HTMLMotionProps, motion } from "motion/react";
import { Dialog as DialogPrimitive } from "radix-ui";
import type * as React from "react";
import { useTranslation } from "react-i18next";
@@ -72,20 +72,24 @@ type DialogPortalProps = Omit<
"forceMount"
>;
function DialogPortal(props: DialogPortalProps) {
function DialogPortal({
children,
container: portalContainer,
...props
}: DialogPortalProps) {
const { isOpen, container } = useDialog();
if (!isOpen) return null;
return (
<AnimatePresence>
{isOpen && (
<DialogPrimitive.Portal
data-slot="dialog-portal"
forceMount
container={container ?? props.container}
{...props}
/>
)}
</AnimatePresence>
<DialogPrimitive.Portal
data-slot="dialog-portal"
forceMount
container={container ?? portalContainer}
{...props}
>
{children}
</DialogPrimitive.Portal>
);
}
@@ -106,7 +110,6 @@ function DialogOverlay({
key="dialog-overlay"
initial={{ opacity: 0, filter: "blur(4px)" }}
animate={{ opacity: 1, filter: "blur(0px)" }}
exit={{ opacity: 0, filter: "blur(4px)" }}
transition={transition}
className={cn("fixed inset-0 z-9999 bg-background/50", className)}
{...props}
@@ -217,8 +220,9 @@ function DialogContent({
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay />
<DialogOverlay key="dialog-overlay" />
<DialogPrimitive.Content
key="dialog-content"
asChild
forceMount
onOpenAutoFocus={onOpenAutoFocus}
@@ -243,22 +247,15 @@ function DialogContent({
<motion.div
key="dialog-content"
data-slot="dialog-content"
// Open/close motion modeled on transitions.dev's modal: a subtle
// scale from 0.96 → 1 with opacity, eased with cubic-bezier(0.22, 1,
// 0.36, 1). Open is 250ms; close is a quicker 150ms. The centering
// translate stays in `style` so `scale` animates around the center
// without fighting the transform-based positioning.
// Open motion modeled on transitions.dev's modal: a subtle scale
// from 0.96 → 1 with opacity, eased with cubic-bezier(0.22, 1, 0.36,
// 1). The portal unmounts immediately on close so a closed Radix
// surface cannot linger over the app. The centering translate stays
// in `style` so `scale` animates around the center without fighting
// the transform-based positioning.
style={{ transformOrigin: "center" }}
initial={{ opacity: 0, scale: 0.96 }}
animate={{ opacity: 1, scale: 1 }}
exit={{
opacity: 0,
scale: 0.96,
transition: transition ?? {
duration: 0.15,
ease: [0.22, 1, 0.36, 1],
},
}}
transition={
transition ?? { duration: 0.25, ease: [0.22, 1, 0.36, 1] }
}
+1 -1
View File
@@ -32,7 +32,7 @@ function PopoverContent({
sideOffset={sideOffset}
collisionPadding={collisionPadding}
className={cn(
"z-50000 max-h-(--radix-popover-content-available-height) origin-(--radix-popover-content-transform-origin) overflow-y-auto rounded-md surface-material-popover border p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
"z-50000 max-h-(--radix-popover-content-available-height) origin-(--radix-popover-content-transform-origin) overflow-y-auto rounded-md surface-material-popover border p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:pointer-events-none data-[state=closed]:animate-none data-[state=closed]:opacity-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
className,
)}
{...props}