Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 18 additions & 1 deletion src/elements/content-sidebar/AddTaskButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import { TASK_TYPE_APPROVAL } from '../../constants';
import type { TaskFormProps } from './activity-feed/task-form/TaskForm';
import type { TaskType } from '../../common/types/tasks';
import type { ElementsXhrError } from '../../common/types/api';
import type { InternalSidebarNavigation, InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation';

type Props = {|
history: RouterHistory,
internalSidebarNavigation?: InternalSidebarNavigation,
internalSidebarNavigationHandler?: InternalSidebarNavigationHandler,
isDisabled: boolean,
onTaskModalClose: () => void,
routerDisabled?: boolean,
taskFormProps: TaskFormProps,
|};

Expand Down Expand Up @@ -40,7 +44,20 @@ class AddTaskButton extends React.Component<Props, State> {
2. Preventing the sidebar from closing keeps the task modal open upon edit and resize
*/
handleClickMenuItem = (taskType: TaskType) => {
this.props.history.replace({ state: { open: true } });
const { history, internalSidebarNavigation, internalSidebarNavigationHandler, routerDisabled } = this.props;

if (routerDisabled && internalSidebarNavigationHandler) {
internalSidebarNavigationHandler(
{
...internalSidebarNavigation,
open: true,
},
true,
);
} else {
history.replace({ state: { open: true } });
}

this.setState({ isTaskFormOpen: true, taskType });
};

Expand Down
153 changes: 118 additions & 35 deletions src/elements/content-sidebar/__tests__/AddTaskButton.test.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,139 @@
import * as React from 'react';
import { mount } from 'enzyme';
import { render, screen, userEvent } from '../../../test-utils/testing-library';
import { AddTaskButtonComponent as AddTaskButton } from '../AddTaskButton';

jest.mock('../../../components/date-picker/DatePicker', () => props => {
// only spread `input` attritutes to the input field
const { name, value = '', className, onChange, placeholder } = props;
const localInputProps = {
name,
value,
className,
onChange,
placeholder,
};
return (
<input type="date" {...localInputProps} {...props.inputProps} /> // eslint-disable-line react/prop-types
);
});
jest.mock('../AddTaskMenu', () => ({ onMenuItemClick, isDisabled, setAddTaskButtonRef }) => (
<div data-testid="add-task-menu">
<button
type="button"
onClick={() => onMenuItemClick('GENERAL')}
disabled={isDisabled}
ref={setAddTaskButtonRef}
data-testid="menu-item-general"
>
Add General Task
</button>
</div>
));

jest.mock('../TaskModal', () => ({ isTaskFormOpen, onModalClose, taskType }) => (
<div data-testid="task-modal" data-open={isTaskFormOpen} data-task-type={taskType}>
<button type="button" onClick={onModalClose} data-testid="modal-close">
Close Modal
</button>
</div>
));

describe('elements/content-sidebar/AddTaskButton', () => {
/*
1. Pushing the open state into history keeps the sidebar open upon resize and refresh
2. Preventing the sidebar from closing keeps the task modal open upon edit and resize
*/

test('should call history.replace state with force open state when task menu items are clicked', () => {
const defaultProps = {
history: { replace: jest.fn() },
isDisabled: false,
onTaskModalClose: jest.fn(),
taskFormProps: {
approvers: [],
approverSelectorContacts: [],
completionRule: 'ALL',
createTask: jest.fn(),
getApproverWithQuery: jest.fn(),
getAvatarUrl: jest.fn(),
id: '',
message: '',
},
};

const renderComponent = (props = {}) => {
return render(<AddTaskButton {...defaultProps} {...props} />);
};

beforeEach(() => {
jest.clearAllMocks();
});

test('should call history.replace state with force open state when task menu items are clicked', async () => {
const historyMock = { replace: jest.fn() };
const wrapper = mount(<AddTaskButton history={historyMock} />);
const user = userEvent();

const button = wrapper.find('Button');
button.simulate('click');
renderComponent({ history: historyMock });

const menuItem = wrapper.find('MenuItem').first();
menuItem.simulate('click');
const menuItem = screen.getByTestId('menu-item-general');
await user.click(menuItem);

expect(historyMock.replace).toHaveBeenCalledWith({ state: { open: true } });
});

test('should set state.isTaskFormOpen to false and call onTaskModalClose when task modal is closed', () => {
test('should set state.isTaskFormOpen to false and call onTaskModalClose when task modal is closed', async () => {
const onTaskModalCloseMock = jest.fn();
const mockButtonRef = {
current: {
focus: jest.fn(),
},
};
const wrapper = shallow(<AddTaskButton onTaskModalClose={onTaskModalCloseMock} />);
wrapper.instance().buttonRef = mockButtonRef;
wrapper.setState({ isTaskFormOpen: true });

wrapper.instance().handleModalClose();

expect(wrapper.state('isTaskFormOpen')).toBe(false);
expect(mockButtonRef.current.focus).toHaveBeenCalledTimes(1);
const user = userEvent();

renderComponent({ onTaskModalClose: onTaskModalCloseMock });

// First click a menu item to open the modal
const menuItem = screen.getByTestId('menu-item-general');
await user.click(menuItem);

// Verify modal is open
const modal = screen.getByTestId('task-modal');
expect(modal).toHaveAttribute('data-open', 'true');

// Close the modal
const closeButton = screen.getByTestId('modal-close');
await user.click(closeButton);

// Verify modal is closed and callback was called
expect(modal).toHaveAttribute('data-open', 'false');
expect(onTaskModalCloseMock).toHaveBeenCalledTimes(1);
});

describe('when routerDisabled is true', () => {
test('should use internalSidebarNavigationHandler when task menu items are clicked', async () => {
const mockNavigationHandler = jest.fn();
const user = userEvent();

renderComponent({
routerDisabled: true,
internalSidebarNavigationHandler: mockNavigationHandler,
});

const menuItem = screen.getByTestId('menu-item-general');
await user.click(menuItem);

expect(mockNavigationHandler).toHaveBeenCalledTimes(1);
expect(mockNavigationHandler).toHaveBeenCalledWith({ open: true }, true);
});

test('should preserve internalSidebarNavigation state when using navigation handler', async () => {
const mockNavigationHandler = jest.fn();
const mockInternalSidebarNavigation = {
sidebar: 'activity',
activeFeedEntryType: 'comments',
activeFeedEntryId: '123',
};
const user = userEvent();

renderComponent({
routerDisabled: true,
internalSidebarNavigationHandler: mockNavigationHandler,
internalSidebarNavigation: mockInternalSidebarNavigation,
});

const menuItem = screen.getByTestId('menu-item-general');
await user.click(menuItem);

expect(mockNavigationHandler).toHaveBeenCalledTimes(1);
expect(mockNavigationHandler).toHaveBeenCalledWith(
{
sidebar: 'activity',
activeFeedEntryType: 'comments',
activeFeedEntryId: '123',
open: true,
},
true,
);
});
});
});