Skip to content

Commit

Permalink
Created a multiple select field.
Browse files Browse the repository at this point in the history
  • Loading branch information
ser888gio committed Nov 20, 2024
1 parent 4261773 commit 86d1ccf
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 75 deletions.
35 changes: 35 additions & 0 deletions apps/design-system/stories/select-input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Meta, StoryObj } from "@storybook/react";
import { SingleSelectField } from "../../../packages/design-system/src/ui/input/combobobx/singleSelect/singleSelectField";
import { SearchIcon } from "@repo/design-system/demo";
import { HomeIcon } from "@repo/design-system/demo";

const meta: Meta<typeof SingleSelectField> = {
title: "Components/SingleSelectField",
component: SingleSelectField,
argTypes: {
label: { control: "text" },
error: { control: "text" },
showClearButton: { control: "boolean" },
icon: {
control: { type: "select" },
options: {
NoIcon: undefined,
SearchIcon: SearchIcon,
HomeIcon: HomeIcon,
},
mapping: {
SearchIcon: SearchIcon,
HomeIcon: HomeIcon,
},
},
},
tags: ["autodocs"],
};

type SingleSelectFieldStory = StoryObj<typeof meta>;

export const Default: SingleSelectFieldStory = {
args: {},
};

export default meta;
2 changes: 1 addition & 1 deletion packages/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"scripts": {
"build": "npm run fonts && npm run tailwind",
"dev": "npm run fonts && npm run tailwind:watch",
"fonts": "mkdir -p ./dist && cp -r ./src/fonts ./dist/fonts",
"fonts": "if not exist .\\dist mkdir .\\dist && xcopy .\\src\\fonts .\\dist\\fonts /E /I",
"_comment": "Changed the code from line 14, which is a script for fonts from {mkdir -p ./dist && cp -r ./src/fonts ./dist/fonts} to use on Windows {if not exist .\\dist mkdir .\\dist && xcopy .\\src\\fonts .\\dist\\fonts /E /I}",
"format": "prettier . --check",
"format:fix": "prettier . --write",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,74 +1,74 @@
import React, { forwardRef } from "react";
import { Description } from "../../description";
import { Field } from "../../field";
import { Combobox, Input } from "./combobox";
import { Label } from "../../label";
import { twMerge } from "tailwind-merge";

/*
We choose what Input field properties we allow to be passed to the Input component
This is needed to allow to manage the Input field using things like ...register("field") react-form-hook
We omit className to disable extra styling from the outside
*/
type InheritedInputProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"className" | "style"
>;

type Props = {
label?: string;
error?: string;
showClearButton?: boolean;
icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;
} & InheritedInputProps;

// Test options
const options = [
{ id: 1, value: "option1", label: "Option 1" },
{ id: 2, value: "option2", label: "Option 2" },
{ id: 3, value: "option3", label: "Option 3" },
{ id: 4, value: "option4", label: "Option 4" },
];

const MultipleSelectField = forwardRef<React.ElementRef<typeof Input>, Props>(
({ label, error, showClearButton, icon }: Props, ref) => {
// if error is present, we pass it to all the sub-components
const hasError = !!error;

console.info("InputField", { hasError, label, error, showClearButton });

// We show label instead of placeholder if label is provided
const showPlaceholder = !label;

// We need to pass hasIcon to some sub-components
const Icon = icon;
const hasIcon = !!Icon;

return (
<Field state={hasError ? "error" : "default"}>
{hasIcon && <Icon className={twMerge("k1-w-6 k1-h-6 k1-min-w-6")} />}
<Combobox
className="k1-relative k1-w-full k1-bg-transparent k1-outline-none k1-flex"
ref={ref}
showClearButton={showClearButton}
options={options}
defaultValue={[]}
onChange={(value) => console.log("Selected value:", value)}
onInputChange={(value) => console.log("Input value:", value)}
onClear={() => console.log("Cleared")}
showPlaceholder={showPlaceholder}
>
{label && (
<Label state={hasError ? "error" : "default"} hasIcon={hasIcon}>
{label}
</Label>
)}
</Combobox>
{hasError && <Description state="error">{error}</Description>}
</Field>
);
}
);

export { MultipleSelectField };
import React, { forwardRef } from "react";
import { Description } from "../../description";
import { Field } from "../../field";
import { Combobox, Input } from "./combobox";
import { Label } from "../../label";
import { twMerge } from "tailwind-merge";

/*
We choose what Input field properties we allow to be passed to the Input component
This is needed to allow to manage the Input field using things like ...register("field") react-form-hook
We omit className to disable extra styling from the outside
*/
type InheritedInputProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"className" | "style"
>;

type Props = {
label?: string;
error?: string;
showClearButton?: boolean;
icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;
} & InheritedInputProps;

// Test options
const options = [
{ id: 1, value: "option1", label: "Option 1" },
{ id: 2, value: "option2", label: "Option 2" },
{ id: 3, value: "option3", label: "Option 3" },
{ id: 4, value: "option4", label: "Option 4" },
];

const MultipleSelectField = forwardRef<React.ElementRef<typeof Input>, Props>(
({ label, error, showClearButton, icon }: Props, ref) => {
// if error is present, we pass it to all the sub-components
const hasError = !!error;

console.info("InputField", { hasError, label, error, showClearButton });

// We show label instead of placeholder if label is provided
const showPlaceholder = !label;

// We need to pass hasIcon to some sub-components
const Icon = icon;
const hasIcon = !!Icon;

return (
<Field state={hasError ? "error" : "default"}>
{hasIcon && <Icon className={twMerge("k1-w-6 k1-h-6 k1-min-w-6")} />}
<Combobox
className="k1-relative k1-w-full k1-bg-transparent k1-outline-none k1-flex"
ref={ref}
showClearButton={showClearButton}
options={options}
defaultValue={[]}
onChange={(value) => console.log("Selected value:", value)}
onInputChange={(value) => console.log("Input value:", value)}
onClear={() => console.log("Cleared")}
showPlaceholder={showPlaceholder}
>
{label && (
<Label state={hasError ? "error" : "default"} hasIcon={hasIcon}>
{label}
</Label>
)}
</Combobox>
{hasError && <Description state="error">{error}</Description>}
</Field>
);
}
);

export { MultipleSelectField };
22 changes: 22 additions & 0 deletions packages/design-system/src/ui/input/combobobx/option.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComboboxOption as OptionPrimitive } from "@headlessui/react";
import { forwardRef } from "react";
import { twMerge } from "tailwind-merge";

const Option = forwardRef<
React.ElementRef<typeof OptionPrimitive>,
React.ComponentPropsWithoutRef<typeof OptionPrimitive>
>(({ className, children, ...props }, ref) => (
<OptionPrimitive
ref={ref}
className={twMerge(
"k1-px-4 k1-py-3 k1-border-b k1-border-neutral k1-justify-start k1-items-start k1-inline-flex hover:k1-bg-neutral-backdrop-hover active:k1-bg-neutral-backdrop-active k1-text-neutral-fg k1-text-base k1-w-full",
className
)}
{...props}
>
{children}
</OptionPrimitive>
));
Option.displayName = "Combobox.Option";

export { Option };

0 comments on commit 86d1ccf

Please sign in to comment.