Skip to content
This repository has been archived by the owner on Jul 10, 2023. It is now read-only.

Feat: Revamp searchable dropdown #223

Merged
merged 26 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
26d7dc4
initial work
pKorsholm Jan 3, 2022
030d454
initial number input
pKorsholm Jan 4, 2022
7717705
Merge branch 'feat/revamp' into feat/revamp-input-field
pKorsholm Jan 4, 2022
85f8e1d
finished input with number
pKorsholm Jan 4, 2022
8307123
finish input field
pKorsholm Jan 4, 2022
2239360
prevent propagation and better styling
pKorsholm Jan 4, 2022
78c3fa5
move input to molecules
pKorsholm Jan 4, 2022
3f874ab
add whitespace
pKorsholm Jan 4, 2022
31a7ff2
style adjustments
pKorsholm Jan 4, 2022
32ce95f
Merge branch 'feat/revamp' into feat/revamp-input-field
pKorsholm Jan 4, 2022
09b8721
make dropdown input container
pKorsholm Jan 5, 2022
e3fd28b
upgrade react-multi-select-component to 4.1.15
pKorsholm Jan 5, 2022
6e1aebf
checkpoint
pKorsholm Jan 5, 2022
219b0b5
initial implementation
pKorsholm Jan 5, 2022
5fb671b
convert to typescript
pKorsholm Jan 5, 2022
9dc49fe
move searchable dropdown to molecules
pKorsholm Jan 5, 2022
6d695ef
styling overrides
pKorsholm Jan 6, 2022
25e3172
now also as single select
pKorsholm Jan 6, 2022
00b3d88
rename multi-select to select
pKorsholm Jan 6, 2022
049788e
merge
pKorsholm Jan 6, 2022
b1b5028
merge feat/revamp
pKorsholm Jan 7, 2022
9bef135
focus bg
pKorsholm Jan 7, 2022
aceb704
make onCreateOption optional
pKorsholm Jan 10, 2022
f36b482
add option boolean option "ClearSelected" to add clear icon to dropdown
pKorsholm Jan 10, 2022
a8ff533
rename "ClearSelected" to "clearSelected"
pKorsholm Jan 10, 2022
d0051d6
update single select capabilities
pKorsholm Jan 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"react-hook-form": "^6.7.2",
"react-hotkeys-hook": "^2.4.0",
"react-json-view": "^1.19.1",
"react-multi-select-component": "^2.0.12",
"react-multi-select-component": "^4.1.15",
"react-popper": "^2.2.5",
"react-select": "^4.1.0",
"react-toast-notifications": "^2.4.0",
Expand Down
9 changes: 5 additions & 4 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
plugins: [
require("tailwindcss/nesting"),
require("tailwindcss"),
require("autoprefixer"),
],
}
51 changes: 51 additions & 0 deletions src/assets/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,56 @@
.mono-xsmall-semibold {
@apply font-mono text-xsmall leading-xsmall font-bold;
}

.multiselect-styling {
@apply p-0 -mx-3 mt-1 outline-0 cursor-text;

.dropdown-container {
@apply border-none bg-inherit focus-within:shadow-none cursor-text !important;
}

.dropdown-content {
@apply rounded-rounded w-full -mt-3 pt-0 -top-full shadow-input !important;
}

.select-item {
@apply p-2 my-1 mx-2 rounded bg-grey-0 hover:bg-grey-10 flex !important;
}

.select-panel > div::before {
content: url("../../assets/svg-2.0/search.svg");
width: 2rem;
height: 1.5rem;
top: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
}

.search {
@apply py-4 flex items-center px-3 !important;
}
.search input {
@apply h-6 text-grey-90 !important;
}
.search .search-clear-button {
@apply mr-4 !important;
}

.options {
@apply mt-1 !important;
}

.dropdown-heading-dropdown-arrow,
.dropdown-search-clear-icon {
@apply hidden;
}

.dropdown-heading {
@apply py-0 px-3 outline-0 h-6 cursor-text !important;
}
}
}

@tailwind utilities;
Expand All @@ -176,6 +226,7 @@
-webkit-appearance: none;
margin: 0;
}

/* Firefox */
.remove-number-spinner {
-moz-appearance: textfield;
Expand Down
3 changes: 3 additions & 0 deletions src/assets/svg-2.0/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions src/components/fundamentals/icons/arrow-down-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react"
import IconProps from "./types/icon-type"

const ArrowDownIcon: React.FC<IconProps> = ({
size = "24px",
color = "currentColor",
attributes,
}) => {
return (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...attributes}
>
<path
d="M8 3.33331V12.6666"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12.667 8L8.00033 12.6667L3.33366 8"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
)
}

export default ArrowDownIcon
36 changes: 36 additions & 0 deletions src/components/fundamentals/icons/arrow-up-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react"
import IconProps from "./types/icon-type"

const ArrowUpIcon: React.FC<IconProps> = ({
size = "24px",
color = "currentColor",
attributes,
}) => {
return (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...attributes}
>
<path
d="M8 12.6667V3.33335"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M3.33301 8L7.99967 3.33333L12.6663 8"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
)
}

export default ArrowUpIcon
29 changes: 29 additions & 0 deletions src/components/fundamentals/icons/check-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react"
import IconProps from "./types/icon-type"

const CheckIcon: React.FC<IconProps> = ({
size = "24px",
color = "currentColor",
attributes,
}) => {
return (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...attributes}
>
<path
d="M13.3334 4L6.00008 11.3333L2.66675 8"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
)
}

export default CheckIcon
43 changes: 43 additions & 0 deletions src/components/fundamentals/icons/x-circle-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react"
import IconProps from "./types/icon-type"

const XCircleIcon: React.FC<IconProps> = ({
size = "24px",
color = "currentColor",
attributes,
}) => {
return (
<svg
width={size}
height={size}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...attributes}
>
<path
d="M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5Z"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12.5 7.5L7.5 12.5"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M7.5 7.5L12.5 12.5"
stroke={color}
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
)
}

export default XCircleIcon
7 changes: 7 additions & 0 deletions src/components/fundamentals/input-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@ type InputContainerProps = {
key?: string
props?: React.HTMLAttributes<HTMLDivElement>
onClick?: MouseEventHandler<HTMLDivElement>
onFocusLost?: () => void
}

const InputContainer: React.FC<InputContainerProps> = ({
key,
props,
onClick,
onFocusLost,
children,
}) => {
return (
<div
tabIndex={-1}
className="bg-grey-5 inter-base-regular w-full p-3 flex h-18 flex-col cursor-text border border-grey-20 focus-within:shadow-input focus-within:border-violet-60 rounded-base my-4"
onClick={onClick}
onBlur={e => {
if (onFocusLost && !e.currentTarget.contains(e.relatedTarget)) {
onFocusLost()
}
}}
key={key}
{...props}
>
Expand Down
130 changes: 130 additions & 0 deletions src/components/molecules/select/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { ChangeEventHandler, useState } from "react"
import { MultiSelect } from "react-multi-select-component"
import InputContainer from "../../fundamentals/input-container"
import InputHeader from "../../fundamentals/input-header"
import CheckIcon from "../../fundamentals/icons/check-icon"
import ArrowDownIcon from "../../fundamentals/icons/arrow-down-icon"
import XCircleIcon from "../../fundamentals/icons/x-circle-icon"

type Option = React.OptionHTMLAttributes<HTMLOptionElement> & {
key?: string
}

type ItemRendererProps = {
checked?: boolean
onClick?: ChangeEventHandler<HTMLElement>
disabled?: boolean
option?: Option
}

type MultiSelectProps = {
// component props
label: string
required?: boolean
name?: string
// Multiselect props
isMultiSelect?: boolean
labelledBy?: string
options: { label: string; value: string; disabled?: boolean }[]
value: { label: string; value: string }[]
hasSelectAll?: boolean
isLoading?: boolean
shouldToggleOnHover?: boolean
overrideStrings?: object
onChange: (values: any[]) => void
disabled?: boolean
enableSearch?: boolean
isCreatable?: boolean
onCreateOption: any
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved
}

const ItemRenderer: React.FC<ItemRendererProps> = ({
checked,
option,
onClick,
disabled,
}) => (
<div className={`item-renderer ${disabled && "disabled"} w-full h-full`}>
<div className="items-center h-full flex">
<div
className={`w-5 h-5 flex justify-center text-grey-0 border-grey-30 border rounded-base ${
checked && "bg-violet-60"
}`}
>
<span className="self-center">
{checked && <CheckIcon size={16} />}
</span>
</div>
<input
className="hidden"
type="checkbox"
onChange={onClick}
checked={checked}
tabIndex={-1}
disabled={disabled}
/>
<span className="ml-3 text-grey-90">{option.label}</span>
</div>
</div>
)

const Select = React.forwardRef(
(
{
label,
name,
required,
value,
onChange,
isMultiSelect,
hasSelectAll,
enableSearch,
overrideStrings,
labelledBy = "label",
...selectOptions
}: MultiSelectProps,
ref
) => {
const [isOpen, setIsOpen] = useState(false)

const handleSelect = values => {
if (values.length) {
onChange(isMultiSelect ? values : [values[values.length - 1]])
} else {
onChange([])
}
}

return (
<InputContainer
key={name}
onFocusLost={() => setIsOpen(false)}
onClick={() => setIsOpen(true)}
>
<div className="w-full flex text-grey-50 justify-between">
<InputHeader {...{ label, required }} />
<ArrowDownIcon size={16} />
</div>
<MultiSelect
labelledBy={labelledBy}
value={value}
isOpen={isOpen}
hasSelectAll={hasSelectAll}
ItemRenderer={ItemRenderer}
className="multiselect-styling"
overrideStrings={{ search: "Search...", ...overrideStrings }}
ClearIcon={
<span className="text-grey-40">
<XCircleIcon size={20} />
</span>
}
onChange={handleSelect}
{...selectOptions}
disableSearch={!enableSearch}
/>
pKorsholm marked this conversation as resolved.
Show resolved Hide resolved
</InputContainer>
)
}
)

export default Select
Loading