Skip to content

Commit

Permalink
Merge pull request #2343 from djnunez-aot/filter-bug
Browse files Browse the repository at this point in the history
Make dropdowns visible if Overflowing
  • Loading branch information
djnunez-aot authored Jul 30, 2024
2 parents 810720a + c989b96 commit c58ce1b
Showing 1 changed file with 140 additions and 107 deletions.
247 changes: 140 additions & 107 deletions epictrack-web/src/components/shared/filterSelect/FilterSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useRef } from "react";
import Select from "react-select";
import Menu from "./components/Menu";
import Option from "./components/Option";
Expand All @@ -21,9 +21,11 @@ const FilterSelect = (props: SelectProps) => {
const [selectValue, setSelectValue] = React.useState<any>(
defaultValue ?? standardDefault
);
const menuRef = useRef<HTMLDivElement | null>(null);
const [menuIsOpen, setMenuIsOpen] = React.useState<boolean>(
!!props.menuIsOpen
);
const [menuStyle, setMenuStyle] = React.useState<any>({}); // eslint-disable-line
const selectRef = React.useRef<any | null>(null);

const selectAllOption = React.useMemo(
Expand All @@ -34,19 +36,19 @@ const FilterSelect = (props: SelectProps) => {
[]
);

useEffect(() => {
if (menuIsOpen) {
adjustDropdownPosition();
updateSelectedOptions();
}
}, [menuIsOpen]);

const isSelectAllSelected = () =>
selectedOptions.includes(selectAllOption.value);

const isOptionSelected = (o: OptionType) =>
isMulti ? selectedOptions.includes(o.value) : selectedOptions === o.value;

React.useEffect(() => {
const currentValues = isMulti
? selectValue.map((v: OptionType) => v.value)
: selectValue.value;
setSelectedOptions(currentValues);
}, [menuIsOpen]);

const handleChange = (newValue: any, actionMeta: any) => {
if (!isMulti) {
if (isOptionSelected(newValue)) {
Expand Down Expand Up @@ -126,6 +128,32 @@ const FilterSelect = (props: SelectProps) => {
selectRef.current?.blur();
};

const adjustDropdownPosition = () => {
if (menuRef?.current) {
const menuRect = menuRef.current.getBoundingClientRect();
const windowWidth = window.innerWidth - 50;
const rightEdgeOfMenu = menuRect.left + menuRect.width;

if (rightEdgeOfMenu > windowWidth) {
const overflow = rightEdgeOfMenu - windowWidth;
const newPosition = {
transform: `translateX(${-(overflow + 80)}px)`,
};
setMenuStyle(newPosition);
} else {
setMenuStyle({});
}
}
};

// Function to update selected options
const updateSelectedOptions = () => {
const currentValues = isMulti
? selectValue.map((v: OptionType) => v.value)
: selectValue.value;
setSelectedOptions(currentValues);
};

React.useEffect(() => {
let filterOptions = props.options as OptionType[];
if (isMulti) filterOptions = [selectAllOption, ...filterOptions];
Expand Down Expand Up @@ -153,108 +181,113 @@ const FilterSelect = (props: SelectProps) => {
}, [props.value]);

return (
<Select
value={selectValue}
placeholder={props.placeholder || "Filter"}
onMenuClose={onCancel}
name={name}
options={options}
isMulti={isMulti}
onChange={handleChange}
components={{
Option,
Menu,
MultiValue,
SingleValue,
IndicatorSeparator: () => null,
DropdownIndicator,
}}
filterProps={{
applyFilters,
clearFilters,
selectedOptions,
onCancel,
variant: props.variant || "inline",
}}
menuIsOpen={menuIsOpen}
closeMenuOnSelect={false}
hideSelectedOptions={false}
onFocus={() => setMenuIsOpen(true)}
onBlur={() => setMenuIsOpen(false)}
ref={selectRef}
styles={{
option: (base, provided) => ({
...base,
whiteSpace: "normal",
overflow: "hidden",
textOverflow: "ellipsis",
display: "flex",
alignItems: "center",
padding: ".5rem .75rem .5rem 0px",
fontWeight: "normal",
fontSize: "1rem",
maxWidth: props.maxWidth ?? "100%",
background: provided.isFocused
? Palette.neutral.bg.main
: "transparent",
color: provided.isSelected
? Palette.primary.accent.main
: Palette.neutral.accent.dark,
cursor: provided.isFocused ? "pointer" : "default",
}),
control: (base, props) => ({
...base,
background: props.hasValue ? Palette.primary.bg.light : Palette.white,
borderWidth: "2px",
borderStyle: props.hasValue ? "none" : "solid",
borderColor:
props.isFocused || props.menuIsOpen
? Palette.primary.accent.light
: Palette.neutral.accent.light,
boxShadow: "none",
...(props.selectProps.filterProps?.variant === "bar" && {
borderColor: props.isFocused
? Palette.primary.accent.light
<div ref={menuRef}>
<Select
value={selectValue}
placeholder={props.placeholder || "Filter"}
onMenuClose={onCancel}
name={name}
options={options}
isMulti={isMulti}
onChange={handleChange}
components={{
Option,
Menu,
MultiValue,
SingleValue,
IndicatorSeparator: () => null,
DropdownIndicator,
}}
filterProps={{
applyFilters,
clearFilters,
selectedOptions,
onCancel,
variant: props.variant || "inline",
}}
menuIsOpen={menuIsOpen}
closeMenuOnSelect={false}
hideSelectedOptions={false}
onFocus={() => setMenuIsOpen(true)}
onBlur={() => setMenuIsOpen(false)}
ref={selectRef}
styles={{
option: (base, provided) => ({
...base,
whiteSpace: "normal",
overflow: "hidden",
textOverflow: "ellipsis",
display: "flex",
alignItems: "center",
padding: ".5rem .75rem .5rem 0px",
fontWeight: "normal",
fontSize: "1rem",
maxWidth: props.maxWidth ?? "100%",
background: provided.isFocused
? Palette.neutral.bg.main
: "transparent",
color: provided.isSelected
? Palette.primary.accent.main
: Palette.neutral.accent.dark,
cursor: provided.isFocused ? "pointer" : "default",
}),
control: (base, props) => ({
...base,
background: props.hasValue
? Palette.primary.bg.light
: Palette.white,
borderWidth: "2px",
borderStyle: props.hasValue ? "none" : "solid",
borderColor:
props.isFocused || props.menuIsOpen
? Palette.primary.accent.light
: Palette.neutral.accent.light,
boxShadow: "none",
...(props.selectProps.filterProps?.variant === "bar" && {
borderColor: props.isFocused
? Palette.primary.accent.light
: "transparent",
}),
}),
menu: (base, props) => ({
...base,
position: "relative",
marginBlock: "0px",
border: `1px solid ${Palette.neutral.accent.light}`,
borderRadius: "4px",
...menuStyle,
}),
placeholder: (base, props) => ({
...base,
fontWeight: MET_Header_Font_Weight_Regular,
color: Palette.neutral.light,
fontSize: INPUT_SIZE,
lineHeight: "1rem",
...(props.selectProps.filterProps?.variant == "bar" && {
color: Palette.primary.accent.main,
fontWeight: 700,
}),
}),
menuPortal: (base, props) => ({
...base,
zIndex: theme.zIndex.modal,
marginTop: "4px",
}),
}),
menu: (base, props) => ({
...base,
position: "relative",
marginBlock: "0px",
border: `1px solid ${Palette.neutral.accent.light}`,
borderRadius: "4px",
}),
placeholder: (base, props) => ({
...base,
fontWeight: MET_Header_Font_Weight_Regular,
color: Palette.neutral.light,
fontSize: INPUT_SIZE,
lineHeight: "1rem",
...(props.selectProps.filterProps?.variant == "bar" && {
color: Palette.primary.accent.main,
fontWeight: 700,
input: (base, props) => ({
...base,
fontWeight: "400",
fontSize: INPUT_SIZE,
}),
}),
menuPortal: (base, props) => ({
...base,
zIndex: theme.zIndex.modal,
marginTop: "4px",
}),
input: (base, props) => ({
...base,
fontWeight: "400",
fontSize: INPUT_SIZE,
}),
}}
isClearable={false}
menuPortalTarget={document.body}
controlShouldRenderValue={props.controlShouldRenderValue}
isLoading={props.isLoading}
loadingMessage={() => "Loading..."}
isDisabled={props.isDisabled}
isSearchable={isSearchable()}
/>
}}
isClearable={false}
menuPortalTarget={document.body}
controlShouldRenderValue={props.controlShouldRenderValue}
isLoading={props.isLoading}
loadingMessage={() => "Loading..."}
isDisabled={props.isDisabled}
isSearchable={isSearchable()}
/>
</div>
);
};

Expand Down

0 comments on commit c58ce1b

Please sign in to comment.