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

UIBULKED-349 Update Electronic access - URL relationship #409

Merged
merged 13 commits into from
Nov 14, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* [UIBULKED-371](https://issues.folio.org/browse/UIBULKED-371) Bulk edit - Grouped form controls missing accessible name.
* [UIBULKED-353](https://issues.folio.org/browse/UIBULKED-353) Update Electronic access - URI.
* [UIBULKED-356](https://issues.folio.org/browse/UIBULKED-356) Separate holdings notes by note type
* [UIBULKED-349](https://issues.folio.org/browse/UIBULKED-349) Update Electronic access - URL relationship

## [4.0.0](https://github.com/folio-org/ui-bulk-edit/tree/v4.0.0) (2023-10-12)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ export const ActionsRow = ({ option, actions, onChange }) => {

{/* Render value fields only in case if actions selected AND action is not from FINAL_ACTIONS */}
{action.name && !FINAL_ACTIONS.includes(action.name) && (
<ValuesColumn option={option} action={action} actionIndex={actionIndex} onChange={onChange} />
<ValuesColumn
option={option}
action={action}
allActions={actions}
actionIndex={actionIndex}
onChange={onChange}
/>
)}

{/* Render additional actions */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ import {
BASE_DATE_FORMAT,
CAPABILITIES,
CONTROL_TYPES,
getDuplicateNoteOptions, getHoldingsNotes,
getDuplicateNoteOptions,
getHoldingsNotes,
getItemStatusOptions,
getItemsWithPlaceholder,
getNotesOptions,
} from '../../../../../constants';
import { FIELD_VALUE_KEY, TEMPORARY_LOCATIONS } from './helpers';
import { useLoanTypes, usePatronGroup } from '../../../../../hooks/api';
import { useItemNotes } from '../../../../../hooks/api/useItemNotes';
import { usePreselectedValue } from '../../../../../hooks/usePreselectedValue';
import { useHoldingsNotes } from '../../../../../hooks/api/useHoldingsNotes';
import { useElectronicAccessRelationships } from '../../../../../hooks/api/useElectronicAccess';

export const ValuesColumn = ({ action, actionIndex, onChange, option }) => {
export const ValuesColumn = ({ action, allActions, actionIndex, onChange, option }) => {
const { formatMessage } = useIntl();
const location = useLocation();
const search = new URLSearchParams(location.search);
Expand All @@ -38,6 +41,12 @@ export const ValuesColumn = ({ action, actionIndex, onChange, option }) => {
const { userGroups } = usePatronGroup({ enabled: isUserCapability });
const { loanTypes, isLoanTypesLoading } = useLoanTypes({ enabled: isItemCapability });
const { itemNotes, usItemNotesLoading } = useItemNotes({ enabled: isItemCapability });

const { electronicAccessRelationships, isElectronicAccessLoading } = useElectronicAccessRelationships({ enabled: isHoldingsCapability });
// exclude from second action the first action value
const filteredElectronicAccessRelationships = electronicAccessRelationships.filter(item => actionIndex === 0 || item.value !== allActions[0]?.value);
const accessRelationshipsWithPlaceholder = getItemsWithPlaceholder(filteredElectronicAccessRelationships);

const { holdingsNotes, isHoldingsNotesLoading } = useHoldingsNotes({ enabled: isHoldingsCapability });
const duplicateNoteOptions = getDuplicateNoteOptions(formatMessage).filter(el => el.value !== option);

Expand Down Expand Up @@ -130,16 +139,16 @@ export const ValuesColumn = ({ action, actionIndex, onChange, option }) => {
<>
<LocationSelection
value={actionValue}
onSelect={location => onChange({ actionIndex, value: location.id, fieldName: FIELD_VALUE_KEY })}
onSelect={loc => onChange({ actionIndex, value: loc.id, fieldName: FIELD_VALUE_KEY })}
placeholder={formatMessage({ id: 'ui-bulk-edit.layer.selectLocation' })}
data-test-id={`textField-${actionIndex}`}
aria-label={formatMessage({ id: 'ui-bulk-edit.ariaLabel.location' })}
/>
<LocationLookup
marginBottom0
isTemporaryLocation={TEMPORARY_LOCATIONS.includes(option)}
onLocationSelected={(location) => onChange({
actionIndex, value: location.id, fieldName: FIELD_VALUE_KEY,
onLocationSelected={(loc) => onChange({
actionIndex, value: loc.id, fieldName: FIELD_VALUE_KEY,
})}
data-testid={`locationLookup-${actionIndex}`}
/>
Expand Down Expand Up @@ -204,6 +213,17 @@ export const ValuesColumn = ({ action, actionIndex, onChange, option }) => {
/>
);

const renderElectronicAccessRelationshipSelect = () => controlType === CONTROL_TYPES.ELECTRONIC_ACCESS_RELATIONSHIP_SELECT && (
<Select
id="urlRelationship"
value={action.value}
loading={isElectronicAccessLoading}
onChange={e => onChange({ actionIndex, value: e.target.value, fieldName: FIELD_VALUE_KEY })}
dataOptions={accessRelationshipsWithPlaceholder}
aria-label={formatMessage({ id: 'ui-bulk-edit.ariaLabel.urlRelationshipSelect' })}
/>
);

return (
<Col xs={2} sm={2}>
{renderTextField()}
Expand All @@ -215,6 +235,7 @@ export const ValuesColumn = ({ action, actionIndex, onChange, option }) => {
{renderLoanTypeSelect()}
{renderNoteTypeSelect()}
{renderNoteDuplicateTypeSelect()}
{renderElectronicAccessRelationshipSelect()}
</Col>
);
};
Expand All @@ -227,5 +248,10 @@ ValuesColumn.propTypes = {
value: PropTypes.string,
}),
actionIndex: PropTypes.number,
allActions: PropTypes.arrayOf(PropTypes.shape({
controlType: PropTypes.func,
name: PropTypes.string,
value: PropTypes.string,
})),
onChange: PropTypes.func,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createMemoryHistory } from 'history';
import { queryClient } from '../../../../../../test/jest/utils/queryClient';
import { ValuesColumn } from './ValuesColumn';
import { useLoanTypes, usePatronGroup } from '../../../../../hooks/api';
import { CONTROL_TYPES } from '../../../../../constants';
import { CAPABILITIES, CONTROL_TYPES } from '../../../../../constants';

jest.mock('../../../../../hooks/api/useLoanTypes');
jest.mock('../../../../../hooks/api/usePatronGroup');
Expand Down Expand Up @@ -54,8 +54,8 @@ describe('ValuesColumn Component', () => {
});

it('should render TextField when action type is INPUT', async () => {
const { getByTestId } = renderComponent(() => CONTROL_TYPES.INPUT);
const element = getByTestId('input-email-0');
const { getByRole } = renderComponent(() => CONTROL_TYPES.INPUT);
const element = getByRole('textbox');

expect(element).toBeInTheDocument();

Expand All @@ -64,10 +64,21 @@ describe('ValuesColumn Component', () => {
await waitFor(() => expect(onChange).toHaveBeenCalled());
});

it('should render TextArea when action type is TEXTAREA', async () => {
const { getByRole } = renderComponent(() => CONTROL_TYPES.TEXTAREA);
const element = getByRole('textbox');

expect(element).toBeInTheDocument();

fireEvent.change(element, { target: { value: 'textarea value' } });

await waitFor(() => expect(onChange).toHaveBeenCalled());
});

/// continue from the above code
it('should render Select with patron groups when action type is PATRON_GROUP_SELECT', async () => {
const { getByTestId } = renderComponent(() => CONTROL_TYPES.PATRON_GROUP_SELECT);
const element = getByTestId('select-patronGroup-0');
const { getByRole } = renderComponent(() => CONTROL_TYPES.PATRON_GROUP_SELECT);
const element = getByRole('combobox');

expect(element).toBeInTheDocument();

Expand All @@ -77,8 +88,8 @@ describe('ValuesColumn Component', () => {
});

it('should render Datepicker when action type is DATE', async () => {
const { getByTestId } = renderComponent(() => CONTROL_TYPES.DATE);
const element = getByTestId('dataPicker-experation-date-0');
const { getByRole } = renderComponent(() => CONTROL_TYPES.DATE);
const element = getByRole('textbox');

expect(element).toBeInTheDocument();

Expand All @@ -88,8 +99,8 @@ describe('ValuesColumn Component', () => {
});

it('should render Select with status options when action type is STATUS_SELECT', async () => {
const { getByTestId } = renderComponent(() => CONTROL_TYPES.STATUS_SELECT);
const element = getByTestId('select-statuses-0');
const { getByRole } = renderComponent(() => CONTROL_TYPES.STATUS_SELECT);
const element = getByRole('combobox');

expect(element).toBeInTheDocument();

Expand All @@ -109,25 +120,50 @@ describe('ValuesColumn Component', () => {
await waitFor(() => expect(onChange).toHaveBeenCalled());
});

it('should render select with item note types when action type is LOAN_TYPE', async () => {
const { container } = renderComponent(() => CONTROL_TYPES.NOTE_SELECT);
const element = container.querySelector('#noteType');
it('should render select with item note types when action type is NOTE_SELECT', async () => {
const { getByRole } = renderComponent(() => CONTROL_TYPES.NOTE_SELECT);
const element = getByRole('combobox');

expect(element).toBeInTheDocument();

fireEvent.change(element, { target: { value: 'newLoanType' } });
fireEvent.change(element, { target: { value: 'new note select value' } });

await waitFor(() => expect(onChange).toHaveBeenCalled());
});

it('should render select with item note types when action type is NOTE_SELECT + HOLDINS CAPABILITY', async () => {
const spy = jest.spyOn(URLSearchParams.prototype, 'get');
spy.mockReturnValueOnce(CAPABILITIES.HOLDING);

const { getByRole } = renderComponent(() => CONTROL_TYPES.NOTE_SELECT);
const element = getByRole('combobox');

expect(element).toBeInTheDocument();

fireEvent.change(element, { target: { value: 'new note holding value' } });

await waitFor(() => expect(onChange).toHaveBeenCalled());
});

it('should render select with item note types when action type is DUPLICATE', async () => {
const { container } = renderComponent(() => CONTROL_TYPES.NOTE_DUPLICATE_SELECT);
const element = container.querySelector('#noteTypeDuplicate');
const { getByRole } = renderComponent(() => CONTROL_TYPES.NOTE_DUPLICATE_SELECT);
const element = getByRole('combobox');

expect(element).toBeInTheDocument();

fireEvent.change(element, { target: { value: 'CHECK OUT' } });

await waitFor(() => expect(onChange).toHaveBeenCalled());
});

it('should render select with url relationship types when action type is FIND', async () => {
const { getByRole } = renderComponent(() => CONTROL_TYPES.ELECTRONIC_ACCESS_RELATIONSHIP_SELECT);
const element = getByRole('combobox');

expect(element).toBeInTheDocument();

fireEvent.change(element, { target: { value: 'RESOURCE' } });

await waitFor(() => expect(onChange).toHaveBeenCalled());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
CAPABILITIES,
emailActionsFind,
emailActionsReplace,
noteAdditionalActions,
commonAdditionalActions,
patronActions,
expirationActions,
replaceClearActions,
Expand Down Expand Up @@ -293,7 +293,21 @@ export const getDefaultActions = (option, options, formatMessage) => {
],
};

case OPTIONS.URI:
case OPTIONS.ELECTRONIC_ACCESS_URL_RELATIONSHIP:
return {
type: '',
actions: [
null,
{
actionsList: electronicAccessActions,
controlType: () => CONTROL_TYPES.ELECTRONIC_ACCESS_RELATIONSHIP_SELECT,
[ACTION_VALUE_KEY]: electronicAccessActions[0].value,
[FIELD_VALUE_KEY]: '',
},
],
};

case OPTIONS.ELECTRONIC_ACCESS_URI:
return {
type: '',
actions: [
Expand Down Expand Up @@ -374,14 +388,22 @@ export const getExtraActions = (option, action, formattedMessage) => {
switch (`${option}-${action}`) {
case `${OPTIONS.ITEM_NOTE}-${ACTIONS.FIND}`:
case `${OPTIONS.ADMINISTRATIVE_NOTE}-${ACTIONS.FIND}`:
case `${OPTIONS.URI}-${ACTIONS.FIND}`:
case `${OPTIONS.ELECTRONIC_ACCESS_URI}-${ACTIONS.FIND}`:
case `${OPTIONS.CHECK_IN_NOTE}-${ACTIONS.FIND}`:
case `${OPTIONS.CHECK_OUT_NOTE}-${ACTIONS.FIND}`:
case `${OPTIONS.HOLDINGS_NOTE}-${ACTIONS.FIND}`:
return [{
actionsList: noteAdditionalActions(formattedMessage),
actionsList: commonAdditionalActions(formattedMessage),
controlType: () => CONTROL_TYPES.TEXTAREA,
[ACTION_VALUE_KEY]: noteAdditionalActions(formattedMessage)[0].value,
[ACTION_VALUE_KEY]: commonAdditionalActions(formattedMessage)[0].value,
[FIELD_VALUE_KEY]: '',
}];

case `${OPTIONS.ELECTRONIC_ACCESS_URL_RELATIONSHIP}-${ACTIONS.FIND}`:
return [{
actionsList: commonAdditionalActions(formattedMessage),
controlType: () => CONTROL_TYPES.ELECTRONIC_ACCESS_RELATIONSHIP_SELECT,
[ACTION_VALUE_KEY]: commonAdditionalActions(formattedMessage)[0].value,
[FIELD_VALUE_KEY]: '',
}];

Expand Down
Loading
Loading