-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ERM-3056, ERM-3057, ERM-3058, ERM-3059 Filter for documents in licens… (
#645) * ERM-3056, ERM-3057, ERM-3058, ERM-3059 Filter for documents in licenses and amendments * copy document filter components from ui-agreements * add extra filter and rule component for supplementary documents, leave documents filter and rule for core documents * add component for rule constants * add translations * ERM-3056, ERM-3057, ERM-3058, ERM-3059 Filter for documents in licenses and amendments * fix lint errors * * fix naming and type errors * ERM-3056, ERM-3057, ERM-3058, ERM-3059 Filter for documents in licenses and amendments * avoid duplicate code * * fix translations * improve constants component * * remove comment * * change atTypeValues to refdataValues, selectify atTypeValues in DocumentFilterRule * * add comment * ERM-3056, ERM-3057, ERM-3058, ERM-3059 Filter for documents in licenses and amendments * get atTypeValues like other refdata values are passed * remove unnecessary selectify * ERM-3056, ERM-3057, ERM-3058, ERM-3059 Filter for documents in licenses and amendments * remove replaceAll function
- Loading branch information
Showing
9 changed files
with
572 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import PropTypes from 'prop-types'; | ||
import { useState } from 'react'; | ||
import { | ||
Accordion, | ||
FilterAccordionHeader, | ||
Layout, | ||
} from '@folio/stripes/components'; | ||
import { FormattedMessage } from 'react-intl'; | ||
|
||
import { | ||
deparseKiwtQueryFilters, | ||
parseKiwtQueryFilters, | ||
} from '@k-int/stripes-kint-components'; | ||
|
||
import DocumentFilterForm from './DocumentFilterForm'; | ||
|
||
const DocumentFilter = ({ activeFilters, atTypeValues = [], filterHandlers }) => { | ||
// atTypeValues are only passed for SupplementaryDocumentFilter | ||
const filterType = atTypeValues.length > 0 ? 'supplementaryDocuments' : 'documents'; | ||
const [editingFilters, setEditingFilters] = useState(false); | ||
const openEditModal = () => setEditingFilters(true); | ||
const closeEditModal = () => setEditingFilters(false); | ||
|
||
// Due to how filters are handled within SearchAndSortQuery the filter string needs to be parsed back into a usual object | ||
const parseQueryString = (filterArray) => { | ||
if (filterArray?.length) { | ||
// Since the filters are grouped, the docuements filterstring will be within the first array element | ||
const parsedFilters = parseKiwtQueryFilters(filterArray?.[0]); | ||
|
||
// This reduce function removes all array elements that contain solely comparators so the initial value shape is returned | ||
// --- | ||
// Before parsing: | ||
// [ | ||
// [{ path, comparator, value }], | ||
// '||', | ||
// [{ path, comparator, value }, '||', { path, comparator, value }], | ||
// ]; | ||
// --- | ||
// After: parsing | ||
// [ | ||
// [{ path, comparator, value }], | ||
// [ | ||
// { path, comparator, value }, | ||
// { path, comparator, value }, | ||
// ], | ||
// ]; | ||
const filters = parsedFilters.reduce((acc, curr) => { | ||
if (typeof curr === 'string') { | ||
return [...acc]; | ||
} | ||
return [...acc, { rules: curr.filter((e) => typeof e !== 'string') }]; | ||
}, []); | ||
return filters; | ||
} | ||
return []; | ||
}; | ||
|
||
const parsedFilterData = parseQueryString(activeFilters?.[filterType] || []); | ||
|
||
const handleSubmit = (values) => { | ||
// In order to convert the form values into the shape for them to be deparsed we do the inverse of the above | ||
// Adding a || operator between all elements of the filters array and a && operator between all elements of the nested arrays | ||
// With special logic to ensure that operators are not added infront of the first elements of any arrays, to ensure no grouping errors | ||
const kiwtQueryShape = values?.filters?.reduce((acc, curr, index) => { | ||
let newAcc = [...acc]; | ||
|
||
if (index !== 0) { | ||
newAcc = [...newAcc, '||']; | ||
} | ||
|
||
newAcc = [ | ||
...newAcc, | ||
curr.rules.reduce((a, c, i) => { | ||
return [ | ||
...a, | ||
i !== 0 ? '&&' : null, // Don't group on first entry | ||
c, | ||
].filter(Boolean); | ||
}, []), | ||
]; | ||
|
||
return newAcc; | ||
}, []); | ||
|
||
filterHandlers.state({ | ||
...activeFilters, | ||
[filterType]: [ | ||
// Currently the deparse function returns a query string containing whitespace which leads to grouping errors | ||
// This regex removes all whitespace from the querystring | ||
deparseKiwtQueryFilters(kiwtQueryShape), | ||
], | ||
}); | ||
setEditingFilters(false); | ||
}; | ||
|
||
return ( | ||
<Accordion | ||
closedByDefault | ||
displayClearButton={!!parsedFilterData?.length} | ||
header={FilterAccordionHeader} | ||
id={`clickable-agreement-${filterType}-filter`} | ||
label={<FormattedMessage id={`stripes-erm-components.documentFilter.${filterType}`} />} | ||
onClearFilter={() => filterHandlers.state({ ...activeFilters, [filterType]: [] }) | ||
} | ||
separator={false} | ||
> | ||
{!!parsedFilterData?.length && ( | ||
<Layout className="padding-bottom-gutter"> | ||
<FormattedMessage | ||
id="stripes-erm-components.documentFilter.filtersApplied" | ||
values={{ filtersLength: parsedFilterData?.length }} | ||
/> | ||
</Layout> | ||
)} | ||
<DocumentFilterForm | ||
atTypeValues={atTypeValues} | ||
editingFilters={editingFilters} | ||
filters={parsedFilterData} | ||
handlers={{ | ||
closeEditModal, | ||
openEditModal, | ||
}} | ||
onSubmit={handleSubmit} | ||
/> | ||
</Accordion> | ||
); | ||
}; | ||
|
||
DocumentFilter.propTypes = { | ||
activeFilters: PropTypes.object, | ||
atTypeValues: PropTypes.arrayOf(PropTypes.object), | ||
filterHandlers: PropTypes.object, | ||
}; | ||
|
||
export default DocumentFilter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import PropTypes from 'prop-types'; | ||
import { Button, Row, Col, Label } from '@folio/stripes/components'; | ||
import { useForm, useFormState } from 'react-final-form'; | ||
import { FieldArray } from 'react-final-form-arrays'; | ||
import { FormattedMessage } from 'react-intl'; | ||
import DocumentFilterRule from './DocumentFilterRule'; | ||
|
||
const DocumentFilterField = ({ atTypeValues, index, name }) => { | ||
const { | ||
mutators: { push }, | ||
} = useForm(); | ||
const { values } = useFormState(); | ||
const renderRuleComponent = (ruleFields, ruleFieldName, ruleFieldIndex) => { | ||
return ( | ||
<DocumentFilterRule | ||
key={ruleFieldName} | ||
ariaLabelledby={`selected-document-item-name-${index}`} | ||
atTypeValues={atTypeValues} | ||
index={ruleFieldIndex} | ||
name={ruleFieldName} | ||
onDelete={() => ruleFields.remove(ruleFieldIndex)} | ||
value={values.filters[index]?.rules[ruleFieldIndex]} | ||
/> | ||
); | ||
}; | ||
|
||
return ( | ||
<> | ||
<Row> | ||
<Col xs={2} /> | ||
<Col xs={3}> | ||
<Label id="rule-column-header-attribute" required> | ||
<FormattedMessage id="stripes-erm-components.documentFilter.attribute" /> | ||
</Label> | ||
</Col> | ||
<Col xs={3}> | ||
<Label id="rule-column-header-comparator" required> | ||
<FormattedMessage id="stripes-erm-components.comparator" /> | ||
</Label> | ||
</Col> | ||
<Col xs={3}> | ||
<Label id="rule-column-header-value" required> | ||
<FormattedMessage id="stripes-erm-components.value" /> | ||
</Label> | ||
</Col> | ||
<Col xs={1} /> | ||
</Row> | ||
|
||
<FieldArray name={`${name}.rules`}> | ||
{({ fields: ruleFields }) => ruleFields.map((ruleFieldName, ruleFieldIndex) => ( | ||
renderRuleComponent(ruleFields, ruleFieldName, ruleFieldIndex) | ||
)) | ||
} | ||
</FieldArray> | ||
<Button | ||
data-test-add-rule-btn | ||
onClick={() => push(`${name}.rules`)} | ||
> | ||
<FormattedMessage id="stripes-erm-components.documentFilter.addRule" /> | ||
</Button> | ||
</> | ||
); | ||
}; | ||
|
||
DocumentFilterField.propTypes = { | ||
atTypeValues: PropTypes.arrayOf(PropTypes.object), | ||
index: PropTypes.number, | ||
name: PropTypes.string, | ||
}; | ||
|
||
export default DocumentFilterField; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { useForm } from 'react-final-form'; | ||
import { FieldArray } from 'react-final-form-arrays'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { | ||
Button, | ||
Card, | ||
IconButton, | ||
Layout, | ||
Tooltip, | ||
} from '@folio/stripes/components'; | ||
import { FormattedMessage } from 'react-intl'; | ||
import DocumentFilterField from './DocumentFilterField'; | ||
|
||
const propTypes = { | ||
atTypeValues: PropTypes.arrayOf(PropTypes.object), | ||
}; | ||
|
||
const DocumentFilterFieldArray = ({ atTypeValues }) => { | ||
const { | ||
mutators: { push }, | ||
} = useForm(); | ||
|
||
return ( | ||
<> | ||
<FieldArray name="filters"> | ||
{({ fields }) => fields.map((name, index) => { | ||
return ( | ||
<> | ||
<Card | ||
key={`document-filter-card[${index}]`} | ||
headerEnd={ | ||
fields?.length > 1 ? ( | ||
<Tooltip | ||
id={`document-filter-card-delete-[${index}]-tooltip`} | ||
text={ | ||
<FormattedMessage | ||
id="stripes-erm-components.documentFilter.deleteFilterIndex" | ||
values={{ number: index + 1 }} | ||
/> | ||
} | ||
> | ||
{({ ref, ariaIds }) => ( | ||
<IconButton | ||
ref={ref} | ||
aria-labelledby={ariaIds.text} | ||
icon="trash" | ||
id={`document-filter-card-delete-[${index}]`} | ||
onClick={() => fields.remove(index)} | ||
/> | ||
)} | ||
</Tooltip> | ||
) : null | ||
} | ||
headerStart={ | ||
<strong> | ||
<FormattedMessage | ||
id="stripes-erm-components.documentFilter.documentFilterIndex" | ||
values={{ number: index + 1 }} | ||
/> | ||
</strong> | ||
} | ||
marginBottom0={index !== fields.length - 1} | ||
> | ||
<DocumentFilterField | ||
atTypeValues={atTypeValues} | ||
fields={fields} | ||
index={index} | ||
name={name} | ||
/> | ||
</Card> | ||
{index < fields.value.length - 1 && ( | ||
<Layout className="textCentered"> | ||
<FormattedMessage id="stripes-erm-components.OR" /> | ||
</Layout> | ||
)} | ||
</> | ||
); | ||
}) | ||
} | ||
</FieldArray> | ||
<Button onClick={() => push('filters', { rules: [{}] })}> | ||
<FormattedMessage id="stripes-erm-components.documentFilter.addFilter" /> | ||
</Button> | ||
</> | ||
); | ||
}; | ||
|
||
DocumentFilterFieldArray.propTypes = propTypes; | ||
export default DocumentFilterFieldArray; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import PropTypes from 'prop-types'; | ||
import { Button } from '@folio/stripes/components'; | ||
import arrayMutators from 'final-form-arrays'; | ||
import { FormModal } from '@k-int/stripes-kint-components'; | ||
import { FormattedMessage } from 'react-intl'; | ||
import DocumentFilterFieldArray from './DocumentFilterFieldArray'; | ||
|
||
const DocumentFilterForm = ({ | ||
atTypeValues, | ||
editingFilters, | ||
filters, | ||
handlers: { openEditModal, closeEditModal }, | ||
onSubmit, | ||
}) => { | ||
const filterBuilder = atTypeValues.length > 0 ? 'supplementaryDocumentFilterBuilder' : 'coreDocumentFilterBuilder'; | ||
return ( | ||
<> | ||
<Button onClick={openEditModal}> | ||
<FormattedMessage id="stripes-erm-components.documentFilter.editDocumentFilters" /> | ||
</Button> | ||
<FormModal | ||
initialValues={{ | ||
filters: filters?.length ? filters : [{ rules: [{}] }], | ||
}} | ||
modalProps={{ | ||
dismissible: true, | ||
enforceFocus: false, | ||
label: <FormattedMessage id={`stripes-erm-components.documentFilter.${filterBuilder}`} />, | ||
onClose: closeEditModal, | ||
open: editingFilters, | ||
size: 'medium', | ||
}} | ||
mutators={{ ...arrayMutators }} | ||
onSubmit={onSubmit} | ||
> | ||
<DocumentFilterFieldArray atTypeValues={atTypeValues} /> | ||
</FormModal> | ||
</> | ||
); | ||
}; | ||
|
||
DocumentFilterForm.propTypes = { | ||
atTypeValues: PropTypes.arrayOf(PropTypes.object), | ||
editingFilters: PropTypes.bool, | ||
filters: PropTypes.arrayOf(PropTypes.object), | ||
handlers: PropTypes.shape({ | ||
closeEditModal: PropTypes.func.isRequired, | ||
openEditModal: PropTypes.func.isRequired, | ||
}), | ||
onSubmit: PropTypes.func, | ||
}; | ||
export default DocumentFilterForm; |
Oops, something went wrong.