Skip to content

Commit

Permalink
fix: improve customizability of Select component
Browse files Browse the repository at this point in the history
  • Loading branch information
bgptr authored Jan 17, 2022
1 parent 3ec5ef2 commit 0ea935a
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 49 deletions.
4 changes: 1 addition & 3 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ const config = {
external(),
postcss({
extract: true,
modules: {
globalModulePaths: ["src/components/Select/styles.css"],
},
modules: true,
}),
url({
include: ["**/*.woff", "**/*.ttf", "**/*.png", "**/*.svg"],
Expand Down
119 changes: 75 additions & 44 deletions src/components/Select/Select.jsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,126 @@
import React from "react";
import ReactSelect, { components } from "react-select";
import AsyncSelect from "react-select/async";
import CreatableSelect from "react-select/creatable";
import PropTypes from "prop-types";
import { classNames } from "../../utils";
import styles from "./styles.css";

const DropdownIndicator = (props) => {
return (
<components.DropdownIndicator {...props}>
<div
className={classNames(
styles.arrowAnchor,
props.selectProps.menuIsOpen && styles.arrowAnchorOpen
)}
/>
</components.DropdownIndicator>
);
};
const DropdownIndicator = (props) => (
<components.DropdownIndicator {...props}>
<div
className={classNames(
styles.arrowAnchor,
props.selectProps.menuIsOpen && styles.arrowAnchorOpen
)}
/>
</components.DropdownIndicator>
);

DropdownIndicator.propTypes = {
selectProps: PropTypes.object,
};

const Select = ({ width, isMobile, ...props }) => {
const customStyles = {
const Select = ({
width,
isMobile,
isAsync,
isCreatable,
customStyles,
customComponents,
...props
}) => {
const defaultStyles = {
indicatorSeparator: () => ({
display: "none",
}),
container: (provided) => ({
...provided,
width: width ? width : provided.width,
padding: "0.2rem 0 0.2rem 0.4rem",
}),
control: (provided) => ({
...provided,
"user-select": "none",
"box-shadow": "none",
"border-radius": "0.2rem",
"min-height": isMobile ? "4.4rem" : "3rem",
"min-width": "6rem",
"border-color": "var(--input-border-color)",
"background-color": "var(--card-background)",
control: () => ({
userSelect: "none",
boxShadow: "none",
borderRadius: "0.2rem",
minHeight: isMobile ? "4.4rem" : "3rem",
minWidth: "6rem",
borderColor: "var(--input-border-color)",
backgroundColor: "var(--card-background)",
"&:hover": {
"border-color": "var(--input-border-color)",
borderColor: "var(--input-border-color)",
},
}),
menu: (provided) => ({
...provided,
"z-index": "999",
"box-shadow": "0px 1rem 2rem rgba(0, 0, 0, 0.16)",
"border-radius": "0.2rem",
"background-color": "var(--card-background)",
menu: () => ({
zIndex: "999",
boxShadow: "0px 1rem 2rem rgba(0, 0, 0, 0.16)",
borderRadius: "0.2rem",
backgroundColor: "var(--card-background)",
}),
menuList: () => ({
padding: "0",
}),
singleValue: (provided) => ({
...provided,
singleValue: () => ({
color: "var(--color-primary-dark)",
}),
option: (provided) => ({
...provided,
option: (_, state) => ({
color: "var(--tab-text-color)",
"background-color": "var(--card-background)",
backgroundColor: state.isFocused
? "var(--color-blue-lighter)"
: "var(--card-background)",

cursor: "pointer",
"&:hover": {
"background-color": "var(--color-blue-lighter)",
},
}),
};

const mergedCustomStyles = [
...new Set([...Object.keys(defaultStyles), ...Object.keys(customStyles)]),
]
.map((key) => ({
[key]: (provided, state) => ({
...provided,
...(defaultStyles[key] && defaultStyles[key](provided, state)),
...(customStyles[key] && customStyles[key](provided, state)),
}),
}))
.reduce((prev, curr) => ({ ...prev, ...curr }), {});

const SelectComponent = isAsync
? AsyncSelect
: isCreatable
? CreatableSelect
: ReactSelect;

return (
<div>
<ReactSelect
styles={customStyles}
components={{ DropdownIndicator }}
<SelectComponent
styles={mergedCustomStyles}
components={{
DropdownIndicator,
...(customComponents && customComponents),
}}
{...props}
/>
</div>
);
};

Select.propTypes = {
options: PropTypes.array.isRequired,
options: PropTypes.array,
isSearchable: PropTypes.bool,
width: PropTypes.string,
isCreatable: PropTypes.bool,
width: PropTypes.number,
value: PropTypes.object,
isMobile: PropTypes.bool,
onChange: PropTypes.func,
customStyles: PropTypes.object,
customComponents: PropTypes.object,
};

Select.defaultProps = {
isSearchable: false,
isCreatable: false,
customStyles: {},
customComponents: {},
};

export default React.memo(Select);
4 changes: 2 additions & 2 deletions src/components/Select/test/__snapshots__/select.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports[`Select component Matches the snapshot 1`] = `
className="css-1f43avz-a11yText-A11yText"
/>
<div
className=" css-n1d6tz-control"
className=" css-1jhmgce-control"
onMouseDown={[Function]}
onTouchEnd={[Function]}
>
Expand Down Expand Up @@ -54,7 +54,7 @@ exports[`Select component Matches the snapshot 1`] = `
className=" css-1hb7zxy-IndicatorsContainer"
>
<span
className=" css-1xjgjl1-IndicatorSeparator"
className=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
Expand Down
134 changes: 134 additions & 0 deletions src/docs/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,137 @@ import { Select } from "../index";
);
}}
</Playground>

### Async select

<Playground>
{() => {
const options = [
{
value: "top",
label: "Top",
},
{
value: "new",
label: "New",
},
{
value: "old",
label: "Old",
},
];

const loadOptions = (
inputValue,
callback
) => {
callback(options.filter((i) =>
i.label.toLowerCase().includes(inputValue.toLowerCase())
));
};

return (
<Fragment>
<Select
isAsync
isSearchable
defaultOptions
options={options}
loadOptions={loadOptions}
placeholder=""
/>
</Fragment>
);

}}

</Playground>

### Custom select

<Playground>
{() => {
const options = [
{
value: "top",
label: "Top",
},
{
value: "new",
label: "New",
},
{
value: "old",
label: "Old",
},
];
const [selected, setSelected] = useState({
value: "old",
label: "Old",
});
const selectChangeHandler = (option) => {
setSelected(option);
};

const customStyles = {
control: () => ({
borderRadius: "none",
borderLeft: "none",
borderRight: "none",
borderTop: "none",
}),
dropdownIndicator: () => ({
paddingRight: 0
}),
};
return (
<Fragment>
<Select
options={options}
value={selected}
onChange={selectChangeHandler}
customStyles={customStyles}
/>
</Fragment>
);

}}

</Playground>

### Creatable select

<Playground>
{() => {
const options = [
{
value: "top",
label: "Top",
},
{
value: "new",
label: "New",
},
{
value: "old",
label: "Old",
},
];
const [selected, setSelected] = useState(null);
const selectChangeHandler = (option) => {
setSelected(option);
};
return (
<Fragment>
<Select
isCreatable
isSearchable
isClearable
options={options}
value={selected}
onChange={selectChangeHandler}
/>
</Fragment>
);
}}
</Playground>

0 comments on commit 0ea935a

Please sign in to comment.