Skip to content

Commit

Permalink
Fix bugs in text filtering, intial cut at active filter indication in…
Browse files Browse the repository at this point in the history
… col header (#1853)
  • Loading branch information
entrotech authored Sep 19, 2024
1 parent ffea644 commit 9f9eef9
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const useStyles = createUseStyles({
const MultiSelectText = ({ options, selectedOptions, setSelectedOptions }) => {
const classes = useStyles();
const [searchString, setSearchString] = useState("");
const [selectedListItems, setSelectedListItems] = useState([
...selectedOptions
]);

const filteredOptions = options
.filter(o => !!o)
Expand All @@ -43,17 +46,31 @@ const MultiSelectText = ({ options, selectedOptions, setSelectedOptions }) => {
setSearchString(e.target.value);
};

const handleCheckboxChange = o => {
if (selectedOptions.find(so => so.value == o)) {
setSelectedOptions(
selectedOptions.filter(selectedOption => selectedOption.value !== o)
const handleCheckboxChange = e => {
const optionValue = e.target.name;
if (selectedListItems.find(so => so.value == optionValue)) {
const newSelectedListItems = selectedListItems.filter(
selectedOption => selectedOption.value !== optionValue
);
setSelectedListItems(newSelectedListItems);
setSelectedOptions(newSelectedListItems);
} else {
selectedOptions.push({ value: o, label: o });
setSelectedOptions(selectedOptions);
const newSelectedListItems = [
...selectedListItems,
{ value: optionValue, label: optionValue }
];
setSelectedListItems(newSelectedListItems);
setSelectedOptions(newSelectedListItems);
}
};

const isChecked = optionValue => {
const checked = selectedListItems.find(
option => option.value == optionValue
);
return !!checked;
};

return (
<>
<div className={classes.searchBarWrapper}>
Expand All @@ -72,16 +89,17 @@ const MultiSelectText = ({ options, selectedOptions, setSelectedOptions }) => {
</div>

<div style={{ overflow: "scroll", maxHeight: "15rem" }}>
{/* <pre>{JSON.stringify(selectedOptions, null, 2)}</pre>
<pre>{JSON.stringify(options, null, 2)}</pre> */}
<pre>{JSON.stringify(selectedOptions, null, 2)}</pre>
{/* <pre>{JSON.stringify(options, null, 2)}</pre> */}

{filteredOptions.map(o => (
<div key={o} className={classes.listItem}>
<input
style={{ height: "1.5rem" }}
type="checkbox"
checked={selectedOptions.find(option => option.value == o)}
onChange={() => handleCheckboxChange(o)}
name={o}
checked={isChecked(o)}
onChange={handleCheckboxChange}
/>
<span>{o}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from "react";
import PropTypes from "prop-types";
import "react-datepicker/dist/react-datepicker.css";
import { MdFilterAlt } from "react-icons/md";
import { MdFilterAlt, MdFilterList } from "react-icons/md";
import Popup from "reactjs-popup";
import DatePopup from "./DatePopup";
import TextPopup from "./TextPopup";
import VisibilityPopup from "./VisibilityPopup";
import StatusPopup from "./StatusPopup";
import { useTheme } from "react-jss";

const ProjectTableColumnHeader = ({
projects,
Expand All @@ -20,6 +21,31 @@ const ProjectTableColumnHeader = ({
setCheckedProjectIds,
setSelectAllChecked
}) => {
const theme = useTheme();

// Filter is considered Applied if it is not set
// to the default criteria values.
const isFilterApplied = () => {
let propertyName = header.id;
if (header.popupType === "text") {
propertyName += "List";
return criteria[propertyName].length > 0;
}
if (header.popupType === "datetime") {
return (
criteria[header.startDatePropertyName] ||
criteria[header.endDatePropertyName]
);
}
if (header.id === "dateHidden") {
return criteria.visibility !== "visible";
}
if (header.id === "dateSnapshotted") {
return criteria.type !== "all" || criteria.status !== "active";
}
return false;
};

return (
<div style={{ width: "100%", height: "100%" }}>
{header.id !== "checkAllProjects" && header.id !== "contextMenu" ? (
Expand All @@ -33,14 +59,25 @@ const ProjectTableColumnHeader = ({
}}
>
<span>{header.label}</span>
<MdFilterAlt
style={{
backgroundColor: "transparent",
color: "white",
marginLeft: "0.5rem"
}}
alt={`Show column filter and sort popup`}
/>
{isFilterApplied() ? (
<MdFilterAlt
style={{
backgroundColor: "transparent",
color: "white",
marginLeft: "0.5rem"
}}
alt={`Show column filter and sort popup`}
/>
) : (
<MdFilterList
style={{
backgroundColor: "transparent",
color: theme.colorLightGray,
marginLeft: "0.5rem"
}}
alt={`Show column filter and sort popup`}
/>
)}
{/* <FontAwesomeIcon
style={{
backgroundColor: "transparent",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const StatusPopup = ({
// TODO More state variables for status filtering go here

const setDefault = () => {
setTypeSetting("all");
setShowDeleted(false);
setCriteria({
...criteria,
status: showDeleted ? "all" : "active",
Expand Down
141 changes: 130 additions & 11 deletions client/src/components/Projects/ColumnHeaderPopups/TextPopup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,46 @@ import "react-datepicker/dist/react-datepicker.css";
import { MdClose } from "react-icons/md";
import SearchIcon from "../../../images/search.png";
import Select from "react-select";
import MultiSelectText from "./MultiSelectText";
import { createUseStyles } from "react-jss";

const useStyles = createUseStyles({
searchBarWrapper: {
position: "relative",
alignSelf: "center",
marginBottom: "0.5rem"
},
searchBar: {
maxWidth: "100%",
width: "20em",
padding: "12px 12px 12px 48px",
marginRight: "0.5rem"
},
searchIcon: {
position: "absolute",
left: "16px",
top: "14px"
},
listItem: {
display: "flex",
flexDirection: "row",
alignItems: "center",
height: "2rem",
"&:hover": {
backgroundColor: "lightblue"
}
},
toggleButton: {
marginRight: "0",
marginTop: "4px",
marginBottom: "4px",
backgroundColor: "transparent",
border: "0",
cursor: "pointer",
textDecoration: "underline",
display: "flex",
fontWeight: "normal"
}
});

const TextPopup = ({
projects,
Expand All @@ -21,12 +60,15 @@ const TextPopup = ({
setCheckedProjectIds,
setSelectAllChecked
}) => {
const classes = useStyles();

const [newOrder, setNewOrder] = useState(
header.id !== orderBy ? null : order
);
const [selectedListItems, setSelectedListItems] = useState(
criteria[header.id + "List"].map(s => ({ value: s, label: s }))
);
const [searchString, setSearchString] = useState("");

// To build the drop-down list, we want to apply all the criteria that
// are currently selected EXCEPT the criteria we are currently editing.
Expand All @@ -37,6 +79,37 @@ const TextPopup = ({
.filter(value => value !== null)
.sort();

const filteredOptions = selectOptions
.filter(o => !!o)
.filter(opt => opt.toLowerCase().includes(searchString.toLowerCase()));

const onChangeSearchString = e => {
setSearchString(e.target.value);
};

const handleCheckboxChange = e => {
const optionValue = e.target.name;
if (!e.target.checked) {
const newSelectedListItems = selectedListItems.filter(
selectedOption => selectedOption.value !== optionValue
);
setSelectedListItems(newSelectedListItems);
} else {
const newSelectedListItems = [
...selectedListItems,
{ value: optionValue, label: optionValue }
];
setSelectedListItems(newSelectedListItems);
}
};

const isChecked = optionValue => {
const checked = selectedListItems.find(
option => option.value == optionValue
);
return !!checked;
};

const applyChanges = () => {
setCriteria({
...criteria,
Expand All @@ -51,6 +124,7 @@ const TextPopup = ({
};

const setDefault = () => {
setNewOrder(null);
setSelectedListItems([]);
setCheckedProjectIds([]);
setSelectAllChecked(false);
Expand Down Expand Up @@ -101,13 +175,60 @@ const TextPopup = ({
so PMs, designers and possibly stakeholders can evaluate two different implementations of the TextPopup,
and experiment with the UX, in order to make a decision on how to evolve this filter. */}
{header.id === "address" ? (
<MultiSelectText
options={selectOptions}
selectedOptions={selectedListItems}
setSelectedOptions={e => {
setSelectedListItems(e);
}}
/>
// <MultiSelectText
// options={selectOptions}
// selectedOptions={selectedListItems}
// setSelectedOptions={setSelectedListItems}
// />
<>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "baseline"
}}
>
<button
className={classes.toggleButton}
onClick={() => setSelectedListItems([])}
>
clear
</button>
<div>{`${selectedListItems.length} selected`}</div>
</div>
<div className={classes.searchBarWrapper}>
<input
type="text"
value={searchString}
onChange={onChangeSearchString}
placeholder="Search"
className={classes.searchBar}
/>
<img
className={classes.searchIcon}
src={SearchIcon}
alt="Search Icon"
/>
</div>

<div style={{ overflow: "scroll", maxHeight: "15rem" }}>
{/* <pre>{JSON.stringify(selectedListItems, null, 2)}</pre> */}
{/* <pre>{JSON.stringify(options, null, 2)}</pre> */}

{filteredOptions.map(o => (
<div key={o} className={classes.listItem}>
<input
style={{ height: "1.5rem" }}
type="checkbox"
name={o}
checked={isChecked(o)}
onChange={handleCheckboxChange}
/>
<span>{o}</span>
</div>
))}
</div>
</>
) : (
<Select
options={selectOptions.map(text => ({
Expand All @@ -116,9 +237,7 @@ const TextPopup = ({
}))}
name={property}
disabled={false}
onChange={e => {
setSelectedListItems(e);
}}
onChange={setSelectedListItems}
value={selectedListItems}
styles={{ maxHeight: "50rem", maxWidth: "50rem" }}
placeholder={placeholderComponent}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const VisibilityPopup = ({
);

const setDefault = () => {
setVisibilitySetting("visible");
setCriteria({
...criteria,
visibility: "visible"
Expand Down
2 changes: 2 additions & 0 deletions client/src/styles/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module.exports = {
colorPrimary: "#a7c539", //lime green
colorText: "#0F2940", //dark blue
colorLADOT: "#002E6D",
colorGray: "#808080",
colorLightGray: "#A0A0A0",
colorEarnedPoints: "rgb(255, 168, 4)", //orange
colorDisabled: "rgba(0, 0, 0, .05)", //lightest grey transparent
colorCancel: "rgba(0, 0, 0, 0.5)", //light grey, e.g. cancel button
Expand Down

0 comments on commit 9f9eef9

Please sign in to comment.