Skip to content

Commit

Permalink
Replace IComCom library with custom component (#3636)
Browse files Browse the repository at this point in the history
Co-authored-by: Chris Beer <chris@cbeer.info>
  • Loading branch information
marlo-longley and cbeer authored Feb 21, 2023
1 parent f42b0e1 commit 2c2f553
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 17 deletions.
5 changes: 3 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"page": true,
"document": true
},
"plugins": ["jest"],
"plugins": ["jest", "react", "react-hooks"],
"rules": {
"import/prefer-default-export": "off",
"no-console": "off",
Expand Down Expand Up @@ -40,6 +40,7 @@
"ignoreRegExpLiterals": true
}],
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off"
"react/react-in-jsx-scope": "off",
"react-hooks/exhaustive-deps": "error"
}
}
6 changes: 3 additions & 3 deletions __tests__/src/components/AccessTokenSender.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ describe('AccessTokenSender', () => {
writable: true,
});
wrapper = createWrapper({ url: 'http://example.com' });
expect(wrapper.find('IComCom').length).toBe(1);
expect(wrapper.find('IComCom').props().attributes.src).toBe('http://example.com?origin=http://localhost&messageId=http://example.com');
expect(wrapper.find('IIIFIFrameCommunication').length).toBe(1);
expect(wrapper.find('IIIFIFrameCommunication').props().src).toBe('http://example.com?origin=http://localhost&messageId=http://example.com');
});

it('triggers an action when the iframe sends a message', () => {
const handleAccessTokenMessage = jest.fn();
wrapper = createWrapper({ handleAccessTokenMessage, url: 'http://example.com' });
expect(wrapper.find('IComCom').props().handleReceiveMessage).toEqual(wrapper.instance().onReceiveAccessTokenMessage);
expect(wrapper.find('IIIFIFrameCommunication').props().handleReceiveMessage).toEqual(wrapper.instance().onReceiveAccessTokenMessage);

wrapper.instance().onReceiveAccessTokenMessage({ data: { messageId: 'http://example.com' } });
expect(handleAccessTokenMessage).toHaveBeenCalledWith({ messageId: 'http://example.com' });
Expand Down
44 changes: 44 additions & 0 deletions __tests__/src/components/IIIFIFrameCommunication.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { shallow, mount } from 'enzyme';
import { IIIFIFrameCommunication } from '../../../src/components/IIIFIFrameCommunication';

/** */
function createWrapper(props) {
return shallow(
<IIIFIFrameCommunication
src="https://iiifauth.digtest.co.uk/auth/token/login/01_Icarus_Breughel.jpg?origin=http://localhost:4444&messageId=https://iiifauth.digtest.co.uk/auth/token/login/01_Icarus_Breughel.jpg"
title="AccessTokenSender"
handleReceiveMessage={() => {}}
/>,
);
}

describe('IIIFIFrameCommunication', () => {
it('should render an iframe', () => {
const wrapper = createWrapper();
expect(wrapper.find('iframe')).toHaveLength(1);
});
});

describe('Register event listener', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should call handleReceiveMessage on message event', () => {
const events = {};
jest.spyOn(window, 'addEventListener').mockImplementation((event, onReceiveMessage) => {
events[event] = onReceiveMessage;
});
jest.spyOn(window, 'removeEventListener').mockImplementation((event, onReceiveMessage) => {
events[event] = undefined;
});
const props = { handleReceiveMessage: jest.fn() };
const wrapper = mount(<IIIFIFrameCommunication {...props} />);
events.message();

expect(props.handleReceiveMessage).toBeCalledTimes(1);
expect(window.addEventListener).toBeCalledWith('message', expect.any(Function));

wrapper.unmount();
expect(window.removeEventListener).toBeCalledWith('message', expect.any(Function), false);
});
});
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
"deepmerge": "^4.2.2",
"dompurify": "^2.0.11",
"i18next": "^21.0.0 || ^22.0.0",
"icomcom-react": "^1.0.1",
"jss": "^10.3.0",
"jss-rtl": "^0.3.0",
"lodash": "^4.17.11",
Expand Down Expand Up @@ -106,7 +105,7 @@
"eslint-plugin-jest": "^27.1.5",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react-hooks": "^4.6.0",
"glob": "^8.0.3",
"http-server": "^14.1.0",
"jest": "^29.3.1",
Expand Down
13 changes: 4 additions & 9 deletions src/components/AccessTokenSender.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component } from 'react';
import PropTypes from 'prop-types';
import IComCom from 'icomcom-react';
import { IIIFIFrameCommunication } from './IIIFIFrameCommunication';

/**
* Opens a new window for click
Expand Down Expand Up @@ -29,14 +29,9 @@ export class AccessTokenSender extends Component {
external, no-op
*/
return (
<IComCom
attributes={{
'aria-hidden': true,
height: 1,
src: `${url}?origin=${window.origin}&messageId=${url}`,
style: { visibility: 'hidden' },
width: 1,
}}
<IIIFIFrameCommunication
src={`${url}?origin=${window.origin}&messageId=${url}`}
title="AccessTokenSender"
handleReceiveMessage={this.onReceiveAccessTokenMessage}
/>
);
Expand Down
48 changes: 48 additions & 0 deletions src/components/IIIFIFrameCommunication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useEffect } from 'react';
import PropTypes from 'prop-types';

/**
* Handle IIIF Auth token validation using iframe message events
*/
export function IIIFIFrameCommunication({ handleReceiveMessage, ...props }) {
// Attaches the 'message' event listener to the window.
useEffect(() => {
if (!handleReceiveMessage) return undefined;

window.addEventListener('message', handleReceiveMessage);

// cleanup function
return () => window.removeEventListener('message', handleReceiveMessage, false);
}, [handleReceiveMessage]);

return (
// iframe "title" attribute is passed in via props for accessibility
// eslint-disable-next-line jsx-a11y/iframe-has-title
<iframe
{...props}
/>
);
}

IIIFIFrameCommunication.propTypes = {
ariaHidden: PropTypes.bool,
frameBorder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
handleReceiveMessage: PropTypes.func,
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
name: PropTypes.string,
scrolling: PropTypes.string,
src: PropTypes.string.isRequired,
style: PropTypes.shape({}),
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};

IIIFIFrameCommunication.defaultProps = {
ariaHidden: true,
frameBorder: 0,
handleReceiveMessage: undefined,
height: 1,
name: undefined,
scrolling: undefined,
style: { visibility: 'hidden' },
width: 1,
};
4 changes: 3 additions & 1 deletion src/state/actions/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ export function receiveAccessTokenFailure(authId, serviceId, error) {
/**
* resolveAccessTokenRequest - action creator
*
* @param {Object} message
* @param {String} authServiceId
* @param {String} tokenServiceId
* @param {Object} json
* @memberof ActionCreators
*/
export function resolveAccessTokenRequest(authServiceId, tokenServiceId, json) {
Expand Down

0 comments on commit 2c2f553

Please sign in to comment.