Skip to content

Commit

Permalink
Merge pull request #27 from concord-consortium/186752905-filter-funct…
Browse files Browse the repository at this point in the history
…ion-fixes

186752905 filter function fixes
  • Loading branch information
eireland authored Jan 26, 2024
2 parents 69d723a + 3bcdf7a commit cd7661f
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const kPluginName = "NOAA Weather Station Data";
const kVersion = "0014";
const kInitialDimensions = {
width: 360,
height: 495
height: 650
};

export const App = () => {
Expand Down
14 changes: 10 additions & 4 deletions src/components/attribute-filter.scss
Original file line number Diff line number Diff line change
Expand Up @@ -208,27 +208,33 @@ table tr:nth-child(even) {
}

.filter-operator-selection-container {
width: 148px;
height: 190px;
padding: 9px;
width: 165px;
max-height: 190px;
padding: 9px 12px 5px 9px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5);
background-color: #fff;
position: relative;
right: -22px;
right: -5px;
top: -50px;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;

.operator-selection {
border: none;
width: 100%;
height: 176px;
outline: none;
box-sizing: border-box;

option {
font-family: "Montserrat", sans-serif;
font-size: 10px;
font-weight: 500;
color: #000;
padding: 2px 0;
height: 12px;

&:hover {
background-color: $teal-medium;
}
Expand Down
106 changes: 75 additions & 31 deletions src/components/attribute-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const AttributeFilter = () => {
const attrMap = selectedFrequency === "hourly" ? hourlyAttrMap : dailyMonthlyAttrMap;
const [hasFilter, setHasFilter] = useState(false);
const [filteringIndex, setFilteringIndex] = useState<number | undefined>(undefined);
const [targetFilterBottom, setTargetFilterBottom] = useState(0);
const [filterModalPosition, setFilterModalPosition] = useState({ top: 0 });
const [showFilterModal, setShowFilterModal] = useState(false);
const [attributeToFilter, setAttributeToFilter] = useState<AttrType | undefined>(undefined);
Expand All @@ -44,7 +45,6 @@ export const AttributeFilter = () => {
});

if (hasFilters) {

setHasFilter(true);
}
},[frequencies, selectedAttrMap, selectedFrequency]);
Expand All @@ -53,17 +53,18 @@ export const AttributeFilter = () => {
const rect = e.currentTarget.getBoundingClientRect();
const top = rect.bottom + window.scrollY;

setTargetFilterBottom(rect.bottom);
setFilterModalPosition({top});
setShowFilterModal(true);
setAttributeToFilter(frequencies[selectedFrequency].attrs[index]);
setFilteringIndex(index);
};

const handleUnitsToggle = () => {
setState(draft => {
draft.units = draft.units === "standard" ? "metric" : "standard";
});
};
const handleUnitsToggle = () => {
setState(draft => {
draft.units = draft.units === "standard" ? "metric" : "standard";
});
};

if (selectedAttrMap && selectedAttrMap.length > 0) {
return (
Expand All @@ -88,7 +89,7 @@ export const AttributeFilter = () => {
: filterAboveOrBelowMean
? `${operatorTextMap[attrFilter.operator]}`
: attrFilter.operator === "between"
? `${attrFilter.lowerValue} - ${attrFilter.upperValue} ${attr.unit[units]}`
? `${attrFilter.lowerValue} ${attr.unit[units]} - ${attrFilter.upperValue} ${attr.unit[units]}`
: attrFilter.operator === "top" || attrFilter.operator === "bottom"
? `${operatorTextMap[attrFilter.operator]} ${attrFilter.value}`
:`${operatorSymbolMap[attrFilter.operator]} ${attrFilter.value} ${attr.unit[units]}`;
Expand All @@ -115,7 +116,10 @@ export const AttributeFilter = () => {
</tbody>
</table>
{(attributeToFilter && showFilterModal) &&
<FilterModal attr={attributeToFilter} setShowFilterModal={setShowFilterModal} position={filterModalPosition}/>}
<FilterModal attr={attributeToFilter} setShowFilterModal={setShowFilterModal} position={filterModalPosition}
setFilterModalPosition={setFilterModalPosition} targetFilterBottom={targetFilterBottom}
key={attributeToFilter.name}
/>}
</div>
);
} else {
Expand All @@ -126,10 +130,12 @@ export const AttributeFilter = () => {
interface IFilterModalProps {
attr: AttrType;
position: {top: number};
targetFilterBottom?: number;
setShowFilterModal: (show: boolean) => void
setFilterModalPosition: (position: {top: number}) => void;
}

const FilterModal = ({attr, position, setShowFilterModal}: IFilterModalProps) => {
const FilterModal = ({attr, position, targetFilterBottom, setShowFilterModal, setFilterModalPosition}: IFilterModalProps) => {
const {state, setState} = useStateContext();
const {frequencies, units, selectedFrequency} = state;
const currentAttr = frequencies[selectedFrequency].attrs.find(a => a.name === attr.name);
Expand All @@ -148,23 +154,56 @@ const FilterModal = ({attr, position, setShowFilterModal}: IFilterModalProps) =>
const filterLowerValueInputElRef = useRef<HTMLInputElement>(null);
const filterUpperValueInputElRef = useRef<HTMLInputElement>(null);
const operatorSelectionModalRef = useRef<HTMLDivElement>(null);
const [operatorSelectionListHeight, setOperatorSelectionListHeight] = useState({height: 190});
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
const dropdownRect = operatorSelectionModalRef.current?.getBoundingClientRect();
const dropdownBottom = dropdownRect?.bottom;

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (event.target) {
if (operatorSelectionModalRef.current && !operatorSelectionModalRef.current.contains(event.target as Node)) {
setShowOperatorSelectionModal(false);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (event.target) {
if (operatorSelectionModalRef.current && !operatorSelectionModalRef.current.contains(event.target as Node)) {
setShowOperatorSelectionModal(false);
}
}
}
}
const handleResize = () => {
setWindowHeight(window.innerHeight);
};

// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
window.addEventListener("resize", handleResize);
document.addEventListener("mousedown", handleClickOutside);

return () => {
document.removeEventListener("mousedown", handleClickOutside);
window.removeEventListener("resize", handleResize);
};
// eslint-disable-next-line react-hooks/exhaustive-deps

Check warning on line 181 in src/components/attribute-filter.tsx

View workflow job for this annotation

GitHub Actions / Build & Run Tests

'react-hooks/exhaustive-deps' rule is disabled but never reported

Check warning on line 181 in src/components/attribute-filter.tsx

View workflow job for this annotation

GitHub Actions / S3 Deploy

'react-hooks/exhaustive-deps' rule is disabled but never reported
}, []);

// Move modal to bottom of window if window is too short
useEffect(() => {
const modalHeight = 89;
if (position.top + modalHeight > windowHeight) {
setFilterModalPosition({top: windowHeight - modalHeight});
} else {
setFilterModalPosition({top: targetFilterBottom || 0});
}
// Adding the other dependencies causes the modal to jump around
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [windowHeight]);

// Change filter-operator-selection-container height if window is shorter than dropdown
useEffect(() => {
if (showOperatorSelectionModal) {
if (dropdownBottom && dropdownBottom > windowHeight) {
const cutOffAmount = dropdownBottom - windowHeight;
setOperatorSelectionListHeight({height: 190 - cutOffAmount - 3});
} else {
setOperatorSelectionListHeight({height: 190});
}
}
},[dropdownBottom, showOperatorSelectionModal, windowHeight]);

const handleReset = () => {
setOperator(currentAttrFilter?.operator || "equals");
Expand Down Expand Up @@ -237,7 +276,7 @@ useEffect(() => {
// key attribute forces inputs to rerender when operator changes
if (operator === "between") {
return (
<div className="between-inputs-wrapper" key="between">
<div className="between-inputs-wrapper" key={`${operator}-${units}`}>
<input ref={filterLowerValueInputElRef} className="filter-value between-low-value"
defaultValue={`${lowerVal} ${currentAttr?.unit[units]}`}>
</input>
Expand All @@ -250,40 +289,45 @@ useEffect(() => {
} else if (operator === "aboveMean" || operator === "belowMean") {
return null;
} else if (operator === "top" || operator === "bottom") {
return <input ref={filterValueTopBottomInputElRef} key={operator} className="filter-value" defaultValue={`${currentFilterValue || "100"}`}></input>;
return <input ref={filterValueTopBottomInputElRef} key={`${operator}-${units}`} className="filter-value" defaultValue={`${currentFilterValue || "100"}`}></input>;
} else {
return <input ref={filterValueInputElRef} key={operator} className="filter-value" defaultValue={`${currentFilterValue} ${currentAttr?.unit[units]}`}></input>;
return <input ref={filterValueInputElRef} key={`${operator}-${units}`} className="filter-value" defaultValue={`${currentFilterValue} ${currentAttr?.unit[units]}`}></input>;
}
};

const handleChangeFilterOperator = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
setShowOperatorSelectionModal(true);
};

const handleSelectFilterOperator = (newOperator: TOperators) => {
const handleSelectFilterOperator = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newOperator = e.currentTarget.value as TOperators;
e.stopPropagation();
setOperator(newOperator);
setShowOperatorSelectionModal(false);
};

const wideModal = !["equals", "top", "bottom"].includes(operator);

return (
<div className={classnames("filter-modal", {"wide": wideModal})} style={position}>
<div className="filter-wrapper">
<div className="filter-operator-wrapper">
<div className="filter-operator" onClick={handleChangeFilterOperator}>
<div className="filter-operator-wrapper" onClick={handleChangeFilterOperator}>
<div className="filter-operator">
{operatorTextMap[operator] || "equals"}
</div>
<EditIcon />
</div>
{!noValueFilter && renderFilterInputs() }
{renderFilterInputs()}
{(operator === "top" || operator === "bottom") && <span>{` results`}</span>}
</div>
<div className="filter-modal-footer">
<button className="filter-button reset" onClick={handleReset}>Reset</button>
<button className="filter-button done" onClick={handleSubmitFilter}>Done</button>
</div>
{showOperatorSelectionModal &&
<div ref={operatorSelectionModalRef} className="filter-operator-selection-container">
<select className="operator-selection" size={11} onChange={(e)=>handleSelectFilterOperator(e.currentTarget.value as TOperators)}>
<div ref={operatorSelectionModalRef} className="filter-operator-selection-container" style={operatorSelectionListHeight}>
<select className="operator-selection" size={11} onChange={handleSelectFilterOperator}>
<option value="equals">{operatorTextMap.equals} ...</option>
<option value="doesNotEqual">{operatorTextMap.doesNotEqual} ...</option>
<option value="greaterThan">{operatorTextMap.greaterThan} ...</option>
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export interface AttrType {
export type TOperators = "equals" | "doesNotEqual" | "greaterThan" | "greaterThanOrEqualTo" | "lessThan"
| "lessThanOrEqualTo" | "between" | "top" | "bottom" | "aboveMean" | "belowMean";

export const operatorTextMap = {equals: "equals", doesNotEqual: "does not equal", greaterThan: "greater than", greaterThanOrEqualTo: "great than or equal to",
lessThan: "less than", lessThanOrEqualTo: "less than or equal to", between: "between", top: "top", bottom: "bottom",
export const operatorTextMap = {equals: "equals", doesNotEqual: "does not equal", greaterThan: "is greater than", greaterThanOrEqualTo: "is greater than or equal to",
lessThan: "is less than", lessThanOrEqualTo: "is less than or equal to", between: "between", top: "top", bottom: "bottom",
aboveMean: "above mean", belowMean: "below mean"};
export const operatorSymbolMap = {equals: "=", doesNotEqual: "≠", greaterThan: ">", greaterThanOrEqualTo: ">=", lessThan: "<", lessThanOrEqualTo: "<="};

Expand Down

0 comments on commit cd7661f

Please sign in to comment.