Skip to content

Commit

Permalink
UIIN-2590: ECS: Show info message when user in member tenant tries to…
Browse files Browse the repository at this point in the history
… view shared instance details without permission (#2328)
  • Loading branch information
mariia-aloshyna authored Nov 6, 2023
1 parent 0ac2099 commit 6515018
Show file tree
Hide file tree
Showing 18 changed files with 307 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Create new instance success toast no longer shows the instance HRID. Fixes UIIN-2635.
* Optimistic locking message not working for instances in non-consortial tenant. Fixes UIIN-2628.
* Add immediate warning message when a local instance is shared. Refs UIIN-2617.
* ECS: Show info message when user in member tenant tries to view shared instance details without permission. Refs UIIN-2590.

## [10.0.1](https://github.com/folio-org/ui-inventory/tree/v10.0.1) (2023-11-03)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v10.0.0...v10.0.1)
Expand Down
83 changes: 17 additions & 66 deletions src/Instance/InstanceDetails/InstanceDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
PaneMenu,
Row,
MessageBanner,
Icon,
} from '@folio/stripes/components';

import { InstanceTitle } from './InstanceTitle';
Expand Down Expand Up @@ -71,8 +70,6 @@ const InstanceDetails = forwardRef(({
tagsEnabled,
userTenantPermissions,
isShared,
isLoading,
isInstanceSharing,
...rest
}, ref) => {
const intl = useIntl();
Expand All @@ -86,15 +83,12 @@ const InstanceDetails = forwardRef(({
const accordionState = useMemo(() => getAccordionState(instance, accordions), [instance]);
const [helperApp, setHelperApp] = useState();

const isBasicPane = isInstanceSharing || isLoading;
const canCreateHoldings = stripes.hasPerm('ui-inventory.holdings.create');
const tags = instance?.tags?.tagList;
const isUserInCentralTenant = checkIfUserInCentralTenant(stripes);

const canCreateHoldings = stripes.hasPerm('ui-inventory.holdings.create');
const isConsortialHoldingsVisible = instance?.shared || isInstanceShadowCopy(instance?.source);

const detailsLastMenu = useMemo(() => {
if (isBasicPane) return null;

return (
<PaneMenu>
{
Expand All @@ -110,15 +104,9 @@ const InstanceDetails = forwardRef(({
}
</PaneMenu>
);
}, [isBasicPane, tagsEnabled, tags, intl]);
const detailsActionMenu = useMemo(
() => (isBasicPane ? null : actionMenu),
[isBasicPane, actionMenu],
);
}, [tagsEnabled, tags, intl]);

const renderPaneTitle = () => {
if (isBasicPane) return intl.formatMessage({ id: 'ui-inventory.edit' });

const isInstanceShared = Boolean(isShared || isInstanceShadowCopy(instance?.source));

return (
Expand All @@ -134,8 +122,6 @@ const InstanceDetails = forwardRef(({
};

const renderPaneSubtitle = () => {
if (isBasicPane) return null;

return (
<FormattedMessage
id="ui-inventory.instanceRecordSubtitle"
Expand All @@ -147,32 +133,20 @@ const InstanceDetails = forwardRef(({
);
};

const renderDetails = () => {
if (isInstanceSharing) {
return (
<div>
<MessageBanner show={Boolean(isInstanceSharing)} type="warning">
<FormattedMessage id="ui-inventory.warning.instance.sharingLocalInstance" />
</MessageBanner>
</div>
);
}

if (isLoading) {
return (
<div>
<Icon
icon="spinner-ellipsis"
width="100px"
/>
</div>
);
}

const isConsortialHoldingsVisible = instance?.shared || isInstanceShadowCopy(instance?.source);

return (
<>
return (
<>
<Pane
{...rest}
data-test-instance-details
appIcon={<AppIcon app="inventory" iconKey="instance" />}
paneTitle={renderPaneTitle()}
paneSub={renderPaneSubtitle()}
actionMenu={actionMenu}
lastMenu={detailsLastMenu}
dismissible
onClose={onClose}
defaultWidth="fill"
>
<TitleManager record={instance.title} />

<AccordionStatus ref={ref}>
Expand Down Expand Up @@ -293,25 +267,6 @@ const InstanceDetails = forwardRef(({
/>
</AccordionSet>
</AccordionStatus>
</>
);
};

return (
<>
<Pane
{...rest}
data-test-instance-details
appIcon={<AppIcon app="inventory" iconKey="instance" />}
paneTitle={renderPaneTitle()}
paneSub={renderPaneSubtitle()}
actionMenu={detailsActionMenu}
lastMenu={detailsLastMenu}
dismissible
onClose={onClose}
defaultWidth="fill"
>
{renderDetails()}
</Pane>
{ helperApp && <HelperApp appName={helperApp} onClose={setHelperApp} />}
</>
Expand All @@ -326,16 +281,12 @@ InstanceDetails.propTypes = {
tagsToggle: PropTypes.func,
tagsEnabled: PropTypes.bool,
userTenantPermissions: PropTypes.arrayOf(PropTypes.object),
isLoading: PropTypes.bool,
isInstanceSharing: PropTypes.bool,
isShared: PropTypes.bool,
};

InstanceDetails.defaultProps = {
instance: {},
tagsEnabled: false,
isLoading: false,
isInstanceSharing: false,
isShared: false,
};

Expand Down
7 changes: 0 additions & 7 deletions src/Instance/InstanceDetails/InstanceDetails.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,6 @@ describe('InstanceDetails', () => {
expect(screen.getByText('Instance relationship (analytics and bound-with)')).toBeInTheDocument();
});

it('should show a correct Warning message banner when instance sharing is in progress', () => {
renderInstanceDetails({ isInstanceSharing: true });

expect(screen.getByText('Sharing this local instance will take a few moments.' +
' A success message and updated details will be displayed upon completion.')).toBeInTheDocument();
});

it('should show a correct Warning message banner when staff suppressed', () => {
const staffSuppressedInstance = {
...instance,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';

import { AppIcon } from '@folio/stripes/core';
import {
Pane,
Icon,
} from '@folio/stripes/components';

const InstanceLoadingPane = ({ onClose }) => {
const intl = useIntl();

return (
<Pane
data-test-instance-details
appIcon={<AppIcon app="inventory" iconKey="instance" />}
paneTitle={intl.formatMessage({ id: 'ui-inventory.edit' })}
dismissible
onClose={onClose}
defaultWidth="fill"
>
<Icon
icon="spinner-ellipsis"
width="100px"
/>
</Pane>
);
};

InstanceLoadingPane.propTypes = { onClose: PropTypes.func.isRequired };

export default InstanceLoadingPane;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
fireEvent,
screen,
} from '@folio/jest-config-stripes/testing-library/react';

import '../../../../test/jest/__mock__';

import { Icon } from '@folio/stripes/components';

import {
renderWithIntl,
translationsProperties,
} from '../../../../test/jest/helpers';

import InstanceLoadingPane from './InstanceLoadingPane';

Icon.mockClear().mockImplementation(({ icon }) => <span>{icon}</span>);

const mockOnClose = jest.fn();

const renderInstanceLoadingPane = () => {
const component = <InstanceLoadingPane onClose={mockOnClose} />;

return renderWithIntl(component, translationsProperties);
};

describe('InstanceLoadingPane', () => {
it('should render loading spinner', () => {
renderInstanceLoadingPane();

expect(screen.getByText('spinner-ellipsis')).toBeInTheDocument();
});

it('should render correct header', () => {
renderInstanceLoadingPane();

expect(screen.getByText('Edit')).toBeInTheDocument();
});

it('should not render action menu', () => {
renderInstanceLoadingPane();

expect(screen.queryByRole('button', { name: 'Actions' })).not.toBeInTheDocument();
});

it('should call onClose cb when pane is closed', () => {
renderInstanceLoadingPane();

fireEvent.click(screen.getByRole('button', { name: 'Close Edit' }));

expect(mockOnClose).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions src/Instance/InstanceDetails/InstanceLoadingPane/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './InstanceLoadingPane';
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';

import { AppIcon } from '@folio/stripes/core';
import {
Pane,
MessageBanner,
} from '@folio/stripes/components';

const InstanceWarningPane = ({
onClose,
messageBannerText,
}) => {
const intl = useIntl();

return (
<Pane
data-test-instance-details
appIcon={<AppIcon app="inventory" iconKey="instance" />}
paneTitle={intl.formatMessage({ id: 'ui-inventory.edit' })}
dismissible
onClose={onClose}
defaultWidth="fill"
>
<MessageBanner type="warning">
{messageBannerText}
</MessageBanner>
</Pane>
);
};

InstanceWarningPane.propTypes = {
onClose: PropTypes.func.isRequired,
messageBannerText: PropTypes.oneOfType([PropTypes.element, PropTypes.string]).isRequired,
};

export default InstanceWarningPane;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
fireEvent,
screen,
} from '@folio/jest-config-stripes/testing-library/react';

import '../../../../test/jest/__mock__';

import { Icon } from '@folio/stripes/components';

import {
renderWithIntl,
translationsProperties,
} from '../../../../test/jest/helpers';

import InstanceWarningPane from './InstanceWarningPane';

Icon.mockClear().mockImplementation(({ icon }) => <span>{icon}</span>);

const mockOnClose = jest.fn();

const renderInstanceLoadingPane = (messageBannerText = 'warning text') => {
const component = (
<InstanceWarningPane
onClose={mockOnClose}
messageBannerText={messageBannerText}
/>
);

return renderWithIntl(component, translationsProperties);
};

describe('InstanceWarningPane', () => {
it('should render warning banner', () => {
renderInstanceLoadingPane();

expect(screen.getByText('warning text')).toBeInTheDocument();
});

it('should render correct header', () => {
renderInstanceLoadingPane();

expect(screen.getByText('Edit')).toBeInTheDocument();
});

it('should not render action menu', () => {
renderInstanceLoadingPane();

expect(screen.queryByRole('button', { name: 'Actions' })).not.toBeInTheDocument();
});

it('should call onClose cb when pane is closed', () => {
renderInstanceLoadingPane();

fireEvent.click(screen.getByRole('button', { name: 'Close Edit' }));

expect(mockOnClose).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions src/Instance/InstanceDetails/InstanceWarningPane/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './InstanceWarningPane';
2 changes: 2 additions & 0 deletions src/Instance/InstanceDetails/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export * from './InstanceTitle';
export * from './InstanceNotesView';
export * from './InstanceNewHolding';

export { default as InstanceLoadingPane } from './InstanceLoadingPane';
export { default as InstanceWarningPane } from './InstanceWarningPane';
export { default as SubInstanceList } from './SubInstanceList';
export { default as InstanceDetails } from './InstanceDetails';
export { default as SubInstanceGroup } from './SubInstanceGroup';
Loading

0 comments on commit 6515018

Please sign in to comment.