Skip to content

Commit

Permalink
feat: Show chapter menu, move up, move down and delete
Browse files Browse the repository at this point in the history
  • Loading branch information
josebui committed Sep 13, 2023
1 parent f54fef8 commit b162e42
Show file tree
Hide file tree
Showing 5 changed files with 502 additions and 107 deletions.
56 changes: 56 additions & 0 deletions src/common/components/ConfirmMenuItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright © 2021-2023 Technology Matters
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import React, { useCallback, useEffect, useState } from 'react';
import { MenuItem } from '@mui/material';

import ConfirmationDialog from 'common/components/ConfirmationDialog';

const ConfirmMenuItem = props => {
const [openConfirmation, setOpenConfirmation] = useState(false);
const { confirmTitle, confirmMessage, confirmButton, onConfirm, children } =
props;

useEffect(() => {
setOpenConfirmation(false);
}, []);

const onClick = useCallback(event => {
setOpenConfirmation(true);
event.stopPropagation();
}, []);

const onCancel = useCallback(event => {
setOpenConfirmation(false);
event.stopPropagation();
}, []);

return (
<>
<ConfirmationDialog
open={openConfirmation}
title={confirmTitle}
message={confirmMessage}
confirmButtonLabel={confirmButton}
onCancel={onCancel}
onConfirm={onConfirm}
/>
<MenuItem onClick={onClick}>{children}</MenuItem>
</>
);
};

export default ConfirmMenuItem;
4 changes: 4 additions & 0 deletions src/localization/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,10 @@
"form_chapter_alignment_left": "Left",
"form_chapter_alignment_center": "Center",
"form_chapter_alignment_right": "Right",
"form_chapter_open_menu": "Open menu",
"form_chapter_menu_label": "{{chapterLabel}} menu",
"form_chapter_move_up": "Move Chapter Up",
"form_chapter_move_down": "Move Chapter Down",
"form_location_dialog_title": "Set map location for <1>{{title}}</1>",
"form_location_dialog_title_blank": "Set map location",
"location_dialog_cancel_button": "Cancel",
Expand Down
212 changes: 206 additions & 6 deletions src/storyMap/components/StoryMapForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ const BASE_CONFIG = {
zoom: 5,
},
},
{
id: 'chapter-3',
title: 'Chapter 3',
description: 'Chapter 3 description',
},
],
};

Expand Down Expand Up @@ -395,13 +400,13 @@ test('StoryMapForm: Sidebar navigation', async () => {
});

const title = within(sidebarList).getByRole('button', {
name: 'T Title',
name: 'Title',
});
const chapter1 = within(sidebarList).getByRole('button', {
name: '1 Chapter 1',
name: 'Chapter 1',
});
const chapter2 = within(sidebarList).getByRole('button', {
name: '2 Chapter 2',
name: 'Chapter 2',
});

await waitFor(() => expect(scrollama).toHaveBeenCalled());
Expand Down Expand Up @@ -505,7 +510,7 @@ test('StoryMapForm: Adds new chapter', async () => {
description: 'Chapter 2 description',
})
);
expect(saveCall[0].chapters[2]).toEqual(
expect(saveCall[0].chapters[3]).toEqual(
expect.objectContaining({
alignment: 'left',
title: 'New chapter',
Expand All @@ -516,14 +521,14 @@ test('StoryMapForm: Adds new chapter', async () => {
onChapterExit: [],
})
);
expect(saveCall[0].chapters[2].media).toEqual(
expect(saveCall[0].chapters[3].media).toEqual(
expect.objectContaining({
filename: 'test.jpg',
type: 'image/jpeg',
})
);

expect(saveCall[0].chapters[2].media.contentId).toEqual(
expect(saveCall[0].chapters[3].media.contentId).toEqual(
Object.keys(saveCall[1])[0]
);
});
Expand Down Expand Up @@ -674,3 +679,198 @@ test('StoryMapForm: Change chapter location', async () => {
})
);
});

test('StoryMapForm: Move chapter down with menu', async () => {
const { onSaveDraft } = await setup(BASE_CONFIG);

const chaptersSection = screen.getByRole('navigation', {
name: 'Chapters sidebar',
});

const chapter1 = within(chaptersSection).getByRole('button', {
name: 'Chapter 1',
});

const menuButton = within(chapter1).getByRole('button', {
name: 'Open menu',
});
await act(async () => fireEvent.click(menuButton));

const menu = screen.getByRole('menu', {
name: 'Chapter 1 menu',
});

const moveDownButton = within(menu).getByRole('menuitem', {
name: 'Move Chapter Down',
});

await act(async () => fireEvent.click(moveDownButton));

await act(async () =>
fireEvent.click(screen.getByRole('button', { name: 'Save draft' }))
);
expect(onSaveDraft).toHaveBeenCalledTimes(1);
const saveCall = onSaveDraft.mock.calls[0];

expect(saveCall[0].chapters[0]).toEqual(
expect.objectContaining({
id: 'chapter-2',
title: 'Chapter 2',
description: 'Chapter 2 description',
})
);
expect(saveCall[0].chapters[1]).toEqual(
expect.objectContaining({
id: 'chapter-1',
title: 'Chapter 1',
description: 'Chapter 1 description',
})
);
});

test('StoryMapForm: Move chapter up with menu', async () => {
const { onSaveDraft } = await setup(BASE_CONFIG);

const chaptersSection = screen.getByRole('navigation', {
name: 'Chapters sidebar',
});

const chapter2 = within(chaptersSection).getByRole('button', {
name: 'Chapter 2',
});

const menuButton = within(chapter2).getByRole('button', {
name: 'Open menu',
});
await act(async () => fireEvent.click(menuButton));

const menu = screen.getByRole('menu', {
name: 'Chapter 2 menu',
});

const moveUpButton = within(menu).getByRole('menuitem', {
name: 'Move Chapter Up',
});

await act(async () => fireEvent.click(moveUpButton));

await act(async () =>
fireEvent.click(screen.getByRole('button', { name: 'Save draft' }))
);
expect(onSaveDraft).toHaveBeenCalledTimes(1);
const saveCall = onSaveDraft.mock.calls[0];

expect(saveCall[0].chapters[0]).toEqual(
expect.objectContaining({
id: 'chapter-2',
title: 'Chapter 2',
description: 'Chapter 2 description',
})
);
expect(saveCall[0].chapters[1]).toEqual(
expect.objectContaining({
id: 'chapter-1',
title: 'Chapter 1',
description: 'Chapter 1 description',
})
);
});

test('StoryMapForm: Show correct sort buttons if chapter is first', async () => {
await setup(BASE_CONFIG);

const chaptersSection = screen.getByRole('navigation', {
name: 'Chapters sidebar',
});

const chapter1 = within(chaptersSection).getByRole('button', {
name: 'Chapter 1',
});
const menuButton = within(chapter1).getByRole('button', {
name: 'Open menu',
});
await act(async () => fireEvent.click(menuButton));
const menu = screen.getByRole('menu', {
name: 'Chapter 1 menu',
});
const moveUpButton = within(menu).queryByRole('menuitem', {
name: 'Move Chapter Up',
});

expect(moveUpButton).not.toBeInTheDocument();
});

test('StoryMapForm: Show correct sort buttons if chapter is last', async () => {
await setup(BASE_CONFIG);

const chaptersSection = screen.getByRole('navigation', {
name: 'Chapters sidebar',
});

const chapter3 = within(chaptersSection).getByRole('button', {
name: 'Chapter 3',
});
const menuButton = within(chapter3).getByRole('button', {
name: 'Open menu',
});
await act(async () => fireEvent.click(menuButton));
const menu = screen.getByRole('menu', {
name: 'Chapter 3 menu',
});
const moveDownButton = within(menu).queryByRole('menuitem', {
name: 'Move Chapter Down',
});

expect(moveDownButton).not.toBeInTheDocument();
});

test('StoryMapForm: Delete chapter', async () => {
const { onSaveDraft } = await setup(BASE_CONFIG);

const chaptersSection = screen.getByRole('navigation', {
name: 'Chapters sidebar',
});

const chapter1 = within(chaptersSection).getByRole('button', {
name: 'Chapter 1',
});
const menuButton = within(chapter1).getByRole('button', {
name: 'Open menu',
});
await act(async () => fireEvent.click(menuButton));
const menu = screen.getByRole('menu', {
name: 'Chapter 1 menu',
});
const deleteButton = within(menu).getByRole('menuitem', {
name: 'Delete Chapter',
});

await act(async () => fireEvent.click(deleteButton));

// Confirmation dialog
await act(async () =>
fireEvent.click(screen.getByRole('button', { name: 'Delete Chapter' }))
);

await act(async () =>
fireEvent.click(screen.getByRole('button', { name: 'Save draft' }))
);
expect(onSaveDraft).toHaveBeenCalledTimes(1);
const saveCall = onSaveDraft.mock.calls[0];

expect(saveCall[0].chapters.length).toEqual(2);
expect(saveCall[0].chapters[0]).toEqual(
expect.objectContaining({
id: 'chapter-2',
title: 'Chapter 2',
description: 'Chapter 2 description',
})
);
expect(saveCall[0].chapters[1]).toEqual(
expect.objectContaining({
id: 'chapter-3',
title: 'Chapter 3',
description: 'Chapter 3 description',
})
);
});
Loading

0 comments on commit b162e42

Please sign in to comment.