mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-25 21:36:52 +08:00
fix(ds): add test id support (#9904)
This commit is contained in:
parent
b8b4f58a79
commit
59b8009d7a
@ -5,13 +5,13 @@ import {
|
||||
type AccordionItemPropsPublic,
|
||||
} from "./components/AccordionItem";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
|
||||
export type AccordionProps = HTMLProps<"div"> & {
|
||||
expandedKeys: string[];
|
||||
type?: "multi" | "single";
|
||||
setExpandedKeys(keys: string[]): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
type AccordionType = React.FC<PropsWithChildren<AccordionProps>> & {
|
||||
Item: React.FC<PropsWithChildren<AccordionItemPropsPublic>>;
|
||||
@ -23,6 +23,7 @@ const Accordion: AccordionType = ({
|
||||
setExpandedKeys,
|
||||
children,
|
||||
type = "multi",
|
||||
testId,
|
||||
...props
|
||||
}) => {
|
||||
const onChange = useCallback(
|
||||
@ -54,6 +55,7 @@ const Accordion: AccordionType = ({
|
||||
return (
|
||||
<div
|
||||
className={cn("flex flex-col gap-y-2.5 items-start", className)}
|
||||
data-testid={testId}
|
||||
{...props}
|
||||
>
|
||||
{items}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import { Icon, type IconProps } from "../../icon/Icon";
|
||||
import { Typography } from "../../typography/Typography";
|
||||
@ -10,7 +10,7 @@ export type AccordionHeaderProps = Omit<
|
||||
> & {
|
||||
icon: IconProps["icon"];
|
||||
expanded: boolean;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const AccordionHeader = ({
|
||||
className,
|
||||
@ -43,7 +43,8 @@ export const AccordionHeader = ({
|
||||
// hover modifier
|
||||
"data-[expanded=true]:hover:bg-light-neutral-900",
|
||||
// focus modifier
|
||||
"data-[expanded=false]:focus:bg-light-neutral-900"
|
||||
"data-[expanded=false]:focus:bg-light-neutral-900",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Icon icon={icon} className={cn(iconCss, "w-6 h-6")} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import { type IconProps } from "../../icon/Icon";
|
||||
import { AccordionHeader } from "./AccordionHeader";
|
||||
@ -11,10 +11,10 @@ export type AccordionItemProps = HTMLProps<"div"> & {
|
||||
value: string;
|
||||
label: React.ReactNode;
|
||||
onExpandedChange(value: boolean): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
export type AccordionItemPropsPublic = Omit<
|
||||
AccordionItemProps,
|
||||
"expanded" | "onExpandedChange"
|
||||
"expanded" | "onExpandedChange" | "className" | "style" | "testId"
|
||||
>;
|
||||
|
||||
export const AccordionItem = ({
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
|
||||
export type AccordionPanelProps = Omit<HTMLProps<"div">, "aria-expanded"> & {
|
||||
expanded: boolean;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const AccordionPanel = ({
|
||||
className,
|
||||
|
||||
@ -4,7 +4,11 @@ import {
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import type { ComponentVariant, HTMLProps } from "../../shared/types";
|
||||
import type {
|
||||
BaseProps,
|
||||
ComponentVariant,
|
||||
HTMLProps,
|
||||
} from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { buttonStyles, useAndApplyBoldTextWidth } from "./utils";
|
||||
import { cloneIcon } from "../../shared/utils/clone-icon";
|
||||
@ -15,7 +19,7 @@ export type ButtonProps = Omit<HTMLProps<"button">, "aria-disabled"> & {
|
||||
variant?: ComponentVariant;
|
||||
start?: ReactElement<HTMLProps<"svg">>;
|
||||
end?: ReactElement<HTMLProps<"svg">>;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Button = ({
|
||||
size = "small",
|
||||
@ -24,6 +28,7 @@ export const Button = ({
|
||||
children,
|
||||
start,
|
||||
end,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<ButtonProps>) => {
|
||||
const buttonClassNames = buttonStyles[variant];
|
||||
@ -35,6 +40,7 @@ export const Button = ({
|
||||
<button
|
||||
{...props}
|
||||
aria-disabled={props.disabled ? "true" : "false"}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
size === "small" ? "px-2 py-3 min-w-32" : "px-3 py-4 min-w-64",
|
||||
"flex flex-row items-center gap-x-8",
|
||||
|
||||
@ -34,6 +34,7 @@ export const Checkbox = ({
|
||||
disabled && "cursor-not-allowed",
|
||||
className
|
||||
)}
|
||||
data-testid={testId}
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
@ -42,7 +43,6 @@ export const Checkbox = ({
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
className="sr-only peer"
|
||||
data-testid={testId}
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { chipStyles, type ChipColor, type ChipVariant } from "./utils";
|
||||
@ -7,18 +7,20 @@ import { chipStyles, type ChipColor, type ChipVariant } from "./utils";
|
||||
export type ChipProps = Omit<HTMLProps<"div">, "label"> & {
|
||||
color?: ChipColor;
|
||||
variant?: ChipVariant;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Chip = ({
|
||||
className,
|
||||
color = "gray",
|
||||
variant = "pill",
|
||||
children,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<ChipProps>) => {
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"flex flex-row items-center px-1.5 py-1",
|
||||
variant === "pill" ? "rounded-full" : "rounded-lg",
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
import {
|
||||
useEffect,
|
||||
useId,
|
||||
useRef,
|
||||
useState,
|
||||
type PropsWithChildren,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import { useId, type PropsWithChildren } from "react";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Icon } from "../icon/Icon";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
FloatingOverlay,
|
||||
FloatingPortal,
|
||||
@ -24,13 +17,14 @@ import { FocusTrap } from "focus-trap-react";
|
||||
export type DialogProps = HTMLProps<"div"> & {
|
||||
open: boolean;
|
||||
onOpenChange(value: boolean): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Dialog = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
className,
|
||||
children,
|
||||
testId,
|
||||
}: PropsWithChildren<DialogProps>) => {
|
||||
const id = useId();
|
||||
|
||||
@ -80,6 +74,7 @@ export const Dialog = ({
|
||||
aria-describedby={`${id}-description`}
|
||||
{...getFloatingProps()}
|
||||
style={styles}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"rounded-4xl border-1 border-light-neutral-500 outline-none",
|
||||
"transition-all will-change-transform",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
export type DividerProps = Omit<
|
||||
@ -6,11 +6,12 @@ export type DividerProps = Omit<
|
||||
"role" | "aria-orientation"
|
||||
> & {
|
||||
type?: "horizontal" | "vertical";
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Divider = ({
|
||||
type = "horizontal",
|
||||
className,
|
||||
testId,
|
||||
...props
|
||||
}: DividerProps) => {
|
||||
return (
|
||||
@ -23,6 +24,7 @@ export const Divider = ({
|
||||
)}
|
||||
role="separator"
|
||||
aria-orientation={type}
|
||||
data-testid={testId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
type ReactElement,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { cloneIcon } from "../../shared/utils/clone-icon";
|
||||
@ -19,7 +19,7 @@ export type InputProps = Omit<
|
||||
end?: ReactElement<HTMLProps<"svg">>;
|
||||
error?: string;
|
||||
hint?: string;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Input = ({
|
||||
className,
|
||||
@ -34,6 +34,7 @@ export const Input = ({
|
||||
type,
|
||||
hint,
|
||||
readOnly,
|
||||
testId,
|
||||
...props
|
||||
}: InputProps) => {
|
||||
const generatedId = useId();
|
||||
@ -45,65 +46,64 @@ export const Input = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor={id}
|
||||
<label
|
||||
htmlFor={id}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"flex flex-col gap-y-2",
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
|
||||
)}
|
||||
>
|
||||
<Typography.Text fontSize="s" className="text-light-neutral-200">
|
||||
{label}
|
||||
</Typography.Text>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col gap-y-2",
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
|
||||
"flex flex-row items-center gap-x-2.5",
|
||||
"py-4.25 px-4",
|
||||
"border-light-neutral-500 border-1 rounded-2xl",
|
||||
// base
|
||||
"bg-light-neutral-950",
|
||||
// hover modifier
|
||||
"hover:bg-light-neutral-900",
|
||||
// focus modifier
|
||||
"focus-within:bg-light-neutral-900",
|
||||
// error state
|
||||
error && " border-red-400 bg-light-neutral-970",
|
||||
readOnly &&
|
||||
"bg-light-neutral-985 border-none hover:bg-light-neutral-985 cursor-auto",
|
||||
// disabled modifier
|
||||
disabled && "hover:bg-light-neutral-950"
|
||||
)}
|
||||
>
|
||||
<Typography.Text fontSize="s" className="text-light-neutral-200">
|
||||
{label}
|
||||
</Typography.Text>
|
||||
<div
|
||||
{cloneIcon(start, {
|
||||
className: iconCss,
|
||||
})}
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
aria-invalid={error ? "true" : "false"}
|
||||
readOnly={readOnly}
|
||||
className={cn(
|
||||
"flex flex-row items-center gap-x-2.5",
|
||||
"py-4.25 px-4",
|
||||
"border-light-neutral-500 border-1 rounded-2xl",
|
||||
// base
|
||||
"bg-light-neutral-950",
|
||||
// hover modifier
|
||||
"hover:bg-light-neutral-900",
|
||||
// focus modifier
|
||||
"focus-within:bg-light-neutral-900",
|
||||
// error state
|
||||
error && " border-red-400 bg-light-neutral-970",
|
||||
readOnly &&
|
||||
"bg-light-neutral-985 border-none hover:bg-light-neutral-985 cursor-auto",
|
||||
// disabled modifier
|
||||
disabled && "hover:bg-light-neutral-950"
|
||||
"flex-1 outline-none caret-primary-500 text-white",
|
||||
"placeholder:text-light-neutral-300",
|
||||
error && "text-red-400"
|
||||
)}
|
||||
>
|
||||
{cloneIcon(start, {
|
||||
className: iconCss,
|
||||
})}
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
aria-invalid={error ? "true" : "false"}
|
||||
readOnly={readOnly}
|
||||
className={cn(
|
||||
"flex-1 outline-none caret-primary-500 text-white",
|
||||
"placeholder:text-light-neutral-300",
|
||||
error && "text-red-400"
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{cloneIcon(end, {
|
||||
className: iconCss,
|
||||
})}
|
||||
</div>
|
||||
<Typography.Text
|
||||
fontSize="xs"
|
||||
className={cn("text-light-neutral-600 ml-4", error && "text-red-400")}
|
||||
>
|
||||
{error ?? hint}
|
||||
</Typography.Text>
|
||||
</label>
|
||||
</div>
|
||||
{...props}
|
||||
/>
|
||||
{cloneIcon(end, {
|
||||
className: iconCss,
|
||||
})}
|
||||
</div>
|
||||
<Typography.Text
|
||||
fontSize="xs"
|
||||
className={cn("text-light-neutral-600 ml-4", error && "text-red-400")}
|
||||
>
|
||||
{error ?? hint}
|
||||
</Typography.Text>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type PropsWithChildren, type ReactElement } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { cloneIcon } from "../../shared/utils/clone-icon";
|
||||
import "./index.css";
|
||||
@ -16,7 +16,7 @@ export type InteractiveChipProps = Omit<
|
||||
chipType?: InteractiveChipType;
|
||||
start?: ReactElement<HTMLProps<"svg">>;
|
||||
end?: ReactElement<HTMLProps<"svg">>;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const InteractiveChip = ({
|
||||
chipType = "elevated",
|
||||
@ -24,6 +24,7 @@ export const InteractiveChip = ({
|
||||
children,
|
||||
start,
|
||||
end,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<InteractiveChipProps>) => {
|
||||
const buttonClassNames = buttonStyles[chipType];
|
||||
@ -34,6 +35,7 @@ export const InteractiveChip = ({
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
data-testid={testId}
|
||||
aria-disabled={props.disabled ? "true" : "false"}
|
||||
className={cn(
|
||||
"px-1.5 py-1 min-w-32",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useId } from "react";
|
||||
import type { HTMLProps, IOption } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps, IOption } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { RadioOption } from "./RadioOption";
|
||||
|
||||
@ -11,7 +11,7 @@ export type RadioGroupProps<T extends string> = Omit<
|
||||
value: T;
|
||||
onChange: (option: IOption<T>) => void;
|
||||
labelClassName?: string;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const RadioGroup = <T extends string>({
|
||||
value,
|
||||
@ -21,13 +21,17 @@ export const RadioGroup = <T extends string>({
|
||||
labelClassName,
|
||||
disabled,
|
||||
id: propId,
|
||||
testId,
|
||||
...props
|
||||
}: RadioGroupProps<T>) => {
|
||||
const generatedId = useId();
|
||||
const id = propId ?? generatedId;
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-y-1", className)}>
|
||||
<div
|
||||
data-testid={testId}
|
||||
className={cn("flex flex-col gap-y-1", className)}
|
||||
>
|
||||
{options.map((o) => (
|
||||
<RadioOption
|
||||
{...props}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useId } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
@ -7,7 +7,7 @@ type RadioOptionProps = Omit<HTMLProps<"input">, "id" | "checked"> & {
|
||||
label: React.ReactNode;
|
||||
labelClassName?: string;
|
||||
id: string;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const RadioOption = ({
|
||||
className,
|
||||
@ -17,6 +17,7 @@ export const RadioOption = ({
|
||||
id: propId,
|
||||
disabled,
|
||||
onChange,
|
||||
testId,
|
||||
...props
|
||||
}: RadioOptionProps) => {
|
||||
const generatedId = useId();
|
||||
@ -25,6 +26,7 @@ export const RadioOption = ({
|
||||
return (
|
||||
<label
|
||||
htmlFor={id}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"flex items-center gap-x-4",
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
export type ScrollableMode = "auto" | "scroll";
|
||||
@ -8,7 +8,7 @@ export type ScrollableType = "horizontal" | "vertical";
|
||||
export type ScrollableProps = HTMLProps<"div"> & {
|
||||
mode?: ScrollableMode;
|
||||
type?: ScrollableType;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
const scrollableStyles: Record<
|
||||
ScrollableType,
|
||||
@ -30,11 +30,13 @@ export const Scrollable = ({
|
||||
tabIndex,
|
||||
mode = "auto",
|
||||
type = "vertical",
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<ScrollableProps>) => {
|
||||
const style = scrollableStyles[type][mode];
|
||||
return (
|
||||
<div
|
||||
data-testid={testId}
|
||||
tabIndex={tabIndex ?? 0}
|
||||
{...props}
|
||||
className={cn(
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useId, useMemo, useState } from "react";
|
||||
import type { HTMLProps, IOption } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps, IOption } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import ReactSelect, { createFilter } from "react-select";
|
||||
import { Typography } from "../typography/Typography";
|
||||
@ -16,7 +16,7 @@ export type SelectProps<T> = Omit<HTMLProps<"input">, "value" | "onChange"> & {
|
||||
options: IOption<T>[];
|
||||
noOptionsText?: string;
|
||||
onChange(value: IOption<T> | null): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Select = <T extends string>(props: SelectProps<T>) => {
|
||||
const {
|
||||
@ -32,6 +32,8 @@ export const Select = <T extends string>(props: SelectProps<T>) => {
|
||||
onChange,
|
||||
readOnly,
|
||||
noOptionsText,
|
||||
className,
|
||||
testId,
|
||||
} = props;
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const generatedId = useId();
|
||||
@ -50,10 +52,12 @@ export const Select = <T extends string>(props: SelectProps<T>) => {
|
||||
|
||||
return (
|
||||
<label
|
||||
data-testid={testId}
|
||||
htmlFor={id}
|
||||
className={cn(
|
||||
"flex flex-col gap-y-2",
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Typography.Text fontSize="s" className="text-light-neutral-200">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMemo } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import "./index.css";
|
||||
|
||||
@ -15,7 +15,11 @@ export type IndeterminateSpinnerProps = BaseSpinnerProps & {
|
||||
value?: never;
|
||||
};
|
||||
|
||||
export type SpinnerProps = DeterminateSpinnerProps | IndeterminateSpinnerProps;
|
||||
export type SpinnerProps = (
|
||||
| DeterminateSpinnerProps
|
||||
| IndeterminateSpinnerProps
|
||||
) &
|
||||
BaseProps;
|
||||
|
||||
const SIZE = 48;
|
||||
const STROKE_WIDTH = 6;
|
||||
@ -26,6 +30,7 @@ export const Spinner = ({
|
||||
value = 10,
|
||||
determinate = false,
|
||||
className,
|
||||
testId,
|
||||
...props
|
||||
}: SpinnerProps) => {
|
||||
const offset = useMemo(
|
||||
@ -34,7 +39,13 @@ export const Spinner = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<svg width={SIZE} height={SIZE} className={className} {...props}>
|
||||
<svg
|
||||
data-testid={testId}
|
||||
width={SIZE}
|
||||
height={SIZE}
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
<circle
|
||||
cx={SIZE / 2}
|
||||
cy={SIZE / 2}
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import React from "react";
|
||||
import {
|
||||
@ -16,13 +16,13 @@ import { useElementOverflow } from "./hooks/use-element-overflow";
|
||||
import { useElementScroll } from "./hooks/use-element-scroll";
|
||||
import { TabScroller } from "./components/TabScroller";
|
||||
|
||||
export type TabsProps = HTMLProps<"div">;
|
||||
export type TabsProps = HTMLProps<"div"> & BaseProps;
|
||||
|
||||
type TabsType = React.FC<PropsWithChildren<TabsProps>> & {
|
||||
Item: React.FC<PropsWithChildren<TabItemPropsPublic>>;
|
||||
};
|
||||
|
||||
const Tabs: TabsType = ({ children, ...props }) => {
|
||||
const Tabs: TabsType = ({ children, className, testId, ...props }) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const tabListRef = useRef<HTMLDivElement>(null);
|
||||
@ -55,7 +55,7 @@ const Tabs: TabsType = ({ children, ...props }) => {
|
||||
}) ?? [];
|
||||
|
||||
return (
|
||||
<div className={cn("w-full")}>
|
||||
<div data-testid={testId} className={cn("w-full", className)}>
|
||||
<div className={cn("flex flex-row items-stretch")} ref={containerRef}>
|
||||
{canScrollLeft && isOverflowing && (
|
||||
<TabScroller onScroll={scrollLeft} position="left" />
|
||||
|
||||
@ -5,6 +5,7 @@ import { Typography } from "../typography/Typography";
|
||||
import { toastStyles } from "./utils";
|
||||
import type { JSX } from "react";
|
||||
import { invariant } from "../../shared/utils/invariant";
|
||||
import type { BaseProps } from "../../shared/types";
|
||||
|
||||
type RenderContentProps = {
|
||||
onDismiss: () => void;
|
||||
|
||||
@ -17,6 +17,7 @@ import {
|
||||
} from "@floating-ui/react";
|
||||
import { useRef, useState, type PropsWithChildren } from "react";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import type { BaseProps } from "../../shared/types";
|
||||
|
||||
type ControlledTooltipProps = {
|
||||
open: boolean;
|
||||
@ -33,10 +34,9 @@ type TooltipTriggerType = "click" | "hover";
|
||||
type BaseTooltipProps = {
|
||||
text: string;
|
||||
withArrow?: boolean;
|
||||
className?: string;
|
||||
placement?: UseFloatingOptions["placement"];
|
||||
trigger?: TooltipTriggerType;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export type TooltipProps = BaseTooltipProps &
|
||||
(ControlledTooltipProps | UncontrolledTooltipProps);
|
||||
@ -50,6 +50,7 @@ export const Tooltip = ({
|
||||
open,
|
||||
setOpen: setOpenProp,
|
||||
trigger = "hover",
|
||||
testId,
|
||||
}: PropsWithChildren<TooltipProps>) => {
|
||||
const [localOpen, setLocalOpen] = useState(false);
|
||||
const arrowRef = useRef(null);
|
||||
@ -95,6 +96,7 @@ export const Tooltip = ({
|
||||
ref={refs.setReference}
|
||||
{...getReferenceProps()}
|
||||
className={className}
|
||||
data-testid={testId}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
type FontWeight,
|
||||
} from "./utils";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import type { BaseProps } from "../../shared/types";
|
||||
|
||||
type SupportedReactNodes = "h6" | "h5" | "h4" | "h3" | "h2" | "h1" | "span";
|
||||
|
||||
@ -13,7 +14,7 @@ export type BaseTypographyProps = React.HTMLAttributes<HTMLElement> & {
|
||||
fontSize?: FontSize;
|
||||
fontWeight?: FontWeight;
|
||||
as: SupportedReactNodes;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const BaseTypography = ({
|
||||
fontSize,
|
||||
@ -21,6 +22,7 @@ export const BaseTypography = ({
|
||||
className,
|
||||
children,
|
||||
as,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<BaseTypographyProps>) => {
|
||||
const Component = as;
|
||||
@ -28,6 +30,7 @@ export const BaseTypography = ({
|
||||
return (
|
||||
<Component
|
||||
{...props}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"tg-family-outfit text-white leading-[100%]",
|
||||
fontSize ? fontSizes[fontSize] : undefined,
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
"email": "stephan@all-hands.dev"
|
||||
}
|
||||
],
|
||||
"version": "1.0.0-beta.7",
|
||||
"version": "1.0.0-beta.8",
|
||||
"description": "OpenHands UI Components",
|
||||
"keywords": [
|
||||
"openhands",
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
export type BaseProps = {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
testId?: string;
|
||||
};
|
||||
|
||||
export type HTMLProps<T extends React.ElementType> = Omit<
|
||||
React.ComponentPropsWithoutRef<T>,
|
||||
"children"
|
||||
"children" | "style" | "className"
|
||||
>;
|
||||
|
||||
export type ComponentVariant = "primary" | "secondary" | "tertiary";
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { cloneElement, isValidElement, type ReactElement } from "react";
|
||||
import type { ComponentVariant, HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
|
||||
export const cloneIcon = (
|
||||
icon?: ReactElement<HTMLProps<"svg">>,
|
||||
props?: HTMLProps<"svg">
|
||||
icon?: ReactElement<HTMLProps<"svg"> & BaseProps>,
|
||||
props?: HTMLProps<"svg"> & BaseProps
|
||||
) => {
|
||||
if (!icon) {
|
||||
return null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user