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

feat(ts): convert aboutmodal to TS #1968

Merged
merged 16 commits into from
May 21, 2019

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: 'About modal'
cssPrefix: 'pf-c-about-modal-box'
typescript: true
---
import { AboutModal, Button, TextContent, TextList, TextListItem } from '@patternfly/react-core';
import brandImg from './examples/brandImg.svg';
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import { AboutModal } from './AboutModal';
import { KEY_CODES } from '../../helpers/constants';

const mockListener = jest.spyOn(ReactDOM, 'createPortal');
jest.spyOn(document, 'createElement');
jest.spyOn(document, 'addEventListener');

mockListener.mockImplementation(node => node as React.ReactPortal);

const props = {
onClose: jest.fn(),
children: 'modal content',
productName: 'Product Name',
trademark: 'Trademark and copyright information here',
brandImageSrc: 'brandImg...',
brandImageAlt: 'Brand Image',
logoImageSrc: 'logoImg...',
logoImageAlt: 'AboutModal Logo'
};

test('AboutModal creates a container element once for div', () => {
const view = shallow(<AboutModal {...props}> Test About Modal </AboutModal>);
view.update();
expect(document.createElement).toBeCalledWith('div');
expect(document.createElement).toHaveBeenCalledTimes(1);
});

test('About Modal closes with escape', () => {
shallow(
<AboutModal {...props} isOpen>
Test About Modal
</AboutModal>
);
const [event, handler] = (document.addEventListener as any).mock.calls[0];
expect(event).toBe('keydown');
handler({ keyCode: KEY_CODES.ESCAPE_KEY });
expect(props.onClose).toBeCalled();
});

test('modal does not call onClose for esc key if it is not open', () => {
shallow(<AboutModal {...props} />);
const [event, handler] = (document.addEventListener as any).mock.calls[0];
expect(event).toBe('keydown');
handler({ keyCode: KEY_CODES.ESCAPE_KEY });
expect(props.onClose).not.toBeCalled();
});

test('Each modal is given new ariaDescribedById and ariaLabelledbyId', () => {
const first = new AboutModal(props);
const second = new AboutModal(props);
expect(first.ariaLabelledBy).not.toBe(second.ariaLabelledBy);
expect(first.ariaDescribedBy).not.toBe(second.ariaDescribedBy);
});

test('Console error is generated when the logoImageSrc is provided without logoImageAlt', () => {
const noImgAltrops = {
onClose: jest.fn(),
children: 'modal content',
productName: 'Product Name',
trademark: 'Trademark and copyright information here',
brandImageSrc: 'brandImg...',
logoImageSrc: 'logoImg...'
};
const myMock = jest.fn() as any;
global.console = { error: myMock } as any;
const JSAboutModal = AboutModal as any;
shallow(<JSAboutModal {...noImgAltrops}> Test About Modal </JSAboutModal>);
expect(myMock).toBeCalled();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { css } from '@patternfly/react-styles';
import styles from '@patternfly/patternfly/components/Backdrop/backdrop.css';
import { canUseDOM } from 'exenv';
import { KEY_CODES } from '../../helpers/constants';
import { AboutModalContainer } from './AboutModalContainer';

export interface AboutModalProps {
/** Content rendered inside the about modal */
children: React.ReactNode;
/** Additional classes added to the about modal */
className?: string;
/** Flag to show the about modal */
isOpen?: boolean;
/** A callback for when the close button is clicked */
onClose?: () => void;
/** Product name */
productName?: string;
/** Trademark information */
trademark?: string;
/** The URL of the image for the brand */
brandImageSrc: string;
/** The alternate text of the brand image */
brandImageAlt: string;
/** The URL of the image for the background */
backgroundImageSrc?: string;
/** Prevents the about modal from rendering content inside a container; allows for more flexible layouts */
noAboutModalBoxContentContainer?: boolean;
};

export class AboutModal extends React.Component<AboutModalProps> {
private static currentId = 0;
private container: HTMLElement;
private id = AboutModal.currentId++;
ariaLabelledBy = `pf-about-modal-title-${this.id}`;
ariaDescribedBy = `pf-about-modal-content-${this.id}`;
static defaultProps = {
className: '',
isOpen: false,
onClose: (): any => undefined,
productName: '',
trademark: '',
backgroundImageSrc: '',
noAboutModalBoxContentContainer: false
};

constructor(props: AboutModalProps) {
super(props);
if (props.brandImageSrc && !props.brandImageAlt) {
// tslint:disable-next-line:no-console
console.error('AboutModal:', 'brandImageAlt is required when a brandImageSrc is specified');
}
}

private handleEscKeyClick = (event: KeyboardEvent) => {
if (event.keyCode === KEY_CODES.ESCAPE_KEY && this.props.isOpen) {
this.props.onClose();
}
};

componentDidMount() {
if (!this.container) {
this.container = document.createElement('div');
document.body.appendChild(this.container);
document.addEventListener('keydown', this.handleEscKeyClick, false);
}
if (this.props.isOpen) {
document.body.classList.add(css(styles.backdropOpen));
} else {
document.body.classList.remove(css(styles.backdropOpen));
}
}

componentDidUpdate() {
if (this.props.isOpen) {
document.body.classList.add(css(styles.backdropOpen));
} else {
document.body.classList.remove(css(styles.backdropOpen));
}
}

componentWillUnmount() {
if (this.container) {
document.body.removeChild(this.container);
}
document.removeEventListener('keydown', this.handleEscKeyClick, false);
}

render() {
if (!canUseDOM || !this.container) {
return null;
}

return ReactDOM.createPortal(
<AboutModalContainer
ariaLabelledbyId={this.ariaLabelledBy}
ariaDescribedById={this.ariaDescribedBy}
{...this.props}
/>,
this.container
);
}
}

This file was deleted.

Loading