-
Notifications
You must be signed in to change notification settings - Fork 4
Feature: new filter component #927
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { createAction } from "openstack-uicore-foundation/lib/utils/actions"; | ||
|
|
||
| export const SAVE_FILTERS = "SAVE_FILTERS"; | ||
|
|
||
| export const saveFilters = | ||
| (id, filters = [], joinOperator = "all") => | ||
| (dispatch) => { | ||
| dispatch(createAction(SAVE_FILTERS)({ id, filters, joinOperator })); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /** | ||
| * Copyright 2026 OpenStack Foundation | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * */ | ||
|
|
||
| import React from "react"; | ||
| import T from "i18n-react/dist/i18n-react"; | ||
| import { Select, FormControl, MenuItem, InputLabel } from "@mui/material"; | ||
| import PropTypes from "prop-types"; | ||
|
|
||
| const Dropdown = ({ | ||
| id, | ||
| value, | ||
| options, | ||
| placeholder, | ||
| label, | ||
| onChange, | ||
| ...rest | ||
| }) => { | ||
| const finalPlaceholder = | ||
| placeholder || T.translate("general.select_an_option"); | ||
|
|
||
| return ( | ||
| <FormControl fullWidth> | ||
| {label && <InputLabel id={`${id}-label`}>{label}</InputLabel>} | ||
| <Select | ||
| value={value} | ||
| label={label} | ||
| onChange={onChange} | ||
| labelId={`${id}-label`} | ||
| displayEmpty | ||
| // eslint-disable-next-line react/jsx-props-no-spreading | ||
| {...rest} | ||
| renderValue={(selected) => { | ||
| if (selected == null || selected === "") { | ||
| return <em>{finalPlaceholder}</em>; | ||
| } | ||
| const selectedOption = options.find( | ||
| ({ value }) => value === selected | ||
| ); | ||
| return selectedOption ? selectedOption.label : ""; | ||
| }} | ||
| > | ||
| {options?.map((op) => ( | ||
| <MenuItem key={`selectop-${op.value}`} value={op.value}> | ||
| {op.label} | ||
| </MenuItem> | ||
| ))} | ||
| </Select> | ||
| </FormControl> | ||
| ); | ||
| }; | ||
|
|
||
| Dropdown.propTypes = { | ||
| id: PropTypes.string.isRequired, | ||
| value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | ||
| options: PropTypes.arrayOf( | ||
| PropTypes.shape({ | ||
| value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) | ||
| .isRequired, | ||
| label: PropTypes.string.isRequired | ||
| }) | ||
| ).isRequired, | ||
| label: PropTypes.string, | ||
| placeholder: PropTypes.string, | ||
| onChange: PropTypes.func.isRequired | ||
| }; | ||
|
|
||
| Dropdown.defaultProps = { | ||
| value: null, | ||
| label: "", | ||
| placeholder: "" | ||
| }; | ||
|
|
||
| export default Dropdown; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,145 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Copyright 2026 OpenStack Foundation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * you may not use this file except in compliance with the License. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * You may obtain a copy of the License at | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Unless required by applicable law or agreed to in writing, software | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * See the License for the specific language governing permissions and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * limitations under the License. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import T from "i18n-react/dist/i18n-react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Box, Grid2, IconButton } from "@mui/material"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import DeleteIcon from "@mui/icons-material/Delete"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import AddIcon from "@mui/icons-material/Add"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import PropTypes from "prop-types"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Dropdown from "./Dropdown"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ValueInput from "./ValueInput"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+14
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Import This file uses Suggested fix import React from "react";
+import T from "i18n-react/dist/i18n-react";
import { Button, Grid2 } from "react-bootstrap";Also applies to: 47-64 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import RoundButton from "./RoundButton"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const Filter = ({ id, value, criterias, onChange, onAdd, onDelete }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const criteriaOptions = criterias.map(({ key, label }) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const criteriaObj = criterias.find(({ key }) => key === value?.criteria); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const operatorOptions = criteriaObj?.operators || []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const valueSettings = criteriaObj?.values || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleChange = (prop, val) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange({ ...value, [prop]: val }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleChangeCriteria = (ev) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const val = ev.target.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleChange("criteria", val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleChangeOperator = (ev) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const val = ev.target.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleChange("operator", val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reset dependent fields when criteria changes. When criteria changes, previous operator/value are kept, which can produce invalid filter combinations for the new criteria. 💡 Proposed fix const handleChangeCriteria = (ev) => {
const val = ev.target.value;
- handleChange("criteria", val);
+ onChange({
+ ...value,
+ criteria: val,
+ operator: "",
+ value: ""
+ });
};Also applies to: 63-80 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleChangeValue = (ev) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const val = ev.target.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleChange("value", val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Grid2 container spacing={2} sx={{ alignItems: "center", mb: 2 }}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Grid2 size={11}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Box sx={{ display: "flex", alignItems: "center", gap: "14px" }}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Dropdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id={`${id}-column`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={value?.criteria || ""} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder={T.translate("grid_filter.select_criteria")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options={criteriaOptions} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={handleChangeCriteria} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Dropdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id={`${id}-operator`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={value?.operator || ""} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder={T.translate("grid_filter.select_operator")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options={operatorOptions} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={!value?.criteria} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={handleChangeOperator} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ValueInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id={`${id}-value`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={value?.value || ""} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type={valueSettings.type} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder={T.translate("grid_filter.select_values")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={!value?.criteria} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line react/jsx-props-no-spreading | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {...valueSettings.props} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={handleChangeValue} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: For MUI Select (Material UI): - onChange receives a SelectChangeEvent as its first argument. - The current selected value is exposed as event.target.value (any). [1] - The event signature is onChange(event: SelectChangeEvent, child?: object) => void. [2] For MUI TextField (Material UI): - onChange receives the usual event object from the underlying input. - The current text value is exposed as event.target.value. [3][4] Practically, this means: - Select: onChange={(event) => setState(event.target.value)} - TextField: onChange={(event) => setState(event.target.value)} (If you’re using TypeScript, MUI Select’s event.target.value may be typed as unknown depending on your setup; the docs still indicate you read it from event.target.value.) [2][5] Citations:
🏁 Script executed: # Find Dropdown and ValueInput components
fd -t f -e jsx -e js -e tsx -e ts | head -20Repository: fntechgit/summit-admin Length of output: 796 🏁 Script executed: # Search for Dropdown component definition
rg "export.*Dropdown" -t jsx -t js -t tsx -t ts -A 5Repository: fntechgit/summit-admin Length of output: 94 🏁 Script executed: # Search for ValueInput component definition
rg "export.*ValueInput" -t jsx -t js -t tsx -t ts -A 5Repository: fntechgit/summit-admin Length of output: 94 🏁 Script executed: # Check the Filter.jsx file to understand context better
fd -t f -name "Filter.jsx" -exec cat {} \;Repository: fntechgit/summit-admin Length of output: 297 🏁 Script executed: # Find Dropdown component definition
rg "export.*Dropdown|function Dropdown|const Dropdown" -A 10Repository: fntechgit/summit-admin Length of output: 2795 🏁 Script executed: # Find ValueInput component definition
rg "export.*ValueInput|function ValueInput|const ValueInput" -A 10Repository: fntechgit/summit-admin Length of output: 1220 🏁 Script executed: # Locate and read Filter.jsx file
find . -name "Filter.jsx" -type fRepository: fntechgit/summit-admin Length of output: 116 🏁 Script executed: # Read complete Dropdown.jsx
cat src/components/GridFilter/components/Dropdown.jsxRepository: fntechgit/summit-admin Length of output: 2354 🏁 Script executed: # Read complete Filter.jsx
cat src/components/GridFilter/components/Filter.jsxRepository: fntechgit/summit-admin Length of output: 4092 🏁 Script executed: # Read complete ValueInput implementation
cat src/components/GridFilter/components/ValueInput/index.jsxRepository: fntechgit/summit-admin Length of output: 1651 Extract The Suggested fix <Dropdown
id={`${id}-column`}
value={value.criteria}
placeholder={T.translate("grid_filter.select_criteria")}
// eslint-disable-next-line react/jsx-props-no-spreading
{...criteriaSettings}
- onChange={(val) => handleChange("criteria", val)}
+ onChange={(event) => handleChange("criteria", event.target.value)}
/>
<Dropdown
id={`${id}-operator`}
value={value.operator}
placeholder={T.translate("grid_filter.select_operator")}
// eslint-disable-next-line react/jsx-props-no-spreading
{...operatorSettings}
- onChange={(val) => handleChange("operator", val)}
+ onChange={(event) => handleChange("operator", event.target.value)}
/>
<ValueInput
id={`${id}-value`}
value={value.value}
type={valueSettings.type}
placeholder={T.translate("grid_filter.select_values")}
// eslint-disable-next-line react/jsx-props-no-spreading
{...valueSettings.props}
- onChange={(val) => handleChange("value", val)}
+ onChange={(event) => handleChange("value", event.target.value)}
/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Box> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Grid2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Grid2 size={1}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {value?.id !== "new" ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <IconButton | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| aria-label="delete-filter" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => onDelete(value)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="large" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DeleteIcon fontSize="large" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </IconButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <RoundButton | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variant="contained" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| aria-label="add-filter" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => onAdd()} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={!value?.criteria || !value?.operator || !value?.value} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allow valid falsy values ( The current disable condition treats falsy values as missing, so valid values like 💡 Proposed fix+ const hasValue =
+ value?.value !== null &&
+ value?.value !== undefined &&
+ value?.value !== "" &&
+ (!Array.isArray(value?.value) || value.value.length > 0);
@@
- disabled={!value?.criteria || !value?.operator || !value?.value}
+ disabled={!value?.criteria || !value?.operator || !hasValue}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sx={{ ml: "4px" }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <AddIcon fontSize="large" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </RoundButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Grid2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Grid2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Filter.propTypes = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: PropTypes.string.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: PropTypes.shape({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| criteria: PropTypes.string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| operator: PropTypes.string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: PropTypes.oneOfType([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropTypes.string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropTypes.number, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropTypes.bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropTypes.array | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| criterias: PropTypes.arrayOf( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropTypes.shape({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key: PropTypes.string.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: PropTypes.string.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| operators: PropTypes.arrayOf( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropTypes.shape({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: PropTypes.string.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: PropTypes.string.isRequired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| values: PropTypes.shape({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: PropTypes.string.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| props: PropTypes.object.isRequired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange: PropTypes.func.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onAdd: PropTypes.func.isRequired, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onDelete: PropTypes.func.isRequired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Filter.defaultProps = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default Filter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * Copyright 2026 OpenStack Foundation | ||||||||||||||||||||||||||||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||||||||||||||||
| * you may not use this file except in compliance with the License. | ||||||||||||||||||||||||||||||||
| * You may obtain a copy of the License at | ||||||||||||||||||||||||||||||||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||||||||||||
| * Unless required by applicable law or agreed to in writing, software | ||||||||||||||||||||||||||||||||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||||||||||||||||||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||||||||||||||||||||
| * See the License for the specific language governing permissions and | ||||||||||||||||||||||||||||||||
| * limitations under the License. | ||||||||||||||||||||||||||||||||
| * */ | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import React from "react"; | ||||||||||||||||||||||||||||||||
| import PropTypes from "prop-types"; | ||||||||||||||||||||||||||||||||
| import { Chip, IconButton } from "@mui/material"; | ||||||||||||||||||||||||||||||||
| import FilterListIcon from "@mui/icons-material/FilterList"; | ||||||||||||||||||||||||||||||||
| import T from "i18n-react/dist/i18n-react"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const FilterButton = ({ filterCount, onClick, onDelete }) => { | ||||||||||||||||||||||||||||||||
| if (filterCount > 0) { | ||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||
| <Chip | ||||||||||||||||||||||||||||||||
| icon={<FilterListIcon fontSize="large" />} | ||||||||||||||||||||||||||||||||
| label={`${filterCount} ${T.translate("grid_filter.filters")}`} | ||||||||||||||||||||||||||||||||
| onClick={onClick} | ||||||||||||||||||||||||||||||||
| onDelete={onDelete} | ||||||||||||||||||||||||||||||||
| sx={{ | ||||||||||||||||||||||||||||||||
| "& .MuiChip-label": { fontSize: "13px" }, | ||||||||||||||||||||||||||||||||
| backgroundColor: "grey.700", | ||||||||||||||||||||||||||||||||
| color: "white", | ||||||||||||||||||||||||||||||||
| "& .MuiChip-icon": { color: "white" }, | ||||||||||||||||||||||||||||||||
| "& .MuiChip-deleteIcon": { | ||||||||||||||||||||||||||||||||
| color: "rgba(255,255,255,0.7)", | ||||||||||||||||||||||||||||||||
| "&:hover": { color: "white" } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||
| <IconButton | ||||||||||||||||||||||||||||||||
| size="large" | ||||||||||||||||||||||||||||||||
| onClick={onClick} | ||||||||||||||||||||||||||||||||
| sx={{ mr: 1, top: "-6px", position: "relative" }} | ||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||
| <FilterListIcon fontSize="large" /> | ||||||||||||||||||||||||||||||||
| </IconButton> | ||||||||||||||||||||||||||||||||
|
Comment on lines
+43
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add an accessible label to the icon-only filter button. The icon-only 💡 Proposed fix return (
<IconButton
+ aria-label={T.translate("grid_filter.filters")}
size="large"
onClick={onClick}
sx={{ mr: 1, top: "-6px", position: "relative" }}
>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| FilterButton.propTypes = { | ||||||||||||||||||||||||||||||||
| filterCount: PropTypes.number.isRequired, | ||||||||||||||||||||||||||||||||
| onClick: PropTypes.func.isRequired, | ||||||||||||||||||||||||||||||||
| onDelete: PropTypes.func.isRequired | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export default FilterButton; | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /** | ||
| * Copyright 2026 OpenStack Foundation | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * */ | ||
|
|
||
| import React from "react"; | ||
| import { Button } from "@mui/material"; | ||
| import PropTypes from "prop-types"; | ||
|
|
||
| const RoundButton = ({ children, sx = {}, ...props }) => ( | ||
| <Button | ||
| // eslint-disable-next-line react/jsx-props-no-spreading | ||
| {...props} | ||
| sx={{ | ||
| width: 40, | ||
| height: 40, | ||
| minWidth: "auto", | ||
| borderRadius: "50%", | ||
| padding: 0, | ||
| ...sx | ||
| }} | ||
| > | ||
| {children} | ||
| </Button> | ||
| ); | ||
|
|
||
| RoundButton.propTypes = { | ||
| children: PropTypes.node.isRequired, | ||
| sx: PropTypes.object | ||
| }; | ||
|
|
||
| RoundButton.defaultProps = { | ||
| sx: {} | ||
| }; | ||
|
|
||
| export default RoundButton; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 9547
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 2663
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 2050
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 1385
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 2075
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 2616
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 210
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 2557
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 1861
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 139
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 839
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 1468
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 962
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 1258
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 1283
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 4731
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 960
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 48
🏁 Script executed:
Repository: fntechgit/summit-admin
Length of output: 50
🏁 Script executed:
# Check the full reducer to see if all metadata properties are used cat src/reducers/summit_speakers/summit-speakers-list-reducer.jsRepository: fntechgit/summit-admin
Length of output: 5477
Replace array spread in metadata with explicit
filtersproperty to avoid polluting state with numeric keys.Line 940 spreads array filters into the metadata object passed to
getRequest, which creates numeric keys (0, 1, 2…) instead of a properfiltersproperty. These numeric keys end up merged into Redux state via the reducer's...restspread, polluting the state structure and reducing clarity.💡 Proposed fix
🤖 Prompt for AI Agents