Skip to content
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

Fix/autocomplete filter not behaving correctly using or operator page 4078 #3500

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 58 additions & 55 deletions src/components/autocompleteInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,20 @@
const belowMinimumMessage = useText(validationBelowMinimum);
const helperTextResolved = useText(helperTextRaw);
const modelProperty = getProperty(property || '') || {};
const labelProperty = getProperty(labelPropertyId) || {};
const { modelId: propertyModelId, referenceModelId } = modelProperty;
const { contextModelId } = model;
const modelId =
contextModelId || referenceModelId || propertyModelId || model || '';
const idProperty = getIdProperty(modelId) || {};
stefan-betty marked this conversation as resolved.
Show resolved Hide resolved
const labelProperty = getProperty(labelPropertyId) || idProperty;
JorisPannekeet marked this conversation as resolved.
Show resolved Hide resolved
const propertyModel = getModel(modelId);
const defaultLabelProperty =
getProperty(
propertyModel && propertyModel.labelPropertyId
? propertyModel.labelPropertyId
: '',
) || {};
const idProperty = getIdProperty(modelId) || {};

const isListProperty =
modelProperty.kind === 'LIST' || modelProperty.kind === 'list';

Expand Down Expand Up @@ -316,12 +317,9 @@
/*
* Build up array for relational label
*/
const idOrPathLabel =
typeof labelPropertyId.id !== 'undefined'
? labelPropertyId.id
: labelPropertyId;
const labelPropertyPath =
typeof idOrPathLabel === 'string' ? [idOrPathLabel] : idOrPathLabel;
const labelPropertyPath = labelPropertyId.id
? labelPropertyId.id
: [labelProperty.id];

/*
* We extend the option filter with the value of the `value` state and the value of the `inputValue` state.
Expand All @@ -330,62 +328,67 @@
*/
/* eslint-disable no-underscore-dangle */

const numberDebouncedInputValue = parseFloat(debouncedInputValue, 10);
// After searching or setting a default value
if (debouncedInputValue) {
// ["relational_property_name", "property_name"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like commented code; Can it be removed?

const propertyNames = labelPropertyPath.map((u) => getProperty(u).name);

// get nested value like this:
// value["relational_property_name"]["property_name"]
const valueFromProperyNames = propertyNames.reduce(
(acc, propertyName) => acc && acc[propertyName],
value,
);

// There is an empty string as fallback, because empty label is not a valid value to search on
const textDebouncedInputValue =
JorisPannekeet marked this conversation as resolved.
Show resolved Hide resolved
debouncedInputValue === EMPTY_LABEL ? '' : debouncedInputValue;
const numberDebouncedInputValue = parseFloat(debouncedInputValue, 10);
const parsedDebouncedValue = searchPropIsNumber
? numberDebouncedInputValue
: debouncedInputValue;

const parsedDebouncedValue = searchPropIsNumber
? numberDebouncedInputValue
: textDebouncedInputValue;
const searchPropertyValue =
typeof value === 'string' ? value : valueFromProperyNames;

const currentSearchValue =
typeof value === 'string' ? value : value[searchProp.name];
// When you select a property with a null value, use an empty string as fallback to prevent doesNotMatch: null
const isEmptySearchValue =
searchPropertyValue === null || searchPropertyValue === undefined;

const remainingRecordsFilter = {
[searchProp.name]: {
[searchPropIsNumber ? 'neq' : 'doesNotMatch']: currentSearchValue,
},
};
const currentSearchValue =
isEmptySearchValue && !searchPropIsNumber ? '' : searchPropertyValue;

// Create the opposite filter of the current value to fetch other records
const remainingRecordsFilter = {
[valueProp.name]: {
[valuePropIsNumber ? 'neq' : 'doesNotMatch']:
typeof value === 'string' ? value : value[valueProp.name],
},
};

// When no filter, load records which do not match the default value
const currentFilter =
Object.keys(filter).length > 0 || value === ''
? filter
: remainingRecordsFilter;
// When no keys in filter, use remaining records filter to fetch other records
const currentFilter =
Object.keys(filter).length > 0 || value === ''
? filter
: remainingRecordsFilter;

// After searching or setting a default value
if (debouncedInputValue) {
if (labelPropertyPath.length > 1) {
// Handle relational properties in label for options option
const newFilter = {
// Create filter with property name(s) as key(s)
const inputFilter = propertyNames.reduceRight(
(acc, propertyName) => ({ [propertyName]: acc }),
{
[labelPropIsNumber ? 'eq' : 'matches']: labelPropIsNumber
? numberDebouncedInputValue
: textDebouncedInputValue,
};
const resolvedUuids = labelPropertyPath.map((u) => getProperty(u).name);
const resolvedFilter = resolvedUuids.reduceRight(
(acc, q) => ({ [q]: acc }),
newFilter,
);
: debouncedInputValue,
},
);

filter = { ...resolvedFilter, ...filter };
} else {
// Use searchProp and debouncedValue to create new filter
const newSearchValue = parsedDebouncedValue !== currentSearchValue;
const operator = newSearchValue ? '_and' : '_or';
filter = {
[operator]: [
{
[searchProp.name]: {
[searchPropIsNumber ? 'eq' : 'matches']: parsedDebouncedValue,
},
},
{ ...currentFilter },
],
};
}
// When searching, the search value is different from the value
const newSearchValue = parsedDebouncedValue !== currentSearchValue;
const operator = newSearchValue ? '_and' : '_or';

filter = {
[operator]:
debouncedInputValue === EMPTY_LABEL
? [currentFilter]
: [inputFilter, currentFilter],
};
} else if (value !== '') {
// Use default value in filter to show it on render
filter = {
Expand Down
2 changes: 1 addition & 1 deletion src/prefabs/autocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ const attributes = {
keywords: ['Form', 'input'],
};

export default prefab('AutocompleteInput', attributes, beforeCreate, [
export default prefab('Autocomplete', attributes, beforeCreate, [
AutocompleteInput({
label: 'Autocomplete',
inputLabel: 'Autocomplete',
Expand Down
Loading