Skip to content

Commit

Permalink
UIBULKED-574 Updates to Errors component (#667)
Browse files Browse the repository at this point in the history
  • Loading branch information
vashjs authored Dec 24, 2024
1 parent 2345a2d commit 4af6b4a
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 116 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [UIBULKED-585](https://folio-org.atlassian.net/browse/UUIBULKED-585) Adding missing translation and filters.
* [UIBULKED-561](https://folio-org.atlassian.net/browse/UUIBULKED-561) Add administrative data accordion to MARC bulk edit form.
* [UIBULKED-562](https://folio-org.atlassian.net/browse/UIBULKED-562) Include statistical code option on Instances bulk edit forms.
* [UIBULKED-574](https://folio-org.atlassian.net/browse/UIBULKED-574) Updates to Errors component.

## [4.2.2](https://github.com/folio-org/ui-bulk-edit/tree/v4.2.2) (2024-11-15)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
useStripes,
} from '@folio/stripes/core';

import { APPROACHES, EDITING_STEPS } from '../../../constants';
import { APPROACHES, EDITING_STEPS, RECORD_TYPES_MAPPING } from '../../../constants';
import { useSearchParams } from '../../../hooks';
import { RootContext } from '../../../context/RootContext';
import { BulkEditListResult } from '../BulkEditListResult';
Expand All @@ -26,6 +26,7 @@ export const BulkEditIdentifiers = ({
approach,
processedFileName,
initialFileName,
currentRecordType,
} = useSearchParams();

const {
Expand Down Expand Up @@ -57,10 +58,10 @@ export const BulkEditIdentifiers = ({
return (
<FormattedMessage
id={`ui-bulk-edit.list.logSubTitle.${step === EDITING_STEPS.UPLOAD ? 'matched' : 'changed'}`}
values={{ count: countOfRecords }}
values={{ count: countOfRecords, recordType: RECORD_TYPES_MAPPING[currentRecordType] }}
/>
);
}, [isIdentifierTabWithPreview, countOfRecords, step]);
}, [isIdentifierTabWithPreview, countOfRecords, step, currentRecordType]);

const paneSub = useMemo(() => {
return (step === EDITING_STEPS.UPLOAD || step === EDITING_STEPS.COMMIT) && isIdentifierTabWithPreview
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import React, { useState } from 'react';
import { PropTypes } from 'prop-types';
import { useLocation } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import {
Accordion,
MultiColumnList,
Headline,
Icon,
TextLink,
TextLink, Layout, Checkbox,
} from '@folio/stripes/components';
import { useStripes } from '@folio/stripes/core';
import { PrevNextPagination } from '@folio/stripes-acq-components';
import css from '../Preview.css';
import { useSearchParams } from '../../../../../hooks';
import { CAPABILITIES, CRITERIA, ERROR_PARAMETERS_KEYS } from '../../../../../constants';

const visibleColumns = ['key', 'message'];
import { CAPABILITIES, ERROR_PARAMETERS_KEYS } from '../../../../../constants';

const getParam = (error, key) => error.parameters.find(param => param.key === key)?.value;

const columnMapping = {
type: <FormattedMessage id="ui-bulk-edit.list.errors.table.status" />,
key: <FormattedMessage id="ui-bulk-edit.list.errors.table.code" />,
message: <FormattedMessage id="ui-bulk-edit.list.errors.table.message" />,
};

const visibleColumns = Object.keys(columnMapping);

const renderErrorMessage = (error, isLinkAvailable) => {
const link = getParam(error, ERROR_PARAMETERS_KEYS.LINK);

Expand All @@ -43,57 +44,34 @@ const renderErrorMessage = (error, isLinkAvailable) => {
);
};

const getResultsFormatter = ({ isLinkAvailable }) => ({
type: () => <FormattedMessage id="ui-bulk-edit.list.errors.table.status.ERROR" />,
key: error => getParam(error, ERROR_PARAMETERS_KEYS.IDENTIFIER),
message: error => renderErrorMessage(error, isLinkAvailable),
});

const ErrorsAccordion = ({
errors = [],
entries,
countOfErrors,
matched,
isInitial,
totalErrors,
isFetching,
pagination,
onChangePage,
}) => {
const { user, okapi } = useStripes();
const centralTenant = user?.user?.consortium?.centralTenantId;
const tenantId = okapi.tenant;
const isCentralTenant = tenantId === centralTenant;
const { capabilities } = useSearchParams();
const isLinkAvailable = (isCentralTenant && capabilities === CAPABILITIES.INSTANCE) || !isCentralTenant;

const resultsFormatter = {
key: error => getParam(error, ERROR_PARAMETERS_KEYS.IDENTIFIER),
message: error => renderErrorMessage(error, isLinkAvailable),
};

const location = useLocation();
const { criteria } = useSearchParams();
const fileName = new URLSearchParams(location.search).get('fileName');
const resultsFormatter = getResultsFormatter({ isLinkAvailable });
const errorLength = errors.length;

const [opened, setOpened] = useState(!!errorLength);
const [showWarnings, setShowWarnings] = useState(false);

const headLineTranslateKey = isInitial ? 'info' : 'infoProcessed';

const headLine = criteria === CRITERIA.QUERY ?
(
<FormattedMessage
id={`ui-bulk-edit.list.errors.query.${headLineTranslateKey}`}
values={{
entries,
matched,
errors: countOfErrors,
}}
/>
)
:
(
<FormattedMessage
id={`ui-bulk-edit.list.errors.${headLineTranslateKey}`}
values={{
fileName,
entries,
matched,
errors: countOfErrors,
}}
/>
);
const handleShowWarnings = () => {
setShowWarnings(prev => !prev);
};

return (
<div className={css.previewAccordion}>
Expand All @@ -106,16 +84,40 @@ const ErrorsAccordion = ({
>
<div className={css.errorAccordionOuter}>
<Headline size="medium" margin="small">
{headLine}
<Layout className="display-flex justified">
<FormattedMessage
id="ui-bulk-edit.list.errors.info"
values={{
errors: totalErrors,
warnings: 0,
}}
/>
<Checkbox
label={<FormattedMessage id="ui-bulk-edit.list.errors.checkbox" />}
checked={showWarnings}
onChange={handleShowWarnings}
/>
</Layout>
</Headline>
<div className={css.errorAccordionList}>
<MultiColumnList
contentData={errors}
columnMapping={columnMapping}
formatter={resultsFormatter}
visibleColumns={visibleColumns}
autosize
/>
<div className={css.previewAccordionInner}>
<div className={css.previewAccordionList}>
<MultiColumnList
contentData={errors}
columnMapping={columnMapping}
formatter={resultsFormatter}
visibleColumns={visibleColumns}
loading={isFetching}
autosize
/>
</div>
{errors.length > 0 && (
<PrevNextPagination
{...pagination}
totalCount={totalErrors}
disabled={false}
onChange={onChangePage}
/>
)}
</div>
</div>
</Accordion>
Expand All @@ -125,10 +127,13 @@ const ErrorsAccordion = ({

ErrorsAccordion.propTypes = {
errors: PropTypes.arrayOf(PropTypes.object),
entries: PropTypes.number,
countOfErrors: PropTypes.number,
matched: PropTypes.number,
isInitial: PropTypes.bool,
totalErrors: PropTypes.number,
isFetching: PropTypes.bool,
pagination: {
limit: PropTypes.number,
offset: PropTypes.number,
},
onChangePage: PropTypes.func,
};

export default ErrorsAccordion;
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import ErrorsAccordion from './ErrorsAccordion';

const defaultProps = {
errors: errorsPreview.errors,
entries: 5,
countOfErrors: errorsPreview.errors.length,
matched: 4,
};

const renderPreviewAccordion = (history, props = defaultProps) => {
Expand All @@ -27,27 +25,17 @@ describe('ErrorsAccordion', () => {
it('should render preview accordion', () => {
const mockHistory = ['/bulk-edit/1/preview'];

renderPreviewAccordion(mockHistory, { ...defaultProps, initial: true });
renderPreviewAccordion(mockHistory, { ...defaultProps });

expect(screen.getByText(/errors.info/)).toBeVisible();
expect(screen.getByText(/errors.table.code/)).toBeVisible();
expect(screen.getByText(errorsPreview.errors[0].message)).toBeVisible();
});

it('should render preview accordion for bulk edit query', () => {
const mockHistory = ['/bulk-edit/1/preview?criteria=query'];

renderPreviewAccordion(mockHistory, { ...defaultProps, initial: true });

expect(screen.getByText(/errors.query.info/)).toBeVisible();
expect(screen.getByText(/errors.table.code/)).toBeVisible();
expect(screen.getByText(errorsPreview.errors[0].message)).toBeVisible();
});

it('should render with no axe errors', async () => {
const mockHistory = ['/bulk-edit/1/preview'];

renderPreviewAccordion(mockHistory, { ...defaultProps, initial: true });
renderPreviewAccordion(mockHistory, { ...defaultProps });

await runAxeTest({
rootNode: document.body,
Expand All @@ -57,26 +45,25 @@ describe('ErrorsAccordion', () => {
it('should render preview accordion', () => {
const mockHistory = ['/bulk-edit/1/preview'];

renderPreviewAccordion(mockHistory, { ...defaultProps, initial: false });
renderPreviewAccordion(mockHistory, { ...defaultProps });

expect(screen.getByText(/errors.infoProcessed/)).toBeVisible();
expect(screen.getByText(/errors.table.code/)).toBeVisible();
expect(screen.getByText(errorsPreview.errors[0].message)).toBeVisible();
});

it('should hide content when title was clicked', () => {
const mockHistory = ['/bulk-edit/1/preview'];

const { getByRole } = renderPreviewAccordion(mockHistory, { ...defaultProps, initial: false });
const { getByRole } = renderPreviewAccordion(mockHistory, { ...defaultProps });

expect(screen.getByText(/errors.infoProcessed/)).toBeVisible();
expect(screen.getByText(/list.errors.info/)).toBeVisible();

const titleButton = getByRole('button', { name: /ui-bulk-edit.list.errors.title/ });

userEvent.click(titleButton);

waitFor(() => {
expect(screen.getByText(/errors.infoProcessed/)).not.toBeVisible();
expect(screen.getByText(/list.errors.info/)).not.toBeVisible();
});
});
});
45 changes: 24 additions & 21 deletions src/components/BulkEditPane/BulkEditListResult/Preview/Preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
import {
CRITERIA,
EDITING_STEPS,
PAGINATION_CONFIG
PAGINATION_CONFIG,
ERRORS_PAGINATION_CONFIG
} from '../../../../constants';
import { usePagination } from '../../../../hooks/usePagination';
import { useBulkOperationStats } from '../../../../hooks/useBulkOperationStats';
Expand All @@ -35,23 +36,22 @@ export const Preview = ({ id, title, isInitial, bulkDetails }) => {
progress,
} = useSearchParams();

const totalRecords = step === EDITING_STEPS.COMMIT ? bulkDetails?.processedNumOfRecords : bulkDetails?.matchedNumOfRecords;
const totalNumOfRecords = step === EDITING_STEPS.COMMIT ? bulkDetails?.processedNumOfRecords : bulkDetails?.matchedNumOfRecords;
const isOtherTabProcessing = progress && criteria !== progress;
const isPreviewEnabled = !isOtherTabProcessing && Boolean(id);

const {
countOfRecords,
countOfErrors,
totalCount,
visibleColumns,
} = useBulkOperationStats({ bulkDetails, step });

const {
pagination,
changePage,
pagination: previewPagination,
changePage: changePreviewPage,
} = usePagination(PAGINATION_CONFIG);

const { contentData, columns, columnMapping, isFetching } = useRecordsPreview({
const { contentData, columns, columnMapping, isFetching: isPreviewFetching } = useRecordsPreview({
key: RECORDS_PREVIEW_KEY,
capabilities: currentRecordType,
id,
Expand All @@ -61,21 +61,24 @@ export const Preview = ({ id, title, isInitial, bulkDetails }) => {
queryOptions: {
enabled: isPreviewEnabled,
},
...pagination,
...previewPagination,
});

const { data } = useErrorsPreview({
const {
pagination: errorsPagination,
changePage: changeErrorPage,
} = usePagination(ERRORS_PAGINATION_CONFIG);

const { errors, isFetching: isErrorsFetching } = useErrorsPreview({
id,
queryOptions: {
enabled: isPreviewEnabled,
},
enabled: isPreviewEnabled,
...errorsPagination,
});

const errors = data?.errors || [];

if (!((bulkDetails.fqlQuery && criteria === CRITERIA.QUERY) || (criteria !== CRITERIA.QUERY && !bulkDetails.fqlQuery))) {
return <NoResultsMessage />;
}

return (
<AccordionStatus>
<div className={css.previewContainer}>
Expand All @@ -97,26 +100,26 @@ export const Preview = ({ id, title, isInitial, bulkDetails }) => {
<div className={css.previewAccordionOuter}>
{Boolean(contentData?.length) && (
<PreviewAccordion
totalRecords={totalRecords}
totalRecords={totalNumOfRecords}
isInitial={isInitial}
columns={columns}
contentData={contentData}
columnMapping={columnMapping}
visibleColumns={visibleColumns}
step={step}
onChangePage={changePage}
pagination={pagination}
isFetching={isFetching}
onChangePage={changePreviewPage}
pagination={previewPagination}
isFetching={isPreviewFetching}
/>
)}

{Boolean(errors?.length) && (
<ErrorsAccordion
errors={errors}
entries={totalCount}
matched={countOfRecords}
countOfErrors={countOfErrors}
isInitial={isInitial}
totalErrors={countOfErrors}
onChangePage={changeErrorPage}
pagination={errorsPagination}
isFetching={isErrorsFetching}
/>
)}
</div>
Expand Down
Loading

0 comments on commit 4af6b4a

Please sign in to comment.