diff --git a/CHANGELOG.md b/CHANGELOG.md
index d22d50d..815d20b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,10 @@
* [UIPQB-168](https://folio-org.atlassian.net/browse/UIPQB-168) Allow editing queries containing no fields.
* [UIPQB-141](https://folio-org.atlassian.net/browse/UIPQB-141) Modal dialog focus inconsistencies across screenreaders.
+* [UIPQB-162](https://folio-org.atlassian.net/browse/UIPQB-162) Errors when query includes a modified custom field.
* [UIPQB-175](https://folio-org.atlassian.net/browse/UIPQB-175) Displays the "Smth went wrong" page, when the user clicks on "Select operator" dropdown and selects any of them, if there are deleted custom fields.
+
## [1.2.6](https://github.com/folio-org/ui-plugin-query-builder/tree/v1.2.6) (2024-12-11)
* [UIPQB-128](https://folio-org.atlassian.net/browse/UIPQB-128) Invalid fields handling > Errors when query includes a deleted custom field.
diff --git a/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js b/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js
index fea5be3..21d4247 100644
--- a/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js
+++ b/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { memo, useEffect, useRef } from 'react';
import {
IconButton,
RepeatableField,
@@ -8,9 +8,10 @@ import {
Row,
getFirstFocusable,
} from '@folio/stripes/components';
+import { useShowCallout } from '@folio/stripes-acq-components';
import PropTypes from 'prop-types';
-import { useIntl } from 'react-intl';
+import { FormattedMessage, useIntl } from 'react-intl';
import { QueryBuilderTitle } from '../../QueryBuilderTitle';
import css from '../QueryBuilderModal.css';
import { COLUMN_KEYS } from '../../../../constants/columnKeys';
@@ -23,9 +24,12 @@ import {
} from '../../helpers/selectOptions';
import { BOOLEAN_OPERATORS } from '../../../../constants/operators';
import { DataTypeInput } from '../DataTypeInput';
+import { findMissingValues } from '../../helpers/query';
-export const RepeatableFields = ({ source, setSource, getParamsSource, columns }) => {
+export const RepeatableFields = memo(({ source, setSource, getParamsSource, columns }) => {
const intl = useIntl();
+ const callout = useShowCallout();
+ const calloutCalledRef = useRef(false);
const fieldOptions = getFieldOptions(columns);
@@ -85,6 +89,7 @@ export const RepeatableFields = ({ source, setSource, getParamsSource, columns }
},
};
}
+
if (isOperator) {
return {
[COLUMN_KEYS.VALUE]: {
@@ -114,6 +119,27 @@ export const RepeatableFields = ({ source, setSource, getParamsSource, columns }
}));
};
+ useEffect(() => {
+ if (calloutCalledRef.current) return;
+
+ const deletedFields = findMissingValues(fieldOptions, source);
+
+ if (deletedFields.length >= 1) {
+ calloutCalledRef.current = true;
+
+ callout({
+ type: 'warning',
+ message: (
+
+ ),
+ timeout: 0,
+ });
+ }
+ }, []);
+
return (
<>
@@ -202,7 +228,7 @@ export const RepeatableFields = ({ source, setSource, getParamsSource, columns }
/>
>
);
-};
+});
RepeatableFields.propTypes = {
source: PropTypes.arrayOf(PropTypes.object),
diff --git a/src/QueryBuilder/QueryBuilder/helpers/query.js b/src/QueryBuilder/QueryBuilder/helpers/query.js
index 0786b6f..5dbfd1b 100644
--- a/src/QueryBuilder/QueryBuilder/helpers/query.js
+++ b/src/QueryBuilder/QueryBuilder/helpers/query.js
@@ -273,3 +273,22 @@ export const mongoQueryToSource = async ({
return [singleItem];
};
+
+export const findMissingValues = (
+ mainArray,
+ secondaryArray,
+) => {
+ const mainValues = new Set(mainArray?.map((item) => item.value));
+
+ const missingValues = [];
+
+ for (const secondaryItem of secondaryArray) {
+ const currentValue = secondaryItem.field.current;
+
+ if (currentValue && !mainValues.has(currentValue)) {
+ missingValues.push(currentValue);
+ }
+ }
+
+ return missingValues;
+};
diff --git a/src/QueryBuilder/QueryBuilder/helpers/query.test.js b/src/QueryBuilder/QueryBuilder/helpers/query.test.js
index e77eafc..f8f2f50 100644
--- a/src/QueryBuilder/QueryBuilder/helpers/query.test.js
+++ b/src/QueryBuilder/QueryBuilder/helpers/query.test.js
@@ -1,4 +1,4 @@
-import { getTransformedValue, isQueryValid, mongoQueryToSource, sourceToMongoQuery } from './query';
+import { findMissingValues, getTransformedValue, isQueryValid, mongoQueryToSource, sourceToMongoQuery } from './query';
import { booleanOptions } from './selectOptions';
import { OPERATORS } from '../../../constants/operators';
import { fieldOptions } from '../../../../test/jest/data/entityType';
@@ -367,3 +367,91 @@ describe('getTransformedValue', () => {
expect(actual).toEqual(expected);
});
});
+
+describe('findMissingValues', () => {
+ it('should return missing values from secondaryArray that are not in mainArray', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ { value: 'value3' },
+ ];
+
+ const secondaryArray = [
+ { field: { current: 'value2' } },
+ { field: { current: 'value4' } },
+ { field: { current: 'value5' } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual(['value4', 'value5']);
+ });
+
+ it('should return an empty array when all values are present in mainArray', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ ];
+
+ const secondaryArray = [
+ { field: { current: 'value1' } },
+ { field: { current: 'value2' } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual([]);
+ });
+
+ it('should handle cases where mainArray is empty', () => {
+ const mainArray = [];
+
+ const secondaryArray = [
+ { field: { current: 'value1' } },
+ { field: { current: 'value2' } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual(['value1', 'value2']);
+ });
+
+ it('should handle cases where secondaryArray is empty', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ ];
+
+ const secondaryArray = [];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual([]);
+ });
+
+ it('should handle cases where both arrays are empty', () => {
+ const mainArray = [];
+ const secondaryArray = [];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual([]);
+ });
+
+ it('should ignore null or undefined values in secondaryArray', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ ];
+
+ const secondaryArray = [
+ { field: { current: 'value3' } },
+ { field: { current: null } },
+ { field: { current: undefined } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual(['value3']);
+ });
+});
diff --git a/translations/ui-plugin-query-builder/en.json b/translations/ui-plugin-query-builder/en.json
index e175289..595c4a8 100644
--- a/translations/ui-plugin-query-builder/en.json
+++ b/translations/ui-plugin-query-builder/en.json
@@ -35,5 +35,7 @@
"error.sww": "Something went wrong",
"error.occurredMessage": "An error occurred.",
+ "warning.deletedField": "{value} in your query is unavailable. Please revise your query. ",
+
"ariaLabel.columnFilter": "Column filter input"
}