Skip to content

Commit

Permalink
Adds Select label (#177)
Browse files Browse the repository at this point in the history
* Feat: Select Enhancements

* chore: reduced default minHeight and removed comments

* bug: fixed sonar bugs and code smell

* feat: added label support and changed icon click handling

* refactor: renderIcon function

* chore: added roles

* chore: removes code smells

* chore: removes code smell
  • Loading branch information
saurabhsutar192 authored Oct 13, 2023
1 parent 00d1b8a commit 27a076a
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 57 deletions.
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

0 comments on commit 27a076a

Please sign in to comment.