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

Change: Refactor DialogNotification into a function component #3998

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/web/components/dialog/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,25 @@ export const DialogFooterLayout = styled(Layout)`
padding: 10px 20px 10px 20px;
`;

const DialogFooter = ({title, onClick, loading = false}) => (
<DialogFooterLayout align={['end', 'center']} shrink="0">
const DialogFooter = ({
title,
onClick,
loading = false,
'data-testid': dataTestId,
}) => (
<DialogFooterLayout
align={['end', 'center']}
shrink="0"
data-testid={dataTestId}
>
<Button onClick={onClick} title={title} loading={loading}>
{title}
</Button>
</DialogFooterLayout>
);

DialogFooter.propTypes = {
'data-testid': PropTypes.string,
loading: PropTypes.bool,
title: PropTypes.string.isRequired,
onClick: PropTypes.func,
Expand Down
171 changes: 171 additions & 0 deletions src/web/components/notification/__tests__/dialognotification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import React from 'react';

import {rendererWith, screen, fireEvent} from 'web/utils/testing';

import DialogNotification from '../dialognotification';
import useDialogNotification from '../useDialogNotification';

const TestComponent = () => {
const {
dialogState,
closeDialog,
showMessage,
showError,
showErrorMessage,
showSuccessMessage,
} = useDialogNotification();
return (
<div>
<button data-testid="show-message" onClick={() => showMessage('foo')} />
<button
data-testid="show-message-with-subject"
onClick={() => showMessage('foo', 'bar')}
/>
<button
data-testid="show-error-message"
onClick={() => showErrorMessage('foo')}
/>
<button
data-testid="show-success-message"
onClick={() => showSuccessMessage('foo')}
/>
<button
data-testid="show-error"
onClick={() => showError(new Error('foo'))}
/>
<DialogNotification {...dialogState} onCloseClick={closeDialog} />
</div>
);
};

describe('DialogNotification tests', () => {
test('should display a message', () => {
const {render} = rendererWith();

render(<TestComponent />);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

const button = screen.getByTestId('show-message');

fireEvent.click(button);

const dialogTitleBar = screen.getByTestId('dialog-title-bar');

expect(dialogTitleBar).toHaveTextContent('Message');

const dialogContent = screen.getByTestId('dialog-notification-message');

expect(dialogContent).toHaveTextContent('foo');
});

test('should display a message with subject', () => {
const {render} = rendererWith();

render(<TestComponent />);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

const button = screen.getByTestId('show-message-with-subject');

fireEvent.click(button);

const dialogTitleBar = screen.getByTestId('dialog-title-bar');

expect(dialogTitleBar).toHaveTextContent('bar');

const dialogContent = screen.getByTestId('dialog-notification-message');

expect(dialogContent).toHaveTextContent('foo');
});

test('should allow to close dialog', () => {
const {render} = rendererWith();

render(<TestComponent />);

const button = screen.getByTestId('show-message');

fireEvent.click(button);

const dialogTitleBar = screen.getByTestId('dialog-title-bar');

expect(dialogTitleBar).toHaveTextContent('Message');

const dialogContent = screen.getByTestId('dialog-notification-message');

expect(dialogContent).toHaveTextContent('foo');

const dialogFooter = screen.getByTestId('dialog-notification-footer');
const closeButton = dialogFooter.querySelector('button');

fireEvent.click(closeButton);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});

test('should display an error message', () => {
const {render} = rendererWith();

render(<TestComponent />);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

const button = screen.getByTestId('show-error-message');

fireEvent.click(button);

const dialogTitleBar = screen.getByTestId('dialog-title-bar');

expect(dialogTitleBar).toHaveTextContent('Error');

const dialogContent = screen.getByTestId('dialog-notification-message');

expect(dialogContent).toHaveTextContent('foo');
});

test('should display a success message', () => {
const {render} = rendererWith();

render(<TestComponent />);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

const button = screen.getByTestId('show-success-message');

fireEvent.click(button);

const dialogTitleBar = screen.getByTestId('dialog-title-bar');

expect(dialogTitleBar).toHaveTextContent('Success');

const dialogContent = screen.getByTestId('dialog-notification-message');

expect(dialogContent).toHaveTextContent('foo');
});

test('should display an error', () => {
const {render} = rendererWith();

render(<TestComponent />);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

const button = screen.getByTestId('show-error');

fireEvent.click(button);

const dialogTitleBar = screen.getByTestId('dialog-title-bar');

expect(dialogTitleBar).toHaveTextContent('Error');

const dialogContent = screen.getByTestId('dialog-notification-message');

expect(dialogContent).toHaveTextContent('foo');
});
});
112 changes: 31 additions & 81 deletions src/web/components/notification/dialognotification.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,100 +17,50 @@
*/
import React from 'react';

import _ from 'gmp/locale';

import {isDefined} from 'gmp/utils/identity';
import {hasValue} from 'gmp/utils/identity';

import PropTypes from 'web/utils/proptypes';

import useTranslation from 'web/hooks/useTranslation';

import Dialog from 'web/components/dialog/dialog';
import DialogContent from 'web/components/dialog/content';
import DialogFooter from 'web/components/dialog/footer';
import DialogTitle from 'web/components/dialog/title';
import ScrollableContent from 'web/components/dialog/scrollablecontent';

class DialogNotification extends React.Component {
constructor(...args) {
super(...args);

this.handleDialogClose = this.handleDialogClose.bind(this);
this.handleShowError = this.handleShowError.bind(this);
this.handleShowErrorMessage = this.handleShowErrorMessage.bind(this);
this.handleShowMessage = this.handleShowMessage.bind(this);
this.handleShowSuccessMessage = this.handleShowSuccessMessage.bind(this);

this.state = {};
}

handleShowError(error) {
this.handleShowErrorMessage(error.message);
}

handleShowErrorMessage(message) {
this.handleShowMessage(message, _('Error'));
}

handleShowSuccessMessage(message) {
this.handleShowMessage(message, _('Success'));
}
const DialogNotification = ({title, message, onCloseClick}) => {
const [_] = useTranslation();

handleShowMessage(message, subject = _('Message')) {
this.setState({
message,
title: subject,
});
if (!hasValue(message)) {
return null;
}

handleDialogClose() {
this.setState({message: undefined});
}

isDialogOpen() {
return isDefined(this.state.message);
}

render() {
const {children} = this.props;

if (!isDefined(children)) {
return null;
}

const {title, message} = this.state;
return (
<React.Fragment>
{children({
showError: this.handleShowError,
showErrorMessage: this.handleShowErrorMessage,
showMessage: this.handleShowMessage,
showSuccessMessage: this.handleShowSuccessMessage,
})}
{this.isDialogOpen() && (
<Dialog width="400px" onClose={this.handleDialogClose}>
{({close, moveProps, heightProps}) => (
<DialogContent>
<DialogTitle
title={title}
onCloseClick={close}
{...moveProps}
/>
<ScrollableContent {...heightProps}>
{message}
</ScrollableContent>
<DialogFooter title={_('Close')} onClick={close} />
</DialogContent>
)}
</Dialog>
)}
</React.Fragment>
);
}
}
return (
<Dialog width="400px" onClose={onCloseClick}>
{({close, moveProps, heightProps}) => (
<DialogContent>
<DialogTitle title={title} onCloseClick={close} {...moveProps} />
<ScrollableContent
{...heightProps}
data-testid="dialog-notification-message"
>
{message}
</ScrollableContent>
<DialogFooter
title={_('Close')}
onClick={close}
data-testid="dialog-notification-footer"
/>
</DialogContent>
)}
</Dialog>
);
};

DialogNotification.propTypes = {
children: PropTypes.func,
message: PropTypes.string,
title: PropTypes.string,
onCloseClick: PropTypes.func,
};

export default DialogNotification;

// vim: set ts=2 sw=2 tw=80:
Loading
Loading