Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Select label #177

Merged
merged 10 commits into from
Oct 13, 2023
27 changes: 27 additions & 0 deletions docs/docs/components/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,32 @@ const options = [
]}
/>

##### Select with Label

```jsx
const options = [
{ label: "First", value: "first" },
{ label: "Second", value: "second", disabled: true },
{ label: "Third", value: "third" },
{ label: "Fourth", value: "fourth" }
];

<div className="App">
<Select label="This is a Select Label" options={options} />
</div>;
```

<Select
width="40%"
label="This is a Select Label"
options={[
{ label: "First", value: "first" },
{ label: "Second", value: "second", disabled: true },
{ label: "Third", value: "third" },
{ label: "Fourth", value: "fourth" }
]}
/>

##### Select Rounded

```jsx
Expand Down Expand Up @@ -307,6 +333,7 @@ const options = [
| Attributes | Values | Default Value | Optional ? |
| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------: | :--------------: | ---------: |
| placeholder | `string` | `Pick one` | Yes |
| label | `string` | | Yes |
| options | `Array of` **_[Options Object](#option-object)_** | | No |
| value | **_[Options Object](#option-object)_** <br/> when isMulti is `true` then value is <br/> Array of **_[Options Object](#option-object)_** | | Yes |
| isMulti | `boolean` | `false` | Yes |
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Select/Pill/Pill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type pillPropTypes = {

export const Pill = ({ clearValue, value }: pillPropTypes) => {
return (
<Flex className={pillStyles} alignItems="center" gap="6px">
<Flex role="listitem" className={pillStyles} alignItems="center" gap="6px">
<span>{value}</span>
<Clear
className={pillIconStyles}
Expand Down
128 changes: 73 additions & 55 deletions packages/react/src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
noDataFoundStyles,
selectInputElementRecipe,
inputRecipe,
loadingContentContainer
loadingContentContainer,
labelStyles
} from "./select.css";
import { SelectPropsType, OptionsType } from "./select.types";
import "./select.global.styles.css";
Expand All @@ -33,12 +34,15 @@ import { useClickOutside } from "../../hooks/useClickOutside";
import { Flex } from "../Flex";
import { Loader } from "../Loader";
import { ArrowDown, Clear } from "../_internal/Icons";
import { Label } from "../Label";

const SelectComponent: ForwardRefRenderFunction<
HTMLDivElement,
SelectPropsType
> = (
{
label,
id,
placeholder,
options,
value,
Expand All @@ -64,7 +68,8 @@ const SelectComponent: ForwardRefRenderFunction<
useDropdownPortal = false,
closeDropdownPortalOnScroll = false,
zIndex = "300",
useSerialSearch = false
useSerialSearch = false,
...props
},
ref
) => {
Expand Down Expand Up @@ -171,7 +176,7 @@ const SelectComponent: ForwardRefRenderFunction<
const closeOnScroll = () => {
if (closeDropdownPortalOnScroll) {
setIsDropped(false);
onDropDownClose && onDropDownClose();
onDropDownClose?.();
}
};

Expand All @@ -180,7 +185,7 @@ const SelectComponent: ForwardRefRenderFunction<
const getComputedStyle =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
document.body && document.body.currentStyle
document?.body?.currentStyle
? function (elem: any) {
return elem.currentStyle;
}
Expand Down Expand Up @@ -246,7 +251,7 @@ const SelectComponent: ForwardRefRenderFunction<
closeOnOutsideClick();
} else {
isDropped && setIsDropped(false);
onDropDownClose && onDropDownClose();
onDropDownClose?.();
}
},
isDropped
Expand All @@ -260,13 +265,11 @@ const SelectComponent: ForwardRefRenderFunction<
!optionsListRef.current?.contains(event.target as Node)
) {
isDropped && setIsDropped(false);
onDropDownClose && onDropDownClose();
}
} else {
if (!inputRef.current?.contains(event.target as Node)) {
isDropped && setIsDropped(false);
onDropDownClose && onDropDownClose();
onDropDownClose?.();
}
} else if (!inputRef.current?.contains(event.target as Node)) {
isDropped && setIsDropped(false);
onDropDownClose?.();
}
});
};
Expand Down Expand Up @@ -305,7 +308,7 @@ const SelectComponent: ForwardRefRenderFunction<
setSearchText("");
const multiValue = Array.isArray(selectValue) ? [...selectValue] : [];
multiValue.push(option);
onChange && onChange(multiValue, event);
onChange?.(multiValue, event);
setSelectValue(multiValue);
} else {
if (
Expand All @@ -314,14 +317,14 @@ const SelectComponent: ForwardRefRenderFunction<
option.value === selectValue?.value
) {
setSelectValue(null);
onChange && onChange(null, event);
onChange?.(null, event);
} else {
setSelectValue(option);
onChange && onChange(option, event);
onChange?.(option, event);
}
setIsDropped(false);
setInternalOptions(options);
onDropDownClose && onDropDownClose();
onDropDownClose?.();
}
};

Expand All @@ -331,12 +334,10 @@ const SelectComponent: ForwardRefRenderFunction<
): OptionsType[] => {
return options?.filter((option) => {
if (useSerialSearch) {
return (
option.label
.trim()
.toLowerCase()
.indexOf(value.trim().toLowerCase()) === 0
);
return option.label
.trim()
.toLowerCase()
.startsWith(value.trim().toLowerCase());
}
return option.label
.trim()
Expand All @@ -362,29 +363,9 @@ const SelectComponent: ForwardRefRenderFunction<
: setInternalOptions(filteredOptions);
};

const handleIconClick = (event: MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
if (isClearable) {
if (isMulti) {
onChange && onChange([], event);
setSelectValue([]);
} else {
onChange && onChange(null, event);
setSelectValue(null);
}
} else {
setIsDropped(!isDropped);
isDropped
? onDropDownClose && onDropDownClose()
: onDropDownOpen && onDropDownOpen();
}
};

const changeDrop = () => {
setIsDropped(!isDropped);
isDropped
? onDropDownClose && onDropDownClose()
: onDropDownOpen && onDropDownOpen();
isDropped ? onDropDownClose?.() : onDropDownOpen?.();
};

const clearPill = (
Expand All @@ -393,7 +374,7 @@ const SelectComponent: ForwardRefRenderFunction<
) => {
let tempArr = Array.isArray(selectValue) ? [...selectValue] : [];
tempArr = tempArr.filter((arr) => arr.value !== clearValue);
onChange && onChange(tempArr, event);
onChange?.(tempArr, event);
setSelectValue(tempArr);
setIsDropped(true);
};
Expand Down Expand Up @@ -533,18 +514,30 @@ const SelectComponent: ForwardRefRenderFunction<
});

const selectInputStyles = selectInputRecipe({
error: error ? true : false,
error: !!error,
disabled: isDisabled
});

const selectInputElement = selectInputElementRecipe({
error: error ? true : false
error: !!error
});
const inputStyles = inputRecipe({
error: error ? true : false,
error: !!error,
isMulti
});

const clearMultiValues = (event: MouseEvent<SVGElement>) => {
event.stopPropagation();
onChange?.([], event);
setSelectValue([]);
};

const clearValues = (event: MouseEvent<SVGElement>) => {
event.stopPropagation();
onChange?.(null, event);
setSelectValue(null);
};

const renderDropDown = () => {
return (
<Flex
Expand Down Expand Up @@ -572,6 +565,26 @@ const SelectComponent: ForwardRefRenderFunction<
);
};

const renderIcon = () => {
if (isClearable) {
if (Array.isArray(selectValue) && selectValue.length) {
return (
<Clear
role="button"
onClick={clearMultiValues}
width={18}
height={18}
/>
);
} else if (!Array.isArray(selectValue) && selectValue?.value) {
return (
<Clear role="button" onClick={clearValues} width={18} height={18} />
);
}
}
return DropIcon || <ArrowDown role="button" width={18} height={18} />;
};

const renderDropDownList = () => {
return Array.isArray(internalOptions) && internalOptions?.length !== 0 ? (
internalOptions.map((option, ind) => {
Expand Down Expand Up @@ -653,7 +666,17 @@ const SelectComponent: ForwardRefRenderFunction<
[selectVars.maxDropDownHeight]: maxDropDownHeight
})
}}
{...props}
>
{label && (
<Label
className={labelStyles}
htmlFor={id || "hover-select-input"}
id={`${id || "hover-select"}-label`}
>
{label}
</Label>
)}
<Flex
role="combobox"
ref={inputRef}
Expand Down Expand Up @@ -693,6 +716,7 @@ const SelectComponent: ForwardRefRenderFunction<
))}
{isSearchable && (
<input
id={id || "hover-select-input"}
disabled={isDisabled}
className={inputStyles}
value={searchText}
Expand All @@ -703,6 +727,7 @@ const SelectComponent: ForwardRefRenderFunction<
)}
{!isMulti && !isSearchable && !Array.isArray(selectValue) && (
<input
id={id || "hover-select-input"}
className={selectInputElement}
value={selectValue?.label ? selectValue.label : ""}
disabled
Expand All @@ -711,15 +736,8 @@ const SelectComponent: ForwardRefRenderFunction<
)}
</Flex>
{!isLoading ? (
<Flex
alignItems="center"
className={selectIconClass}
onClick={handleIconClick}
>
{DropIcon
? !isClearable && DropIcon
: !isClearable && <ArrowDown width={18} height={18} />}
{isClearable && <Clear width={18} height={18} />}
<Flex alignItems="center" className={selectIconClass}>
{renderIcon()}
</Flex>
) : (
loadingOptions?.loader || <Loader color={color} />
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/components/Select/select.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,9 @@ export const loadingContentContainer = style({
padding: "10px 16px",
cursor: "default"
});

export const labelStyles = style({
fontWeight: 500,
fontSize: "inherit",
marginBottom: "5px"
});
3 changes: 2 additions & 1 deletion packages/react/src/components/Select/select.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { KeyboardEvent, MouseEvent, MutableRefObject, ReactNode } from "react";
type divType = Omit<JSX.IntrinsicElements["div"], "onChange">;
type divType = Omit<JSX.IntrinsicElements["div"], "onChange" | "ref">;
export type SelectPropsType = divType & {
label?: ReactNode;
placeholder?: string;
options: OptionsType[];
value?: SelectValueType;
Expand Down
Loading