Skip to content
16 changes: 12 additions & 4 deletions src/lib/button/ButtonIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ const ButtonIcon: React.FC<
Pick<ButtonProps, "Icon" | "icon" | "isDisabled" | "isLoading" | "variant">
> = ({ Icon, icon, isDisabled, isLoading, variant }) => {
const isSecondary = variant === "secondary";
return (
icon ??
(Icon && (
return icon ? (
isLoading ? (
<span
className={cn("button-svg", "mr-2 size-4", "invisible")}
aria-hidden
/>
) : (
icon
)
) : (
Icon && (
<Icon
className={cn(
"button-svg",
Expand All @@ -19,7 +27,7 @@ const ButtonIcon: React.FC<
isDisabled && ["fill-klerosUIComponentsStroke"],
)}
/>
))
)
);
};
export default ButtonIcon;
6 changes: 5 additions & 1 deletion src/lib/dropdown/cascader/dropdown-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface IDropdownContainer
"disabledKeys" | "defaultSelectedKey" | "items" | "callback"
> {
isOpen?: boolean;
className?: string;
}

const DropdownContainer: React.FC<IDropdownContainer> = ({
Expand All @@ -27,6 +28,7 @@ const DropdownContainer: React.FC<IDropdownContainer> = ({
disabledKeys,
defaultSelectedKey,
callback,
className,
}) => {
const gridRef = useRef<HTMLDivElement>(null);
const [selectedKey, setSelectedKey] = useState<Key | null>(
Expand Down Expand Up @@ -78,17 +80,19 @@ const DropdownContainer: React.FC<IDropdownContainer> = ({

return (
<Popover
className={clsx(
className={cn(
"bg-klerosUIComponentsWhiteBackground rounded-base border-klerosUIComponentsStroke box-border border",
"shadow-default focus-visible:outline-klerosUIComponentsPrimaryBlue overflow-hidden",
"w-60 origin-top transform transition lg:w-max lg:max-w-6xl",
isOpen
? "entering:animate-scale-in scale-y-100"
: "exiting:animate-scale-out scale-y-0",
className,
)}
>
<Dialog aria-label="dropdown-dialog">
<Tree
id="dropdown-tree"
ref={gridRef}
autoFocus="first"
items={items}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/dropdown/cascader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface IDropdownCascader extends AriaSelectProps {
/** Callback function passes the Item object as argument. */
callback: (item: IItem) => void;
label?: string;
dropdownClassName?: string;
}

/** A Dropdown Cascader provides users with a way to navigate nested hierarchical information,
Expand All @@ -29,6 +30,7 @@ function DropdownCascader({
disabledKeys,
selectedKey,
defaultSelectedKey,
dropdownClassName,
...props
}: Readonly<IDropdownCascader>) {
return (
Expand Down Expand Up @@ -56,6 +58,7 @@ function DropdownCascader({
defaultSelectedKey: selectedKey ?? defaultSelectedKey,
disabledKeys,
callback,
className: dropdownClassName,
}}
/>
</>
Expand Down
14 changes: 9 additions & 5 deletions src/lib/dropdown/select/dropdown-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@ import React from "react";
import { Collection, ListBox, Popover } from "react-aria-components";
import Scrollbar from "../../scrollbar";
import Item, { IItem } from "./item";
import { cn } from "../../../utils";

const DropdownContainer: React.FC<{ isOpen?: boolean; items: IItem[] }> = ({
isOpen,
items,
}) => {
const DropdownContainer: React.FC<{
isOpen?: boolean;
items: IItem[];
className?: string;
}> = ({ isOpen, items, className }) => {
return (
<Popover
className={clsx(
className={cn(
"bg-klerosUIComponentsWhiteBackground rounded-base border-klerosUIComponentsStroke border",
"shadow-default focus:outline-klerosUIComponentsPrimaryBlue box-border overflow-hidden outline-none",
"origin-top transform transition",
isOpen
? "entering:animate-scale-in scale-y-100"
: "exiting:animate-scale-out scale-y-0",
className,
)}
>
<Scrollbar className="max-h-87.5 w-59.5">
<ListBox
id="listbox"
className={clsx(
"bg-klerosUIComponentsWhiteBackground box-border w-59.5",
"cols-[repeat(auto-fill,_minmax(0,_45px))] grid grow py-4",
Expand Down
6 changes: 5 additions & 1 deletion src/lib/dropdown/select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface SelectProps extends AriaSelectProps {
/** When `simpleButton` is `true`, this scales down the dropdown button size. */
smallButton?: boolean;
label?: string;
dropdownClassName?: string;
}

/** A select displays a collapsible list of options and allows a user to select one of them. */
Expand All @@ -32,6 +33,7 @@ function DropdownSelect({
callback,
placeholder,
className,
dropdownClassName,
...props
}: Readonly<SelectProps>) {
const handleSelection = useCallback(
Expand Down Expand Up @@ -64,7 +66,9 @@ function DropdownSelect({
<DropdownButton {...{ placeholder }} />
)}
<FieldError className="text-klerosUIComponentsError text-sm" />
<DropdownContainer {...{ isOpen, items }} />
<DropdownContainer
{...{ isOpen, items, className: dropdownClassName }}
/>
</>
)}
</AriaSelect>
Expand Down
7 changes: 6 additions & 1 deletion src/lib/form/bignumber-field/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface BigNumberFieldComponentProps extends BigNumberFieldProps {
className?: string;
/** The name of the input element, used when submitting an HTML form.*/
name?: string;
inputRef?: React.Ref<HTMLInputElement>;
buttonRef?: React.Ref<HTMLButtonElement>;
}

/** A number field that handles big numbers.
Expand All @@ -31,6 +33,8 @@ function BigNumberField({
isDisabled,
isReadOnly,
name,
inputRef,
buttonRef,
...props
}: Readonly<BigNumberFieldComponentProps>) {
// Use our custom hook to get all the props and state
Expand All @@ -44,7 +48,6 @@ function BigNumberField({
errorMessageProps,
validationResult,
} = useBigNumberField({ isDisabled, placeholder, isReadOnly, ...props });

return (
<div className={cn("flex w-[278px] flex-col", className)}>
{label && (
Expand All @@ -64,6 +67,7 @@ function BigNumberField({
<>
<Input
{...inputProps}
ref={inputRef}
aria-errormessage={`BigNumberFieldError-${inputProps.id}`}
name={name}
className={cn(
Expand Down Expand Up @@ -98,6 +102,7 @@ function BigNumberField({
>
<Button
{...incrementButtonProps}
ref={buttonRef}
excludeFromTabOrder
className={clsx(
"rounded-base hover:bg-klerosUIComponentsStroke size-3.5 cursor-pointer border-none bg-transparent",
Expand Down
14 changes: 11 additions & 3 deletions src/lib/form/datepicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface DatePickerProps
/** Show time selection if true. */
time?: boolean;
label?: string;
popoverClassName?: string;
}

/** A date picker allow users to enter or select a date and time value. */
Expand All @@ -34,6 +35,7 @@ function DatePicker({
minValue,
defaultValue = now(getLocalTimeZone()),
shouldCloseOnSelect = false,
popoverClassName,
...props
}: Readonly<DatePickerProps>) {
return (
Expand All @@ -59,14 +61,20 @@ function DatePicker({

<Popover
className={clsx(
"bg-klerosUIComponentsWhiteBackground shadow-default rounded-base overflow-y-scroll",
"bg-klerosUIComponentsWhiteBackground shadow-default rounded-base overflow-scroll",
"border-klerosUIComponentsStroke ease-ease scrollbar border transition",
time ? "w-82.5 lg:w-112.5" : "w-82.5",
popoverClassName,
)}
>
<Dialog className="flex size-full flex-wrap">
<Calendar />
{time && <TimeControl {...{ minValue }} />}
<div
className={clsx("flex", time ? "w-82.5 lg:w-112.5" : "w-82.5")}
>
<Calendar />
{time && <TimeControl {...{ minValue }} />}
</div>

<div
className={clsx(
"flex h-16 w-full items-center justify-between px-4",
Expand Down
27 changes: 22 additions & 5 deletions src/lib/form/file-uploader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import UploadIcon from "../../assets/svgs/form/upload-icon.svg";
import SuccessIcon from "../../assets/svgs/status-icons/success.svg";
import ErrorIcon from "../../assets/svgs/status-icons/error.svg";
Expand All @@ -25,6 +25,10 @@ interface FileUploaderProps {
/** Whether the drop target is disabled. If true, the drop target will not accept any drops. */
isDisabled?: boolean;
className?: string;
/** Provide a custom validation function, returning false invalidates the file */
validationFunction?: (file?: File) => boolean;
/** Provide File for controlled behaviour */
selectedFile?: File;
}

/** Allows to upload a file by either dropping it on the dropzone or
Expand All @@ -37,8 +41,16 @@ function FileUploader({
acceptedFileTypes,
fileTriggerProps,
isDisabled = false,
validationFunction,
selectedFile,
}: Readonly<FileUploaderProps>) {
const [fileSelected, setFileSelected] = useState<File>();
const [fileSelected, setFileSelected] = useState<File | undefined>(
selectedFile,
);

useEffect(() => {
setFileSelected(selectedFile);
}, [selectedFile]);

return (
<div className={cn("box-border h-fit w-50", className)}>
Expand Down Expand Up @@ -71,7 +83,10 @@ function FileUploader({

if (item) {
const file = await item.getFile();
setFileSelected(file);
const validated = validationFunction?.(file) ?? true;
if (!validated) return;
if (selectedFile === undefined) setFileSelected(file);

callback(file);
}
}}
Expand All @@ -82,7 +97,9 @@ function FileUploader({
onSelect={(e) => {
if (e) {
const file = e[0];
setFileSelected(file);
const validated = validationFunction?.(file) ?? true;
if (!validated) return;
if (selectedFile === undefined) setFileSelected(file);
callback(file);
}
}}
Expand All @@ -109,7 +126,7 @@ function FileUploader({
</FileTrigger>
</DropZone>
{msg && (
<div className="mt-4 flex items-start">
<div className="mt-4 flex items-center">
{variant === "success" && (
<SuccessIcon className="fill-klerosUIComponentsSuccess mr-2 max-w-4 min-w-4" />
)}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/progress/timeline/custom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import Bullet, { StateProp, VariantProp } from "./bullet";
import { cn } from "../../../utils";

interface TimelineItem extends VariantProp, StateProp {
export interface TimelineItem extends VariantProp, StateProp {
title: string;
party: string | React.ReactElement;
subtitle: string;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/progress/timeline/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import Bullet, { SideProp, VariantProp } from "./bullet";
import clsx from "clsx";
import { cn } from "../../../utils";

interface TimelineItem extends SideProp, VariantProp {
export interface TimelineItem extends SideProp, VariantProp {
title: string;
party: string;
subtitle: string;
}

interface TimelineProps {
export interface TimelineProps {
items: [TimelineItem, ...TimelineItem[]];
className?: string;
}
Expand Down
7 changes: 6 additions & 1 deletion src/lib/tooltip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ function Tooltip({
return (
<TooltipTrigger {...{ delay }} {...props}>
<Focusable>
<div {...props.wrapperProps}>{children}</div>
<div
{...props.wrapperProps}
role={props?.wrapperProps?.role ?? "button"}
>
{children}
</div>
</Focusable>
<AriaTooltip
offset={props.tooltipProps?.offset ?? 8}
Expand Down
42 changes: 42 additions & 0 deletions src/stories/dropdown-select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IPreviewArgs } from "./utils";
import SelectComponent from "../lib/dropdown/select";
import { Form } from "react-aria-components";
import { Button } from "../lib";
import Telegram from "../assets/svgs/telegram.svg";

const meta = {
component: SelectComponent,
Expand Down Expand Up @@ -109,3 +110,44 @@ export const RequiredSelect: Story = {
</Form>
),
};

export const CustomItemIcon: Story = {
args: {
themeUI: "dark",
backgroundUI: "light",
items: [
{
text: "hello 1",
itemValue: 1,
id: 1,
icon: <Telegram className="mr-2 size-4 fill-white" />,
},
{
text: "hello 2",
itemValue: 2,
id: 2,
icon: <Telegram className="mr-2 size-4 fill-white" />,
},
{
text: "hello 3",
itemValue: 3,
id: 3,
icon: <Telegram className="mr-2 size-4 fill-white" />,
},
{
text: "hello 4",
itemValue: 4,
id: 4,
icon: <Telegram className="mr-2 size-4 fill-white" />,
},
{
text: "hello 5",
itemValue: 5,
id: 5,
icon: <Telegram className="mr-2 size-4 fill-white" />,
},
],
placeholder: "Select a value",
callback: () => {},
},
};
Loading