From 07147172c827b4e43a79b6bf70fe9692286eab2e Mon Sep 17 00:00:00 2001 From: Amy Benson Date: Thu, 24 Oct 2024 16:56:19 +0100 Subject: [PATCH 1/2] EES-5589 edit and reorder related pages --- .../content/components/RelatedPageForm.tsx | 57 +++++ .../components/RelatedPagesAddModal.tsx | 31 +++ .../components/RelatedPagesEditModal.tsx | 150 +++++++++++++ .../components/RelatedPagesSection.tsx | 119 ++++------ .../__tests__/RelatedPageForm.test.tsx | 119 ++++++++++ .../__tests__/RelatedPagesEditModal.test.tsx | 204 ++++++++++++++++++ .../__tests__/RelatedPagesSection.test.tsx | 153 ++++--------- ...releaseContentRelatedInformationService.ts | 20 +- .../tests/admin/bau/release_content.robot | 27 ++- .../bau/data_reordering.robot | 8 - .../robot-tests/tests/libs/admin-common.robot | 12 +- .../admin/analyst/role_ui_permissions.robot | 2 +- 12 files changed, 679 insertions(+), 223 deletions(-) create mode 100644 src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPageForm.tsx create mode 100644 src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesAddModal.tsx create mode 100644 src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx create mode 100644 src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPageForm.test.tsx create mode 100644 src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesEditModal.test.tsx diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPageForm.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPageForm.tsx new file mode 100644 index 00000000000..9ba5628a8df --- /dev/null +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPageForm.tsx @@ -0,0 +1,57 @@ +import Button from '@common/components/Button'; +import FormFieldTextInput from '@common/components/form/FormFieldTextInput'; +import FormProvider from '@common/components/form/FormProvider'; +import Form from '@common/components/form/Form'; +import { BasicLink } from '@common/services/publicationService'; +import Yup from '@common/validation/yup'; +import ButtonGroup from '@common/components/ButtonGroup'; +import React from 'react'; + +export type RelatedPageFormValues = Omit; + +interface Props { + initialValues?: RelatedPageFormValues; + onCancel: () => void; + onSubmit: (link: RelatedPageFormValues) => Promise; +} + +export default function RelatedPageForm({ + initialValues, + onCancel, + onSubmit, +}: Props) { + return ( + +
+ + label="Title" + name="description" + /> + + label="Link URL" + name="url" + /> + + + + + +
+ ); +} diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesAddModal.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesAddModal.tsx new file mode 100644 index 00000000000..cf2576e83cb --- /dev/null +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesAddModal.tsx @@ -0,0 +1,31 @@ +import RelatedPageForm, { + RelatedPageFormValues, +} from '@admin/pages/release/content/components/RelatedPageForm'; +import Button from '@common/components/Button'; +import Modal from '@common/components/Modal'; +import useToggle from '@common/hooks/useToggle'; +import React from 'react'; + +interface Props { + onSubmit: (link: RelatedPageFormValues) => Promise; +} + +export default function RelatedPagesAddModal({ onSubmit }: Props) { + const [open, toggleOpen] = useToggle(false); + + const handleSubmit = async (values: RelatedPageFormValues) => { + await onSubmit(values); + toggleOpen.off(); + }; + + return ( + Add related page} + onExit={toggleOpen.off} + > + + + ); +} diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx new file mode 100644 index 00000000000..0f7f8b81377 --- /dev/null +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx @@ -0,0 +1,150 @@ +import Link from '@admin/components/Link'; +import RelatedPageForm, { + RelatedPageFormValues, +} from '@admin/pages/release/content/components/RelatedPageForm'; +import Button from '@common/components/Button'; +import ButtonGroup from '@common/components/ButtonGroup'; +import Modal from '@common/components/Modal'; +import ReorderableList from '@common/components/ReorderableList'; +import VisuallyHidden from '@common/components/VisuallyHidden'; +import useToggle from '@common/hooks/useToggle'; +import { BasicLink } from '@common/services/publicationService'; +import reorder from '@common/utils/reorder'; +import React, { useEffect, useState } from 'react'; + +interface Props { + relatedPages: BasicLink[]; + onUpdate: (pages: BasicLink[]) => Promise; +} + +export default function RelatedPagesEditModal({ + relatedPages: initialRelatedPages, + onUpdate, +}: Props) { + const [open, toggleOpen] = useToggle(false); + const [isReordering, toggleReordering] = useToggle(false); + const [editingPage, setEditingPage] = useState(); + const [relatedPages, setRelatedPages] = + useState(initialRelatedPages); + + useEffect(() => { + setRelatedPages(initialRelatedPages); + }, [initialRelatedPages]); + + const handleRemove = async (id: string) => { + const updatedPages = relatedPages.filter(page => page.id !== id); + await onUpdate(updatedPages); + }; + + const handleSubmit = async (values: RelatedPageFormValues) => { + const updatedPages = relatedPages.map(page => { + if (page.id === editingPage?.id) { + return { id: editingPage.id, ...values }; + } + return page; + }); + await onUpdate(updatedPages); + setEditingPage(undefined); + }; + + return ( + + Edit pages + + } + onExit={toggleOpen.off} + > + {editingPage ? ( + setEditingPage(undefined)} + onSubmit={handleSubmit} + /> + ) : ( + <> + {isReordering ? ( + ({ + id: page.id, + label: page.description, + }))} + testId="reorder-related-pages" + onCancel={() => { + toggleReordering.off(); + setRelatedPages(initialRelatedPages); + }} + onConfirm={async () => { + await onUpdate(relatedPages); + toggleReordering.off(); + }} + onMoveItem={({ prevIndex, nextIndex }) => { + const reordered = reorder(relatedPages, prevIndex, nextIndex); + setRelatedPages(reordered); + }} + onReverse={() => { + setRelatedPages(relatedPages.toReversed()); + }} + /> + ) : ( + <> + + + + + + + + + + + {relatedPages.map(page => ( + + + + + + ))} + +
TitleLink URLActions
{page.description} + {page.url} + + + + + +
+ + + )} + + )} +
+ ); +} diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesSection.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesSection.tsx index 7ef60c17acf..acef59435e6 100644 --- a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesSection.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesSection.tsx @@ -1,108 +1,69 @@ -import EditableLink from '@admin/components/editable/EditableLink'; +import Link from '@admin/components/Link'; +import RelatedPagesEditModal from '@admin/pages/release/content/components/RelatedPagesEditModal'; +import RelatedPagesAddModal from '@admin/pages/release/content/components/RelatedPagesAddModal'; +import { RelatedPageFormValues } from '@admin/pages/release/content/components/RelatedPageForm'; import { useEditingContext } from '@admin/contexts/EditingContext'; import releaseContentRelatedInformationService from '@admin/services/releaseContentRelatedInformationService'; import { EditableRelease } from '@admin/services/releaseContentService'; -import Button from '@common/components/Button'; -import { FormFieldset } from '@common/components/form'; -import FormFieldTextInput from '@common/components/form/FormFieldTextInput'; -import FormProvider from '@common/components/form/FormProvider'; -import Form from '@common/components/form/Form'; -import useToggle from '@common/hooks/useToggle'; import ButtonGroup from '@common/components/ButtonGroup'; import { BasicLink } from '@common/services/publicationService'; -import Yup from '@common/validation/yup'; import React, { useState } from 'react'; -type FormValues = Omit; - interface Props { release: EditableRelease; } export default function RelatedPagesSection({ release }: Props) { - const [links, setLinks] = useState(release.relatedInformation); - const [formOpen, toggleFormOpen] = useToggle(false); + const [relatedPages, setRelatedPages] = useState( + release.relatedInformation, + ); const { editingMode } = useEditingContext(); - const removeLink = (linkId: string) => { - releaseContentRelatedInformationService - .delete(release.id, linkId) - .then(setLinks); + const handleSubmit = async (values: RelatedPageFormValues) => { + const updatedLinks = await releaseContentRelatedInformationService.update( + release.id, + [...relatedPages, values], + ); + setRelatedPages(updatedLinks); }; - const handleSubmit = async (values: FormValues) => { - const newLinks = await releaseContentRelatedInformationService.create( + const handleUpdate = async (updatedPages: BasicLink[]) => { + const updatedLinks = await releaseContentRelatedInformationService.update( release.id, - values, + updatedPages, ); - setLinks(newLinks); - toggleFormOpen.off(); + setRelatedPages(updatedLinks); }; return ( <> - {(editingMode === 'edit' || links.length > 0) && ( - <> - -
    - {links.map(({ id, description, url }) => ( -
  • - removeLink(id)} to={url}> - {description} - -
  • - ))} -
- + {(editingMode === 'edit' || relatedPages.length > 0) && ( + )} + + {relatedPages.length > 0 && ( +
    + {relatedPages.map(({ id, description, url }) => ( +
  • + {description} +
  • + ))} +
+ )} + {editingMode === 'edit' && ( - <> - {formOpen ? ( - -
- - - label="Title" - name="description" - /> - label="Link URL" name="url" /> - - - - - -
-
- ) : ( - + + + {relatedPages.length > 0 && ( + )} - + )} ); diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPageForm.test.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPageForm.test.tsx new file mode 100644 index 00000000000..3b9fba7ee3d --- /dev/null +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPageForm.test.tsx @@ -0,0 +1,119 @@ +import RelatedPageForm from '@admin/pages/release/content/components/RelatedPageForm'; +import { screen, waitFor } from '@testing-library/react'; +import React from 'react'; +import render from '@common-test/render'; +import noop from 'lodash/noop'; + +describe('RelatedPageForm', () => { + test('renders without initial values', async () => { + render( + Promise.resolve()} />, + ); + + expect(screen.getByLabelText('Title')).toBeInTheDocument(); + expect(screen.getByLabelText('Title')).toHaveValue(''); + + expect(screen.getByLabelText('Link URL')).toBeInTheDocument(); + expect(screen.getByLabelText('Link URL')).toHaveValue(''); + + expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + }); + + test('renders with initial values', async () => { + render( + Promise.resolve()} + />, + ); + + expect(screen.getByLabelText('Title')).toBeInTheDocument(); + expect(screen.getByLabelText('Title')).toHaveValue('Page 1'); + + expect(screen.getByLabelText('Link URL')).toBeInTheDocument(); + expect(screen.getByLabelText('Link URL')).toHaveValue('https://gov.uk'); + + expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + }); + + test('shows a validation error when no title is set', async () => { + const { user } = render( + Promise.resolve()} />, + ); + + await user.click(screen.getByLabelText('Title')); + await user.click(screen.getByRole('button', { name: 'Save' })); + + await waitFor(() => { + expect(screen.getByText('There is a problem')).toBeInTheDocument(); + expect( + screen.getByRole('link', { + name: 'Enter a link title', + }), + ).toBeInTheDocument(); + }); + }); + + test('shows a validation error when no url is set', async () => { + const { user } = render( + Promise.resolve()} />, + ); + + await user.click(screen.getByLabelText('Link URL')); + await user.click(screen.getByRole('button', { name: 'Save' })); + + await waitFor(() => { + expect(screen.getByText('There is a problem')).toBeInTheDocument(); + expect( + screen.getByRole('link', { + name: 'Enter a link URL', + }), + ).toBeInTheDocument(); + }); + }); + + test('shows a validation error when the url is invalid', async () => { + const { user } = render( + Promise.resolve()} />, + ); + + await user.type(screen.getByLabelText('Link URL'), 'Not a url'); + await user.click(screen.getByRole('button', { name: 'Save' })); + + await waitFor(() => { + expect(screen.getByText('There is a problem')).toBeInTheDocument(); + expect( + screen.getByRole('link', { + name: 'Enter a valid link URL', + }), + ).toBeInTheDocument(); + }); + }); + + test('calls onSubmit when the form is successfully submitted', async () => { + const handleSubmit = jest.fn(); + + const { user } = render( + , + ); + + await user.type(screen.getByLabelText('Title'), 'Test title'); + await user.type(screen.getByLabelText('Link URL'), 'https://gov.uk'); + + expect(handleSubmit).not.toHaveBeenCalled(); + + await user.click(screen.getByRole('button', { name: 'Save' })); + + expect(handleSubmit).toHaveBeenCalledTimes(1); + expect(handleSubmit).toHaveBeenCalledWith({ + description: 'Test title', + url: 'https://gov.uk', + }); + }); +}); diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesEditModal.test.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesEditModal.test.tsx new file mode 100644 index 00000000000..8f8ef1637ce --- /dev/null +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesEditModal.test.tsx @@ -0,0 +1,204 @@ +import RelatedPagesEditModal from '@admin/pages/release/content/components/RelatedPagesEditModal'; +import { BasicLink } from '@common/services/publicationService'; +import render from '@common-test/render'; +import { screen, within } from '@testing-library/react'; +import React from 'react'; + +describe('RelatedPagesEditModal', () => { + const testRelatedPages: BasicLink[] = [ + { id: 'page-1', description: 'Page 1', url: 'https://gov.uk/1' }, + { id: 'page-2', description: 'Page 2', url: 'https://gov.uk/2' }, + { id: 'page-3', description: 'Page 3', url: 'https://gov.uk/3' }, + ]; + + test('renders the table of related pages', async () => { + const { user } = render( + Promise.resolve()} + />, + ); + + await user.click(screen.getByRole('button', { name: 'Edit pages' })); + + expect( + await screen.findByRole('heading', { name: 'Edit related pages' }), + ).toBeInTheDocument(); + + expect( + screen.getByRole('button', { name: 'Reorder pages' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Close modal' }), + ).toBeInTheDocument(); + + const rows = screen.getAllByRole('row'); + + const row1cells = within(rows[1]).getAllByRole('cell'); + expect(within(row1cells[0]).getByText('Page 1')).toBeInTheDocument(); + expect( + within(row1cells[1]).getByRole('link', { name: 'https://gov.uk/1' }), + ).toBeInTheDocument(); + expect( + within(row1cells[2]).getByRole('button', { name: 'Edit Page 1' }), + ).toBeInTheDocument(); + expect( + within(row1cells[2]).getByRole('button', { name: 'Remove Page 1' }), + ).toBeInTheDocument(); + + const row2cells = within(rows[2]).getAllByRole('cell'); + expect(within(row2cells[0]).getByText('Page 2')).toBeInTheDocument(); + expect( + within(row2cells[1]).getByRole('link', { name: 'https://gov.uk/2' }), + ).toBeInTheDocument(); + expect( + within(row2cells[2]).getByRole('button', { name: 'Edit Page 2' }), + ).toBeInTheDocument(); + expect( + within(row2cells[2]).getByRole('button', { name: 'Remove Page 2' }), + ).toBeInTheDocument(); + + const row3cells = within(rows[3]).getAllByRole('cell'); + expect(within(row3cells[0]).getByText('Page 3')).toBeInTheDocument(); + expect( + within(row3cells[1]).getByRole('link', { name: 'https://gov.uk/3' }), + ).toBeInTheDocument(); + expect( + within(row3cells[2]).getByRole('button', { name: 'Edit Page 3' }), + ).toBeInTheDocument(); + expect( + within(row3cells[2]).getByRole('button', { name: 'Remove Page 3' }), + ).toBeInTheDocument(); + }); + + test('shows the edit form when click an edit button', async () => { + const { user } = render( + Promise.resolve()} + />, + ); + + await user.click(screen.getByRole('button', { name: 'Edit pages' })); + + expect( + await screen.findByRole('heading', { name: 'Edit related pages' }), + ).toBeInTheDocument(); + + await user.click(screen.getByRole('button', { name: 'Edit Page 2' })); + + expect(screen.getByLabelText('Title')).toBeInTheDocument(); + expect(screen.getByLabelText('Title')).toHaveValue('Page 2'); + + expect(screen.getByLabelText('Link URL')).toBeInTheDocument(); + expect(screen.getByLabelText('Link URL')).toHaveValue('https://gov.uk/2'); + + expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + }); + + test('calls onUpdate when save an edited page', async () => { + const handleUpdate = jest.fn(); + const { user } = render( + , + ); + + await user.click(screen.getByRole('button', { name: 'Edit pages' })); + + expect( + await screen.findByRole('heading', { name: 'Edit related pages' }), + ).toBeInTheDocument(); + + await user.click(screen.getByRole('button', { name: 'Edit Page 2' })); + + await user.type(screen.getByLabelText('Title'), '-edited'); + + expect(handleUpdate).not.toHaveBeenCalled(); + await user.click(screen.getByRole('button', { name: 'Save' })); + + expect(handleUpdate).toHaveBeenCalledTimes(1); + expect(handleUpdate).toHaveBeenCalledWith([ + testRelatedPages[0], + { id: 'page-2', description: 'Page 2-edited', url: 'https://gov.uk/2' }, + testRelatedPages[2], + ]); + }); + + test('calls onUpdate when remove a page', async () => { + const handleUpdate = jest.fn(); + const { user } = render( + , + ); + + await user.click(screen.getByRole('button', { name: 'Edit pages' })); + + expect( + await screen.findByRole('heading', { name: 'Edit related pages' }), + ).toBeInTheDocument(); + + expect(handleUpdate).not.toHaveBeenCalled(); + + await user.click(screen.getByRole('button', { name: 'Remove Page 2' })); + + expect(handleUpdate).toHaveBeenCalledTimes(1); + expect(handleUpdate).toHaveBeenCalledWith([ + testRelatedPages[0], + testRelatedPages[2], + ]); + }); + + test('shows the reordering UI when click the reorder button', async () => { + const { user } = render( + Promise.resolve()} + />, + ); + + await user.click(screen.getByRole('button', { name: 'Edit pages' })); + + expect( + await screen.findByRole('heading', { name: 'Edit related pages' }), + ).toBeInTheDocument(); + + await user.click(screen.getByRole('button', { name: 'Reorder pages' })); + + expect( + await screen.findByRole('heading', { name: 'Reorder pages' }), + ).toBeInTheDocument(); + + const reorderableList = screen.getAllByRole('listitem'); + + expect(within(reorderableList[0]).getByText('Page 1')).toBeInTheDocument(); + expect( + within(reorderableList[0]).getByRole('button', { + name: 'Move Page 1 down', + }), + ).toBeInTheDocument(); + + expect(within(reorderableList[1]).getByText('Page 2')).toBeInTheDocument(); + expect( + within(reorderableList[1]).getByRole('button', { + name: 'Move Page 2 up', + }), + ).toBeInTheDocument(); + expect( + within(reorderableList[1]).getByRole('button', { + name: 'Move Page 2 down', + }), + ).toBeInTheDocument(); + + expect(within(reorderableList[2]).getByText('Page 3')).toBeInTheDocument(); + expect( + within(reorderableList[2]).getByRole('button', { + name: 'Move Page 3 up', + }), + ).toBeInTheDocument(); + }); +}); diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesSection.test.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesSection.test.tsx index bfcb106495d..47901a1b42a 100644 --- a/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesSection.test.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/__tests__/RelatedPagesSection.test.tsx @@ -30,7 +30,10 @@ describe('RelatedPagesSection', () => { screen.getByRole('heading', { name: 'Related pages' }), ).toBeInTheDocument(); expect( - screen.getByRole('button', { name: 'Add related page link' }), + screen.getByRole('button', { name: 'Add related page' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Edit pages' }), ).toBeInTheDocument(); const pages = screen.getAllByRole('listitem'); expect(pages).toHaveLength(1); @@ -39,9 +42,6 @@ describe('RelatedPagesSection', () => { name: 'Related information description', }), ).toHaveAttribute('href', 'https://test.com'); - expect( - within(pages[0]).getByRole('button', { name: 'Remove link' }), - ).toBeInTheDocument(); }); test('renders correctly without related pages', () => { @@ -54,8 +54,11 @@ describe('RelatedPagesSection', () => { screen.getByRole('heading', { name: 'Related pages' }), ).toBeInTheDocument(); expect( - screen.getByRole('button', { name: 'Add related page link' }), + screen.getByRole('button', { name: 'Add related page' }), ).toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Edit pages' }), + ).not.toBeInTheDocument(); expect(screen.queryByRole('listitem')).not.toBeInTheDocument(); }); @@ -67,89 +70,14 @@ describe('RelatedPagesSection', () => { ); await user.click( - screen.getByRole('button', { name: 'Add related page link' }), - ); - - expect(screen.getByLabelText('Title')).toBeInTheDocument(); - expect(screen.getByLabelText('Link URL')).toBeInTheDocument(); - expect( - screen.getByRole('button', { name: 'Create link' }), - ).toBeInTheDocument(); - expect( - screen.getByRole('button', { name: 'Cancel' }), - ).toBeInTheDocument(); - }); - - test('shows a validation error when no title is set', async () => { - const { user } = render( - - - , - ); - - await user.click( - screen.getByRole('button', { name: 'Add related page link' }), - ); - - await user.click(screen.getByLabelText('Title')); - await user.click(screen.getByRole('button', { name: 'Create link' })); - - await waitFor(() => { - expect(screen.getByText('There is a problem')).toBeInTheDocument(); - expect( - screen.getByRole('link', { - name: 'Enter a link title', - }), - ).toBeInTheDocument(); - }); - }); - - test('shows a validation error when no url is set', async () => { - const { user } = render( - - - , - ); - - await user.click( - screen.getByRole('button', { name: 'Add related page link' }), + screen.getByRole('button', { name: 'Add related page' }), ); - await user.click(screen.getByLabelText('Link URL')); - await user.click(screen.getByRole('button', { name: 'Create link' })); - - await waitFor(() => { - expect(screen.getByText('There is a problem')).toBeInTheDocument(); - expect( - screen.getByRole('link', { - name: 'Enter a link URL', - }), - ).toBeInTheDocument(); - }); - }); - - test('shows a validation error when the url is invalid', async () => { - const { user } = render( - - - , - ); - - await user.click( - screen.getByRole('button', { name: 'Add related page link' }), - ); - - await user.type(screen.getByLabelText('Link URL'), 'Not a url'); - await user.click(screen.getByRole('button', { name: 'Create link' })); - - await waitFor(() => { - expect(screen.getByText('There is a problem')).toBeInTheDocument(); - expect( - screen.getByRole('link', { - name: 'Enter a valid link URL', - }), - ).toBeInTheDocument(); - }); + const modal = within(screen.getByRole('dialog')); + expect(modal.getByLabelText('Title')).toBeInTheDocument(); + expect(modal.getByLabelText('Link URL')).toBeInTheDocument(); + expect(modal.getByRole('button', { name: 'Save' })).toBeInTheDocument(); + expect(modal.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); }); test('successfully adds a link', async () => { @@ -157,7 +85,7 @@ describe('RelatedPagesSection', () => { { ...testRelease.relatedInformation[0] }, { description: 'Test title', id: 'test-id', url: 'https://gov.uk' }, ]; - releaseContentRelatedInformationService.create.mockResolvedValue( + releaseContentRelatedInformationService.update.mockResolvedValue( newLinks, ); @@ -168,15 +96,18 @@ describe('RelatedPagesSection', () => { ); await user.click( - screen.getByRole('button', { name: 'Add related page link' }), + screen.getByRole('button', { name: 'Add related page' }), ); - await user.type(screen.getByLabelText('Title'), 'Test title'); - await user.type(screen.getByLabelText('Link URL'), 'https://gov.uk'); - await user.click(screen.getByRole('button', { name: 'Create link' })); + const modal = within(screen.getByRole('dialog')); + await user.type(modal.getByLabelText('Title'), 'Test title'); + await user.type(modal.getByLabelText('Link URL'), 'https://gov.uk'); + await user.click(modal.getByRole('button', { name: 'Save' })); - expect( - await screen.findByRole('button', { name: 'Add related page link' }), + await waitFor(() => + expect( + screen.queryByText('Add related page link'), + ).not.toBeInTheDocument(), ); const pages = screen.getAllByRole('listitem'); @@ -186,22 +117,15 @@ describe('RelatedPagesSection', () => { name: 'Related information description', }), ).toHaveAttribute('href', 'https://test.com'); - expect( - within(pages[0]).getByRole('button', { name: 'Remove link' }), - ).toBeInTheDocument(); - expect( within(pages[1]).getByRole('link', { name: 'Test title', }), ).toHaveAttribute('href', 'https://gov.uk'); - expect( - within(pages[1]).getByRole('button', { name: 'Remove link' }), - ).toBeInTheDocument(); }); test('successfully removes a link', async () => { - releaseContentRelatedInformationService.delete.mockResolvedValue([]); + releaseContentRelatedInformationService.update.mockResolvedValue([]); const { user } = render( @@ -209,20 +133,20 @@ describe('RelatedPagesSection', () => { , ); - const pages = screen.getAllByRole('listitem'); - expect(pages).toHaveLength(1); - expect( - within(pages[0]).getByRole('link', { - name: 'Related information description', - }), - ).toHaveAttribute('href', 'https://test.com'); + await user.click(screen.getByRole('button', { name: 'Edit pages' })); + + const modal = within(screen.getByRole('dialog')); await user.click( - within(pages[0]).getByRole('button', { name: 'Remove link' }), + modal.getByRole('button', { + name: 'Remove Related information description', + }), ); waitFor(() => - expect(screen.queryByRole('listitem')).not.toBeInTheDocument(), + expect( + modal.queryByText('Related information description'), + ).not.toBeInTheDocument(), ); }); }); @@ -246,10 +170,10 @@ describe('RelatedPagesSection', () => { ).toHaveAttribute('href', 'https://test.com'); expect( - screen.queryByRole('button', { name: 'Remove link' }), + screen.queryByRole('button', { name: 'Add related page' }), ).not.toBeInTheDocument(); expect( - screen.queryByRole('button', { name: 'Add related page link' }), + screen.queryByRole('button', { name: 'Edit pages' }), ).not.toBeInTheDocument(); }); @@ -263,7 +187,10 @@ describe('RelatedPagesSection', () => { screen.queryByRole('heading', { name: 'Related pages' }), ).not.toBeInTheDocument(); expect( - screen.queryByRole('button', { name: 'Add related page link' }), + screen.queryByRole('button', { name: 'Add related page' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Edit pages' }), ).not.toBeInTheDocument(); expect(screen.queryByRole('listitem')).not.toBeInTheDocument(); }); diff --git a/src/explore-education-statistics-admin/src/services/releaseContentRelatedInformationService.ts b/src/explore-education-statistics-admin/src/services/releaseContentRelatedInformationService.ts index 401b0ee1f1c..24e711e73b0 100644 --- a/src/explore-education-statistics-admin/src/services/releaseContentRelatedInformationService.ts +++ b/src/explore-education-statistics-admin/src/services/releaseContentRelatedInformationService.ts @@ -5,27 +5,13 @@ const releaseContentRelatedInformationService = { getAll: (releaseId: string): Promise => { return client.get(`/release/${releaseId}/content/related-information`); }, - create: ( - releaseId: string, - link: Omit, - ): Promise => { - return client.post( - `/release/${releaseId}/content/related-information`, - link, - ); - }, update: ( releaseId: string, - link: { id: string; description: string; url: string }, + links: (BasicLink | Omit)[], ): Promise => { return client.put( - `/release/${releaseId}/content/related-information/${link.id}`, - link, - ); - }, - delete: (releaseId: string, linkId: string): Promise => { - return client.delete( - `/release/${releaseId}/content/related-information/${linkId}`, + `/release/${releaseId}/content/related-information`, + links, ); }, }; diff --git a/tests/robot-tests/tests/admin/bau/release_content.robot b/tests/robot-tests/tests/admin/bau/release_content.robot index 01b2d740a59..1044ee51db4 100644 --- a/tests/robot-tests/tests/admin/bau/release_content.robot +++ b/tests/robot-tests/tests/admin/bau/release_content.robot @@ -67,11 +67,32 @@ Add release note to release user waits until element contains css:#release-notes li:nth-of-type(1) p Test release note one Add Useful information related page link to release - user clicks button Add related page link + user clicks button Add related page user enters text into element id:relatedPageForm-description Test link one user enters text into element id:relatedPageForm-url http://test1.example.com/test1 - user clicks button Create link id:relatedPageForm - user checks page does not contain element id:${SECONDARY_STATS_TABLE_TAB_ID} + user clicks button Save id:relatedPageForm + +Edit related page link + user clicks button Edit pages + user clicks button Edit Test link one + user enters text into element id:relatedPageForm-description Test link one - edited + user clicks button Save id:relatedPageForm + user clicks button Close + user checks page contains Test link one - edited + +Reorder related pages + user clicks button Add related page + user enters text into element id:relatedPageForm-description Test link two + user enters text into element id:relatedPageForm-url http://test2.example.com/test2 + user clicks button Save id:relatedPageForm + user clicks button Edit pages + user clicks button Reorder pages + user moves item of draggable list down testid:reorder-related-pages 1 + user checks list contains exact items in order testid:reorder-related-pages + ... Test link two + ... Test link one - edited + user clicks button Confirm order + user clicks button Close Add secondary statistics ${expected_select_options}= create list Data Block 1 Data Block 2 Key Stats Data Block 1 diff --git a/tests/robot-tests/tests/admin_and_public/bau/data_reordering.robot b/tests/robot-tests/tests/admin_and_public/bau/data_reordering.robot index 405e1be8720..9226b8296b2 100644 --- a/tests/robot-tests/tests/admin_and_public/bau/data_reordering.robot +++ b/tests/robot-tests/tests/admin_and_public/bau/data_reordering.robot @@ -648,14 +648,6 @@ Validate table cells again *** Keywords *** -user moves item of draggable list down - [Arguments] ${locator} ${item_num} - ${item} user gets list item element ${locator} ${item_num} - set focus to element ${item} - user presses keys ${SPACE} - user presses keys ARROW_DOWN - user presses keys ${SPACE} - user clicks button in reorder list [Arguments] ${button_text} ${item_num} ${list_test_id}=reorder-list user clicks button ${button_text} xpath://ol[@data-testid="${list_test_id}"]/li[position()=${item_num}] diff --git a/tests/robot-tests/tests/libs/admin-common.robot b/tests/robot-tests/tests/libs/admin-common.robot index 1339463699a..ff743643c7d 100644 --- a/tests/robot-tests/tests/libs/admin-common.robot +++ b/tests/robot-tests/tests/libs/admin-common.robot @@ -5,8 +5,8 @@ Library String *** Variables *** -${BAU1_BROWSER}= bau1 -${ANALYST1_BROWSER}= analyst1 +${BAU1_BROWSER} bau1 +${ANALYST1_BROWSER} analyst1 *** Keywords *** @@ -1021,3 +1021,11 @@ user checks checklist errors contains link [Arguments] ${text} user waits until page contains testid releaseChecklist-errors user waits until parent contains element testid:releaseChecklist-errors link:${text} + +user moves item of draggable list down + [Arguments] ${locator} ${item_num} + ${item}= user gets list item element ${locator} ${item_num} + set focus to element ${item} + user presses keys ${SPACE} + user presses keys ARROW_DOWN + user presses keys ${SPACE} diff --git a/tests/robot-tests/tests/libs/admin/analyst/role_ui_permissions.robot b/tests/robot-tests/tests/libs/admin/analyst/role_ui_permissions.robot index 9cfc7ce8c02..41a33f2ba2f 100644 --- a/tests/robot-tests/tests/libs/admin/analyst/role_ui_permissions.robot +++ b/tests/robot-tests/tests/libs/admin/analyst/role_ui_permissions.robot @@ -25,7 +25,7 @@ user cannot see edit controls for release content user waits until h2 is visible ${publication} user waits until page does not contain button Change page view user waits until page does not contain button Add note - user waits until page does not contain button Add related page link + user waits until page does not contain button Add related page user waits until page does not contain button Add secondary stats user waits until page does not contain button Add key statistic from data block user waits until page does not contain button Add free text key statistic From 346dc074c2714cc52dc02340e617b18fbc5388d3 Mon Sep 17 00:00:00 2001 From: Amy Benson Date: Thu, 7 Nov 2024 09:39:52 +0000 Subject: [PATCH 2/2] EES-5589 only show reorder related pages button when mulitple pages --- .../content/components/RelatedPagesEditModal.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx index 0f7f8b81377..1f65cfe5142 100644 --- a/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx @@ -97,13 +97,15 @@ export default function RelatedPagesEditModal({ /> ) : ( <> - + {relatedPages.length > 1 && ( + + )}