-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5356 from dfe-analytical-services/EES-5589
EES-5589 edit and reorder related pages
- Loading branch information
Showing
12 changed files
with
681 additions
and
223 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
...plore-education-statistics-admin/src/pages/release/content/components/RelatedPageForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<BasicLink, 'id'>; | ||
|
||
interface Props { | ||
initialValues?: RelatedPageFormValues; | ||
onCancel: () => void; | ||
onSubmit: (link: RelatedPageFormValues) => Promise<void>; | ||
} | ||
|
||
export default function RelatedPageForm({ | ||
initialValues, | ||
onCancel, | ||
onSubmit, | ||
}: Props) { | ||
return ( | ||
<FormProvider | ||
initialValues={initialValues ?? { description: '', url: '' }} | ||
validationSchema={Yup.object({ | ||
description: Yup.string().required('Enter a link title'), | ||
url: Yup.string() | ||
.url('Enter a valid link URL') | ||
.required('Enter a link URL'), | ||
})} | ||
> | ||
<Form id="relatedPageForm" onSubmit={onSubmit}> | ||
<FormFieldTextInput<RelatedPageFormValues> | ||
label="Title" | ||
name="description" | ||
/> | ||
<FormFieldTextInput<RelatedPageFormValues> | ||
label="Link URL" | ||
name="url" | ||
/> | ||
<ButtonGroup> | ||
<Button type="submit" className="govuk-button govuk-!-margin-right-1"> | ||
Save | ||
</Button> | ||
<Button | ||
className="govuk-button govuk-button--secondary" | ||
variant="secondary" | ||
onClick={onCancel} | ||
> | ||
Cancel | ||
</Button> | ||
</ButtonGroup> | ||
</Form> | ||
</FormProvider> | ||
); | ||
} |
31 changes: 31 additions & 0 deletions
31
...-education-statistics-admin/src/pages/release/content/components/RelatedPagesAddModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<void>; | ||
} | ||
|
||
export default function RelatedPagesAddModal({ onSubmit }: Props) { | ||
const [open, toggleOpen] = useToggle(false); | ||
|
||
const handleSubmit = async (values: RelatedPageFormValues) => { | ||
await onSubmit(values); | ||
toggleOpen.off(); | ||
}; | ||
|
||
return ( | ||
<Modal | ||
open={open} | ||
title="Add related page link" | ||
triggerButton={<Button onClick={toggleOpen.on}>Add related page</Button>} | ||
onExit={toggleOpen.off} | ||
> | ||
<RelatedPageForm onCancel={toggleOpen.off} onSubmit={handleSubmit} /> | ||
</Modal> | ||
); | ||
} |
152 changes: 152 additions & 0 deletions
152
...education-statistics-admin/src/pages/release/content/components/RelatedPagesEditModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
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<void>; | ||
} | ||
|
||
export default function RelatedPagesEditModal({ | ||
relatedPages: initialRelatedPages, | ||
onUpdate, | ||
}: Props) { | ||
const [open, toggleOpen] = useToggle(false); | ||
const [isReordering, toggleReordering] = useToggle(false); | ||
const [editingPage, setEditingPage] = useState<BasicLink>(); | ||
const [relatedPages, setRelatedPages] = | ||
useState<BasicLink[]>(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 ( | ||
<Modal | ||
className="govuk-!-width-one-half" | ||
open={open} | ||
title="Edit related pages" | ||
triggerButton={ | ||
<Button variant="secondary" onClick={toggleOpen.on}> | ||
Edit pages | ||
</Button> | ||
} | ||
onExit={toggleOpen.off} | ||
> | ||
{editingPage ? ( | ||
<RelatedPageForm | ||
initialValues={{ | ||
description: editingPage.description, | ||
url: editingPage.url, | ||
}} | ||
onCancel={() => setEditingPage(undefined)} | ||
onSubmit={handleSubmit} | ||
/> | ||
) : ( | ||
<> | ||
{isReordering ? ( | ||
<ReorderableList | ||
heading="Reorder pages" | ||
id="reorder-related-pages" | ||
list={relatedPages.map(page => ({ | ||
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.length > 1 && ( | ||
<Button | ||
className="govuk-!-margin-bottom-3" | ||
variant="secondary" | ||
onClick={toggleReordering} | ||
> | ||
Reorder pages | ||
</Button> | ||
)} | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Title</th> | ||
<th>Link URL</th> | ||
<th>Actions</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{relatedPages.map(page => ( | ||
<tr key={page.id}> | ||
<td>{page.description}</td> | ||
<td> | ||
<Link to={page.url}>{page.url}</Link> | ||
</td> | ||
<td> | ||
<ButtonGroup className="govuk-!-margin-bottom-0"> | ||
<Button onClick={() => setEditingPage(page)}> | ||
Edit | ||
<VisuallyHidden> {page.description}</VisuallyHidden> | ||
</Button> | ||
<Button | ||
variant="warning" | ||
onClick={() => handleRemove(page.id)} | ||
> | ||
Remove{' '} | ||
<VisuallyHidden> {page.description}</VisuallyHidden> | ||
</Button> | ||
</ButtonGroup> | ||
</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
<Button onClick={toggleOpen.off}> | ||
Close <VisuallyHidden> modal</VisuallyHidden> | ||
</Button> | ||
</> | ||
)} | ||
</> | ||
)} | ||
</Modal> | ||
); | ||
} |
119 changes: 40 additions & 79 deletions
119
...e-education-statistics-admin/src/pages/release/content/components/RelatedPagesSection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.