From 245bd8e2baa9f641d5a5e1e9ac019a4e23d99ee3 Mon Sep 17 00:00:00 2001 From: filantrop Date: Wed, 10 Aug 2022 19:09:34 +0300 Subject: [PATCH 1/2] Creating a select component using HeadlessUI --- src/assets/svg/select-arrow.svg | 3 + src/components/Select.stories.tsx | 57 ++++++++++++++ src/components/Select.tsx | 122 ++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 src/assets/svg/select-arrow.svg create mode 100644 src/components/Select.stories.tsx create mode 100644 src/components/Select.tsx diff --git a/src/assets/svg/select-arrow.svg b/src/assets/svg/select-arrow.svg new file mode 100644 index 0000000..9c2dd2d --- /dev/null +++ b/src/assets/svg/select-arrow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/Select.stories.tsx b/src/components/Select.stories.tsx new file mode 100644 index 0000000..fe42885 --- /dev/null +++ b/src/components/Select.stories.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { ComponentMeta, ComponentStory } from "@storybook/react"; +import { useState } from "react"; + +import Select from "./Select"; + +export default { + // Title inside navigation bar + title: "Select", + // Component to test + component: Select, + // Clarifying the way how to process specific + // properties of your component and which values + // it can accept. + argTypes: {}, +} as ComponentMeta; + +/** + * This is a way to define a tempalte for your component. + * + * This template should cover all the states. + * + * In most cases you should just distruct args attribute + * on a returning component. + */ + +const Template: ComponentStory = (args) => { + const { options = [{ name: String }] } = args; + const [selected, setSelected] = useState(options[0]); + return ( +
+ + options={options} + value={selected} + onChange={setSelected} + displayValueResolver={(options) => options.name} + /> +
+ ); +}; + +/* -------------------------------------------------------------------------- */ +/* States of your component */ +/* -------------------------------------------------------------------------- */ + +export const Default = Template.bind({}); + +Default.args = { + options: [ + { name: "Wade Cooper" }, + { name: "Arlene Mccoy" }, + { name: "Devon Webb" }, + { name: "Tom Cook" }, + { name: "Tanya Fox" }, + { name: "Hellen Schmidt" }, + ], +}; diff --git a/src/components/Select.tsx b/src/components/Select.tsx new file mode 100644 index 0000000..adbf048 --- /dev/null +++ b/src/components/Select.tsx @@ -0,0 +1,122 @@ +/* -------------------------------------------------------------------------- */ +/* Imports */ +/* -------------------------------------------------------------------------- */ +import React from "react"; +import { Fragment } from "react"; +import { Listbox, Transition } from "@headlessui/react"; +import classNames from "classnames"; +import "../index.css"; +import { ReactComponent as SelectIcon } from "../assets/svg/select-arrow.svg"; + +/* -------------------------------------------------------------------------- */ +/* Component props */ +/* -------------------------------------------------------------------------- */ + +type Props = { + options?: T[]; + disabled?: boolean; + className?: string; + value: T; + displayValueResolver?: (element: T) => any; + onChange: (element: T) => void; +} & Omit, "value" | "onChange">; + +/* -------------------------------------------------------------------------- */ +/* styles */ +/* -------------------------------------------------------------------------- */ + +const SelectButtonStyle = ` + relative w-full + cursor-default + rounded + border border-gray-50 + outline-8 + bg-white + py-2 pl-3 pr-10 text-left + shadow-md + hover:border-gray-300 + focus:outline-1 + focus-visible:border-gray-500 + sm:text-sm + `; + +const SelectOptionsStyle = ` + absolute z-10 mt-1 w-full max-h-56 + bg-white shadow-lg + rounded py-1 + overflow-auto + focus:outline-none + text-base + sm:text-sm + `; + +const SelectIconStyle = ` + pointer-events-none + absolute inset-y-0 right-0 + flex items-center pr-2 + `; +/* -------------------------------------------------------------------------- */ +/* Component implementation */ +/* -------------------------------------------------------------------------- */ +function Select({ + className, + options = [], + value, + onChange, + displayValueResolver, + disabled, + ...props +}: Props): JSX.Element { + return ( +
+ +
+ + {({ open }) => ( + <> + {`${ + displayValueResolver ? displayValueResolver(value) : value + }`} + + + + + )} + + + + + {options.map((option, id) => ( + + classNames( + active ? "text-gray-900 bg-blue-50" : "font-normal ", + "cursor-default select-none relative py-2 pl-3 pr-9", + selected ? "text-gray-900 bg-blue-100" : "font-normal " + ) + } + value={option} + > + {`${ + displayValueResolver ? displayValueResolver(option) : option + }`} + + ))} + + +
+
+
+ ); +} +export default Select; From 6e4de7255ad49a0379c109cc57661d81b3f414c8 Mon Sep 17 00:00:00 2001 From: filantrop Date: Thu, 11 Aug 2022 13:04:06 +0300 Subject: [PATCH 2/2] delete shadow from select --- src/components/Select.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Select.tsx b/src/components/Select.tsx index adbf048..752939c 100644 --- a/src/components/Select.tsx +++ b/src/components/Select.tsx @@ -33,7 +33,6 @@ const SelectButtonStyle = ` outline-8 bg-white py-2 pl-3 pr-10 text-left - shadow-md hover:border-gray-300 focus:outline-1 focus-visible:border-gray-500