Skip to content

Commit

Permalink
#9510: Improving GeoFence rule filtering capabilities (#9557)
Browse files Browse the repository at this point in the history
* #9510: enhance rule manager grid by adding checkbox filter mode
  • Loading branch information
mahmoudadel54 authored Nov 3, 2023
1 parent 7c71fbf commit 70e0acf
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 56 deletions.
5 changes: 3 additions & 2 deletions web/client/actions/rulesmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ export function delRules(ids) {
};
}

export function setFilter(key, value) {
export function setFilter(key, value, isResetField) {
return {
type: SET_FILTER,
key,
value
value,
isResetField
};
}

Expand Down
2 changes: 1 addition & 1 deletion web/client/api/geofence/RuleService.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const Api = ({addBaseUrl, addBaseUrlGS, getGeoServerInstance}) => ({
.then( (response) => {
return toJSONPromise(response.data);
}
).then(({RuleList = {}}) => ({rules: RuleList.rule || []}));
).then(({RuleList = {}}) => ({rules: [].concat(RuleList.rule || [])}));
},

getRulesCount: (rulesFiltersValues) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,15 @@ export default compose(
error: undefined,
isEditing: editing
}),
setFilters: (state, {filters = {}, setFilters}) => ({column, filterTerm}) => {
setFilters: (state, {filters = {}, setFilters}) => ({column, filterTerm, isResetField}) => {
// Can add some logic here to clean related filters
if (column.key === "workspace" && filters.layer) {
setFilters("layer");
} else if (column.key === "service" && filters.request) {
setFilters("request");
}
setFilters(column.key, filterTerm);

setFilters(column.key, filterTerm, isResetField);
return {rowsCount: 0, pages: {}};
},
incrementVersion: ({ version }) => () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const parentFiltersSel = createSelector(workspaceSelector, (workspace) => ({
}));
const selector = createSelector([filterSelector, parentFiltersSel], (filter, parentsFilter) => ({
selected: filter.layer,
parentsFilter
parentsFilter,
anyFieldVal: filter.layerAny
}));

export default compose(
Expand All @@ -33,17 +34,20 @@ export default compose(
loadData: loadLayers,
parentsFilter: {},
filter: false,
placeholder: "rulesmanager.placeholders.filter",
placeholder: "rulesmanager.placeholders.filterAny",
unCheckedAnyField: "rulesmanager.tooltip.filterRuleList",
checkedAnyField: "rulesmanager.tooltip.showAllRules",
loadingErrorMsg: {
title: "rulesmanager.errorTitle",
message: "rulesmanager.errorLoadingLayers"
}
},
anyFilterRuleMode: 'layerAny'
}),
withHandlers({
onValueSelected: ({column = {}, onFilterChange = () => {}}) => filterTerm => {
onFilterChange({column, filterTerm});
}
}),
localizedProps(["placeholder"]),
localizedProps(["placeholder", "loadingErroMsg", "checkedAnyField", "unCheckedAnyField"]),
autoComplete
)(PagedCombo);
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const selector = createSelector([filterSelector, parentFiltersSel, servicesConfi
disabled: !filter.service,
service: filter.service,
parentsFilter,
services
services,
anyFieldVal: filter.requestAny
}));


Expand All @@ -34,7 +35,9 @@ export default compose(
valueField: "value",
parentsFilter: {},
filter: "startsWith",
placeholder: "rulesmanager.placeholders.filter",
placeholder: "rulesmanager.placeholders.filterAny",
unCheckedAnyField: "rulesmanager.tooltip.filterRuleList",
checkedAnyField: "rulesmanager.tooltip.showAllRules",
services: {
"WFS": [
"DescribeFeatureType",
Expand All @@ -52,7 +55,8 @@ export default compose(
"GetMap",
"GetStyles"
]
}
},
anyFilterRuleMode: 'requestAny'
}),
withPropsOnChange(["service", "services"], ({services = {}, service}) => {
return {
Expand All @@ -64,6 +68,6 @@ export default compose(
onFilterChange({column, filterTerm});
}
}),
localizedProps(["placeholder"]),
localizedProps(["placeholder", "checkedAnyField", "unCheckedAnyField"]),
fixedOptions
)(PagedCombo);
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { createSelector } from 'reselect';
import { error } from '../../../../../actions/notifications';
import { filterSelector } from '../../../../../selectors/rulesmanager';
const selector = createSelector(filterSelector, (filter) => ({
selected: filter.rolename
selected: filter.rolename,
anyFieldVal: filter.groupAny
}));

export default compose(
Expand All @@ -28,17 +29,20 @@ export default compose(
loadData: getRoles,
parentsFilter: {},
filter: false,
placeholder: "rulesmanager.placeholders.filter",
placeholder: "rulesmanager.placeholders.filterAny",
unCheckedAnyField: "rulesmanager.tooltip.filterRuleList",
checkedAnyField: "rulesmanager.tooltip.showAllRules",
loadingErrorMsg: {
title: "rulesmanager.errorTitle",
message: "rulesmanager.errorLoadingRoles"
}
},
anyFilterRuleMode: 'groupAny'
}),
withHandlers({
onValueSelected: ({column = {}, onFilterChange = () => {}}) => filterTerm => {
onFilterChange({column, filterTerm});
}
}),
localizedProps(["placeholder", "loadingErroMsg"]),
localizedProps(["placeholder", "loadingErroMsg", "checkedAnyField", "unCheckedAnyField"]),
autoComplete
)(PagedCombo);
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { filterSelector, servicesSelector } from '../../../../../selectors/rules

const selector = createSelector(filterSelector, servicesSelector, (filter, services) => ({
selected: filter.service,
services
services,
anyFieldVal: filter.serviceAny
}));

export default compose(
Expand All @@ -27,19 +28,22 @@ export default compose(
valueField: "value",
parentsFilter: {},
filter: "startsWith",
placeholder: "rulesmanager.placeholders.filter",
placeholder: "rulesmanager.placeholders.filterAny",
unCheckedAnyField: "rulesmanager.tooltip.filterRuleList",
checkedAnyField: "rulesmanager.tooltip.showAllRules",
data: [
{value: "WMS", label: "WMS"},
{value: "WFS", label: "WFS"},
{value: "WCS", label: "WCS"}
]
],
anyFilterRuleMode: 'serviceAny'
}),
withPropsOnChange(["services"], ({services, data}) => ({data: services || data})),
withHandlers({
onValueSelected: ({column = {}, onFilterChange = () => {}}) => filterTerm => {
onFilterChange({column, filterTerm});
}
}),
localizedProps(["placeholder"]),
localizedProps(["placeholder", "checkedAnyField", "unCheckedAnyField"]),
fixedOptions
)(PagedCombo);
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { createSelector } from 'reselect';
import { filterSelector } from '../../../../../selectors/rulesmanager';
import { error } from '../../../../../actions/notifications';
const selector = createSelector(filterSelector, (filter) => ({
selected: filter.username
selected: filter.username,
anyFieldVal: filter.userAny
}));

export default compose(
Expand All @@ -28,17 +29,20 @@ export default compose(
loadData: getUsers,
parentsFilter: {},
filter: false,
placeholder: "rulesmanager.placeholders.filter",
placeholder: "rulesmanager.placeholders.filterAny",
unCheckedAnyField: "rulesmanager.tooltip.filterRuleList",
checkedAnyField: "rulesmanager.tooltip.showAllRules",
loadingErrorMsg: {
title: "rulesmanager.errorTitle",
message: "rulesmanager.errorLoadingUsers"
}
},
anyFilterRuleMode: 'userAny'
}),
withHandlers({
onValueSelected: ({column = {}, onFilterChange = () => {}}) => filterTerm => {
onFilterChange({column, filterTerm});
}
}),
localizedProps(["placeholder"]),
localizedProps(["placeholder", "loadingErroMsg", "checkedAnyField", "unCheckedAnyField"]),
autoComplete
)(PagedCombo);
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { createSelector } from 'reselect';
import { filterSelector } from '../../../../../selectors/rulesmanager';

const selector = createSelector(filterSelector, (filter) => ({
selected: filter.workspace
selected: filter.workspace,
anyFieldVal: filter.workspaceAny
}));


Expand All @@ -31,17 +32,20 @@ export default compose(
loadData: getWorkspaces,
parentsFilter: {},
filter: "startsWith",
placeholder: "rulesmanager.placeholders.filter",
placeholder: "rulesmanager.placeholders.filterAny",
unCheckedAnyField: "rulesmanager.tooltip.filterRuleList",
checkedAnyField: "rulesmanager.tooltip.showAllRules",
loadingErrorMsg: {
title: "rulesmanager.errorTitle",
message: "rulesmanager.errorLoadingWorkspaces"
}
},
anyFilterRuleMode: 'workspaceAny'
}),
withHandlers({
onValueSelected: ({column = {}, onFilterChange = () => {}}) => filterTerm => {
onFilterChange({column, filterTerm});
}
}),
localizedProps(["placeholder"]),
localizedProps(["placeholder", "loadingErroMsg", "checkedAnyField", "unCheckedAnyField"]),
autoComplete
)(PagedCombo);
31 changes: 31 additions & 0 deletions web/client/components/misc/__tests__/PagedCombobox-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,35 @@ describe("This test for PagedCombobox component", () => {
done();
}, 50);
});
it('tests PagedCombobox anyFilter Mode', (done) => {
const actions = {
onSelect: () => {}
};
const spy = expect.spyOn(actions, "onSelect");
const data = [{
label: "label", value: "value"
}];
const comp = ReactDOM.render(<PagedCombobox onSelect={actions.onSelect} anyFieldVal={false} anyFilterRuleMode={"userAny"} data={data}/>, document.getElementById("container"));
expect(comp).toExist();
const domNode = ReactDOM.findDOMNode(comp);
expect(domNode).toExist();
const inputs = domNode.getElementsByTagName("input");
const checkbox = inputs[1];
expect(inputs.length).toEqual(2);
expect(checkbox.checked).toEqual(true);
expect(checkbox.name).toEqual('userAny');

const tool = ReactDOM.findDOMNode(TestUtils.scryRenderedDOMComponentsWithClass(comp, "rw-i rw-i-caret-down")[0]);
tool.click();
// this tests if the option list is opened
const firstOption = ReactDOM.findDOMNode(TestUtils.scryRenderedDOMComponentsWithClass(comp, "rw-list-option")[0]);
expect(firstOption).toExist();
const valueOption = firstOption.getElementsByTagName("span")[0];
expect(valueOption).toExist();
TestUtils.Simulate.click(firstOption);
setTimeout(() => {
expect(spy.calls.length).toEqual(1);
done();
}, 50);
});
});
53 changes: 46 additions & 7 deletions web/client/components/misc/combobox/PagedCombobox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,13 @@ class PagedCombobox extends React.Component {
stopPropagation: PropTypes.bool,
clearable: PropTypes.bool,
onReset: PropTypes.func,
attribute: PropTypes.string
attribute: PropTypes.string,
anyFilterRuleMode: PropTypes.string,
onFilterChange: PropTypes.func,
anyFieldVal: PropTypes.bool,
column: PropTypes.object,
checkedAnyField: PropTypes.string,
unCheckedAnyField: PropTypes.string
};

static contextTypes = {
Expand Down Expand Up @@ -94,7 +100,12 @@ class PagedCombobox extends React.Component {
placement: "top"
},
valueField: "value",
clearable: false
clearable: false,
anyFilterRuleMode: '',
onFilterChange: ()=>{},
column: {},
checkedAnyField: "",
unCheckedAnyField: ""
};

componentDidUpdate(prevProps) {
Expand All @@ -118,6 +129,21 @@ class PagedCombobox extends React.Component {
</OverlayTrigger>);
};

renderTooltipCheckbox = () => {
const { onFilterChange, anyFieldVal } = this.props;

let checkboxInput = (
<input onChange={(evt)=>{
onFilterChange({column: {key: evt.target.name}, filterTerm: !evt.target.checked});
}} type="checkbox" checked={!!(typeof anyFieldVal === 'boolean' && !anyFieldVal)}
name={this.props.anyFilterRuleMode} />
);
const tooltip = (<Tooltip id={this.props.tooltip.id + anyFieldVal ? "checked" : "unchecked"}>
{ !!(typeof anyFieldVal === 'boolean' && !anyFieldVal) ? this.props.checkedAnyField : this.props.unCheckedAnyField }</Tooltip>);
return (<OverlayTrigger key={this.props.tooltip.overlayTriggerKey} placement={this.props.tooltip.placement} overlay={tooltip}>
{ checkboxInput }
</OverlayTrigger>);
}

renderPagination = () => {
const firstPage = this.props.pagination.firstPage;
Expand Down Expand Up @@ -182,17 +208,30 @@ class PagedCombobox extends React.Component {
return this.props.tooltip && this.props.tooltip.enabled ? this.renderWithTooltip(field) : field;
}
render() {
const {selectedValue: v, disabled, onReset, label: l, clearable} = this.props;
const {selectedValue: v, disabled, onReset, label: l, clearable, onFilterChange } = this.props;
let label = l ? (<label>{l}</label>) : (<span/>); // TODO change "the else case" value with null ?
return (
<div className="autocompleteField">
{label}
<div className={`autocompleteField ${this.props.anyFilterRuleMode ? 'd-flex' : ''}`}>
{clearable ? (
<div className={`rw-combo-clearable ${disabled && 'disabled' || ''}`}>
{this.renderField()}
<span className={`rw-combo-clear ${!v && 'hidden' || ''}`} onClick={onReset}>x</span>
</div>) : this.renderField()
<span className={`rw-combo-clear ${!v && 'hidden' || ''}`} onClick={()=>{
if (this.props.anyFilterRuleMode) {
// reset the checkbox as well
onFilterChange({column: {key: this.props.column?.key}, filterTerm: undefined, isResetField: true});
} else {
onReset();
}
}}>x</span>
</div>) :
this.renderField()
}
&nbsp;
<div>
{label}
{ this.props.anyFilterRuleMode ?
this.renderTooltipCheckbox() : null}
</div>
</div>);
}
}
Expand Down
Loading

0 comments on commit 70e0acf

Please sign in to comment.