From 09d6a5602402f4662f3e34b2ae16f0b3554026b0 Mon Sep 17 00:00:00 2001 From: Jonathan Petto Date: Mon, 23 Feb 2026 11:54:24 -0600 Subject: [PATCH 1/2] feat(delete collections): remove collections from the tool - pocket decommission - HNT-1730 - fix lint issues - update README - add allowAds and followable to satisfy sections type checking --- .aws/src/config/index.ts | 3 - .aws/src/pocketAlbApplication.ts | 4 - Dockerfile | 3 +- README.md | 26 +- src/App.tsx | 14 +- src/_shared/components/Header/Header.tsx | 2 +- .../HeaderConnector/HeaderConnector.tsx | 2 +- src/_shared/hooks/index.ts | 1 - .../useFetchMoreResults.ts | 3 + .../hooks/useMozillaAuth/useMozillaAuth.ts | 2 - src/_shared/pages/LandingPage/LandingPage.tsx | 17 +- src/_shared/utils/flattenAuthors.ts | 20 +- src/_shared/utils/transformAuthors.ts | 8 +- src/api/client.ts | 21 - .../getCollectionAuthorsFieldPolicy.ts | 30 - .../getCollectionPartnersFieldPolicy.ts | 30 - src/api/field-policies/index.ts | 3 - .../searchCollectionsFieldPolicy.ts | 43 - src/api/fragments/CollectionAuthorData.ts | 15 - src/api/fragments/CollectionData.ts | 52 - .../CollectionPartnerAssociationData.ts | 21 - src/api/fragments/CollectionPartnerData.ts | 14 - src/api/fragments/CollectionStoryData.ts | 22 - src/api/fragments/SectionData.ts | 2 +- src/api/generatedTypes.ts | 3838 ++--------------- src/api/helpers/mergePaginatedDataInCache.ts | 41 - src/api/helpers/readPaginatedDataFromCache.ts | 49 - src/api/mutations/createCollection.ts | 14 - src/api/mutations/createCollectionAuthor.ts | 28 - src/api/mutations/createCollectionPartner.ts | 21 - .../createCollectionPartnerAssociation.ts | 32 - src/api/mutations/createCollectionStory.ts | 36 - src/api/mutations/createLabel.ts | 12 - .../deleteCollectionPartnerAssociation.ts | 14 - src/api/mutations/deleteCollectionStory.ts | 14 - src/api/mutations/imageUpload.ts | 24 - src/api/mutations/updateCollection.ts | 14 - src/api/mutations/updateCollectionAuthor.ts | 30 - .../updateCollectionAuthorImageUrl.ts | 19 - src/api/mutations/updateCollectionImageUrl.ts | 16 - src/api/mutations/updateCollectionPartner.ts | 28 - .../updateCollectionPartnerAssociation.ts | 32 - ...ateCollectionPartnerAssociationImageUrl.ts | 19 - .../updateCollectionPartnerImageUrl.ts | 19 - src/api/mutations/updateCollectionStory.ts | 36 - .../updateCollectionStoryImageUrl.ts | 19 - .../updateCollectionStorySortOrder.ts | 19 - src/api/mutations/updateLabel.ts | 13 - src/api/queries/getAuthorById.ts | 14 - src/api/queries/getAuthors.ts | 21 - src/api/queries/getCollectionById.ts | 14 - src/api/queries/getCollectionPartner.ts | 14 - .../getCollectionPartnerAssociation.ts | 15 - src/api/queries/getCollectionPartners.ts | 21 - src/api/queries/getCollectionStories.ts | 19 - src/api/queries/getCollections.ts | 29 - .../queries/getInitialCollectionFormData.ts | 40 - src/api/queries/getSearchCollections.ts | 26 - src/api/queries/getStoryFromParser.ts | 32 - src/api/queries/labels.ts | 10 - .../components/AuthorForm/AuthorForm.test.tsx | 203 - .../components/AuthorForm/AuthorForm.tsx | 140 - .../AuthorForm/AuthorForm.validation.tsx | 24 - .../components/AuthorInfo/AuthorInfo.test.tsx | 78 - .../components/AuthorInfo/AuthorInfo.tsx | 39 - .../AuthorListCard/AuthorListCard.test.tsx | 117 - .../AuthorListCard/AuthorListCard.tsx | 74 - .../ChipLabelsList/ChipLabelsList.test.tsx | 131 - .../ChipLabelsList/ChipLabelsList.tsx | 119 - .../CollectionForm/CollectionForm.test.tsx | 426 -- .../CollectionForm/CollectionForm.tsx | 443 -- .../CollectionForm.validation.tsx | 71 - .../CollectionInfo/CollectionInfo.test.tsx | 288 -- .../CollectionInfo/CollectionInfo.tsx | 119 - .../CollectionListCard.test.tsx | 275 -- .../CollectionListCard/CollectionListCard.tsx | 108 - .../CollectionPartnerAssociationForm.test.tsx | 322 -- .../CollectionPartnerAssociationForm.tsx | 169 - ...ctionPartnerAssociationForm.validation.tsx | 35 - .../CollectionPartnerAssociationInfo.tsx | 196 - .../CollectionSearchForm.tsx | 167 - .../CollectionSearchForm.validation.tsx | 15 - .../components/ImageUpload/ImageUpload.tsx | 236 - .../components/LabelForm/LabelForm.test.tsx | 80 - .../components/LabelForm/LabelForm.tsx | 77 - .../LabelForm/LabelForm.validation.tsx | 14 - .../LabelFormConnector.test.tsx | 397 -- .../LabelFormConnector/LabelFormConnector.tsx | 118 - .../LabelListCard/LabelListCard.test.tsx | 30 - .../LabelListCard/LabelListCard.tsx | 76 - .../components/LabelModal/LabelModal.test.tsx | 57 - .../components/LabelModal/LabelModal.tsx | 77 - .../PartnerForm/PartnerForm.test.tsx | 178 - .../components/PartnerForm/PartnerForm.tsx | 99 - .../PartnerForm/PartnerForm.validation.tsx | 21 - .../PartnerInfo/PartnerInfo.test.tsx | 44 - .../components/PartnerInfo/PartnerInfo.tsx | 32 - .../PartnerListCard/PartnerListCard.test.tsx | 82 - .../PartnerListCard/PartnerListCard.tsx | 74 - .../ReorderableCollectionStoryList.tsx | 93 - .../components/StoryCard/StoryCard.test.tsx | 135 - .../components/StoryCard/StoryCard.tsx | 86 - .../components/StoryForm/StoryForm.tsx | 403 -- .../StoryForm/StoryForm.validation.tsx | 18 - .../StoryListCard/StoryListCard.tsx | 175 - .../TextSwitchLink/TextSwitchLink.tsx | 89 - .../TextSwitchLinkComponent.tsx | 32 - src/collections/components/index.ts | 22 - .../integration-test-mocks/createLabels.ts | 69 - .../integration-test-mocks/updateLabels.ts | 52 - .../AddAuthorPage/AddAuthorPage.test.tsx | 57 - .../pages/AddAuthorPage/AddAuthorPage.tsx | 92 - .../AddCollectionPage/AddCollectionPage.tsx | 135 - .../pages/AddPartnerPage/AddPartnerPage.tsx | 90 - .../pages/AuthorListPage/AuthorListPage.tsx | 67 - .../pages/AuthorPage/AuthorPage.tsx | 190 - .../CollectionListPage/CollectionListPage.tsx | 264 -- .../pages/CollectionPage/CollectionPage.tsx | 753 ---- .../CollectionSearchPage.tsx | 94 - .../CollectionsHomePage.tsx | 124 - .../CollectionsLandingPage.tsx | 110 - .../pages/LabelListPage/LabelListPage.tsx | 49 - .../pages/PartnerListPage/PartnerListPage.tsx | 72 - .../pages/PartnerPage/PartnerPage.tsx | 195 - src/collections/pages/index.ts | 17 - src/config/index.ts | 3 - .../ApprovedItemListCard.test.tsx | 1 - .../CustomSectionTable.test.tsx | 2 +- .../EditCustomSectionModal.test.tsx | 2 +- .../ScheduleHistoryModal.test.tsx | 1 - .../SectionDetails/SectionDetails.test.tsx | 10 + src/curated-corpus/helpers/definitions.ts | 3 +- .../getSectionsWithSectionItems.ts | 5 + 133 files changed, 327 insertions(+), 13434 deletions(-) delete mode 100644 src/api/field-policies/getCollectionAuthorsFieldPolicy.ts delete mode 100644 src/api/field-policies/getCollectionPartnersFieldPolicy.ts delete mode 100644 src/api/field-policies/index.ts delete mode 100644 src/api/field-policies/searchCollectionsFieldPolicy.ts delete mode 100644 src/api/fragments/CollectionAuthorData.ts delete mode 100644 src/api/fragments/CollectionData.ts delete mode 100644 src/api/fragments/CollectionPartnerAssociationData.ts delete mode 100644 src/api/fragments/CollectionPartnerData.ts delete mode 100644 src/api/fragments/CollectionStoryData.ts delete mode 100644 src/api/helpers/mergePaginatedDataInCache.ts delete mode 100644 src/api/helpers/readPaginatedDataFromCache.ts delete mode 100644 src/api/mutations/createCollection.ts delete mode 100644 src/api/mutations/createCollectionAuthor.ts delete mode 100644 src/api/mutations/createCollectionPartner.ts delete mode 100644 src/api/mutations/createCollectionPartnerAssociation.ts delete mode 100644 src/api/mutations/createCollectionStory.ts delete mode 100644 src/api/mutations/createLabel.ts delete mode 100644 src/api/mutations/deleteCollectionPartnerAssociation.ts delete mode 100644 src/api/mutations/deleteCollectionStory.ts delete mode 100644 src/api/mutations/imageUpload.ts delete mode 100644 src/api/mutations/updateCollection.ts delete mode 100644 src/api/mutations/updateCollectionAuthor.ts delete mode 100644 src/api/mutations/updateCollectionAuthorImageUrl.ts delete mode 100644 src/api/mutations/updateCollectionImageUrl.ts delete mode 100644 src/api/mutations/updateCollectionPartner.ts delete mode 100644 src/api/mutations/updateCollectionPartnerAssociation.ts delete mode 100644 src/api/mutations/updateCollectionPartnerAssociationImageUrl.ts delete mode 100644 src/api/mutations/updateCollectionPartnerImageUrl.ts delete mode 100644 src/api/mutations/updateCollectionStory.ts delete mode 100644 src/api/mutations/updateCollectionStoryImageUrl.ts delete mode 100644 src/api/mutations/updateCollectionStorySortOrder.ts delete mode 100644 src/api/mutations/updateLabel.ts delete mode 100644 src/api/queries/getAuthorById.ts delete mode 100644 src/api/queries/getAuthors.ts delete mode 100644 src/api/queries/getCollectionById.ts delete mode 100644 src/api/queries/getCollectionPartner.ts delete mode 100644 src/api/queries/getCollectionPartnerAssociation.ts delete mode 100644 src/api/queries/getCollectionPartners.ts delete mode 100644 src/api/queries/getCollectionStories.ts delete mode 100644 src/api/queries/getCollections.ts delete mode 100644 src/api/queries/getInitialCollectionFormData.ts delete mode 100644 src/api/queries/getSearchCollections.ts delete mode 100644 src/api/queries/getStoryFromParser.ts delete mode 100644 src/api/queries/labels.ts delete mode 100644 src/collections/components/AuthorForm/AuthorForm.test.tsx delete mode 100644 src/collections/components/AuthorForm/AuthorForm.tsx delete mode 100644 src/collections/components/AuthorForm/AuthorForm.validation.tsx delete mode 100644 src/collections/components/AuthorInfo/AuthorInfo.test.tsx delete mode 100644 src/collections/components/AuthorInfo/AuthorInfo.tsx delete mode 100644 src/collections/components/AuthorListCard/AuthorListCard.test.tsx delete mode 100644 src/collections/components/AuthorListCard/AuthorListCard.tsx delete mode 100644 src/collections/components/ChipLabelsList/ChipLabelsList.test.tsx delete mode 100644 src/collections/components/ChipLabelsList/ChipLabelsList.tsx delete mode 100644 src/collections/components/CollectionForm/CollectionForm.test.tsx delete mode 100644 src/collections/components/CollectionForm/CollectionForm.tsx delete mode 100644 src/collections/components/CollectionForm/CollectionForm.validation.tsx delete mode 100644 src/collections/components/CollectionInfo/CollectionInfo.test.tsx delete mode 100644 src/collections/components/CollectionInfo/CollectionInfo.tsx delete mode 100644 src/collections/components/CollectionListCard/CollectionListCard.test.tsx delete mode 100644 src/collections/components/CollectionListCard/CollectionListCard.tsx delete mode 100644 src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.test.tsx delete mode 100644 src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.tsx delete mode 100644 src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.validation.tsx delete mode 100644 src/collections/components/CollectionPartnerAssociationInfo/CollectionPartnerAssociationInfo.tsx delete mode 100644 src/collections/components/CollectionSearchForm/CollectionSearchForm.tsx delete mode 100644 src/collections/components/CollectionSearchForm/CollectionSearchForm.validation.tsx delete mode 100644 src/collections/components/ImageUpload/ImageUpload.tsx delete mode 100644 src/collections/components/LabelForm/LabelForm.test.tsx delete mode 100644 src/collections/components/LabelForm/LabelForm.tsx delete mode 100644 src/collections/components/LabelForm/LabelForm.validation.tsx delete mode 100644 src/collections/components/LabelFormConnector/LabelFormConnector.test.tsx delete mode 100644 src/collections/components/LabelFormConnector/LabelFormConnector.tsx delete mode 100644 src/collections/components/LabelListCard/LabelListCard.test.tsx delete mode 100644 src/collections/components/LabelListCard/LabelListCard.tsx delete mode 100644 src/collections/components/LabelModal/LabelModal.test.tsx delete mode 100644 src/collections/components/LabelModal/LabelModal.tsx delete mode 100644 src/collections/components/PartnerForm/PartnerForm.test.tsx delete mode 100644 src/collections/components/PartnerForm/PartnerForm.tsx delete mode 100644 src/collections/components/PartnerForm/PartnerForm.validation.tsx delete mode 100644 src/collections/components/PartnerInfo/PartnerInfo.test.tsx delete mode 100644 src/collections/components/PartnerInfo/PartnerInfo.tsx delete mode 100644 src/collections/components/PartnerListCard/PartnerListCard.test.tsx delete mode 100644 src/collections/components/PartnerListCard/PartnerListCard.tsx delete mode 100644 src/collections/components/ReorderableCollectionStoryList/ReorderableCollectionStoryList.tsx delete mode 100644 src/collections/components/StoryCard/StoryCard.test.tsx delete mode 100644 src/collections/components/StoryCard/StoryCard.tsx delete mode 100644 src/collections/components/StoryForm/StoryForm.tsx delete mode 100644 src/collections/components/StoryForm/StoryForm.validation.tsx delete mode 100644 src/collections/components/StoryListCard/StoryListCard.tsx delete mode 100644 src/collections/components/TextSwitchLink/TextSwitchLink.tsx delete mode 100644 src/collections/components/TextSwitchLink/TextSwitchLinkComponent.tsx delete mode 100644 src/collections/components/index.ts delete mode 100644 src/collections/integration-test-mocks/createLabels.ts delete mode 100644 src/collections/integration-test-mocks/updateLabels.ts delete mode 100644 src/collections/pages/AddAuthorPage/AddAuthorPage.test.tsx delete mode 100644 src/collections/pages/AddAuthorPage/AddAuthorPage.tsx delete mode 100644 src/collections/pages/AddCollectionPage/AddCollectionPage.tsx delete mode 100644 src/collections/pages/AddPartnerPage/AddPartnerPage.tsx delete mode 100644 src/collections/pages/AuthorListPage/AuthorListPage.tsx delete mode 100644 src/collections/pages/AuthorPage/AuthorPage.tsx delete mode 100644 src/collections/pages/CollectionListPage/CollectionListPage.tsx delete mode 100644 src/collections/pages/CollectionPage/CollectionPage.tsx delete mode 100644 src/collections/pages/CollectionSearchPage/CollectionSearchPage.tsx delete mode 100644 src/collections/pages/CollectionsHomePage/CollectionsHomePage.tsx delete mode 100644 src/collections/pages/CollectionsLandingPage/CollectionsLandingPage.tsx delete mode 100644 src/collections/pages/LabelListPage/LabelListPage.tsx delete mode 100644 src/collections/pages/PartnerListPage/PartnerListPage.tsx delete mode 100644 src/collections/pages/PartnerPage/PartnerPage.tsx delete mode 100644 src/collections/pages/index.ts diff --git a/.aws/src/config/index.ts b/.aws/src/config/index.ts index 1965a922e..303587ae7 100644 --- a/.aws/src/config/index.ts +++ b/.aws/src/config/index.ts @@ -5,8 +5,6 @@ const prodDomain = 'readitlater.com'; export const isDev = process.env.NODE_ENV === 'development'; const environment = isDev ? 'Dev' : 'Prod'; const domain = `${domainPrefix}.` + (isDev ? devDomain : prodDomain); -const collectionApiEndpoint = - 'https://collection-api.' + (isDev ? devDomain : prodDomain) + '/admin'; // We use production client api always, // because the dev one will not have item data and that is all we are going to use it for const clientApiEndpoint = 'https://client-api.getpocket.com'; @@ -34,7 +32,6 @@ export const config = { branch, }, envVars: { - collectionApiEndpoint, clientApiEndpoint, oauth2ClientId, oauth2Provider, diff --git a/.aws/src/pocketAlbApplication.ts b/.aws/src/pocketAlbApplication.ts index 99e7702dc..543ff1bdb 100644 --- a/.aws/src/pocketAlbApplication.ts +++ b/.aws/src/pocketAlbApplication.ts @@ -73,10 +73,6 @@ export function createPocketAlbApplication( name: 'NODE_ENV', value: process.env.NODE_ENV, // this gives us a nice lowercase production and development }, - { - name: 'REACT_APP_COLLECTION_API_ENDPOINT', - value: config.envVars.collectionApiEndpoint, - }, { name: 'REACT_APP_CLIENT_API_ENDPOINT', value: config.envVars.clientApiEndpoint, diff --git a/Dockerfile b/Dockerfile index 938ee5752..efdeaf875 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,7 @@ RUN REACT_APP_ENV=${APP_ENV} npm run build # production environment FROM nginx:1.21.6@sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514 -# Copy collections build into nginx html directory for now, -# collections will be at the root of the server +# Copy app build into nginx html directory COPY --from=builder /usr/src/app/build /usr/share/nginx/html COPY --from=builder /usr/src/app/.docker/nginx.conf /etc/nginx/nginx.conf ENV PORT 80 diff --git a/README.md b/README.md index bce5fe809..cbe399b38 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This repository is home to a suite of internal tools for Pocket curators developed with React and TypeScript. With React, we use functional components and hooks throughout. We also use: + - [Material UI](https://mui.com/) for the UI, - [Apollo Client](https://www.apollographql.com/docs/react/) for connecting to the APIs the tools interact with, - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) for unit testing, @@ -84,24 +85,24 @@ npm run test - The `src/api` folder contains GraphQL queries and mutations the app needs to execute to retrieve and manipulate data, as well as generated types for these and the Apollo Client connection. -- `src/collections` is the home for Collections Admin Tool that lets Pocket curators create, edit and publish collections of stories for Pocket users. - - `src/curated-corpus` houses the Curated Corpus Tool. -Within the folder for each tool, the structure is as follows (taking Collections as an example): +- `src/moderation` DEPRECATED home of moderation of a legacy Pocket feature + +Within the folder for each tool, the structure is as follows (taking Curated Corpus as an example): ```bash -collections/ +curated-corpus/ ├─ components/ ├─ pages/ ├─ utils/ ``` -The `components` folder houses any components specific to that particular tool, for example, `CollectionListCard`. The `pages` folder is home to page-level components, i.e. `AddCollectionPage`. +The `components` folder houses any components specific to that particular tool, for example, `ApprovedItemListCard`. The `pages` folder is home to page-level components, i.e. `CorpusItemPage`. All components are functional React components. Each component lives in its own folder. Unit tests, styles, validation schemas (for form components) are placed in separate files alongside each component. -Routing within the app is set up with React Router. The entrypoint to the app, [App.tsx](/src/App.tsx), sets up all requests to be routed to specific paths within the apps - `/collections` for Collections and `/prospects` for the Prospecting tool, for example. More detailed routing, for example, individual collection pages, is set up within the landing page - see [this example for Collections](/src/collections/pages/CollectionsLandingPage/CollectionsLandingPage.tsx) +Routing within the app is set up with React Router. The entrypoint to the app, [App.tsx](/src/App.tsx), sets up all requests to be routed to specific paths within the apps - `/prospects` for the Prospecting tool, for example. More detailed routing, for example, individual curated corpus pages, is set up within the landing page - see [this example for Curated Corpus](/src/curated-corpus/pages/CuratedCorpusLandingPage/CuratedCorpusLandingPage.tsx) ## Working with the Admin API @@ -116,12 +117,13 @@ The generated types and custom hooks are saved in a file named `generatedTypes.t Now that the generated code is ready to use, you can: -- Use the generated types elsewhere in the code to type hint the shape of the returned data or data that is expected by components in the app, for example, `Collection` or `CollectionAuthor`. -- Use strongly typed custom Apollo hooks to fetch and manipulate data. Apollo Client has generic `useQuery`, `useLazyQuery`, `useMutation` and `useSubscription` hooks. GraphQL Code Generator builds on that by providing hooks that are specific to the queries and mutations you need to run, for example, `useGetCollectionByExternalIdQuery`. +- Use the generated types elsewhere in the code to type hint the shape of the returned data or data that is expected by components in the app, for example, `ApprovedCorpusItem` or `CorpusLanguage`. +- Use strongly typed custom Apollo hooks to fetch and manipulate data. Apollo Client has generic `useQuery`, `useLazyQuery`, `useMutation` and `useSubscription` hooks. GraphQL Code Generator builds on that by providing hooks that are specific to the queries and mutations you need to run, for example, `useGetSectionsWithSectionItemsQuery`. ## Troubleshooting + 1. If you receive an error similar to the following `error:xyz:digital envelope routines`, make sure you are using the correct Node version. Run `nvm use` in the project root to switch to the version specified in `.nvmrc` (currently Node 18). If you don't have it installed: - ```shell - nvm install - nvm use - ``` + ```shell + nvm install + nvm use + ``` diff --git a/src/App.tsx b/src/App.tsx index 75e351567..1a9d5037f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,19 +8,14 @@ import theme from './theme'; import { LandingPage } from './_shared/pages'; import { PageNotFound } from './_shared/components/'; -import { CollectionsLandingPage } from './collections/pages'; import { CuratedCorpusLandingPage } from './curated-corpus/pages'; import { ModerationLandingPage } from './moderation/pages'; import { useMozillaAuth } from './_shared/hooks'; import { client } from './api/client'; function App(): JSX.Element { - const { - canAccessCollections, - canAccessCuration, - canAccessModeration, - jwtIdToken, - } = useMozillaAuth(); + const { canAccessCuration, canAccessModeration, jwtIdToken } = + useMozillaAuth(); // create an ApolloLink that adds the authorization header const authLink = setContext((_, { headers }) => { @@ -47,11 +42,6 @@ function App(): JSX.Element { - {canAccessCollections && ( - - - - )} {canAccessCuration && ( diff --git a/src/_shared/components/Header/Header.tsx b/src/_shared/components/Header/Header.tsx index 4cb151958..50f567e07 100644 --- a/src/_shared/components/Header/Header.tsx +++ b/src/_shared/components/Header/Header.tsx @@ -60,7 +60,7 @@ interface HeaderProps { productLink: string; /** - * The name of the Admin UI, i.e. 'Collections' + * The name of the Admin UI, i.e. 'Curated Corpus' */ productName: string; } diff --git a/src/_shared/components/HeaderConnector/HeaderConnector.tsx b/src/_shared/components/HeaderConnector/HeaderConnector.tsx index 13331a78e..23820cb04 100644 --- a/src/_shared/components/HeaderConnector/HeaderConnector.tsx +++ b/src/_shared/components/HeaderConnector/HeaderConnector.tsx @@ -4,7 +4,7 @@ import { useMozillaAuth } from '../../hooks'; interface HeaderConnectorProps { /** - * The name of the Admin UI, i.e. 'Collections' + * The name of the Admin UI, i.e. 'Curated Corpus' */ productName: string; diff --git a/src/_shared/hooks/index.ts b/src/_shared/hooks/index.ts index a2a1bdfdc..20e7b359f 100644 --- a/src/_shared/hooks/index.ts +++ b/src/_shared/hooks/index.ts @@ -1,4 +1,3 @@ -export { useFetchMoreResults } from './useFetchMoreResults/useFetchMoreResults'; export { useNotifications } from './useNotifications/useNotifications'; export { useToggle } from './useToggle/useToggle'; export { useRunMutation } from './useRunMutation/useRunMutation'; diff --git a/src/_shared/hooks/useFetchMoreResults/useFetchMoreResults.ts b/src/_shared/hooks/useFetchMoreResults/useFetchMoreResults.ts index ee555738c..a1db35275 100644 --- a/src/_shared/hooks/useFetchMoreResults/useFetchMoreResults.ts +++ b/src/_shared/hooks/useFetchMoreResults/useFetchMoreResults.ts @@ -3,6 +3,9 @@ import * as Apollo from '@apollo/client'; import { ApolloError } from '@apollo/client'; import { useNotifications } from '../'; +// 2026-02-23: after removing collections, this function is not currently in +// use. leaving this in the code base as we may again need this functionality. + /** * This hook handles loading lists of data in paginated chunks: * coupled with field policies on queries, i.e. getCollectionAuthors, diff --git a/src/_shared/hooks/useMozillaAuth/useMozillaAuth.ts b/src/_shared/hooks/useMozillaAuth/useMozillaAuth.ts index ed070a491..e98aae30f 100644 --- a/src/_shared/hooks/useMozillaAuth/useMozillaAuth.ts +++ b/src/_shared/hooks/useMozillaAuth/useMozillaAuth.ts @@ -26,7 +26,6 @@ export const useMozillaAuth = (): { authService: AuthService; parsedIdToken: IDToken; jwtIdToken: string; - canAccessCollections: boolean; canAccessCuration: boolean; canAccessModeration: boolean; } => { @@ -50,7 +49,6 @@ export const useMozillaAuth = (): { // Everyone at Pocket has read-only access to this tool // Access groups are checked on the backend when actions (mutations) // are performed. - canAccessCollections: true, //parsedIdToken.groups.includes('asd'), canAccessCuration: true, //parsedIdToken.groups.includes('asd'), canAccessModeration: true, }; diff --git a/src/_shared/pages/LandingPage/LandingPage.tsx b/src/_shared/pages/LandingPage/LandingPage.tsx index 68fdf5106..72fdce0f9 100644 --- a/src/_shared/pages/LandingPage/LandingPage.tsx +++ b/src/_shared/pages/LandingPage/LandingPage.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Grid, Paper, Typography } from '@mui/material'; -import CollectionsBookmarkIcon from '@mui/icons-material/CollectionsBookmark'; import GavelIcon from '@mui/icons-material/Gavel'; import LibraryAddCheckIcon from '@mui/icons-material/LibraryAddCheck'; import { HeaderConnector } from '../../components'; @@ -8,8 +7,7 @@ import { StyledContainer, StyledRouterLink } from '../../styled'; import { useMozillaAuth } from '../../hooks'; export const LandingPage = (): JSX.Element => { - const { canAccessCuration, canAccessCollections, canAccessModeration } = - useMozillaAuth(); + const { canAccessCuration, canAccessModeration } = useMozillaAuth(); return ( <> @@ -26,19 +24,6 @@ export const LandingPage = (): JSX.Element => { flexGrow: 1, }} > - {canAccessCollections && ( - - - - - - -

Collections

-
-
-
- )} - {canAccessCuration && ( diff --git a/src/_shared/utils/flattenAuthors.ts b/src/_shared/utils/flattenAuthors.ts index b83cadeff..1e6147a8b 100644 --- a/src/_shared/utils/flattenAuthors.ts +++ b/src/_shared/utils/flattenAuthors.ts @@ -1,23 +1,15 @@ -import { - CollectionAuthor, - CollectionStoryAuthor, - CorpusItemAuthor, -} from '../../api/generatedTypes'; +import { CorpusItemAuthor } from '../../api/generatedTypes'; /** - * A helper function that takes in an array of corpus/collection [story] authors + * A helper function that takes in an array of corpus item authors * and returns a comma-separated list suitable for displaying to end users. * * @param authors */ -export const flattenAuthors = ( - authors: CollectionStoryAuthor[] | CollectionAuthor[] | CorpusItemAuthor[], -): string => { +export const flattenAuthors = (authors: CorpusItemAuthor[]): string => { return authors - .map( - (author: CollectionStoryAuthor | CollectionAuthor | CorpusItemAuthor) => { - return author.name; - }, - ) + .map((author: CorpusItemAuthor) => { + return author.name; + }) .join(', '); }; diff --git a/src/_shared/utils/transformAuthors.ts b/src/_shared/utils/transformAuthors.ts index 213bb5958..6183c69b0 100644 --- a/src/_shared/utils/transformAuthors.ts +++ b/src/_shared/utils/transformAuthors.ts @@ -1,8 +1,4 @@ -import { - CollectionStoryAuthor, - CorpusItemAuthor, - Maybe, -} from '../../api/generatedTypes'; +import { CorpusItemAuthor, Maybe } from '../../api/generatedTypes'; /** * A helper function that transforms a comma-separated string of names @@ -12,7 +8,7 @@ import { */ export const transformAuthors = ( authors: string | Maybe | undefined, -): CollectionStoryAuthor[] | CorpusItemAuthor[] => { +): CorpusItemAuthor[] => { // get rid of any whitespace on the sides authors = authors ? authors.trim() : authors; diff --git a/src/api/client.ts b/src/api/client.ts index 7ba8a6672..b698cf32b 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -1,11 +1,6 @@ import { ApolloClient, ApolloLink, InMemoryCache } from '@apollo/client'; import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'; import { config } from '../config'; -import { - getCollectionAuthorsFieldPolicy, - getCollectionPartnersFieldPolicy, - searchCollectionsFieldPolicy, -} from './field-policies'; /** * Custom type policies for Apollo Cache. Now in their own @@ -13,15 +8,6 @@ import { */ export const apolloCache = new InMemoryCache({ typePolicies: { - CollectionAuthor: { - keyFields: ['externalId'], - }, - CollectionStory: { - keyFields: ['externalId'], - }, - Collection: { - keyFields: ['externalId'], - }, ApprovedCorpusItem: { keyFields: ['externalId'], }, @@ -34,13 +20,6 @@ export const apolloCache = new InMemoryCache({ ScheduledCorpusItemsResult: { keyFields: ['scheduledDate'], }, - Query: { - fields: { - getCollectionAuthors: getCollectionAuthorsFieldPolicy, - getCollectionPartners: getCollectionPartnersFieldPolicy, - searchCollections: searchCollectionsFieldPolicy, - }, - }, }, }); diff --git a/src/api/field-policies/getCollectionAuthorsFieldPolicy.ts b/src/api/field-policies/getCollectionAuthorsFieldPolicy.ts deleted file mode 100644 index 420823036..000000000 --- a/src/api/field-policies/getCollectionAuthorsFieldPolicy.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CollectionAuthorsResult } from '../generatedTypes'; -import { FieldPolicy } from '@apollo/client/cache/inmemory/policies'; -import { mergePaginatedDataInCache } from '../helpers/mergePaginatedDataInCache'; -import { readPaginatedDataFromCache } from '../helpers/readPaginatedDataFromCache'; - -/** - * A field policy for the 'getCollectionAuthors' query that is used - * on the Authors page and also in the 'Authors' dropdown - * in the 'Add/Edit Collection' form. - * - * The field policy is required to implement caching on the frontend properly. - */ -export const getCollectionAuthorsFieldPolicy: FieldPolicy< - CollectionAuthorsResult, - CollectionAuthorsResult, - CollectionAuthorsResult -> = { - // Ignore all query variables when merging CollectionAuthor objects in the cache. - keyArgs: false, - - // Merge the authors into a single list in the cache. - merge: function (existing, incoming, options): CollectionAuthorsResult { - return mergePaginatedDataInCache(existing, incoming, options, 'authors'); - }, - - // Read the right chunk of data from the cache - read: function (existing, options): CollectionAuthorsResult { - return readPaginatedDataFromCache(existing, options, 'authors'); - }, -}; diff --git a/src/api/field-policies/getCollectionPartnersFieldPolicy.ts b/src/api/field-policies/getCollectionPartnersFieldPolicy.ts deleted file mode 100644 index 98c811e38..000000000 --- a/src/api/field-policies/getCollectionPartnersFieldPolicy.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { FieldPolicy } from '@apollo/client/cache/inmemory/policies'; -import { mergePaginatedDataInCache } from '../helpers/mergePaginatedDataInCache'; -import { readPaginatedDataFromCache } from '../helpers/readPaginatedDataFromCache'; -import { CollectionPartnersResult } from '../generatedTypes'; - -/** - * A field policy for the 'getCollectionPartners' query that is used - * on the Partners page and also in the 'Partners' dropdown - * in the 'Add/Edit Partnership' form. - * - * The field policy is required to implement caching on the frontend properly. - */ -export const getCollectionPartnersFieldPolicy: FieldPolicy< - CollectionPartnersResult, - CollectionPartnersResult, - CollectionPartnersResult -> = { - // Ignore all query variables when merging CollectionAuthor objects in the cache. - keyArgs: false, - - // Merge the authors into a single list in the cache. - merge: function (existing, incoming, options): CollectionPartnersResult { - return mergePaginatedDataInCache(existing, incoming, options, 'partners'); - }, - - // Read the right chunk of data from the cache - read: function (existing, options): CollectionPartnersResult { - return readPaginatedDataFromCache(existing, options, 'partners'); - }, -}; diff --git a/src/api/field-policies/index.ts b/src/api/field-policies/index.ts deleted file mode 100644 index 455feee7b..000000000 --- a/src/api/field-policies/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { getCollectionAuthorsFieldPolicy } from './getCollectionAuthorsFieldPolicy'; -export { getCollectionPartnersFieldPolicy } from './getCollectionPartnersFieldPolicy'; -export { searchCollectionsFieldPolicy } from './searchCollectionsFieldPolicy'; diff --git a/src/api/field-policies/searchCollectionsFieldPolicy.ts b/src/api/field-policies/searchCollectionsFieldPolicy.ts deleted file mode 100644 index 1ba77fba6..000000000 --- a/src/api/field-policies/searchCollectionsFieldPolicy.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { CollectionsResult } from '../generatedTypes'; -import { FieldPolicy } from '@apollo/client/cache/inmemory/policies'; -import { mergePaginatedDataInCache } from '../helpers/mergePaginatedDataInCache'; -import { readPaginatedDataFromCache } from '../helpers/readPaginatedDataFromCache'; - -/** - * A field policy for the 'searchCollections' query. - * - * The field policy is required to implement caching on the frontend properly. - */ -export const searchCollectionsFieldPolicy: FieldPolicy< - CollectionsResult, - CollectionsResult, - CollectionsResult -> = { - // Group collections in the cache by the value of the 'Status' field - // to be able to provide independent pagination to Draft/Published/Archived tabs. - keyArgs: function (args) { - // Collection status is hidden deep in the query arguments and cannot be - // picked up by Apollo with a simple reference to 'status', - // i.e. keyArgs: ['status'], so let's fish it out of the filters - if (args) { - return args.filters.status; - } - return false; - }, - - // Merge the collections into a single list in the cache, segmented by status - // (see keyArgs function above). - merge: function (existing, incoming, options): CollectionsResult { - return mergePaginatedDataInCache( - existing, - incoming, - options, - 'collections', - ); - }, - - // Read the right chunk of data from the cache - read: function (existing, options): CollectionsResult { - return readPaginatedDataFromCache(existing, options, 'collections'); - }, -}; diff --git a/src/api/fragments/CollectionAuthorData.ts b/src/api/fragments/CollectionAuthorData.ts deleted file mode 100644 index 472eaeacd..000000000 --- a/src/api/fragments/CollectionAuthorData.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { gql } from '@apollo/client'; - -/** - * All the properties that are needed to display cards and forms with author data - */ -export const CollectionAuthorData = gql` - fragment CollectionAuthorData on CollectionAuthor { - externalId - name - slug - bio - imageUrl - active - } -`; diff --git a/src/api/fragments/CollectionData.ts b/src/api/fragments/CollectionData.ts deleted file mode 100644 index 337b4f9d9..000000000 --- a/src/api/fragments/CollectionData.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from './CollectionAuthorData'; - -/** - * All the properties that are needed to display and edit collections - * - * Note that the data returned by this fragment does not include collection - * stories which are loaded on the individual collection page separately. - */ -export const CollectionData = gql` - fragment CollectionData on Collection { - externalId - title - slug - excerpt - intro - imageUrl - language - status - authors { - ...CollectionAuthorData - } - curationCategory { - externalId - name - slug - } - IABParentCategory { - externalId - name - slug - } - IABChildCategory { - externalId - name - slug - } - labels { - externalId - name - } - partnership { - externalId - type - name - url - imageUrl - blurb - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/fragments/CollectionPartnerAssociationData.ts b/src/api/fragments/CollectionPartnerAssociationData.ts deleted file mode 100644 index 949c47566..000000000 --- a/src/api/fragments/CollectionPartnerAssociationData.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerData } from './CollectionPartnerData'; - -/** - * All the properties that are needed to display cards and forms - * for collection-partner associations - */ -export const CollectionPartnerAssociationData = gql` - fragment CollectionPartnerAssociationData on CollectionPartnerAssociation { - externalId - type - name - url - imageUrl - blurb - partner { - ...CollectionPartnerData - } - } - ${CollectionPartnerData} -`; diff --git a/src/api/fragments/CollectionPartnerData.ts b/src/api/fragments/CollectionPartnerData.ts deleted file mode 100644 index fee059b04..000000000 --- a/src/api/fragments/CollectionPartnerData.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; - -/** - * All the properties that are needed to display cards and forms with partner data - */ -export const CollectionPartnerData = gql` - fragment CollectionPartnerData on CollectionPartner { - externalId - name - url - imageUrl - blurb - } -`; diff --git a/src/api/fragments/CollectionStoryData.ts b/src/api/fragments/CollectionStoryData.ts deleted file mode 100644 index 72f36d284..000000000 --- a/src/api/fragments/CollectionStoryData.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { gql } from '@apollo/client'; - -/** - * All the properties that are needed to display components with collection - * story data in them (such as media cards or forms) - */ -export const CollectionStoryData = gql` - fragment CollectionStoryData on CollectionStory { - externalId - url - title - excerpt - imageUrl - authors { - name - sortOrder - } - publisher - fromPartner - sortOrder - } -`; diff --git a/src/api/fragments/SectionData.ts b/src/api/fragments/SectionData.ts index ee2647cc2..0547ef979 100644 --- a/src/api/fragments/SectionData.ts +++ b/src/api/fragments/SectionData.ts @@ -20,8 +20,8 @@ export const BaseSectionData = gql` startDate endDate status - followable allowAds + followable } `; export const SectionData = gql` diff --git a/src/api/generatedTypes.ts b/src/api/generatedTypes.ts index 3d675ff4d..dd3c4c3f0 100644 --- a/src/api/generatedTypes.ts +++ b/src/api/generatedTypes.ts @@ -2648,113 +2648,6 @@ export enum Videoness { NoVideos = 'NO_VIDEOS', } -export type CollectionAuthorDataFragment = { - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; -}; - -export type CollectionDataFragment = { - __typename?: 'Collection'; - externalId: string; - title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; -}; - -export type CollectionPartnerAssociationDataFragment = { - __typename?: 'CollectionPartnerAssociation'; - externalId: string; - type: CollectionPartnershipType; - name?: string | null; - url?: any | null; - imageUrl?: any | null; - blurb?: any | null; - partner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; -}; - -export type CollectionPartnerDataFragment = { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; -}; - -export type CollectionStoryDataFragment = { - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; -}; - export type CuratedItemDataWithHistoryFragment = { __typename?: 'ApprovedCorpusItem'; externalId: string; @@ -2805,8 +2698,8 @@ export type BaseSectionDataFragment = { startDate?: any | null; endDate?: any | null; status: SectionStatus; - followable: boolean; allowAds: boolean; + followable: boolean; iab?: { __typename?: 'IABMetadata'; taxonomy: string; @@ -2831,8 +2724,8 @@ export type SectionDataFragment = { startDate?: any | null; endDate?: any | null; status: SectionStatus; - followable: boolean; allowAds: boolean; + followable: boolean; sectionItems: Array<{ __typename?: 'SectionItem'; createdAt: number; @@ -3225,169 +3118,6 @@ export type CreateApprovedCorpusItemMutation = { }; }; -export type CreateCollectionMutationVariables = Exact<{ - data: CreateCollectionInput; -}>; - -export type CreateCollectionMutation = { - __typename?: 'Mutation'; - createCollection: { - __typename?: 'Collection'; - externalId: string; - title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; - }; -}; - -export type CreateCollectionAuthorMutationVariables = Exact<{ - name: Scalars['String']; - slug?: InputMaybe; - bio?: InputMaybe; - imageUrl?: InputMaybe; - active?: InputMaybe; -}>; - -export type CreateCollectionAuthorMutation = { - __typename?: 'Mutation'; - createCollectionAuthor: { - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }; -}; - -export type CreateCollectionPartnerMutationVariables = Exact<{ - name: Scalars['String']; - url: Scalars['Url']; - blurb: Scalars['Markdown']; - imageUrl: Scalars['Url']; -}>; - -export type CreateCollectionPartnerMutation = { - __typename?: 'Mutation'; - createCollectionPartner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; -}; - -export type CreateCollectionPartnerAssociationMutationVariables = Exact<{ - type: CollectionPartnershipType; - partnerExternalId: Scalars['String']; - collectionExternalId: Scalars['String']; - name?: InputMaybe; - url?: InputMaybe; - imageUrl?: InputMaybe; - blurb?: InputMaybe; -}>; - -export type CreateCollectionPartnerAssociationMutation = { - __typename?: 'Mutation'; - createCollectionPartnerAssociation: { - __typename?: 'CollectionPartnerAssociation'; - externalId: string; - type: CollectionPartnershipType; - name?: string | null; - url?: any | null; - imageUrl?: any | null; - blurb?: any | null; - partner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; - }; -}; - -export type CreateCollectionStoryMutationVariables = Exact<{ - collectionExternalId: Scalars['String']; - url: Scalars['Url']; - title: Scalars['String']; - excerpt: Scalars['Markdown']; - imageUrl: Scalars['Url']; - authors: Array | CollectionStoryAuthorInput; - publisher: Scalars['String']; - sortOrder?: InputMaybe; - fromPartner?: InputMaybe; -}>; - -export type CreateCollectionStoryMutation = { - __typename?: 'Mutation'; - createCollectionStory: { - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; - }; -}; - export type CreateCustomSectionMutationVariables = Exact<{ data: CreateCustomSectionInput; }>; @@ -3409,8 +3139,8 @@ export type CreateCustomSectionMutation = { startDate?: any | null; endDate?: any | null; status: SectionStatus; - followable: boolean; allowAds: boolean; + followable: boolean; iab?: { __typename?: 'IABMetadata'; taxonomy: string; @@ -3419,15 +3149,6 @@ export type CreateCustomSectionMutation = { }; }; -export type CreateLabelMutationVariables = Exact<{ - name: Scalars['String']; -}>; - -export type CreateLabelMutation = { - __typename?: 'Mutation'; - createLabel: { __typename?: 'Label'; externalId: string; name: string }; -}; - export type CreateOrUpdatePublisherDomainMutationVariables = Exact<{ data: CreateOrUpdatePublisherDomainInput; }>; @@ -3516,55 +3237,6 @@ export type CreateSectionItemMutation = { }; }; -export type DeleteCollectionPartnerAssociationMutationVariables = Exact<{ - externalId: Scalars['String']; -}>; - -export type DeleteCollectionPartnerAssociationMutation = { - __typename?: 'Mutation'; - deleteCollectionPartnerAssociation: { - __typename?: 'CollectionPartnerAssociation'; - externalId: string; - type: CollectionPartnershipType; - name?: string | null; - url?: any | null; - imageUrl?: any | null; - blurb?: any | null; - partner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; - }; -}; - -export type DeleteCollectionStoryMutationVariables = Exact<{ - externalId: Scalars['String']; -}>; - -export type DeleteCollectionStoryMutation = { - __typename?: 'Mutation'; - deleteCollectionStory: { - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; - }; -}; - export type DeleteCustomSectionMutationVariables = Exact<{ externalId: Scalars['ID']; }>; @@ -3653,8 +3325,8 @@ export type DisableEnableSectionMutation = { startDate?: any | null; endDate?: any | null; status: SectionStatus; - followable: boolean; allowAds: boolean; + followable: boolean; sectionItems: Array<{ __typename?: 'SectionItem'; createdAt: number; @@ -3705,18 +3377,6 @@ export type DisableEnableSectionMutation = { }; }; -export type ImageUploadMutationVariables = Exact<{ - image: Scalars['Upload']; - width: Scalars['Int']; - height: Scalars['Int']; - fileSizeBytes: Scalars['Int']; -}>; - -export type ImageUploadMutation = { - __typename?: 'Mutation'; - collectionImageUpload: { __typename?: 'CollectionImageUrl'; url: string }; -}; - export type ModerateShareableListMutationVariables = Exact<{ data: ModerateShareableListInput; }>; @@ -3999,384 +3659,37 @@ export type UpdateApprovedCorpusItemMutation = { }; }; -export type UpdateCollectionMutationVariables = Exact<{ - data: UpdateCollectionInput; +export type UpdateCustomSectionMutationVariables = Exact<{ + data: UpdateCustomSectionInput; }>; -export type UpdateCollectionMutation = { +export type UpdateCustomSectionMutation = { __typename?: 'Mutation'; - updateCollection: { - __typename?: 'Collection'; + updateCustomSection: { + __typename?: 'Section'; externalId: string; title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; - }; -}; - -export type UpdateCollectionAuthorMutationVariables = Exact<{ - externalId: Scalars['String']; - name: Scalars['String']; - slug: Scalars['String']; - bio?: InputMaybe; - imageUrl?: InputMaybe; - active?: InputMaybe; -}>; - -export type UpdateCollectionAuthorMutation = { - __typename?: 'Mutation'; - updateCollectionAuthor: { - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }; -}; - -export type UpdateCollectionAuthorImageUrlMutationVariables = Exact<{ - externalId: Scalars['String']; - imageUrl: Scalars['Url']; -}>; - -export type UpdateCollectionAuthorImageUrlMutation = { - __typename?: 'Mutation'; - updateCollectionAuthorImageUrl: { - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }; -}; - -export type UpdateCollectionImageUrlMutationVariables = Exact<{ - externalId: Scalars['String']; - imageUrl: Scalars['Url']; -}>; - -export type UpdateCollectionImageUrlMutation = { - __typename?: 'Mutation'; - updateCollectionImageUrl: { - __typename?: 'Collection'; - externalId: string; - title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; - }; -}; - -export type UpdateCollectionPartnerMutationVariables = Exact<{ - externalId: Scalars['String']; - name: Scalars['String']; - url: Scalars['Url']; - blurb: Scalars['Markdown']; - imageUrl?: InputMaybe; -}>; - -export type UpdateCollectionPartnerMutation = { - __typename?: 'Mutation'; - updateCollectionPartner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; -}; - -export type UpdateCollectionPartnerAssociationMutationVariables = Exact<{ - externalId: Scalars['String']; - type: CollectionPartnershipType; - partnerExternalId: Scalars['String']; - name?: InputMaybe; - url?: InputMaybe; - imageUrl?: InputMaybe; - blurb?: InputMaybe; -}>; - -export type UpdateCollectionPartnerAssociationMutation = { - __typename?: 'Mutation'; - updateCollectionPartnerAssociation: { - __typename?: 'CollectionPartnerAssociation'; - externalId: string; - type: CollectionPartnershipType; - name?: string | null; - url?: any | null; - imageUrl?: any | null; - blurb?: any | null; - partner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; - }; -}; - -export type UpdateCollectionPartnerAssociationImageUrlMutationVariables = - Exact<{ - externalId: Scalars['String']; - imageUrl: Scalars['Url']; - }>; - -export type UpdateCollectionPartnerAssociationImageUrlMutation = { - __typename?: 'Mutation'; - updateCollectionPartnerAssociationImageUrl: { - __typename?: 'CollectionPartnerAssociation'; - externalId: string; - type: CollectionPartnershipType; - name?: string | null; - url?: any | null; - imageUrl?: any | null; - blurb?: any | null; - partner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; - }; -}; - -export type UpdateCollectionPartnerImageUrlMutationVariables = Exact<{ - externalId: Scalars['String']; - imageUrl: Scalars['Url']; -}>; - -export type UpdateCollectionPartnerImageUrlMutation = { - __typename?: 'Mutation'; - updateCollectionPartnerImageUrl: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; -}; - -export type UpdateCollectionStoryMutationVariables = Exact<{ - externalId: Scalars['String']; - url: Scalars['Url']; - title: Scalars['String']; - excerpt: Scalars['Markdown']; - imageUrl: Scalars['Url']; - authors: Array | CollectionStoryAuthorInput; - publisher: Scalars['String']; - sortOrder?: InputMaybe; - fromPartner?: InputMaybe; -}>; - -export type UpdateCollectionStoryMutation = { - __typename?: 'Mutation'; - updateCollectionStory: { - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; - }; -}; - -export type UpdateCollectionStoryImageUrlMutationVariables = Exact<{ - externalId: Scalars['String']; - imageUrl: Scalars['Url']; -}>; - -export type UpdateCollectionStoryImageUrlMutation = { - __typename?: 'Mutation'; - updateCollectionStoryImageUrl: { - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; - }; -}; - -export type UpdateCollectionStorySortOrderMutationVariables = Exact<{ - externalId: Scalars['String']; - sortOrder: Scalars['Int']; -}>; - -export type UpdateCollectionStorySortOrderMutation = { - __typename?: 'Mutation'; - updateCollectionStorySortOrder: { - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; - }; -}; - -export type UpdateCustomSectionMutationVariables = Exact<{ - data: UpdateCustomSectionInput; -}>; - -export type UpdateCustomSectionMutation = { - __typename?: 'Mutation'; - updateCustomSection: { - __typename?: 'Section'; - externalId: string; - title: string; - scheduledSurfaceGuid: string; - sort?: number | null; - createSource: ActivitySource; - disabled: boolean; - active: boolean; - description?: string | null; - heroTitle?: string | null; - heroDescription?: string | null; - startDate?: any | null; - endDate?: any | null; - status: SectionStatus; - followable: boolean; - allowAds: boolean; - iab?: { - __typename?: 'IABMetadata'; - taxonomy: string; - categories: Array; + scheduledSurfaceGuid: string; + sort?: number | null; + createSource: ActivitySource; + disabled: boolean; + active: boolean; + description?: string | null; + heroTitle?: string | null; + heroDescription?: string | null; + startDate?: any | null; + endDate?: any | null; + status: SectionStatus; + allowAds: boolean; + followable: boolean; + iab?: { + __typename?: 'IABMetadata'; + taxonomy: string; + categories: Array; } | null; }; }; -export type UpdateLabelMutationVariables = Exact<{ - data: UpdateLabelInput; -}>; - -export type UpdateLabelMutation = { - __typename?: 'Mutation'; - updateLabel: { __typename?: 'Label'; externalId: string; name: string }; -}; - export type UpdateProspectAsCuratedMutationVariables = Exact<{ id: Scalars['ID']; historyFilter?: InputMaybe; @@ -4611,336 +3924,25 @@ export type GetApprovedItemsQuery = { }; }; -export type GetAuthorByIdQueryVariables = Exact<{ - id: Scalars['String']; +export type GetOpenGraphFieldsQueryVariables = Exact<{ + url: Scalars['Url']; }>; -export type GetAuthorByIdQuery = { +export type GetOpenGraphFieldsQuery = { __typename?: 'Query'; - getCollectionAuthor?: { - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; + getOpenGraphFields?: { + __typename?: 'OpenGraphFields'; + description: string; } | null; }; -export type GetAuthorsQueryVariables = Exact<{ - page?: InputMaybe; - perPage?: InputMaybe; +export type GetProspectsQueryVariables = Exact<{ + scheduledSurfaceGuid: Scalars['String']; + prospectType?: InputMaybe; + historyFilter?: InputMaybe; }>; -export type GetAuthorsQuery = { - __typename?: 'Query'; - getCollectionAuthors: { - __typename?: 'CollectionAuthorsResult'; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - pagination?: { - __typename?: 'Pagination'; - currentPage: number; - totalPages: number; - totalResults: number; - } | null; - }; -}; - -export type GetCollectionByExternalIdQueryVariables = Exact<{ - externalId: Scalars['String']; -}>; - -export type GetCollectionByExternalIdQuery = { - __typename?: 'Query'; - getCollection?: { - __typename?: 'Collection'; - externalId: string; - title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; - } | null; -}; - -export type GetCollectionPartnerQueryVariables = Exact<{ - id: Scalars['String']; -}>; - -export type GetCollectionPartnerQuery = { - __typename?: 'Query'; - getCollectionPartner?: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; -}; - -export type GetCollectionPartnerAssociationQueryVariables = Exact<{ - externalId: Scalars['String']; -}>; - -export type GetCollectionPartnerAssociationQuery = { - __typename?: 'Query'; - getCollectionPartnerAssociationForCollection?: { - __typename?: 'CollectionPartnerAssociation'; - externalId: string; - type: CollectionPartnershipType; - name?: string | null; - url?: any | null; - imageUrl?: any | null; - blurb?: any | null; - partner: { - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }; - } | null; -}; - -export type GetCollectionPartnersQueryVariables = Exact<{ - page?: InputMaybe; - perPage?: InputMaybe; -}>; - -export type GetCollectionPartnersQuery = { - __typename?: 'Query'; - getCollectionPartners: { - __typename?: 'CollectionPartnersResult'; - partners: Array<{ - __typename?: 'CollectionPartner'; - externalId: string; - name: string; - url: any; - imageUrl: any; - blurb: any; - }>; - pagination?: { - __typename?: 'Pagination'; - currentPage: number; - totalPages: number; - totalResults: number; - } | null; - }; -}; - -export type GetCollectionStoriesQueryVariables = Exact<{ - id: Scalars['String']; -}>; - -export type GetCollectionStoriesQuery = { - __typename?: 'Query'; - getCollection?: { - __typename?: 'Collection'; - externalId: string; - stories: Array<{ - __typename?: 'CollectionStory'; - externalId: string; - url: any; - title: string; - excerpt: any; - imageUrl?: any | null; - publisher?: string | null; - fromPartner: boolean; - sortOrder?: number | null; - authors: Array<{ - __typename?: 'CollectionStoryAuthor'; - name: string; - sortOrder: number; - }>; - }>; - } | null; -}; - -export type GetCollectionsQueryVariables = Exact<{ - page: Scalars['Int']; - perPage: Scalars['Int']; - status: CollectionStatus; -}>; - -export type GetCollectionsQuery = { - __typename?: 'Query'; - searchCollections: { - __typename?: 'CollectionsResult'; - collections: Array<{ - __typename?: 'Collection'; - externalId: string; - title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; - }>; - pagination: { - __typename?: 'Pagination'; - currentPage: number; - totalPages: number; - totalResults: number; - }; - }; -}; - -export type GetInitialCollectionFormDataQueryVariables = Exact<{ - page?: InputMaybe; - perPage?: InputMaybe; -}>; - -export type GetInitialCollectionFormDataQuery = { - __typename?: 'Query'; - getLanguages: Array; - getCollectionAuthors: { - __typename?: 'CollectionAuthorsResult'; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - }; - labels: Array<{ __typename?: 'Label'; externalId: string; name: string }>; - getCurationCategories: Array<{ - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - }>; - getIABCategories: Array<{ - __typename?: 'IABParentCategory'; - externalId: string; - name: string; - slug: string; - children: Array<{ - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - }>; - }>; -}; - -export type GetOpenGraphFieldsQueryVariables = Exact<{ - url: Scalars['Url']; -}>; - -export type GetOpenGraphFieldsQuery = { - __typename?: 'Query'; - getOpenGraphFields?: { - __typename?: 'OpenGraphFields'; - description: string; - } | null; -}; - -export type GetProspectsQueryVariables = Exact<{ - scheduledSurfaceGuid: Scalars['String']; - prospectType?: InputMaybe; - historyFilter?: InputMaybe; -}>; - -export type GetProspectsQuery = { +export type GetProspectsQuery = { __typename?: 'Query'; getProspects: Array<{ __typename?: 'Prospect'; @@ -5139,78 +4141,6 @@ export type GetScheduledSurfacesForUserQuery = { }>; }; -export type SearchCollectionsQueryVariables = Exact<{ - filters: SearchCollectionsFilters; - page?: InputMaybe; - perPage?: InputMaybe; -}>; - -export type SearchCollectionsQuery = { - __typename?: 'Query'; - searchCollections: { - __typename?: 'CollectionsResult'; - collections: Array<{ - __typename?: 'Collection'; - externalId: string; - title: string; - slug: string; - excerpt?: any | null; - intro?: any | null; - imageUrl?: any | null; - language: CollectionLanguage; - status: CollectionStatus; - authors: Array<{ - __typename?: 'CollectionAuthor'; - externalId: string; - name: string; - slug?: string | null; - bio?: any | null; - imageUrl?: any | null; - active: boolean; - }>; - curationCategory?: { - __typename?: 'CurationCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABParentCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - IABChildCategory?: { - __typename?: 'IABCategory'; - externalId: string; - name: string; - slug: string; - } | null; - labels?: Array<{ - __typename?: 'Label'; - externalId: string; - name: string; - } | null> | null; - partnership?: { - __typename?: 'CollectionPartnership'; - externalId: string; - type: CollectionPartnershipType; - name: string; - url: any; - imageUrl: any; - blurb: any; - } | null; - }>; - pagination: { - __typename?: 'Pagination'; - currentPage: number; - totalPages: number; - totalResults: number; - perPage: number; - }; - }; -}; - export type GetSectionsWithSectionItemsQueryVariables = Exact<{ scheduledSurfaceGuid: Scalars['ID']; createSource?: InputMaybe; @@ -5235,8 +4165,8 @@ export type GetSectionsWithSectionItemsQuery = { startDate?: any | null; endDate?: any | null; status: SectionStatus; - followable: boolean; allowAds: boolean; + followable: boolean; sectionItems: Array<{ __typename?: 'SectionItem'; createdAt: number; @@ -5287,35 +4217,6 @@ export type GetSectionsWithSectionItemsQuery = { }>; }; -export type GetStoryFromParserQueryVariables = Exact<{ - url: Scalars['String']; -}>; - -export type GetStoryFromParserQuery = { - __typename?: 'Query'; - getItemByUrl?: { - __typename?: 'Item'; - resolvedUrl?: any | null; - title?: string | null; - excerpt?: string | null; - topImageUrl?: any | null; - images?: Array<{ - __typename?: 'Image'; - src: string; - width?: number | null; - height?: number | null; - } | null> | null; - authors?: Array<{ - __typename?: 'Author'; - name?: string | null; - } | null> | null; - domainMetadata?: { - __typename?: 'DomainMetadata'; - name?: string | null; - } | null; - } | null; -}; - export type GetUrlMetadataQueryVariables = Exact<{ url: Scalars['String']; }>; @@ -5338,13 +4239,6 @@ export type GetUrlMetadataQuery = { }; }; -export type LabelsQueryVariables = Exact<{ [key: string]: never }>; - -export type LabelsQuery = { - __typename?: 'Query'; - labels: Array<{ __typename?: 'Label'; externalId: string; name: string }>; -}; - export type SearchShareableListQueryVariables = Exact<{ externalId: Scalars['ID']; }>; @@ -5385,138 +4279,46 @@ export type SearchShareableListQuery = { } | null; }; -export const CollectionAuthorDataFragmentDoc = gql` - fragment CollectionAuthorData on CollectionAuthor { +export const BaseSectionDataFragmentDoc = gql` + fragment BaseSectionData on Section { externalId - name - slug - bio - imageUrl + title + scheduledSurfaceGuid + iab { + taxonomy + categories + } + sort + createSource + disabled active + description + heroTitle + heroDescription + startDate + endDate + status + allowAds + followable } `; -export const CollectionDataFragmentDoc = gql` - fragment CollectionData on Collection { +export const BaseSectionItemDataFragmentDoc = gql` + fragment BaseSectionItemData on SectionItem { + externalId + rank + } +`; +export const CuratedItemDataFragmentDoc = gql` + fragment CuratedItemData on ApprovedCorpusItem { externalId + prospectId title - slug - excerpt - intro - imageUrl language - status + publisher + datePublished authors { - ...CollectionAuthorData - } - curationCategory { - externalId - name - slug - } - IABParentCategory { - externalId - name - slug - } - IABChildCategory { - externalId - name - slug - } - labels { - externalId - name - } - partnership { - externalId - type name - url - imageUrl - blurb - } - } - ${CollectionAuthorDataFragmentDoc} -`; -export const CollectionPartnerDataFragmentDoc = gql` - fragment CollectionPartnerData on CollectionPartner { - externalId - name - url - imageUrl - blurb - } -`; -export const CollectionPartnerAssociationDataFragmentDoc = gql` - fragment CollectionPartnerAssociationData on CollectionPartnerAssociation { - externalId - type - name - url - imageUrl - blurb - partner { - ...CollectionPartnerData - } - } - ${CollectionPartnerDataFragmentDoc} -`; -export const CollectionStoryDataFragmentDoc = gql` - fragment CollectionStoryData on CollectionStory { - externalId - url - title - excerpt - imageUrl - authors { - name - sortOrder - } - publisher - fromPartner - sortOrder - } -`; -export const BaseSectionDataFragmentDoc = gql` - fragment BaseSectionData on Section { - externalId - title - scheduledSurfaceGuid - iab { - taxonomy - categories - } - sort - createSource - disabled - active - description - heroTitle - heroDescription - startDate - endDate - status - followable - allowAds - } -`; -export const BaseSectionItemDataFragmentDoc = gql` - fragment BaseSectionItemData on SectionItem { - externalId - rank - } -`; -export const CuratedItemDataFragmentDoc = gql` - fragment CuratedItemData on ApprovedCorpusItem { - externalId - prospectId - title - language - publisher - datePublished - authors { - name - sortOrder + sortOrder } url hasTrustedDomain @@ -5785,345 +4587,6 @@ export type CreateApprovedCorpusItemMutationOptions = CreateApprovedCorpusItemMutation, CreateApprovedCorpusItemMutationVariables >; -export const CreateCollectionDocument = gql` - mutation createCollection($data: CreateCollectionInput!) { - createCollection(data: $data) { - ...CollectionData - } - } - ${CollectionDataFragmentDoc} -`; -export type CreateCollectionMutationFn = Apollo.MutationFunction< - CreateCollectionMutation, - CreateCollectionMutationVariables ->; - -/** - * __useCreateCollectionMutation__ - * - * To run a mutation, you first call `useCreateCollectionMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateCollectionMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createCollectionMutation, { data, loading, error }] = useCreateCollectionMutation({ - * variables: { - * data: // value for 'data' - * }, - * }); - */ -export function useCreateCollectionMutation( - baseOptions?: Apollo.MutationHookOptions< - CreateCollectionMutation, - CreateCollectionMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - CreateCollectionMutation, - CreateCollectionMutationVariables - >(CreateCollectionDocument, options); -} -export type CreateCollectionMutationHookResult = ReturnType< - typeof useCreateCollectionMutation ->; -export type CreateCollectionMutationResult = - Apollo.MutationResult; -export type CreateCollectionMutationOptions = Apollo.BaseMutationOptions< - CreateCollectionMutation, - CreateCollectionMutationVariables ->; -export const CreateCollectionAuthorDocument = gql` - mutation createCollectionAuthor( - $name: String! - $slug: String - $bio: Markdown - $imageUrl: Url - $active: Boolean - ) { - createCollectionAuthor( - data: { - name: $name - slug: $slug - bio: $bio - imageUrl: $imageUrl - active: $active - } - ) { - ...CollectionAuthorData - } - } - ${CollectionAuthorDataFragmentDoc} -`; -export type CreateCollectionAuthorMutationFn = Apollo.MutationFunction< - CreateCollectionAuthorMutation, - CreateCollectionAuthorMutationVariables ->; - -/** - * __useCreateCollectionAuthorMutation__ - * - * To run a mutation, you first call `useCreateCollectionAuthorMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateCollectionAuthorMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createCollectionAuthorMutation, { data, loading, error }] = useCreateCollectionAuthorMutation({ - * variables: { - * name: // value for 'name' - * slug: // value for 'slug' - * bio: // value for 'bio' - * imageUrl: // value for 'imageUrl' - * active: // value for 'active' - * }, - * }); - */ -export function useCreateCollectionAuthorMutation( - baseOptions?: Apollo.MutationHookOptions< - CreateCollectionAuthorMutation, - CreateCollectionAuthorMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - CreateCollectionAuthorMutation, - CreateCollectionAuthorMutationVariables - >(CreateCollectionAuthorDocument, options); -} -export type CreateCollectionAuthorMutationHookResult = ReturnType< - typeof useCreateCollectionAuthorMutation ->; -export type CreateCollectionAuthorMutationResult = - Apollo.MutationResult; -export type CreateCollectionAuthorMutationOptions = Apollo.BaseMutationOptions< - CreateCollectionAuthorMutation, - CreateCollectionAuthorMutationVariables ->; -export const CreateCollectionPartnerDocument = gql` - mutation createCollectionPartner( - $name: String! - $url: Url! - $blurb: Markdown! - $imageUrl: Url! - ) { - createCollectionPartner( - data: { name: $name, url: $url, blurb: $blurb, imageUrl: $imageUrl } - ) { - ...CollectionPartnerData - } - } - ${CollectionPartnerDataFragmentDoc} -`; -export type CreateCollectionPartnerMutationFn = Apollo.MutationFunction< - CreateCollectionPartnerMutation, - CreateCollectionPartnerMutationVariables ->; - -/** - * __useCreateCollectionPartnerMutation__ - * - * To run a mutation, you first call `useCreateCollectionPartnerMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateCollectionPartnerMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createCollectionPartnerMutation, { data, loading, error }] = useCreateCollectionPartnerMutation({ - * variables: { - * name: // value for 'name' - * url: // value for 'url' - * blurb: // value for 'blurb' - * imageUrl: // value for 'imageUrl' - * }, - * }); - */ -export function useCreateCollectionPartnerMutation( - baseOptions?: Apollo.MutationHookOptions< - CreateCollectionPartnerMutation, - CreateCollectionPartnerMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - CreateCollectionPartnerMutation, - CreateCollectionPartnerMutationVariables - >(CreateCollectionPartnerDocument, options); -} -export type CreateCollectionPartnerMutationHookResult = ReturnType< - typeof useCreateCollectionPartnerMutation ->; -export type CreateCollectionPartnerMutationResult = - Apollo.MutationResult; -export type CreateCollectionPartnerMutationOptions = Apollo.BaseMutationOptions< - CreateCollectionPartnerMutation, - CreateCollectionPartnerMutationVariables ->; -export const CreateCollectionPartnerAssociationDocument = gql` - mutation createCollectionPartnerAssociation( - $type: CollectionPartnershipType! - $partnerExternalId: String! - $collectionExternalId: String! - $name: String - $url: Url - $imageUrl: Url - $blurb: Markdown - ) { - createCollectionPartnerAssociation( - data: { - type: $type - partnerExternalId: $partnerExternalId - collectionExternalId: $collectionExternalId - name: $name - url: $url - imageUrl: $imageUrl - blurb: $blurb - } - ) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationDataFragmentDoc} -`; -export type CreateCollectionPartnerAssociationMutationFn = - Apollo.MutationFunction< - CreateCollectionPartnerAssociationMutation, - CreateCollectionPartnerAssociationMutationVariables - >; - -/** - * __useCreateCollectionPartnerAssociationMutation__ - * - * To run a mutation, you first call `useCreateCollectionPartnerAssociationMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateCollectionPartnerAssociationMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createCollectionPartnerAssociationMutation, { data, loading, error }] = useCreateCollectionPartnerAssociationMutation({ - * variables: { - * type: // value for 'type' - * partnerExternalId: // value for 'partnerExternalId' - * collectionExternalId: // value for 'collectionExternalId' - * name: // value for 'name' - * url: // value for 'url' - * imageUrl: // value for 'imageUrl' - * blurb: // value for 'blurb' - * }, - * }); - */ -export function useCreateCollectionPartnerAssociationMutation( - baseOptions?: Apollo.MutationHookOptions< - CreateCollectionPartnerAssociationMutation, - CreateCollectionPartnerAssociationMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - CreateCollectionPartnerAssociationMutation, - CreateCollectionPartnerAssociationMutationVariables - >(CreateCollectionPartnerAssociationDocument, options); -} -export type CreateCollectionPartnerAssociationMutationHookResult = ReturnType< - typeof useCreateCollectionPartnerAssociationMutation ->; -export type CreateCollectionPartnerAssociationMutationResult = - Apollo.MutationResult; -export type CreateCollectionPartnerAssociationMutationOptions = - Apollo.BaseMutationOptions< - CreateCollectionPartnerAssociationMutation, - CreateCollectionPartnerAssociationMutationVariables - >; -export const CreateCollectionStoryDocument = gql` - mutation createCollectionStory( - $collectionExternalId: String! - $url: Url! - $title: String! - $excerpt: Markdown! - $imageUrl: Url! - $authors: [CollectionStoryAuthorInput!]! - $publisher: String! - $sortOrder: Int - $fromPartner: Boolean - ) { - createCollectionStory( - data: { - collectionExternalId: $collectionExternalId - url: $url - title: $title - excerpt: $excerpt - imageUrl: $imageUrl - authors: $authors - publisher: $publisher - sortOrder: $sortOrder - fromPartner: $fromPartner - } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryDataFragmentDoc} -`; -export type CreateCollectionStoryMutationFn = Apollo.MutationFunction< - CreateCollectionStoryMutation, - CreateCollectionStoryMutationVariables ->; - -/** - * __useCreateCollectionStoryMutation__ - * - * To run a mutation, you first call `useCreateCollectionStoryMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateCollectionStoryMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createCollectionStoryMutation, { data, loading, error }] = useCreateCollectionStoryMutation({ - * variables: { - * collectionExternalId: // value for 'collectionExternalId' - * url: // value for 'url' - * title: // value for 'title' - * excerpt: // value for 'excerpt' - * imageUrl: // value for 'imageUrl' - * authors: // value for 'authors' - * publisher: // value for 'publisher' - * sortOrder: // value for 'sortOrder' - * fromPartner: // value for 'fromPartner' - * }, - * }); - */ -export function useCreateCollectionStoryMutation( - baseOptions?: Apollo.MutationHookOptions< - CreateCollectionStoryMutation, - CreateCollectionStoryMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - CreateCollectionStoryMutation, - CreateCollectionStoryMutationVariables - >(CreateCollectionStoryDocument, options); -} -export type CreateCollectionStoryMutationHookResult = ReturnType< - typeof useCreateCollectionStoryMutation ->; -export type CreateCollectionStoryMutationResult = - Apollo.MutationResult; -export type CreateCollectionStoryMutationOptions = Apollo.BaseMutationOptions< - CreateCollectionStoryMutation, - CreateCollectionStoryMutationVariables ->; export const CreateCustomSectionDocument = gql` mutation createCustomSection($data: CreateCustomSectionInput!) { createCustomSection(data: $data) { @@ -6175,57 +4638,6 @@ export type CreateCustomSectionMutationOptions = Apollo.BaseMutationOptions< CreateCustomSectionMutation, CreateCustomSectionMutationVariables >; -export const CreateLabelDocument = gql` - mutation createLabel($name: String!) { - createLabel(name: $name) { - externalId - name - } - } -`; -export type CreateLabelMutationFn = Apollo.MutationFunction< - CreateLabelMutation, - CreateLabelMutationVariables ->; - -/** - * __useCreateLabelMutation__ - * - * To run a mutation, you first call `useCreateLabelMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateLabelMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createLabelMutation, { data, loading, error }] = useCreateLabelMutation({ - * variables: { - * name: // value for 'name' - * }, - * }); - */ -export function useCreateLabelMutation( - baseOptions?: Apollo.MutationHookOptions< - CreateLabelMutation, - CreateLabelMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation( - CreateLabelDocument, - options, - ); -} -export type CreateLabelMutationHookResult = ReturnType< - typeof useCreateLabelMutation ->; -export type CreateLabelMutationResult = - Apollo.MutationResult; -export type CreateLabelMutationOptions = Apollo.BaseMutationOptions< - CreateLabelMutation, - CreateLabelMutationVariables ->; export const CreateOrUpdatePublisherDomainDocument = gql` mutation createOrUpdatePublisherDomain( $data: CreateOrUpdatePublisherDomainInput! @@ -6419,144 +4831,40 @@ export type CreateSectionItemMutationOptions = Apollo.BaseMutationOptions< CreateSectionItemMutation, CreateSectionItemMutationVariables >; -export const DeleteCollectionPartnerAssociationDocument = gql` - mutation deleteCollectionPartnerAssociation($externalId: String!) { - deleteCollectionPartnerAssociation(externalId: $externalId) { - ...CollectionPartnerAssociationData +export const DeleteCustomSectionDocument = gql` + mutation deleteCustomSection($externalId: ID!) { + deleteCustomSection(externalId: $externalId) { + externalId + title } } - ${CollectionPartnerAssociationDataFragmentDoc} `; -export type DeleteCollectionPartnerAssociationMutationFn = - Apollo.MutationFunction< - DeleteCollectionPartnerAssociationMutation, - DeleteCollectionPartnerAssociationMutationVariables - >; +export type DeleteCustomSectionMutationFn = Apollo.MutationFunction< + DeleteCustomSectionMutation, + DeleteCustomSectionMutationVariables +>; /** - * __useDeleteCollectionPartnerAssociationMutation__ + * __useDeleteCustomSectionMutation__ * - * To run a mutation, you first call `useDeleteCollectionPartnerAssociationMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useDeleteCollectionPartnerAssociationMutation` returns a tuple that includes: + * To run a mutation, you first call `useDeleteCustomSectionMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteCustomSectionMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const [deleteCollectionPartnerAssociationMutation, { data, loading, error }] = useDeleteCollectionPartnerAssociationMutation({ + * const [deleteCustomSectionMutation, { data, loading, error }] = useDeleteCustomSectionMutation({ * variables: { * externalId: // value for 'externalId' * }, * }); */ -export function useDeleteCollectionPartnerAssociationMutation( +export function useDeleteCustomSectionMutation( baseOptions?: Apollo.MutationHookOptions< - DeleteCollectionPartnerAssociationMutation, - DeleteCollectionPartnerAssociationMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - DeleteCollectionPartnerAssociationMutation, - DeleteCollectionPartnerAssociationMutationVariables - >(DeleteCollectionPartnerAssociationDocument, options); -} -export type DeleteCollectionPartnerAssociationMutationHookResult = ReturnType< - typeof useDeleteCollectionPartnerAssociationMutation ->; -export type DeleteCollectionPartnerAssociationMutationResult = - Apollo.MutationResult; -export type DeleteCollectionPartnerAssociationMutationOptions = - Apollo.BaseMutationOptions< - DeleteCollectionPartnerAssociationMutation, - DeleteCollectionPartnerAssociationMutationVariables - >; -export const DeleteCollectionStoryDocument = gql` - mutation deleteCollectionStory($externalId: String!) { - deleteCollectionStory(externalId: $externalId) { - ...CollectionStoryData - } - } - ${CollectionStoryDataFragmentDoc} -`; -export type DeleteCollectionStoryMutationFn = Apollo.MutationFunction< - DeleteCollectionStoryMutation, - DeleteCollectionStoryMutationVariables ->; - -/** - * __useDeleteCollectionStoryMutation__ - * - * To run a mutation, you first call `useDeleteCollectionStoryMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useDeleteCollectionStoryMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [deleteCollectionStoryMutation, { data, loading, error }] = useDeleteCollectionStoryMutation({ - * variables: { - * externalId: // value for 'externalId' - * }, - * }); - */ -export function useDeleteCollectionStoryMutation( - baseOptions?: Apollo.MutationHookOptions< - DeleteCollectionStoryMutation, - DeleteCollectionStoryMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - DeleteCollectionStoryMutation, - DeleteCollectionStoryMutationVariables - >(DeleteCollectionStoryDocument, options); -} -export type DeleteCollectionStoryMutationHookResult = ReturnType< - typeof useDeleteCollectionStoryMutation ->; -export type DeleteCollectionStoryMutationResult = - Apollo.MutationResult; -export type DeleteCollectionStoryMutationOptions = Apollo.BaseMutationOptions< - DeleteCollectionStoryMutation, - DeleteCollectionStoryMutationVariables ->; -export const DeleteCustomSectionDocument = gql` - mutation deleteCustomSection($externalId: ID!) { - deleteCustomSection(externalId: $externalId) { - externalId - title - } - } -`; -export type DeleteCustomSectionMutationFn = Apollo.MutationFunction< - DeleteCustomSectionMutation, - DeleteCustomSectionMutationVariables ->; - -/** - * __useDeleteCustomSectionMutation__ - * - * To run a mutation, you first call `useDeleteCustomSectionMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useDeleteCustomSectionMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [deleteCustomSectionMutation, { data, loading, error }] = useDeleteCustomSectionMutation({ - * variables: { - * externalId: // value for 'externalId' - * }, - * }); - */ -export function useDeleteCustomSectionMutation( - baseOptions?: Apollo.MutationHookOptions< - DeleteCustomSectionMutation, - DeleteCustomSectionMutationVariables + DeleteCustomSectionMutation, + DeleteCustomSectionMutationVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; @@ -6684,71 +4992,6 @@ export type DisableEnableSectionMutationOptions = Apollo.BaseMutationOptions< DisableEnableSectionMutation, DisableEnableSectionMutationVariables >; -export const ImageUploadDocument = gql` - mutation imageUpload( - $image: Upload! - $width: Int! - $height: Int! - $fileSizeBytes: Int! - ) { - collectionImageUpload( - data: { - image: $image - width: $width - height: $height - fileSizeBytes: $fileSizeBytes - } - ) { - url - } - } -`; -export type ImageUploadMutationFn = Apollo.MutationFunction< - ImageUploadMutation, - ImageUploadMutationVariables ->; - -/** - * __useImageUploadMutation__ - * - * To run a mutation, you first call `useImageUploadMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useImageUploadMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [imageUploadMutation, { data, loading, error }] = useImageUploadMutation({ - * variables: { - * image: // value for 'image' - * width: // value for 'width' - * height: // value for 'height' - * fileSizeBytes: // value for 'fileSizeBytes' - * }, - * }); - */ -export function useImageUploadMutation( - baseOptions?: Apollo.MutationHookOptions< - ImageUploadMutation, - ImageUploadMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation( - ImageUploadDocument, - options, - ); -} -export type ImageUploadMutationHookResult = ReturnType< - typeof useImageUploadMutation ->; -export type ImageUploadMutationResult = - Apollo.MutationResult; -export type ImageUploadMutationOptions = Apollo.BaseMutationOptions< - ImageUploadMutation, - ImageUploadMutationVariables ->; export const ModerateShareableListDocument = gql` mutation moderateShareableList($data: ModerateShareableListInput!) { moderateShareableList(data: $data) { @@ -7123,1693 +5366,359 @@ export type UpdateApprovedCorpusItemMutationOptions = UpdateApprovedCorpusItemMutation, UpdateApprovedCorpusItemMutationVariables >; -export const UpdateCollectionDocument = gql` - mutation updateCollection($data: UpdateCollectionInput!) { - updateCollection(data: $data) { - ...CollectionData +export const UpdateCustomSectionDocument = gql` + mutation updateCustomSection($data: UpdateCustomSectionInput!) { + updateCustomSection(data: $data) { + ...BaseSectionData } } - ${CollectionDataFragmentDoc} + ${BaseSectionDataFragmentDoc} `; -export type UpdateCollectionMutationFn = Apollo.MutationFunction< - UpdateCollectionMutation, - UpdateCollectionMutationVariables +export type UpdateCustomSectionMutationFn = Apollo.MutationFunction< + UpdateCustomSectionMutation, + UpdateCustomSectionMutationVariables >; /** - * __useUpdateCollectionMutation__ + * __useUpdateCustomSectionMutation__ * - * To run a mutation, you first call `useUpdateCollectionMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionMutation` returns a tuple that includes: + * To run a mutation, you first call `useUpdateCustomSectionMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateCustomSectionMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const [updateCollectionMutation, { data, loading, error }] = useUpdateCollectionMutation({ + * const [updateCustomSectionMutation, { data, loading, error }] = useUpdateCustomSectionMutation({ * variables: { * data: // value for 'data' * }, * }); */ -export function useUpdateCollectionMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionMutation, - UpdateCollectionMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionMutation, - UpdateCollectionMutationVariables - >(UpdateCollectionDocument, options); -} -export type UpdateCollectionMutationHookResult = ReturnType< - typeof useUpdateCollectionMutation ->; -export type UpdateCollectionMutationResult = - Apollo.MutationResult; -export type UpdateCollectionMutationOptions = Apollo.BaseMutationOptions< - UpdateCollectionMutation, - UpdateCollectionMutationVariables ->; -export const UpdateCollectionAuthorDocument = gql` - mutation updateCollectionAuthor( - $externalId: String! - $name: String! - $slug: String! - $bio: Markdown - $imageUrl: Url - $active: Boolean - ) { - updateCollectionAuthor( - data: { - externalId: $externalId - name: $name - slug: $slug - bio: $bio - imageUrl: $imageUrl - active: $active - } - ) { - ...CollectionAuthorData - } - } - ${CollectionAuthorDataFragmentDoc} -`; -export type UpdateCollectionAuthorMutationFn = Apollo.MutationFunction< - UpdateCollectionAuthorMutation, - UpdateCollectionAuthorMutationVariables ->; - -/** - * __useUpdateCollectionAuthorMutation__ - * - * To run a mutation, you first call `useUpdateCollectionAuthorMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionAuthorMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionAuthorMutation, { data, loading, error }] = useUpdateCollectionAuthorMutation({ - * variables: { - * externalId: // value for 'externalId' - * name: // value for 'name' - * slug: // value for 'slug' - * bio: // value for 'bio' - * imageUrl: // value for 'imageUrl' - * active: // value for 'active' - * }, - * }); - */ -export function useUpdateCollectionAuthorMutation( +export function useUpdateCustomSectionMutation( baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionAuthorMutation, - UpdateCollectionAuthorMutationVariables + UpdateCustomSectionMutation, + UpdateCustomSectionMutationVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; return Apollo.useMutation< - UpdateCollectionAuthorMutation, - UpdateCollectionAuthorMutationVariables - >(UpdateCollectionAuthorDocument, options); + UpdateCustomSectionMutation, + UpdateCustomSectionMutationVariables + >(UpdateCustomSectionDocument, options); } -export type UpdateCollectionAuthorMutationHookResult = ReturnType< - typeof useUpdateCollectionAuthorMutation +export type UpdateCustomSectionMutationHookResult = ReturnType< + typeof useUpdateCustomSectionMutation >; -export type UpdateCollectionAuthorMutationResult = - Apollo.MutationResult; -export type UpdateCollectionAuthorMutationOptions = Apollo.BaseMutationOptions< - UpdateCollectionAuthorMutation, - UpdateCollectionAuthorMutationVariables +export type UpdateCustomSectionMutationResult = + Apollo.MutationResult; +export type UpdateCustomSectionMutationOptions = Apollo.BaseMutationOptions< + UpdateCustomSectionMutation, + UpdateCustomSectionMutationVariables >; -export const UpdateCollectionAuthorImageUrlDocument = gql` - mutation updateCollectionAuthorImageUrl( - $externalId: String! - $imageUrl: Url! +export const UpdateProspectAsCuratedDocument = gql` + mutation updateProspectAsCurated( + $id: ID! + $historyFilter: ApprovedCorpusItemScheduledSurfaceHistoryFilters ) { - updateCollectionAuthorImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionAuthorData + updateProspectAsCurated(id: $id) { + ...ProspectDataWithCorpusItems } } - ${CollectionAuthorDataFragmentDoc} + ${ProspectDataWithCorpusItemsFragmentDoc} `; -export type UpdateCollectionAuthorImageUrlMutationFn = Apollo.MutationFunction< - UpdateCollectionAuthorImageUrlMutation, - UpdateCollectionAuthorImageUrlMutationVariables +export type UpdateProspectAsCuratedMutationFn = Apollo.MutationFunction< + UpdateProspectAsCuratedMutation, + UpdateProspectAsCuratedMutationVariables >; /** - * __useUpdateCollectionAuthorImageUrlMutation__ + * __useUpdateProspectAsCuratedMutation__ * - * To run a mutation, you first call `useUpdateCollectionAuthorImageUrlMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionAuthorImageUrlMutation` returns a tuple that includes: + * To run a mutation, you first call `useUpdateProspectAsCuratedMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateProspectAsCuratedMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const [updateCollectionAuthorImageUrlMutation, { data, loading, error }] = useUpdateCollectionAuthorImageUrlMutation({ + * const [updateProspectAsCuratedMutation, { data, loading, error }] = useUpdateProspectAsCuratedMutation({ * variables: { - * externalId: // value for 'externalId' - * imageUrl: // value for 'imageUrl' + * id: // value for 'id' + * historyFilter: // value for 'historyFilter' * }, * }); */ -export function useUpdateCollectionAuthorImageUrlMutation( +export function useUpdateProspectAsCuratedMutation( baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionAuthorImageUrlMutation, - UpdateCollectionAuthorImageUrlMutationVariables + UpdateProspectAsCuratedMutation, + UpdateProspectAsCuratedMutationVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; return Apollo.useMutation< - UpdateCollectionAuthorImageUrlMutation, - UpdateCollectionAuthorImageUrlMutationVariables - >(UpdateCollectionAuthorImageUrlDocument, options); + UpdateProspectAsCuratedMutation, + UpdateProspectAsCuratedMutationVariables + >(UpdateProspectAsCuratedDocument, options); } -export type UpdateCollectionAuthorImageUrlMutationHookResult = ReturnType< - typeof useUpdateCollectionAuthorImageUrlMutation ->; -export type UpdateCollectionAuthorImageUrlMutationResult = - Apollo.MutationResult; -export type UpdateCollectionAuthorImageUrlMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionAuthorImageUrlMutation, - UpdateCollectionAuthorImageUrlMutationVariables - >; -export const UpdateCollectionImageUrlDocument = gql` - mutation updateCollectionImageUrl($externalId: String!, $imageUrl: Url!) { - updateCollectionImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionData - } - } - ${CollectionDataFragmentDoc} -`; -export type UpdateCollectionImageUrlMutationFn = Apollo.MutationFunction< - UpdateCollectionImageUrlMutation, - UpdateCollectionImageUrlMutationVariables +export type UpdateProspectAsCuratedMutationHookResult = ReturnType< + typeof useUpdateProspectAsCuratedMutation >; - -/** - * __useUpdateCollectionImageUrlMutation__ - * - * To run a mutation, you first call `useUpdateCollectionImageUrlMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionImageUrlMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionImageUrlMutation, { data, loading, error }] = useUpdateCollectionImageUrlMutation({ - * variables: { - * externalId: // value for 'externalId' - * imageUrl: // value for 'imageUrl' - * }, - * }); - */ -export function useUpdateCollectionImageUrlMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionImageUrlMutation, - UpdateCollectionImageUrlMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionImageUrlMutation, - UpdateCollectionImageUrlMutationVariables - >(UpdateCollectionImageUrlDocument, options); -} -export type UpdateCollectionImageUrlMutationHookResult = ReturnType< - typeof useUpdateCollectionImageUrlMutation +export type UpdateProspectAsCuratedMutationResult = + Apollo.MutationResult; +export type UpdateProspectAsCuratedMutationOptions = Apollo.BaseMutationOptions< + UpdateProspectAsCuratedMutation, + UpdateProspectAsCuratedMutationVariables >; -export type UpdateCollectionImageUrlMutationResult = - Apollo.MutationResult; -export type UpdateCollectionImageUrlMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionImageUrlMutation, - UpdateCollectionImageUrlMutationVariables - >; -export const UpdateCollectionPartnerDocument = gql` - mutation updateCollectionPartner( - $externalId: String! - $name: String! - $url: Url! - $blurb: Markdown! - $imageUrl: Url - ) { - updateCollectionPartner( - data: { - externalId: $externalId - name: $name - url: $url - blurb: $blurb - imageUrl: $imageUrl - } - ) { - ...CollectionPartnerData +export const UploadApprovedCorpusItemImageDocument = gql` + mutation uploadApprovedCorpusItemImage($image: Upload!) { + uploadApprovedCorpusItemImage(data: $image) { + url } } - ${CollectionPartnerDataFragmentDoc} `; -export type UpdateCollectionPartnerMutationFn = Apollo.MutationFunction< - UpdateCollectionPartnerMutation, - UpdateCollectionPartnerMutationVariables +export type UploadApprovedCorpusItemImageMutationFn = Apollo.MutationFunction< + UploadApprovedCorpusItemImageMutation, + UploadApprovedCorpusItemImageMutationVariables >; /** - * __useUpdateCollectionPartnerMutation__ + * __useUploadApprovedCorpusItemImageMutation__ * - * To run a mutation, you first call `useUpdateCollectionPartnerMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionPartnerMutation` returns a tuple that includes: + * To run a mutation, you first call `useUploadApprovedCorpusItemImageMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUploadApprovedCorpusItemImageMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionPartnerMutation, { data, loading, error }] = useUpdateCollectionPartnerMutation({ - * variables: { - * externalId: // value for 'externalId' - * name: // value for 'name' - * url: // value for 'url' - * blurb: // value for 'blurb' - * imageUrl: // value for 'imageUrl' - * }, - * }); - */ -export function useUpdateCollectionPartnerMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionPartnerMutation, - UpdateCollectionPartnerMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionPartnerMutation, - UpdateCollectionPartnerMutationVariables - >(UpdateCollectionPartnerDocument, options); -} -export type UpdateCollectionPartnerMutationHookResult = ReturnType< - typeof useUpdateCollectionPartnerMutation ->; -export type UpdateCollectionPartnerMutationResult = - Apollo.MutationResult; -export type UpdateCollectionPartnerMutationOptions = Apollo.BaseMutationOptions< - UpdateCollectionPartnerMutation, - UpdateCollectionPartnerMutationVariables ->; -export const UpdateCollectionPartnerAssociationDocument = gql` - mutation updateCollectionPartnerAssociation( - $externalId: String! - $type: CollectionPartnershipType! - $partnerExternalId: String! - $name: String - $url: Url - $imageUrl: Url - $blurb: Markdown - ) { - updateCollectionPartnerAssociation( - data: { - externalId: $externalId - type: $type - partnerExternalId: $partnerExternalId - name: $name - url: $url - imageUrl: $imageUrl - blurb: $blurb - } - ) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationDataFragmentDoc} -`; -export type UpdateCollectionPartnerAssociationMutationFn = - Apollo.MutationFunction< - UpdateCollectionPartnerAssociationMutation, - UpdateCollectionPartnerAssociationMutationVariables - >; - -/** - * __useUpdateCollectionPartnerAssociationMutation__ - * - * To run a mutation, you first call `useUpdateCollectionPartnerAssociationMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionPartnerAssociationMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionPartnerAssociationMutation, { data, loading, error }] = useUpdateCollectionPartnerAssociationMutation({ - * variables: { - * externalId: // value for 'externalId' - * type: // value for 'type' - * partnerExternalId: // value for 'partnerExternalId' - * name: // value for 'name' - * url: // value for 'url' - * imageUrl: // value for 'imageUrl' - * blurb: // value for 'blurb' - * }, - * }); - */ -export function useUpdateCollectionPartnerAssociationMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionPartnerAssociationMutation, - UpdateCollectionPartnerAssociationMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionPartnerAssociationMutation, - UpdateCollectionPartnerAssociationMutationVariables - >(UpdateCollectionPartnerAssociationDocument, options); -} -export type UpdateCollectionPartnerAssociationMutationHookResult = ReturnType< - typeof useUpdateCollectionPartnerAssociationMutation ->; -export type UpdateCollectionPartnerAssociationMutationResult = - Apollo.MutationResult; -export type UpdateCollectionPartnerAssociationMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionPartnerAssociationMutation, - UpdateCollectionPartnerAssociationMutationVariables - >; -export const UpdateCollectionPartnerAssociationImageUrlDocument = gql` - mutation updateCollectionPartnerAssociationImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionPartnerAssociationImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationDataFragmentDoc} -`; -export type UpdateCollectionPartnerAssociationImageUrlMutationFn = - Apollo.MutationFunction< - UpdateCollectionPartnerAssociationImageUrlMutation, - UpdateCollectionPartnerAssociationImageUrlMutationVariables - >; - -/** - * __useUpdateCollectionPartnerAssociationImageUrlMutation__ - * - * To run a mutation, you first call `useUpdateCollectionPartnerAssociationImageUrlMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionPartnerAssociationImageUrlMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionPartnerAssociationImageUrlMutation, { data, loading, error }] = useUpdateCollectionPartnerAssociationImageUrlMutation({ - * variables: { - * externalId: // value for 'externalId' - * imageUrl: // value for 'imageUrl' - * }, - * }); - */ -export function useUpdateCollectionPartnerAssociationImageUrlMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionPartnerAssociationImageUrlMutation, - UpdateCollectionPartnerAssociationImageUrlMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionPartnerAssociationImageUrlMutation, - UpdateCollectionPartnerAssociationImageUrlMutationVariables - >(UpdateCollectionPartnerAssociationImageUrlDocument, options); -} -export type UpdateCollectionPartnerAssociationImageUrlMutationHookResult = - ReturnType; -export type UpdateCollectionPartnerAssociationImageUrlMutationResult = - Apollo.MutationResult; -export type UpdateCollectionPartnerAssociationImageUrlMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionPartnerAssociationImageUrlMutation, - UpdateCollectionPartnerAssociationImageUrlMutationVariables - >; -export const UpdateCollectionPartnerImageUrlDocument = gql` - mutation updateCollectionPartnerImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionPartnerImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionPartnerData - } - } - ${CollectionPartnerDataFragmentDoc} -`; -export type UpdateCollectionPartnerImageUrlMutationFn = Apollo.MutationFunction< - UpdateCollectionPartnerImageUrlMutation, - UpdateCollectionPartnerImageUrlMutationVariables ->; - -/** - * __useUpdateCollectionPartnerImageUrlMutation__ - * - * To run a mutation, you first call `useUpdateCollectionPartnerImageUrlMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionPartnerImageUrlMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionPartnerImageUrlMutation, { data, loading, error }] = useUpdateCollectionPartnerImageUrlMutation({ - * variables: { - * externalId: // value for 'externalId' - * imageUrl: // value for 'imageUrl' - * }, - * }); - */ -export function useUpdateCollectionPartnerImageUrlMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionPartnerImageUrlMutation, - UpdateCollectionPartnerImageUrlMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionPartnerImageUrlMutation, - UpdateCollectionPartnerImageUrlMutationVariables - >(UpdateCollectionPartnerImageUrlDocument, options); -} -export type UpdateCollectionPartnerImageUrlMutationHookResult = ReturnType< - typeof useUpdateCollectionPartnerImageUrlMutation ->; -export type UpdateCollectionPartnerImageUrlMutationResult = - Apollo.MutationResult; -export type UpdateCollectionPartnerImageUrlMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionPartnerImageUrlMutation, - UpdateCollectionPartnerImageUrlMutationVariables - >; -export const UpdateCollectionStoryDocument = gql` - mutation updateCollectionStory( - $externalId: String! - $url: Url! - $title: String! - $excerpt: Markdown! - $imageUrl: Url! - $authors: [CollectionStoryAuthorInput!]! - $publisher: String! - $sortOrder: Int - $fromPartner: Boolean - ) { - updateCollectionStory( - data: { - externalId: $externalId - url: $url - title: $title - excerpt: $excerpt - imageUrl: $imageUrl - authors: $authors - publisher: $publisher - sortOrder: $sortOrder - fromPartner: $fromPartner - } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryDataFragmentDoc} -`; -export type UpdateCollectionStoryMutationFn = Apollo.MutationFunction< - UpdateCollectionStoryMutation, - UpdateCollectionStoryMutationVariables ->; - -/** - * __useUpdateCollectionStoryMutation__ - * - * To run a mutation, you first call `useUpdateCollectionStoryMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionStoryMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionStoryMutation, { data, loading, error }] = useUpdateCollectionStoryMutation({ - * variables: { - * externalId: // value for 'externalId' - * url: // value for 'url' - * title: // value for 'title' - * excerpt: // value for 'excerpt' - * imageUrl: // value for 'imageUrl' - * authors: // value for 'authors' - * publisher: // value for 'publisher' - * sortOrder: // value for 'sortOrder' - * fromPartner: // value for 'fromPartner' - * }, - * }); - */ -export function useUpdateCollectionStoryMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionStoryMutation, - UpdateCollectionStoryMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionStoryMutation, - UpdateCollectionStoryMutationVariables - >(UpdateCollectionStoryDocument, options); -} -export type UpdateCollectionStoryMutationHookResult = ReturnType< - typeof useUpdateCollectionStoryMutation ->; -export type UpdateCollectionStoryMutationResult = - Apollo.MutationResult; -export type UpdateCollectionStoryMutationOptions = Apollo.BaseMutationOptions< - UpdateCollectionStoryMutation, - UpdateCollectionStoryMutationVariables ->; -export const UpdateCollectionStoryImageUrlDocument = gql` - mutation updateCollectionStoryImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionStoryImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryDataFragmentDoc} -`; -export type UpdateCollectionStoryImageUrlMutationFn = Apollo.MutationFunction< - UpdateCollectionStoryImageUrlMutation, - UpdateCollectionStoryImageUrlMutationVariables ->; - -/** - * __useUpdateCollectionStoryImageUrlMutation__ - * - * To run a mutation, you first call `useUpdateCollectionStoryImageUrlMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionStoryImageUrlMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionStoryImageUrlMutation, { data, loading, error }] = useUpdateCollectionStoryImageUrlMutation({ - * variables: { - * externalId: // value for 'externalId' - * imageUrl: // value for 'imageUrl' - * }, - * }); - */ -export function useUpdateCollectionStoryImageUrlMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionStoryImageUrlMutation, - UpdateCollectionStoryImageUrlMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionStoryImageUrlMutation, - UpdateCollectionStoryImageUrlMutationVariables - >(UpdateCollectionStoryImageUrlDocument, options); -} -export type UpdateCollectionStoryImageUrlMutationHookResult = ReturnType< - typeof useUpdateCollectionStoryImageUrlMutation ->; -export type UpdateCollectionStoryImageUrlMutationResult = - Apollo.MutationResult; -export type UpdateCollectionStoryImageUrlMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionStoryImageUrlMutation, - UpdateCollectionStoryImageUrlMutationVariables - >; -export const UpdateCollectionStorySortOrderDocument = gql` - mutation updateCollectionStorySortOrder( - $externalId: String! - $sortOrder: Int! - ) { - updateCollectionStorySortOrder( - data: { externalId: $externalId, sortOrder: $sortOrder } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryDataFragmentDoc} -`; -export type UpdateCollectionStorySortOrderMutationFn = Apollo.MutationFunction< - UpdateCollectionStorySortOrderMutation, - UpdateCollectionStorySortOrderMutationVariables ->; - -/** - * __useUpdateCollectionStorySortOrderMutation__ - * - * To run a mutation, you first call `useUpdateCollectionStorySortOrderMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCollectionStorySortOrderMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCollectionStorySortOrderMutation, { data, loading, error }] = useUpdateCollectionStorySortOrderMutation({ - * variables: { - * externalId: // value for 'externalId' - * sortOrder: // value for 'sortOrder' - * }, - * }); - */ -export function useUpdateCollectionStorySortOrderMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCollectionStorySortOrderMutation, - UpdateCollectionStorySortOrderMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCollectionStorySortOrderMutation, - UpdateCollectionStorySortOrderMutationVariables - >(UpdateCollectionStorySortOrderDocument, options); -} -export type UpdateCollectionStorySortOrderMutationHookResult = ReturnType< - typeof useUpdateCollectionStorySortOrderMutation ->; -export type UpdateCollectionStorySortOrderMutationResult = - Apollo.MutationResult; -export type UpdateCollectionStorySortOrderMutationOptions = - Apollo.BaseMutationOptions< - UpdateCollectionStorySortOrderMutation, - UpdateCollectionStorySortOrderMutationVariables - >; -export const UpdateCustomSectionDocument = gql` - mutation updateCustomSection($data: UpdateCustomSectionInput!) { - updateCustomSection(data: $data) { - ...BaseSectionData - } - } - ${BaseSectionDataFragmentDoc} -`; -export type UpdateCustomSectionMutationFn = Apollo.MutationFunction< - UpdateCustomSectionMutation, - UpdateCustomSectionMutationVariables ->; - -/** - * __useUpdateCustomSectionMutation__ - * - * To run a mutation, you first call `useUpdateCustomSectionMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCustomSectionMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCustomSectionMutation, { data, loading, error }] = useUpdateCustomSectionMutation({ - * variables: { - * data: // value for 'data' - * }, - * }); - */ -export function useUpdateCustomSectionMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateCustomSectionMutation, - UpdateCustomSectionMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateCustomSectionMutation, - UpdateCustomSectionMutationVariables - >(UpdateCustomSectionDocument, options); -} -export type UpdateCustomSectionMutationHookResult = ReturnType< - typeof useUpdateCustomSectionMutation ->; -export type UpdateCustomSectionMutationResult = - Apollo.MutationResult; -export type UpdateCustomSectionMutationOptions = Apollo.BaseMutationOptions< - UpdateCustomSectionMutation, - UpdateCustomSectionMutationVariables ->; -export const UpdateLabelDocument = gql` - mutation updateLabel($data: UpdateLabelInput!) { - updateLabel(data: $data) { - externalId - name - } - } -`; -export type UpdateLabelMutationFn = Apollo.MutationFunction< - UpdateLabelMutation, - UpdateLabelMutationVariables ->; - -/** - * __useUpdateLabelMutation__ - * - * To run a mutation, you first call `useUpdateLabelMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateLabelMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateLabelMutation, { data, loading, error }] = useUpdateLabelMutation({ - * variables: { - * data: // value for 'data' - * }, - * }); - */ -export function useUpdateLabelMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateLabelMutation, - UpdateLabelMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation( - UpdateLabelDocument, - options, - ); -} -export type UpdateLabelMutationHookResult = ReturnType< - typeof useUpdateLabelMutation ->; -export type UpdateLabelMutationResult = - Apollo.MutationResult; -export type UpdateLabelMutationOptions = Apollo.BaseMutationOptions< - UpdateLabelMutation, - UpdateLabelMutationVariables ->; -export const UpdateProspectAsCuratedDocument = gql` - mutation updateProspectAsCurated( - $id: ID! - $historyFilter: ApprovedCorpusItemScheduledSurfaceHistoryFilters - ) { - updateProspectAsCurated(id: $id) { - ...ProspectDataWithCorpusItems - } - } - ${ProspectDataWithCorpusItemsFragmentDoc} -`; -export type UpdateProspectAsCuratedMutationFn = Apollo.MutationFunction< - UpdateProspectAsCuratedMutation, - UpdateProspectAsCuratedMutationVariables ->; - -/** - * __useUpdateProspectAsCuratedMutation__ - * - * To run a mutation, you first call `useUpdateProspectAsCuratedMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateProspectAsCuratedMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateProspectAsCuratedMutation, { data, loading, error }] = useUpdateProspectAsCuratedMutation({ - * variables: { - * id: // value for 'id' - * historyFilter: // value for 'historyFilter' - * }, - * }); - */ -export function useUpdateProspectAsCuratedMutation( - baseOptions?: Apollo.MutationHookOptions< - UpdateProspectAsCuratedMutation, - UpdateProspectAsCuratedMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UpdateProspectAsCuratedMutation, - UpdateProspectAsCuratedMutationVariables - >(UpdateProspectAsCuratedDocument, options); -} -export type UpdateProspectAsCuratedMutationHookResult = ReturnType< - typeof useUpdateProspectAsCuratedMutation ->; -export type UpdateProspectAsCuratedMutationResult = - Apollo.MutationResult; -export type UpdateProspectAsCuratedMutationOptions = Apollo.BaseMutationOptions< - UpdateProspectAsCuratedMutation, - UpdateProspectAsCuratedMutationVariables ->; -export const UploadApprovedCorpusItemImageDocument = gql` - mutation uploadApprovedCorpusItemImage($image: Upload!) { - uploadApprovedCorpusItemImage(data: $image) { - url - } - } -`; -export type UploadApprovedCorpusItemImageMutationFn = Apollo.MutationFunction< - UploadApprovedCorpusItemImageMutation, - UploadApprovedCorpusItemImageMutationVariables ->; - -/** - * __useUploadApprovedCorpusItemImageMutation__ - * - * To run a mutation, you first call `useUploadApprovedCorpusItemImageMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUploadApprovedCorpusItemImageMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [uploadApprovedCorpusItemImageMutation, { data, loading, error }] = useUploadApprovedCorpusItemImageMutation({ - * variables: { - * image: // value for 'image' - * }, - * }); - */ -export function useUploadApprovedCorpusItemImageMutation( - baseOptions?: Apollo.MutationHookOptions< - UploadApprovedCorpusItemImageMutation, - UploadApprovedCorpusItemImageMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - UploadApprovedCorpusItemImageMutation, - UploadApprovedCorpusItemImageMutationVariables - >(UploadApprovedCorpusItemImageDocument, options); -} -export type UploadApprovedCorpusItemImageMutationHookResult = ReturnType< - typeof useUploadApprovedCorpusItemImageMutation ->; -export type UploadApprovedCorpusItemImageMutationResult = - Apollo.MutationResult; -export type UploadApprovedCorpusItemImageMutationOptions = - Apollo.BaseMutationOptions< - UploadApprovedCorpusItemImageMutation, - UploadApprovedCorpusItemImageMutationVariables - >; -export const ApprovedCorpusItemByExternalIdDocument = gql` - query approvedCorpusItemByExternalId( - $externalId: ID! - $historyFilter: ApprovedCorpusItemScheduledSurfaceHistoryFilters - ) { - approvedCorpusItemByExternalId(externalId: $externalId) { - ...CuratedItemDataWithHistory - } - } - ${CuratedItemDataWithHistoryFragmentDoc} -`; - -/** - * __useApprovedCorpusItemByExternalIdQuery__ - * - * To run a query within a React component, call `useApprovedCorpusItemByExternalIdQuery` and pass it any options that fit your needs. - * When your component renders, `useApprovedCorpusItemByExternalIdQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useApprovedCorpusItemByExternalIdQuery({ - * variables: { - * externalId: // value for 'externalId' - * historyFilter: // value for 'historyFilter' - * }, - * }); - */ -export function useApprovedCorpusItemByExternalIdQuery( - baseOptions: Apollo.QueryHookOptions< - ApprovedCorpusItemByExternalIdQuery, - ApprovedCorpusItemByExternalIdQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - ApprovedCorpusItemByExternalIdQuery, - ApprovedCorpusItemByExternalIdQueryVariables - >(ApprovedCorpusItemByExternalIdDocument, options); -} -export function useApprovedCorpusItemByExternalIdLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - ApprovedCorpusItemByExternalIdQuery, - ApprovedCorpusItemByExternalIdQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - ApprovedCorpusItemByExternalIdQuery, - ApprovedCorpusItemByExternalIdQueryVariables - >(ApprovedCorpusItemByExternalIdDocument, options); -} -export type ApprovedCorpusItemByExternalIdQueryHookResult = ReturnType< - typeof useApprovedCorpusItemByExternalIdQuery ->; -export type ApprovedCorpusItemByExternalIdLazyQueryHookResult = ReturnType< - typeof useApprovedCorpusItemByExternalIdLazyQuery ->; -export type ApprovedCorpusItemByExternalIdQueryResult = Apollo.QueryResult< - ApprovedCorpusItemByExternalIdQuery, - ApprovedCorpusItemByExternalIdQueryVariables ->; -export const GetApprovedItemByUrlDocument = gql` - query getApprovedItemByUrl($url: String!) { - getApprovedCorpusItemByUrl(url: $url) { - ...CuratedItemData - } - } - ${CuratedItemDataFragmentDoc} -`; - -/** - * __useGetApprovedItemByUrlQuery__ - * - * To run a query within a React component, call `useGetApprovedItemByUrlQuery` and pass it any options that fit your needs. - * When your component renders, `useGetApprovedItemByUrlQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetApprovedItemByUrlQuery({ - * variables: { - * url: // value for 'url' - * }, - * }); - */ -export function useGetApprovedItemByUrlQuery( - baseOptions: Apollo.QueryHookOptions< - GetApprovedItemByUrlQuery, - GetApprovedItemByUrlQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetApprovedItemByUrlQuery, - GetApprovedItemByUrlQueryVariables - >(GetApprovedItemByUrlDocument, options); -} -export function useGetApprovedItemByUrlLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetApprovedItemByUrlQuery, - GetApprovedItemByUrlQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetApprovedItemByUrlQuery, - GetApprovedItemByUrlQueryVariables - >(GetApprovedItemByUrlDocument, options); -} -export type GetApprovedItemByUrlQueryHookResult = ReturnType< - typeof useGetApprovedItemByUrlQuery ->; -export type GetApprovedItemByUrlLazyQueryHookResult = ReturnType< - typeof useGetApprovedItemByUrlLazyQuery ->; -export type GetApprovedItemByUrlQueryResult = Apollo.QueryResult< - GetApprovedItemByUrlQuery, - GetApprovedItemByUrlQueryVariables ->; -export const GetApprovedItemsDocument = gql` - query getApprovedItems( - $filters: ApprovedCorpusItemFilter - $pagination: PaginationInput - ) { - getApprovedCorpusItems(filters: $filters, pagination: $pagination) { - totalCount - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - edges { - cursor - node { - ...CuratedItemData - } - } - } - } - ${CuratedItemDataFragmentDoc} -`; - -/** - * __useGetApprovedItemsQuery__ - * - * To run a query within a React component, call `useGetApprovedItemsQuery` and pass it any options that fit your needs. - * When your component renders, `useGetApprovedItemsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetApprovedItemsQuery({ - * variables: { - * filters: // value for 'filters' - * pagination: // value for 'pagination' - * }, - * }); - */ -export function useGetApprovedItemsQuery( - baseOptions?: Apollo.QueryHookOptions< - GetApprovedItemsQuery, - GetApprovedItemsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery( - GetApprovedItemsDocument, - options, - ); -} -export function useGetApprovedItemsLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetApprovedItemsQuery, - GetApprovedItemsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetApprovedItemsQuery, - GetApprovedItemsQueryVariables - >(GetApprovedItemsDocument, options); -} -export type GetApprovedItemsQueryHookResult = ReturnType< - typeof useGetApprovedItemsQuery ->; -export type GetApprovedItemsLazyQueryHookResult = ReturnType< - typeof useGetApprovedItemsLazyQuery ->; -export type GetApprovedItemsQueryResult = Apollo.QueryResult< - GetApprovedItemsQuery, - GetApprovedItemsQueryVariables ->; -export const GetAuthorByIdDocument = gql` - query getAuthorById($id: String!) { - getCollectionAuthor(externalId: $id) { - ...CollectionAuthorData - } - } - ${CollectionAuthorDataFragmentDoc} -`; - -/** - * __useGetAuthorByIdQuery__ - * - * To run a query within a React component, call `useGetAuthorByIdQuery` and pass it any options that fit your needs. - * When your component renders, `useGetAuthorByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetAuthorByIdQuery({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useGetAuthorByIdQuery( - baseOptions: Apollo.QueryHookOptions< - GetAuthorByIdQuery, - GetAuthorByIdQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery( - GetAuthorByIdDocument, - options, - ); -} -export function useGetAuthorByIdLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetAuthorByIdQuery, - GetAuthorByIdQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery( - GetAuthorByIdDocument, - options, - ); -} -export type GetAuthorByIdQueryHookResult = ReturnType< - typeof useGetAuthorByIdQuery ->; -export type GetAuthorByIdLazyQueryHookResult = ReturnType< - typeof useGetAuthorByIdLazyQuery ->; -export type GetAuthorByIdQueryResult = Apollo.QueryResult< - GetAuthorByIdQuery, - GetAuthorByIdQueryVariables ->; -export const GetAuthorsDocument = gql` - query getAuthors($page: Int, $perPage: Int) { - getCollectionAuthors(page: $page, perPage: $perPage) { - authors { - ...CollectionAuthorData - } - pagination { - currentPage - totalPages - totalResults - } - } - } - ${CollectionAuthorDataFragmentDoc} -`; - -/** - * __useGetAuthorsQuery__ - * - * To run a query within a React component, call `useGetAuthorsQuery` and pass it any options that fit your needs. - * When your component renders, `useGetAuthorsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetAuthorsQuery({ - * variables: { - * page: // value for 'page' - * perPage: // value for 'perPage' - * }, - * }); - */ -export function useGetAuthorsQuery( - baseOptions?: Apollo.QueryHookOptions< - GetAuthorsQuery, - GetAuthorsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery( - GetAuthorsDocument, - options, - ); -} -export function useGetAuthorsLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetAuthorsQuery, - GetAuthorsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery( - GetAuthorsDocument, - options, - ); -} -export type GetAuthorsQueryHookResult = ReturnType; -export type GetAuthorsLazyQueryHookResult = ReturnType< - typeof useGetAuthorsLazyQuery ->; -export type GetAuthorsQueryResult = Apollo.QueryResult< - GetAuthorsQuery, - GetAuthorsQueryVariables ->; -export const GetCollectionByExternalIdDocument = gql` - query getCollectionByExternalId($externalId: String!) { - getCollection(externalId: $externalId) { - ...CollectionData - } - } - ${CollectionDataFragmentDoc} -`; - -/** - * __useGetCollectionByExternalIdQuery__ - * - * To run a query within a React component, call `useGetCollectionByExternalIdQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCollectionByExternalIdQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetCollectionByExternalIdQuery({ - * variables: { - * externalId: // value for 'externalId' - * }, - * }); - */ -export function useGetCollectionByExternalIdQuery( - baseOptions: Apollo.QueryHookOptions< - GetCollectionByExternalIdQuery, - GetCollectionByExternalIdQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetCollectionByExternalIdQuery, - GetCollectionByExternalIdQueryVariables - >(GetCollectionByExternalIdDocument, options); -} -export function useGetCollectionByExternalIdLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetCollectionByExternalIdQuery, - GetCollectionByExternalIdQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetCollectionByExternalIdQuery, - GetCollectionByExternalIdQueryVariables - >(GetCollectionByExternalIdDocument, options); -} -export type GetCollectionByExternalIdQueryHookResult = ReturnType< - typeof useGetCollectionByExternalIdQuery ->; -export type GetCollectionByExternalIdLazyQueryHookResult = ReturnType< - typeof useGetCollectionByExternalIdLazyQuery ->; -export type GetCollectionByExternalIdQueryResult = Apollo.QueryResult< - GetCollectionByExternalIdQuery, - GetCollectionByExternalIdQueryVariables ->; -export const GetCollectionPartnerDocument = gql` - query getCollectionPartner($id: String!) { - getCollectionPartner(externalId: $id) { - ...CollectionPartnerData - } - } - ${CollectionPartnerDataFragmentDoc} -`; - -/** - * __useGetCollectionPartnerQuery__ - * - * To run a query within a React component, call `useGetCollectionPartnerQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCollectionPartnerQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetCollectionPartnerQuery({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useGetCollectionPartnerQuery( - baseOptions: Apollo.QueryHookOptions< - GetCollectionPartnerQuery, - GetCollectionPartnerQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetCollectionPartnerQuery, - GetCollectionPartnerQueryVariables - >(GetCollectionPartnerDocument, options); -} -export function useGetCollectionPartnerLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetCollectionPartnerQuery, - GetCollectionPartnerQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetCollectionPartnerQuery, - GetCollectionPartnerQueryVariables - >(GetCollectionPartnerDocument, options); -} -export type GetCollectionPartnerQueryHookResult = ReturnType< - typeof useGetCollectionPartnerQuery ->; -export type GetCollectionPartnerLazyQueryHookResult = ReturnType< - typeof useGetCollectionPartnerLazyQuery ->; -export type GetCollectionPartnerQueryResult = Apollo.QueryResult< - GetCollectionPartnerQuery, - GetCollectionPartnerQueryVariables ->; -export const GetCollectionPartnerAssociationDocument = gql` - query getCollectionPartnerAssociation($externalId: String!) { - getCollectionPartnerAssociationForCollection(externalId: $externalId) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationDataFragmentDoc} -`; - -/** - * __useGetCollectionPartnerAssociationQuery__ - * - * To run a query within a React component, call `useGetCollectionPartnerAssociationQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCollectionPartnerAssociationQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetCollectionPartnerAssociationQuery({ - * variables: { - * externalId: // value for 'externalId' - * }, - * }); - */ -export function useGetCollectionPartnerAssociationQuery( - baseOptions: Apollo.QueryHookOptions< - GetCollectionPartnerAssociationQuery, - GetCollectionPartnerAssociationQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetCollectionPartnerAssociationQuery, - GetCollectionPartnerAssociationQueryVariables - >(GetCollectionPartnerAssociationDocument, options); -} -export function useGetCollectionPartnerAssociationLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetCollectionPartnerAssociationQuery, - GetCollectionPartnerAssociationQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetCollectionPartnerAssociationQuery, - GetCollectionPartnerAssociationQueryVariables - >(GetCollectionPartnerAssociationDocument, options); -} -export type GetCollectionPartnerAssociationQueryHookResult = ReturnType< - typeof useGetCollectionPartnerAssociationQuery ->; -export type GetCollectionPartnerAssociationLazyQueryHookResult = ReturnType< - typeof useGetCollectionPartnerAssociationLazyQuery ->; -export type GetCollectionPartnerAssociationQueryResult = Apollo.QueryResult< - GetCollectionPartnerAssociationQuery, - GetCollectionPartnerAssociationQueryVariables ->; -export const GetCollectionPartnersDocument = gql` - query getCollectionPartners($page: Int, $perPage: Int) { - getCollectionPartners(page: $page, perPage: $perPage) { - partners { - ...CollectionPartnerData - } - pagination { - currentPage - totalPages - totalResults - } - } - } - ${CollectionPartnerDataFragmentDoc} -`; - -/** - * __useGetCollectionPartnersQuery__ - * - * To run a query within a React component, call `useGetCollectionPartnersQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCollectionPartnersQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const { data, loading, error } = useGetCollectionPartnersQuery({ + * const [uploadApprovedCorpusItemImageMutation, { data, loading, error }] = useUploadApprovedCorpusItemImageMutation({ * variables: { - * page: // value for 'page' - * perPage: // value for 'perPage' + * image: // value for 'image' * }, * }); */ -export function useGetCollectionPartnersQuery( - baseOptions?: Apollo.QueryHookOptions< - GetCollectionPartnersQuery, - GetCollectionPartnersQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetCollectionPartnersQuery, - GetCollectionPartnersQueryVariables - >(GetCollectionPartnersDocument, options); -} -export function useGetCollectionPartnersLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetCollectionPartnersQuery, - GetCollectionPartnersQueryVariables +export function useUploadApprovedCorpusItemImageMutation( + baseOptions?: Apollo.MutationHookOptions< + UploadApprovedCorpusItemImageMutation, + UploadApprovedCorpusItemImageMutationVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetCollectionPartnersQuery, - GetCollectionPartnersQueryVariables - >(GetCollectionPartnersDocument, options); + return Apollo.useMutation< + UploadApprovedCorpusItemImageMutation, + UploadApprovedCorpusItemImageMutationVariables + >(UploadApprovedCorpusItemImageDocument, options); } -export type GetCollectionPartnersQueryHookResult = ReturnType< - typeof useGetCollectionPartnersQuery ->; -export type GetCollectionPartnersLazyQueryHookResult = ReturnType< - typeof useGetCollectionPartnersLazyQuery ->; -export type GetCollectionPartnersQueryResult = Apollo.QueryResult< - GetCollectionPartnersQuery, - GetCollectionPartnersQueryVariables +export type UploadApprovedCorpusItemImageMutationHookResult = ReturnType< + typeof useUploadApprovedCorpusItemImageMutation >; -export const GetCollectionStoriesDocument = gql` - query getCollectionStories($id: String!) { - getCollection(externalId: $id) { - externalId - stories { - ...CollectionStoryData - } +export type UploadApprovedCorpusItemImageMutationResult = + Apollo.MutationResult; +export type UploadApprovedCorpusItemImageMutationOptions = + Apollo.BaseMutationOptions< + UploadApprovedCorpusItemImageMutation, + UploadApprovedCorpusItemImageMutationVariables + >; +export const ApprovedCorpusItemByExternalIdDocument = gql` + query approvedCorpusItemByExternalId( + $externalId: ID! + $historyFilter: ApprovedCorpusItemScheduledSurfaceHistoryFilters + ) { + approvedCorpusItemByExternalId(externalId: $externalId) { + ...CuratedItemDataWithHistory } } - ${CollectionStoryDataFragmentDoc} + ${CuratedItemDataWithHistoryFragmentDoc} `; /** - * __useGetCollectionStoriesQuery__ + * __useApprovedCorpusItemByExternalIdQuery__ * - * To run a query within a React component, call `useGetCollectionStoriesQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCollectionStoriesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useApprovedCorpusItemByExternalIdQuery` and pass it any options that fit your needs. + * When your component renders, `useApprovedCorpusItemByExternalIdQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useGetCollectionStoriesQuery({ + * const { data, loading, error } = useApprovedCorpusItemByExternalIdQuery({ * variables: { - * id: // value for 'id' + * externalId: // value for 'externalId' + * historyFilter: // value for 'historyFilter' * }, * }); */ -export function useGetCollectionStoriesQuery( +export function useApprovedCorpusItemByExternalIdQuery( baseOptions: Apollo.QueryHookOptions< - GetCollectionStoriesQuery, - GetCollectionStoriesQueryVariables + ApprovedCorpusItemByExternalIdQuery, + ApprovedCorpusItemByExternalIdQueryVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; return Apollo.useQuery< - GetCollectionStoriesQuery, - GetCollectionStoriesQueryVariables - >(GetCollectionStoriesDocument, options); + ApprovedCorpusItemByExternalIdQuery, + ApprovedCorpusItemByExternalIdQueryVariables + >(ApprovedCorpusItemByExternalIdDocument, options); } -export function useGetCollectionStoriesLazyQuery( +export function useApprovedCorpusItemByExternalIdLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< - GetCollectionStoriesQuery, - GetCollectionStoriesQueryVariables + ApprovedCorpusItemByExternalIdQuery, + ApprovedCorpusItemByExternalIdQueryVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; return Apollo.useLazyQuery< - GetCollectionStoriesQuery, - GetCollectionStoriesQueryVariables - >(GetCollectionStoriesDocument, options); + ApprovedCorpusItemByExternalIdQuery, + ApprovedCorpusItemByExternalIdQueryVariables + >(ApprovedCorpusItemByExternalIdDocument, options); } -export type GetCollectionStoriesQueryHookResult = ReturnType< - typeof useGetCollectionStoriesQuery +export type ApprovedCorpusItemByExternalIdQueryHookResult = ReturnType< + typeof useApprovedCorpusItemByExternalIdQuery >; -export type GetCollectionStoriesLazyQueryHookResult = ReturnType< - typeof useGetCollectionStoriesLazyQuery +export type ApprovedCorpusItemByExternalIdLazyQueryHookResult = ReturnType< + typeof useApprovedCorpusItemByExternalIdLazyQuery >; -export type GetCollectionStoriesQueryResult = Apollo.QueryResult< - GetCollectionStoriesQuery, - GetCollectionStoriesQueryVariables +export type ApprovedCorpusItemByExternalIdQueryResult = Apollo.QueryResult< + ApprovedCorpusItemByExternalIdQuery, + ApprovedCorpusItemByExternalIdQueryVariables >; -export const GetCollectionsDocument = gql` - query getCollections( - $page: Int! - $perPage: Int! - $status: CollectionStatus! - ) { - searchCollections( - filters: { status: $status } - page: $page - perPage: $perPage - ) { - collections { - ...CollectionData - } - pagination { - currentPage - totalPages - totalResults - } +export const GetApprovedItemByUrlDocument = gql` + query getApprovedItemByUrl($url: String!) { + getApprovedCorpusItemByUrl(url: $url) { + ...CuratedItemData } } - ${CollectionDataFragmentDoc} + ${CuratedItemDataFragmentDoc} `; /** - * __useGetCollectionsQuery__ + * __useGetApprovedItemByUrlQuery__ * - * To run a query within a React component, call `useGetCollectionsQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCollectionsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useGetApprovedItemByUrlQuery` and pass it any options that fit your needs. + * When your component renders, `useGetApprovedItemByUrlQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useGetCollectionsQuery({ + * const { data, loading, error } = useGetApprovedItemByUrlQuery({ * variables: { - * page: // value for 'page' - * perPage: // value for 'perPage' - * status: // value for 'status' + * url: // value for 'url' * }, * }); */ -export function useGetCollectionsQuery( +export function useGetApprovedItemByUrlQuery( baseOptions: Apollo.QueryHookOptions< - GetCollectionsQuery, - GetCollectionsQueryVariables + GetApprovedItemByUrlQuery, + GetApprovedItemByUrlQueryVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery( - GetCollectionsDocument, - options, - ); + return Apollo.useQuery< + GetApprovedItemByUrlQuery, + GetApprovedItemByUrlQueryVariables + >(GetApprovedItemByUrlDocument, options); } -export function useGetCollectionsLazyQuery( +export function useGetApprovedItemByUrlLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< - GetCollectionsQuery, - GetCollectionsQueryVariables + GetApprovedItemByUrlQuery, + GetApprovedItemByUrlQueryVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery( - GetCollectionsDocument, - options, - ); + return Apollo.useLazyQuery< + GetApprovedItemByUrlQuery, + GetApprovedItemByUrlQueryVariables + >(GetApprovedItemByUrlDocument, options); } -export type GetCollectionsQueryHookResult = ReturnType< - typeof useGetCollectionsQuery +export type GetApprovedItemByUrlQueryHookResult = ReturnType< + typeof useGetApprovedItemByUrlQuery >; -export type GetCollectionsLazyQueryHookResult = ReturnType< - typeof useGetCollectionsLazyQuery +export type GetApprovedItemByUrlLazyQueryHookResult = ReturnType< + typeof useGetApprovedItemByUrlLazyQuery >; -export type GetCollectionsQueryResult = Apollo.QueryResult< - GetCollectionsQuery, - GetCollectionsQueryVariables +export type GetApprovedItemByUrlQueryResult = Apollo.QueryResult< + GetApprovedItemByUrlQuery, + GetApprovedItemByUrlQueryVariables >; -export const GetInitialCollectionFormDataDocument = gql` - query getInitialCollectionFormData($page: Int, $perPage: Int) { - getCollectionAuthors(page: $page, perPage: $perPage) { - authors { - ...CollectionAuthorData +export const GetApprovedItemsDocument = gql` + query getApprovedItems( + $filters: ApprovedCorpusItemFilter + $pagination: PaginationInput + ) { + getApprovedCorpusItems(filters: $filters, pagination: $pagination) { + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } - } - labels { - externalId - name - } - getLanguages - getCurationCategories { - externalId - name - slug - } - getIABCategories { - externalId - name - slug - children { - externalId - name - slug + edges { + cursor + node { + ...CuratedItemData + } } } } - ${CollectionAuthorDataFragmentDoc} + ${CuratedItemDataFragmentDoc} `; /** - * __useGetInitialCollectionFormDataQuery__ + * __useGetApprovedItemsQuery__ * - * To run a query within a React component, call `useGetInitialCollectionFormDataQuery` and pass it any options that fit your needs. - * When your component renders, `useGetInitialCollectionFormDataQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useGetApprovedItemsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetApprovedItemsQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useGetInitialCollectionFormDataQuery({ + * const { data, loading, error } = useGetApprovedItemsQuery({ * variables: { - * page: // value for 'page' - * perPage: // value for 'perPage' + * filters: // value for 'filters' + * pagination: // value for 'pagination' * }, * }); */ -export function useGetInitialCollectionFormDataQuery( +export function useGetApprovedItemsQuery( baseOptions?: Apollo.QueryHookOptions< - GetInitialCollectionFormDataQuery, - GetInitialCollectionFormDataQueryVariables + GetApprovedItemsQuery, + GetApprovedItemsQueryVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetInitialCollectionFormDataQuery, - GetInitialCollectionFormDataQueryVariables - >(GetInitialCollectionFormDataDocument, options); + return Apollo.useQuery( + GetApprovedItemsDocument, + options, + ); } -export function useGetInitialCollectionFormDataLazyQuery( +export function useGetApprovedItemsLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< - GetInitialCollectionFormDataQuery, - GetInitialCollectionFormDataQueryVariables + GetApprovedItemsQuery, + GetApprovedItemsQueryVariables >, ) { const options = { ...defaultOptions, ...baseOptions }; return Apollo.useLazyQuery< - GetInitialCollectionFormDataQuery, - GetInitialCollectionFormDataQueryVariables - >(GetInitialCollectionFormDataDocument, options); + GetApprovedItemsQuery, + GetApprovedItemsQueryVariables + >(GetApprovedItemsDocument, options); } -export type GetInitialCollectionFormDataQueryHookResult = ReturnType< - typeof useGetInitialCollectionFormDataQuery +export type GetApprovedItemsQueryHookResult = ReturnType< + typeof useGetApprovedItemsQuery >; -export type GetInitialCollectionFormDataLazyQueryHookResult = ReturnType< - typeof useGetInitialCollectionFormDataLazyQuery +export type GetApprovedItemsLazyQueryHookResult = ReturnType< + typeof useGetApprovedItemsLazyQuery >; -export type GetInitialCollectionFormDataQueryResult = Apollo.QueryResult< - GetInitialCollectionFormDataQuery, - GetInitialCollectionFormDataQueryVariables +export type GetApprovedItemsQueryResult = Apollo.QueryResult< + GetApprovedItemsQuery, + GetApprovedItemsQueryVariables >; export const GetOpenGraphFieldsDocument = gql` query getOpenGraphFields($url: Url!) { @@ -9199,79 +6108,6 @@ export type GetScheduledSurfacesForUserQueryResult = Apollo.QueryResult< GetScheduledSurfacesForUserQuery, GetScheduledSurfacesForUserQueryVariables >; -export const SearchCollectionsDocument = gql` - query searchCollections( - $filters: SearchCollectionsFilters! - $page: Int - $perPage: Int - ) { - searchCollections(filters: $filters, page: $page, perPage: $perPage) { - collections { - ...CollectionData - } - pagination { - currentPage - totalPages - totalResults - perPage - } - } - } - ${CollectionDataFragmentDoc} -`; - -/** - * __useSearchCollectionsQuery__ - * - * To run a query within a React component, call `useSearchCollectionsQuery` and pass it any options that fit your needs. - * When your component renders, `useSearchCollectionsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useSearchCollectionsQuery({ - * variables: { - * filters: // value for 'filters' - * page: // value for 'page' - * perPage: // value for 'perPage' - * }, - * }); - */ -export function useSearchCollectionsQuery( - baseOptions: Apollo.QueryHookOptions< - SearchCollectionsQuery, - SearchCollectionsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - SearchCollectionsQuery, - SearchCollectionsQueryVariables - >(SearchCollectionsDocument, options); -} -export function useSearchCollectionsLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - SearchCollectionsQuery, - SearchCollectionsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - SearchCollectionsQuery, - SearchCollectionsQueryVariables - >(SearchCollectionsDocument, options); -} -export type SearchCollectionsQueryHookResult = ReturnType< - typeof useSearchCollectionsQuery ->; -export type SearchCollectionsLazyQueryHookResult = ReturnType< - typeof useSearchCollectionsLazyQuery ->; -export type SearchCollectionsQueryResult = Apollo.QueryResult< - SearchCollectionsQuery, - SearchCollectionsQueryVariables ->; export const GetSectionsWithSectionItemsDocument = gql` query getSectionsWithSectionItems( $scheduledSurfaceGuid: ID! @@ -9338,78 +6174,6 @@ export type GetSectionsWithSectionItemsQueryResult = Apollo.QueryResult< GetSectionsWithSectionItemsQuery, GetSectionsWithSectionItemsQueryVariables >; -export const GetStoryFromParserDocument = gql` - query getStoryFromParser($url: String!) { - getItemByUrl(url: $url) { - resolvedUrl - title - excerpt - topImageUrl - images { - src - width - height - } - authors { - name - } - domainMetadata { - name - } - } - } -`; - -/** - * __useGetStoryFromParserQuery__ - * - * To run a query within a React component, call `useGetStoryFromParserQuery` and pass it any options that fit your needs. - * When your component renders, `useGetStoryFromParserQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetStoryFromParserQuery({ - * variables: { - * url: // value for 'url' - * }, - * }); - */ -export function useGetStoryFromParserQuery( - baseOptions: Apollo.QueryHookOptions< - GetStoryFromParserQuery, - GetStoryFromParserQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GetStoryFromParserQuery, - GetStoryFromParserQueryVariables - >(GetStoryFromParserDocument, options); -} -export function useGetStoryFromParserLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GetStoryFromParserQuery, - GetStoryFromParserQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GetStoryFromParserQuery, - GetStoryFromParserQueryVariables - >(GetStoryFromParserDocument, options); -} -export type GetStoryFromParserQueryHookResult = ReturnType< - typeof useGetStoryFromParserQuery ->; -export type GetStoryFromParserLazyQueryHookResult = ReturnType< - typeof useGetStoryFromParserLazyQuery ->; -export type GetStoryFromParserQueryResult = Apollo.QueryResult< - GetStoryFromParserQuery, - GetStoryFromParserQueryVariables ->; export const GetUrlMetadataDocument = gql` query getUrlMetadata($url: String!) { getUrlMetadata(url: $url) { @@ -9469,54 +6233,6 @@ export type GetUrlMetadataQueryResult = Apollo.QueryResult< GetUrlMetadataQuery, GetUrlMetadataQueryVariables >; -export const LabelsDocument = gql` - query labels { - labels { - externalId - name - } - } -`; - -/** - * __useLabelsQuery__ - * - * To run a query within a React component, call `useLabelsQuery` and pass it any options that fit your needs. - * When your component renders, `useLabelsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useLabelsQuery({ - * variables: { - * }, - * }); - */ -export function useLabelsQuery( - baseOptions?: Apollo.QueryHookOptions, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery( - LabelsDocument, - options, - ); -} -export function useLabelsLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery( - LabelsDocument, - options, - ); -} -export type LabelsQueryHookResult = ReturnType; -export type LabelsLazyQueryHookResult = ReturnType; -export type LabelsQueryResult = Apollo.QueryResult< - LabelsQuery, - LabelsQueryVariables ->; export const SearchShareableListDocument = gql` query searchShareableList($externalId: ID!) { searchShareableList(externalId: $externalId) { diff --git a/src/api/helpers/mergePaginatedDataInCache.ts b/src/api/helpers/mergePaginatedDataInCache.ts deleted file mode 100644 index cc60c4d52..000000000 --- a/src/api/helpers/mergePaginatedDataInCache.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { SafeReadonly } from '@apollo/client/cache/core/types/common'; -import { FieldFunctionOptions } from '@apollo/client/cache/inmemory/policies'; - -/** - * A helper function that merges paginated data from a query into a single list - * in Apollo Client cache. - * - * @param existing - * @param incoming - * @param options - * @param dataPropName - */ -export const mergePaginatedDataInCache = ( - existing: SafeReadonly, - incoming: SafeReadonly, - options: FieldFunctionOptions, - dataPropName: string, -) => { - const { args } = options; - - if (!args || !existing) { - return incoming; - } else { - // We only need to merge the data part of the result, i.e. 'authors' or 'collections' - const mergedData = existing ? existing[dataPropName].slice(0) : []; - - // Insert the incoming elements in the right places, according to args. - const offset = (args.page - 1) * args.perPage; - const end = offset + Math.min(args.perPage, incoming[dataPropName].length); - - for (let i = offset; i < end; ++i) { - mergedData[i] = incoming[dataPropName][i - offset]; - } - - // Return the merged list and the updated pagination values - return { - [dataPropName]: mergedData, - pagination: incoming.pagination, - }; - } -}; diff --git a/src/api/helpers/readPaginatedDataFromCache.ts b/src/api/helpers/readPaginatedDataFromCache.ts deleted file mode 100644 index 2f1fdd965..000000000 --- a/src/api/helpers/readPaginatedDataFromCache.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { SafeReadonly } from '@apollo/client/cache/core/types/common'; -import { FieldFunctionOptions } from '@apollo/client/cache/inmemory/policies'; - -/** - * A helper function that returns the right slice of paginated data for a query. - * - * @param existing - * @param options - * @param dataPropName - */ -export const readPaginatedDataFromCache = ( - existing: SafeReadonly, - options: FieldFunctionOptions, - dataPropName: string, -) => { - const { args } = options; - - if (!args) { - return existing; - } else { - // Args object always returns 'page: 1' when it should return the actual page - // requested in follow-up fetchMore() calls ¯\_(ツ)_/¯. - // Work around this by using the data returned with the query - // in the `pagination` object. - const currentPage = - existing && existing.pagination - ? existing.pagination.currentPage - : args.page; - - const offset = (currentPage - 1) * args.perPage; - - // Always return the data from the first item in the cache array - // so that on each subsequent click of the "Load more..." button - // the old results are delivered alongside with a page's worth - // of new ones. - const pagefulOfData = - existing && existing[dataPropName].slice(0, offset + args.perPage); - - // Only return values if there's actually something in the cache. - // If nothing is returned from this function, Apollo Client will - // attempt to fetch fresh data from the GraphQL API. - if (pagefulOfData && pagefulOfData.length > 0) { - return { - [dataPropName]: pagefulOfData, - pagination: existing.pagination, - }; - } - } -}; diff --git a/src/api/mutations/createCollection.ts b/src/api/mutations/createCollection.ts deleted file mode 100644 index 273150439..000000000 --- a/src/api/mutations/createCollection.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionData } from '../fragments/CollectionData'; - -/** - * Create a collection - */ -export const createCollection = gql` - mutation createCollection($data: CreateCollectionInput!) { - createCollection(data: $data) { - ...CollectionData - } - } - ${CollectionData} -`; diff --git a/src/api/mutations/createCollectionAuthor.ts b/src/api/mutations/createCollectionAuthor.ts deleted file mode 100644 index d44595e6a..000000000 --- a/src/api/mutations/createCollectionAuthor.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from '../fragments/CollectionAuthorData'; - -/** - * Create an author - */ -export const createCollectionAuthor = gql` - mutation createCollectionAuthor( - $name: String! - $slug: String - $bio: Markdown - $imageUrl: Url - $active: Boolean - ) { - createCollectionAuthor( - data: { - name: $name - slug: $slug - bio: $bio - imageUrl: $imageUrl - active: $active - } - ) { - ...CollectionAuthorData - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/mutations/createCollectionPartner.ts b/src/api/mutations/createCollectionPartner.ts deleted file mode 100644 index 9588c4972..000000000 --- a/src/api/mutations/createCollectionPartner.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerData } from '../fragments/CollectionPartnerData'; - -/** - * Create a collection partner - */ -export const createCollectionPartner = gql` - mutation createCollectionPartner( - $name: String! - $url: Url! - $blurb: Markdown! - $imageUrl: Url! - ) { - createCollectionPartner( - data: { name: $name, url: $url, blurb: $blurb, imageUrl: $imageUrl } - ) { - ...CollectionPartnerData - } - } - ${CollectionPartnerData} -`; diff --git a/src/api/mutations/createCollectionPartnerAssociation.ts b/src/api/mutations/createCollectionPartnerAssociation.ts deleted file mode 100644 index e71d200e8..000000000 --- a/src/api/mutations/createCollectionPartnerAssociation.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerAssociationData } from '../fragments/CollectionPartnerAssociationData'; - -/** - * Create a collection-partner association - */ -export const createCollectionPartnerAssociation = gql` - mutation createCollectionPartnerAssociation( - $type: CollectionPartnershipType! - $partnerExternalId: String! - $collectionExternalId: String! - $name: String - $url: Url - $imageUrl: Url - $blurb: Markdown - ) { - createCollectionPartnerAssociation( - data: { - type: $type - partnerExternalId: $partnerExternalId - collectionExternalId: $collectionExternalId - name: $name - url: $url - imageUrl: $imageUrl - blurb: $blurb - } - ) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationData} -`; diff --git a/src/api/mutations/createCollectionStory.ts b/src/api/mutations/createCollectionStory.ts deleted file mode 100644 index 1dc9b805b..000000000 --- a/src/api/mutations/createCollectionStory.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionStoryData } from '../fragments/CollectionStoryData'; - -/** - * Create a collection story - */ -export const createCollectionStory = gql` - mutation createCollectionStory( - $collectionExternalId: String! - $url: Url! - $title: String! - $excerpt: Markdown! - $imageUrl: Url! - $authors: [CollectionStoryAuthorInput!]! - $publisher: String! - $sortOrder: Int - $fromPartner: Boolean - ) { - createCollectionStory( - data: { - collectionExternalId: $collectionExternalId - url: $url - title: $title - excerpt: $excerpt - imageUrl: $imageUrl - authors: $authors - publisher: $publisher - sortOrder: $sortOrder - fromPartner: $fromPartner - } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryData} -`; diff --git a/src/api/mutations/createLabel.ts b/src/api/mutations/createLabel.ts deleted file mode 100644 index 68b17c6b8..000000000 --- a/src/api/mutations/createLabel.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { gql } from '@apollo/client'; -/** - * Create a label - */ -export const createLabel = gql` - mutation createLabel($name: String!) { - createLabel(name: $name) { - externalId - name - } - } -`; diff --git a/src/api/mutations/deleteCollectionPartnerAssociation.ts b/src/api/mutations/deleteCollectionPartnerAssociation.ts deleted file mode 100644 index 0f7ff9655..000000000 --- a/src/api/mutations/deleteCollectionPartnerAssociation.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerAssociationData } from '../fragments/CollectionPartnerAssociationData'; - -/** - * Delete a collection-partner association - */ -export const deleteCollectionPartnerAssociation = gql` - mutation deleteCollectionPartnerAssociation($externalId: String!) { - deleteCollectionPartnerAssociation(externalId: $externalId) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationData} -`; diff --git a/src/api/mutations/deleteCollectionStory.ts b/src/api/mutations/deleteCollectionStory.ts deleted file mode 100644 index 184ed4c1b..000000000 --- a/src/api/mutations/deleteCollectionStory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionStoryData } from '../fragments/CollectionStoryData'; - -/** - * Delete a collection story - */ -export const deleteCollectionStory = gql` - mutation deleteCollectionStory($externalId: String!) { - deleteCollectionStory(externalId: $externalId) { - ...CollectionStoryData - } - } - ${CollectionStoryData} -`; diff --git a/src/api/mutations/imageUpload.ts b/src/api/mutations/imageUpload.ts deleted file mode 100644 index a4d8bcd52..000000000 --- a/src/api/mutations/imageUpload.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { gql } from '@apollo/client'; - -/** - * Upload an image to S3 - */ -export const imageUpload = gql` - mutation imageUpload( - $image: Upload! - $width: Int! - $height: Int! - $fileSizeBytes: Int! - ) { - collectionImageUpload( - data: { - image: $image - width: $width - height: $height - fileSizeBytes: $fileSizeBytes - } - ) { - url - } - } -`; diff --git a/src/api/mutations/updateCollection.ts b/src/api/mutations/updateCollection.ts deleted file mode 100644 index 963d19c70..000000000 --- a/src/api/mutations/updateCollection.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionData } from '../fragments/CollectionData'; - -/** - * Update a collection - */ -export const updateCollection = gql` - mutation updateCollection($data: UpdateCollectionInput!) { - updateCollection(data: $data) { - ...CollectionData - } - } - ${CollectionData} -`; diff --git a/src/api/mutations/updateCollectionAuthor.ts b/src/api/mutations/updateCollectionAuthor.ts deleted file mode 100644 index 958620fee..000000000 --- a/src/api/mutations/updateCollectionAuthor.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from '../fragments/CollectionAuthorData'; - -/** - * Update an author - */ -export const updateCollectionAuthor = gql` - mutation updateCollectionAuthor( - $externalId: String! - $name: String! - $slug: String! - $bio: Markdown - $imageUrl: Url - $active: Boolean - ) { - updateCollectionAuthor( - data: { - externalId: $externalId - name: $name - slug: $slug - bio: $bio - imageUrl: $imageUrl - active: $active - } - ) { - ...CollectionAuthorData - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/mutations/updateCollectionAuthorImageUrl.ts b/src/api/mutations/updateCollectionAuthorImageUrl.ts deleted file mode 100644 index 9fda8942e..000000000 --- a/src/api/mutations/updateCollectionAuthorImageUrl.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from '../fragments/CollectionAuthorData'; - -/** - * Update a collection author's image url - */ -export const updateCollectionAuthorImageUrl = gql` - mutation updateCollectionAuthorImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionAuthorImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionAuthorData - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/mutations/updateCollectionImageUrl.ts b/src/api/mutations/updateCollectionImageUrl.ts deleted file mode 100644 index f801285a7..000000000 --- a/src/api/mutations/updateCollectionImageUrl.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionData } from '../fragments/CollectionData'; - -/** - * Update a collection's image url - */ -export const updateCollectionImageUrl = gql` - mutation updateCollectionImageUrl($externalId: String!, $imageUrl: Url!) { - updateCollectionImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionData - } - } - ${CollectionData} -`; diff --git a/src/api/mutations/updateCollectionPartner.ts b/src/api/mutations/updateCollectionPartner.ts deleted file mode 100644 index dbec9aeff..000000000 --- a/src/api/mutations/updateCollectionPartner.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerData } from '../fragments/CollectionPartnerData'; - -/** - * Update a collection partner - */ -export const updateCollectionPartner = gql` - mutation updateCollectionPartner( - $externalId: String! - $name: String! - $url: Url! - $blurb: Markdown! - $imageUrl: Url - ) { - updateCollectionPartner( - data: { - externalId: $externalId - name: $name - url: $url - blurb: $blurb - imageUrl: $imageUrl - } - ) { - ...CollectionPartnerData - } - } - ${CollectionPartnerData} -`; diff --git a/src/api/mutations/updateCollectionPartnerAssociation.ts b/src/api/mutations/updateCollectionPartnerAssociation.ts deleted file mode 100644 index f0739ca92..000000000 --- a/src/api/mutations/updateCollectionPartnerAssociation.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerAssociationData } from '../fragments/CollectionPartnerAssociationData'; - -/** - * Update a collection-partner association - */ -export const updateCollectionPartnerAssociation = gql` - mutation updateCollectionPartnerAssociation( - $externalId: String! - $type: CollectionPartnershipType! - $partnerExternalId: String! - $name: String - $url: Url - $imageUrl: Url - $blurb: Markdown - ) { - updateCollectionPartnerAssociation( - data: { - externalId: $externalId - type: $type - partnerExternalId: $partnerExternalId - name: $name - url: $url - imageUrl: $imageUrl - blurb: $blurb - } - ) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationData} -`; diff --git a/src/api/mutations/updateCollectionPartnerAssociationImageUrl.ts b/src/api/mutations/updateCollectionPartnerAssociationImageUrl.ts deleted file mode 100644 index d685caa0c..000000000 --- a/src/api/mutations/updateCollectionPartnerAssociationImageUrl.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerAssociationData } from '../fragments/CollectionPartnerAssociationData'; - -/** - * Update the image URL for a collection-partner association - */ -export const updateCollectionPartnerAssociationImageUrl = gql` - mutation updateCollectionPartnerAssociationImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionPartnerAssociationImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationData} -`; diff --git a/src/api/mutations/updateCollectionPartnerImageUrl.ts b/src/api/mutations/updateCollectionPartnerImageUrl.ts deleted file mode 100644 index c29b9d7d9..000000000 --- a/src/api/mutations/updateCollectionPartnerImageUrl.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerData } from '../fragments/CollectionPartnerData'; - -/** - * Update a collection partner's image url - */ -export const updateCollectionPartnerImageUrl = gql` - mutation updateCollectionPartnerImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionPartnerImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionPartnerData - } - } - ${CollectionPartnerData} -`; diff --git a/src/api/mutations/updateCollectionStory.ts b/src/api/mutations/updateCollectionStory.ts deleted file mode 100644 index 259c5cc14..000000000 --- a/src/api/mutations/updateCollectionStory.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionStoryData } from '../fragments/CollectionStoryData'; - -/** - * Update a collection story - */ -export const updateCollectionStory = gql` - mutation updateCollectionStory( - $externalId: String! - $url: Url! - $title: String! - $excerpt: Markdown! - $imageUrl: Url! - $authors: [CollectionStoryAuthorInput!]! - $publisher: String! - $sortOrder: Int - $fromPartner: Boolean - ) { - updateCollectionStory( - data: { - externalId: $externalId - url: $url - title: $title - excerpt: $excerpt - imageUrl: $imageUrl - authors: $authors - publisher: $publisher - sortOrder: $sortOrder - fromPartner: $fromPartner - } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryData} -`; diff --git a/src/api/mutations/updateCollectionStoryImageUrl.ts b/src/api/mutations/updateCollectionStoryImageUrl.ts deleted file mode 100644 index 8d823b1d6..000000000 --- a/src/api/mutations/updateCollectionStoryImageUrl.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionStoryData } from '../fragments/CollectionStoryData'; - -/** - * Update a collection story's image url - */ -export const updateCollectionStoryImageUrl = gql` - mutation updateCollectionStoryImageUrl( - $externalId: String! - $imageUrl: Url! - ) { - updateCollectionStoryImageUrl( - data: { externalId: $externalId, imageUrl: $imageUrl } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryData} -`; diff --git a/src/api/mutations/updateCollectionStorySortOrder.ts b/src/api/mutations/updateCollectionStorySortOrder.ts deleted file mode 100644 index bf538f492..000000000 --- a/src/api/mutations/updateCollectionStorySortOrder.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionStoryData } from '../fragments/CollectionStoryData'; - -/** - * Update a sort order for a collection story - */ -export const updateCollectionStorySortOrder = gql` - mutation updateCollectionStorySortOrder( - $externalId: String! - $sortOrder: Int! - ) { - updateCollectionStorySortOrder( - data: { externalId: $externalId, sortOrder: $sortOrder } - ) { - ...CollectionStoryData - } - } - ${CollectionStoryData} -`; diff --git a/src/api/mutations/updateLabel.ts b/src/api/mutations/updateLabel.ts deleted file mode 100644 index 2d9b4a453..000000000 --- a/src/api/mutations/updateLabel.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { gql } from '@apollo/client'; - -/** - * Updates a label not associated with a collection. - */ -export const updateLabel = gql` - mutation updateLabel($data: UpdateLabelInput!) { - updateLabel(data: $data) { - externalId - name - } - } -`; diff --git a/src/api/queries/getAuthorById.ts b/src/api/queries/getAuthorById.ts deleted file mode 100644 index 73241eb30..000000000 --- a/src/api/queries/getAuthorById.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from '../fragments/CollectionAuthorData'; - -/** - * Get author information by their external id. - */ -export const getAuthorById = gql` - query getAuthorById($id: String!) { - getCollectionAuthor(externalId: $id) { - ...CollectionAuthorData - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/queries/getAuthors.ts b/src/api/queries/getAuthors.ts deleted file mode 100644 index 5b82d38cd..000000000 --- a/src/api/queries/getAuthors.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from '../fragments/CollectionAuthorData'; - -/** - * Get a list of authors - */ -export const getAuthors = gql` - query getAuthors($page: Int, $perPage: Int) { - getCollectionAuthors(page: $page, perPage: $perPage) { - authors { - ...CollectionAuthorData - } - pagination { - currentPage - totalPages - totalResults - } - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/queries/getCollectionById.ts b/src/api/queries/getCollectionById.ts deleted file mode 100644 index 2acea810e..000000000 --- a/src/api/queries/getCollectionById.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionData } from '../fragments/CollectionData'; - -/** - * Get collection by its external id. - */ -export const getCollectionById = gql` - query getCollectionByExternalId($externalId: String!) { - getCollection(externalId: $externalId) { - ...CollectionData - } - } - ${CollectionData} -`; diff --git a/src/api/queries/getCollectionPartner.ts b/src/api/queries/getCollectionPartner.ts deleted file mode 100644 index 72430dc43..000000000 --- a/src/api/queries/getCollectionPartner.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerData } from '../fragments/CollectionPartnerData'; - -/** - * Get partner information by their external id. - */ -export const getCollectionPartner = gql` - query getCollectionPartner($id: String!) { - getCollectionPartner(externalId: $id) { - ...CollectionPartnerData - } - } - ${CollectionPartnerData} -`; diff --git a/src/api/queries/getCollectionPartnerAssociation.ts b/src/api/queries/getCollectionPartnerAssociation.ts deleted file mode 100644 index 3742e62df..000000000 --- a/src/api/queries/getCollectionPartnerAssociation.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerAssociationData } from '../fragments/CollectionPartnerAssociationData'; - -/** - * Get collection-partner association information by the external id. - * of the collection it's related to. - */ -export const getCollectionPartnerAssociation = gql` - query getCollectionPartnerAssociation($externalId: String!) { - getCollectionPartnerAssociationForCollection(externalId: $externalId) { - ...CollectionPartnerAssociationData - } - } - ${CollectionPartnerAssociationData} -`; diff --git a/src/api/queries/getCollectionPartners.ts b/src/api/queries/getCollectionPartners.ts deleted file mode 100644 index 3ca3f6720..000000000 --- a/src/api/queries/getCollectionPartners.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionPartnerData } from '../fragments/CollectionPartnerData'; - -/** - * Get a list of collection partners - */ -export const getCollectionPartners = gql` - query getCollectionPartners($page: Int, $perPage: Int) { - getCollectionPartners(page: $page, perPage: $perPage) { - partners { - ...CollectionPartnerData - } - pagination { - currentPage - totalPages - totalResults - } - } - } - ${CollectionPartnerData} -`; diff --git a/src/api/queries/getCollectionStories.ts b/src/api/queries/getCollectionStories.ts deleted file mode 100644 index 7a7d271c7..000000000 --- a/src/api/queries/getCollectionStories.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionStoryData } from '../fragments/CollectionStoryData'; - -/** - * Get collection stories for a given collection external id. - * Deliberately fetching stories for a collection in a separate - * query to make managing cache updates easier - */ -export const getCollectionStories = gql` - query getCollectionStories($id: String!) { - getCollection(externalId: $id) { - externalId - stories { - ...CollectionStoryData - } - } - } - ${CollectionStoryData} -`; diff --git a/src/api/queries/getCollections.ts b/src/api/queries/getCollections.ts deleted file mode 100644 index fcc4fc9b0..000000000 --- a/src/api/queries/getCollections.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionData } from '../fragments/CollectionData'; - -/** - * Get a paginated list of collections with a given status - */ -export const getCollections = gql` - query getCollections( - $page: Int! - $perPage: Int! - $status: CollectionStatus! - ) { - searchCollections( - filters: { status: $status } - page: $page - perPage: $perPage - ) { - collections { - ...CollectionData - } - pagination { - currentPage - totalPages - totalResults - } - } - } - ${CollectionData} -`; diff --git a/src/api/queries/getInitialCollectionFormData.ts b/src/api/queries/getInitialCollectionFormData.ts deleted file mode 100644 index e46aff99e..000000000 --- a/src/api/queries/getInitialCollectionFormData.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionAuthorData } from '../fragments/CollectionAuthorData'; - -/** - * Get a list of authors - */ -export const getInitialCollectionFormData = gql` - query getInitialCollectionFormData($page: Int, $perPage: Int) { - getCollectionAuthors(page: $page, perPage: $perPage) { - authors { - ...CollectionAuthorData - } - } - - labels { - externalId - name - } - - getLanguages - - getCurationCategories { - externalId - name - slug - } - - getIABCategories { - externalId - name - slug - children { - externalId - name - slug - } - } - } - ${CollectionAuthorData} -`; diff --git a/src/api/queries/getSearchCollections.ts b/src/api/queries/getSearchCollections.ts deleted file mode 100644 index a3acd730c..000000000 --- a/src/api/queries/getSearchCollections.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { gql } from '@apollo/client'; -import { CollectionData } from '../fragments/CollectionData'; - -/** - * Seach collections - */ -export const getSearchCollections = gql` - query searchCollections( - $filters: SearchCollectionsFilters! - $page: Int - $perPage: Int - ) { - searchCollections(filters: $filters, page: $page, perPage: $perPage) { - collections { - ...CollectionData - } - pagination { - currentPage - totalPages - totalResults - perPage - } - } - } - ${CollectionData} -`; diff --git a/src/api/queries/getStoryFromParser.ts b/src/api/queries/getStoryFromParser.ts deleted file mode 100644 index 7d7e8daf3..000000000 --- a/src/api/queries/getStoryFromParser.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { gql } from '@apollo/client'; - -/** - * Get author information by their external id. - * - * Note that we request the `topImageUrl` to get the publisher's preferred image - * for a story, and if that's not there, the frontend will check the array of - * `images` and pick the first image off there (this is usually the hero image). - * - * `domainMetadata.name` is fed into the 'Publisher' field on the frontend. - */ -export const getStoryFromParser = gql` - query getStoryFromParser($url: String!) { - getItemByUrl(url: $url) { - resolvedUrl - title - excerpt - topImageUrl - images { - src - width - height - } - authors { - name - } - domainMetadata { - name - } - } - } -`; diff --git a/src/api/queries/labels.ts b/src/api/queries/labels.ts deleted file mode 100644 index be1d78566..000000000 --- a/src/api/queries/labels.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { gql } from '@apollo/client'; - -export const labels = gql` - query labels { - labels { - externalId - name - } - } -`; diff --git a/src/collections/components/AuthorForm/AuthorForm.test.tsx b/src/collections/components/AuthorForm/AuthorForm.test.tsx deleted file mode 100644 index e26c4eb2f..000000000 --- a/src/collections/components/AuthorForm/AuthorForm.test.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { AuthorForm } from './AuthorForm'; -import { CollectionAuthor } from '../../../api/generatedTypes'; - -describe('The AuthorForm component', () => { - let author: CollectionAuthor; - const handleSubmit = jest.fn(); - - beforeEach(() => { - author = { - externalId: '124abc', - name: 'Nigel Rutherford', - slug: 'nigel-rutherford', - imageUrl: 'http://placeimg.com/640/480/people?random=494', - bio: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - active: true, - }; - }); - - it('renders successfully', () => { - render(); - - // there is at least a form and nothing falls over - const form = screen.getByRole('form'); - expect(form).toBeInTheDocument(); - }); - - it('shows three action buttons by default', () => { - render(); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toEqual(3); - }); - - it('only shows two buttons if cancel button is not requested', () => { - render( - , - ); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toEqual(2); - }); - - it('displays author information', () => { - render(); - - const nameField = screen.getByLabelText('Full name'); - expect(nameField).toBeInTheDocument(); - - const slugField = screen.getByLabelText('Slug'); - expect(slugField).toBeInTheDocument(); - - const bioField = screen.getByLabelText('Bio'); - expect(bioField).toBeInTheDocument(); - - const activeField = screen.getByLabelText('Active'); - expect(activeField).toBeInTheDocument(); - }); - - it('validates the "name" field', async () => { - render(); - - const nameField = screen.getByLabelText(/full name/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(nameField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the full name of the author/i), - ).toBeInTheDocument(); - expect( - screen.queryByText(/name must be at least 2 characters/i), - ).not.toBeInTheDocument(); - - // Submit a name that is too short (under 2 characters) - // Note that a full name is expected here - few first name + last name combinations - // are shorter than this. - // **NOTE** As of November 28, 2022. We changed the minimum author name requirement to 2 as per curators' request. - userEvent.type(nameField, 'J'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the full name of the author/i), - ).not.toBeInTheDocument(); - expect( - screen.queryByText(/name must be at least 2 characters/i), - ).toBeInTheDocument(); - - // Submit a name that satisfies all the requirements - userEvent.type(nameField, 'John Citizen'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the full name of the author/i), - ).not.toBeInTheDocument(); - expect( - screen.queryByText(/name must be at least 2 characters/i), - ).not.toBeInTheDocument(); - }); - - it('validates the "slug" field', async () => { - render(); - - const slugField = screen.getByLabelText(/slug/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(slugField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a slug/i)).toBeInTheDocument(); - expect( - screen.queryByText(/slug must be at least 2 characters/i), - ).not.toBeInTheDocument(); - - // Submit a slug that is too short (under 2 characters) - // **NOTE** As of November 28, 2022. We changed the minimum author name requirement to 2 as per curators' request. This affects the slug length as well. - userEvent.type(slugField, 'q'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a slug/i)).not.toBeInTheDocument(); - expect( - screen.queryByText(/slug must be at least 2 characters/i), - ).toBeInTheDocument(); - - // Submit a slug that satisfies all the requirements - userEvent.type(slugField, 'queen-bee'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a slug/i)).not.toBeInTheDocument(); - expect( - screen.queryByText(/slug must be at least 2 characters/i), - ).not.toBeInTheDocument(); - }); - - it('suggests the slug correctly', async () => { - render(); - - // The "Suggest slug" button slugifies whatever is present in the "Full name" field - const slugField = screen.getByLabelText(/slug/i); - const nameField = screen.getByLabelText(/name/i); - const suggestSlugButton = screen.getByText(/suggest slug/i); - - // Try with an empty field first - userEvent.clear(slugField); - userEvent.clear(nameField); - - await waitFor(() => { - userEvent.click(suggestSlugButton); - }); - - // Slugify button returned an empty string - expect(slugField).toHaveTextContent(''); - - // Try with an actual name - userEvent.type(nameField, 'John Citizen'); - - await waitFor(() => { - userEvent.click(suggestSlugButton); - }); - - // Slugified version of the name appears in the slug input field - expect(screen.getByDisplayValue('john-citizen')).toBeInTheDocument(); - }); - - it('displays the "active" status correctly', async () => { - render(); - - // By default the mock author set up before each test is active - const checkbox = screen.getByLabelText(/active/i); - expect(checkbox).toBeInTheDocument(); - - // Let's uncheck it - await waitFor(() => { - userEvent.click(checkbox); - }); - - // Checkbox now displays "Inactive" as its label - expect(screen.queryByLabelText(/inactive/i)).toBeInTheDocument(); - }); - - it('has markdown preview tabs', () => { - render(); - - expect(screen.getByText(/write/i)).toBeInTheDocument(); - expect(screen.getByText(/preview/i)).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/AuthorForm/AuthorForm.tsx b/src/collections/components/AuthorForm/AuthorForm.tsx deleted file mode 100644 index 4282e327a..000000000 --- a/src/collections/components/AuthorForm/AuthorForm.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React from 'react'; -import { - Box, - FormControlLabel, - Grid, - LinearProgress, - Switch, -} from '@mui/material'; -import slugify from 'slugify'; -import { FormikValues, useFormik } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { - Button, - FormikTextField, - MarkdownPreview, - SharedFormButtons, - SharedFormButtonsProps, -} from '../../../_shared/components'; -import { validationSchema } from './AuthorForm.validation'; -import { config } from '../../../config'; -import { CollectionAuthor } from '../../../api/generatedTypes'; - -interface AuthorFormProps { - /** - * An object with everything author-related in it. - */ - author: CollectionAuthor; - - /** - * What do we do with the submitted data? - */ - onSubmit: (values: FormikValues, formikHelpers: FormikHelpers) => void; -} - -/** - * A form for adding authors or editing information for existing authors - */ -export const AuthorForm: React.FC = ( - props, -): JSX.Element => { - const { author, onSubmit, editMode, onCancel } = props; - - /** - * Set up form validation - */ - const formik = useFormik({ - initialValues: { - name: author.name ?? '', - slug: author.slug ?? '', - bio: author.bio ?? '', - active: author.active ?? true, - }, - // We don't want to irritate users by displaying validation errors - // before they actually submit the form - validateOnChange: false, - validateOnBlur: false, - validationSchema, - onSubmit: (values, formikHelpers) => { - onSubmit(values, formikHelpers); - }, - }); - - /** - * Suggest a slug for the author - works off the "name" field - */ - const suggestSlug = () => { - const newSlug = slugify(formik.values.name, config.slugify); - formik.setFieldValue('slug', newSlug); - }; - - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - } - label={formik.values.active ? 'Active' : 'Inactive'} - labelPlacement="end" - /> - - - {formik.isSubmitting && ( - - - - )} - - - - - -
- ); -}; diff --git a/src/collections/components/AuthorForm/AuthorForm.validation.tsx b/src/collections/components/AuthorForm/AuthorForm.validation.tsx deleted file mode 100644 index c9f147879..000000000 --- a/src/collections/components/AuthorForm/AuthorForm.validation.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as yup from 'yup'; - -/** - * Validation schema for the Author add/edit form - */ -export const validationSchema = yup.object({ - name: yup - .string() - .required('Please enter the full name of the author') - .min(2), - slug: yup - .string() - .trim() - .required( - 'Please enter a slug or use the "Suggest slug" button to generate one from the name of the author', - ) - .matches( - /^[a-z0-9-]+$/, - 'Slug can only contain lowercase alphanumeric characters and hyphens', - ) - .min(2), - bio: yup.string(), - active: yup.boolean().required(), -}); diff --git a/src/collections/components/AuthorInfo/AuthorInfo.test.tsx b/src/collections/components/AuthorInfo/AuthorInfo.test.tsx deleted file mode 100644 index 664f9232c..000000000 --- a/src/collections/components/AuthorInfo/AuthorInfo.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { AuthorInfo } from './AuthorInfo'; -import { CollectionAuthor } from '../../../api/generatedTypes'; -import { MockedProvider } from '@apollo/client/testing'; - -describe('The AuthorInfo component', () => { - let author: CollectionAuthor; - - beforeEach(() => { - author = { - externalId: '124abc', - name: 'Nigel Rutherford', - slug: 'nigel-rutherford', - imageUrl: 'http://placeimg.com/640/480/people?random=494', - bio: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - active: true, - }; - }); - - it('shows basic author information', () => { - render( - - - - - , - ); - - // The author bio is present - const bio = screen.getByText(/voluptatem est aut/i); - expect(bio).toBeInTheDocument(); - }); - - it('shows "active" status correctly', () => { - render( - - - - - , - ); - - // Shows 'Active' subtitle for an active author - const activeSubtitle = screen.getByText(/^active/i); - expect(activeSubtitle).toBeInTheDocument(); - - // Doesn't show 'Inactive' for an active author - const inactiveSubtitle = screen.queryByText(/^inactive/i); - expect(inactiveSubtitle).not.toBeInTheDocument(); - }); - - it('shows "inactive" status correctly', () => { - author.active = false; - - render( - - - - - , - ); - - // Shows 'Inactive' subtitle for an inactive author - const inactiveSubtitle = screen.getByText(/^inactive/i); - expect(inactiveSubtitle).toBeInTheDocument(); - - // Doesn't show 'Active' for an inactive author - const activeSubtitle = screen.queryByText(/^active/i); - expect(activeSubtitle).not.toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/AuthorInfo/AuthorInfo.tsx b/src/collections/components/AuthorInfo/AuthorInfo.tsx deleted file mode 100644 index d02dcb4c9..000000000 --- a/src/collections/components/AuthorInfo/AuthorInfo.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Typography } from '@mui/material'; -import ReactMarkdown from 'react-markdown'; -import { CollectionAuthor } from '../../../api/generatedTypes'; - -interface AuthorInfoProps { - /** - * An object with everything author-related in it. - */ - author: CollectionAuthor; -} - -/** - * A simple component that shows author information. The name of the author - * and their photo are rendered by other components. - * - * @param props - * @constructor - */ -export const AuthorInfo: React.FC = (props): JSX.Element => { - const { author } = props; - - return ( - <> - - {author.active ? 'Active' : 'Inactive'} - - {author.bio} - - ); -}; diff --git a/src/collections/components/AuthorListCard/AuthorListCard.test.tsx b/src/collections/components/AuthorListCard/AuthorListCard.test.tsx deleted file mode 100644 index cdcab3b9f..000000000 --- a/src/collections/components/AuthorListCard/AuthorListCard.test.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter, Router } from 'react-router-dom'; -import { AuthorListCard } from './AuthorListCard'; -import { CollectionAuthor } from '../../../api/generatedTypes'; -import { MockedProvider } from '@apollo/client/testing'; -import userEvent from '@testing-library/user-event'; -import { createMemoryHistory } from 'history'; - -describe('The AuthorListCard component', () => { - let author: CollectionAuthor; - - beforeEach(() => { - author = { - externalId: '124abc', - name: 'Nigel Rutherford', - slug: 'nigel-rutherford', - imageUrl: 'http://placeimg.com/640/480/people?random=494', - bio: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - active: true, - }; - }); - - it('shows basic author information', () => { - render( - - - , - ); - - // The author photo is present and the alt text is the author's name - const authorPhoto = screen.getByAltText(author.name); - expect(authorPhoto).toBeInTheDocument(); - - // The link to the author page is present and is well-formed - const linkToAuthorPage = screen.getByRole('link'); - expect(linkToAuthorPage).toBeInTheDocument(); - expect(linkToAuthorPage).toHaveAttribute( - 'href', - expect.stringContaining(author.externalId), - ); - - // The author bio is also present - const bio = screen.getByText(/voluptatem est aut/i); - expect(bio).toBeInTheDocument(); - }); - - it('shows "active" status correctly', () => { - render( - - - , - ); - - // Shows 'Active' subtitle for an active author - const activeSubtitle = screen.getByText(/^active/i); - expect(activeSubtitle).toBeInTheDocument(); - - // Doesn't show 'Inactive' for an active author - const inactiveSubtitle = screen.queryByText(/^inactive/i); - expect(inactiveSubtitle).not.toBeInTheDocument(); - }); - - it('shows "inactive" status correctly', () => { - author.active = false; - - render( - - - , - ); - - // Shows 'Inactive' subtitle for an inactive author - const inactiveSubtitle = screen.getByText(/^inactive/i); - expect(inactiveSubtitle).toBeInTheDocument(); - - // Doesn't show 'Active' for an inactive author - const activeSubtitle = screen.queryByText(/^active/i); - expect(activeSubtitle).not.toBeInTheDocument(); - }); - - it("links to an individual author's page", () => { - const history = createMemoryHistory({ - initialEntries: ['/collections/authors/'], - }); - - render( - - - - - , - ); - - // While the entire card is a giant link, we can click on - // anything we like within that link - i.e., the author's name - userEvent.click(screen.getByText(author.name)); - expect(history.location.pathname).toEqual( - `/collections/authors/${author.externalId}/`, - ); - - // Let's go back to the Authors page - history.goBack(); - expect(history.location.pathname).toEqual('/collections/authors/'); - - // And click on the image this time - userEvent.click(screen.getByRole('img')); - expect(history.location.pathname).toEqual( - `/collections/authors/${author.externalId}/`, - ); - }); -}); diff --git a/src/collections/components/AuthorListCard/AuthorListCard.tsx b/src/collections/components/AuthorListCard/AuthorListCard.tsx deleted file mode 100644 index ccde22785..000000000 --- a/src/collections/components/AuthorListCard/AuthorListCard.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import { Link as RouterLink } from 'react-router-dom'; -import ReactMarkdown from 'react-markdown'; -import { CardMedia, Grid, Typography } from '@mui/material'; -import { CollectionAuthor } from '../../../api/generatedTypes'; -import { StyledCardLink, StyledListCard } from '../../../_shared/styled'; - -interface AuthorListCardProps { - /** - * An object with everything author-related in it. - */ - author: CollectionAuthor; -} - -/** - * A compact card that displays author information and links to the author page. - * - * @param props - */ -export const AuthorListCard: React.FC = (props) => { - const { author } = props; - - // We pass the author object along with the link so that when the user clicks - // on the card to go to an individual author's page, the page is loaded instantly. - return ( - - - - - 0 - ? author.imageUrl - : '/placeholders/authorSmall.svg' - } - alt={author.name} - sx={{ - borderRadius: 1, - }} - /> - - - - {author.name} - - - {author.active ? 'Active' : 'Inactive'} - - - - {author.bio ? author.bio.substring(0, 100) : ''} - - - - - - - ); -}; diff --git a/src/collections/components/ChipLabelsList/ChipLabelsList.test.tsx b/src/collections/components/ChipLabelsList/ChipLabelsList.test.tsx deleted file mode 100644 index 9c834946f..000000000 --- a/src/collections/components/ChipLabelsList/ChipLabelsList.test.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import userEvent from '@testing-library/user-event'; -import { ChipLabelsList } from './ChipLabelsList'; -import { - Collection, - CollectionLanguage, - CollectionStatus, -} from '../../../api/generatedTypes'; - -describe('The ChipLabelsList component', () => { - let collection: Omit; - - beforeEach(() => { - collection = { - externalId: '124abc', - title: 'Hidden Histories of Presidential Medical Dramas', - slug: 'collection-slug', - imageUrl: 'https://placeimg.com/640/480/people?random=494', - excerpt: - 'There’s a long history of presidential ailments, including George Washington’s near-death encounter with the flu, Grover Cleveland’s secret tumor, and the clandestine suffering of John F. Kennedy. ', - intro: 'Intro text is generally longer than the excerpt.', - language: CollectionLanguage.De, - status: CollectionStatus.Draft, - authors: [{ externalId: '123-abc', name: 'Joe Bloggs', active: true }], - }; - }); - - it('shows label if labels are set', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - ]; - - render( - - - , - ); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - }); - - it('shows 2 out of 3 labels, click on expand button and show one more label', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - { - externalId: 'label-3', - name: 'region-south-africa', - }, - ]; - - render( - - - , - ); - - // grab expand button, expect +1 as there are total of 3 labels - const expandButton = screen.getByRole('button', { - name: /\+ 1/i, - }); - expect(expandButton).toBeInTheDocument(); - - // click on the expand button - userEvent.click(expandButton); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - expect(screen.getByText('region-south-africa')).toBeInTheDocument(); - }); - - it('shows 2 out of 3 labels, one label is hidden', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - { - externalId: 'label-3', - name: 'region-south-africa', - }, - ]; - - render( - - - , - ); - - // grab expand button, expect +1 as there are total of 3 labels - const expandButton = screen.getByRole('button', { - name: /\+ 1/i, - }); - expect(expandButton).toBeInTheDocument(); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - // expand button not clicked, don't expect this label to shown - expect(screen.queryByText('region-south-africa')).not.toBeInTheDocument(); - }); - - it('omits label if labels are not set', () => { - render( - - - , - ); - - expect(screen.queryByText('region-east-africa')).not.toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/ChipLabelsList/ChipLabelsList.tsx b/src/collections/components/ChipLabelsList/ChipLabelsList.tsx deleted file mode 100644 index 9aa6b29f7..000000000 --- a/src/collections/components/ChipLabelsList/ChipLabelsList.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React, { useState } from 'react'; -import { Button, Collapse } from '@mui/material'; -import LabelOutlinedIcon from '@mui/icons-material/LabelOutlined'; -import ExpandLess from '@mui/icons-material/ExpandLess'; -import ExpandMore from '@mui/icons-material/ExpandMore'; - -import { Collection } from '../../../api/generatedTypes'; -import { StyledChipComponent } from '../../../_shared/styled'; - -interface ChipLabelsListProps { - /** - * An object with everything collection-related in it. - * Except for stories. - */ - collection: Omit; - - /** - * Boolean flag whether or not enable the onClick event. - * */ - enableOnClick: boolean; -} - -/** - * A custom styled chip label list component to display labels and expand more/less button. - * - * @param props - */ -export const ChipLabelsList: React.FC = (props) => { - const { collection, enableOnClick } = props; - - // expand more/less button state - const [expandButtonToggled, setExpandButtonToggle] = useState(true); - - // onMouseDown event is used when pressing the mouse button on the expand button. - // this event is needed in order to fire logic as soon as the mouse is pressed, instead of waiting for the full click. - const handleMouseDown = (event: React.MouseEvent) => { - event.preventDefault(); - // invert - setExpandButtonToggle(!expandButtonToggled); - }; - - // onClick event fires after a full click on a button. This event is also needed - // to prevent the clickable collection card component to fire its chain of reactions - // having both onMouseDown & onClick events allows to control the button inside a clickable - // component - const handleClick = (event: React.MouseEvent) => { - event.preventDefault(); - setExpandButtonToggle(expandButtonToggled); - }; - - return ( - - {/* display only the first two labels if label length > 2*/} - {collection.labels && - collection.labels.length > 2 && [ - collection.labels.slice(0, 2).map((data) => { - return ( - - } - />{' '} - - ); - }), - , -
, -
, - - {/* display the rest of the labels under the expand button*/} - {collection.labels.slice(2).map((data) => { - return ( - - } - />{' '} - - ); - })} - , - ]}{' '} - {/* display labels where length <= 2*/} - {collection.labels && - collection.labels.length <= 2 && - collection.labels.map((data) => { - return ( - - } - />{' '} - - ); - })} -
- ); -}; diff --git a/src/collections/components/CollectionForm/CollectionForm.test.tsx b/src/collections/components/CollectionForm/CollectionForm.test.tsx deleted file mode 100644 index bff3924c7..000000000 --- a/src/collections/components/CollectionForm/CollectionForm.test.tsx +++ /dev/null @@ -1,426 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { CollectionForm } from './CollectionForm'; -import { - Collection, - CollectionAuthor, - CollectionLanguage, - CollectionStatus, - CurationCategory, - IabParentCategory, - Label, -} from '../../../api/generatedTypes'; - -describe('The CollectionForm component', () => { - let collection: Omit; - let authors: CollectionAuthor[]; - let curationCategories: CurationCategory[]; - let iabCategories: IabParentCategory[]; - let labels: Label[]; - let languages: CollectionLanguage[]; - const handleSubmit = jest.fn(); - - // This test suite occasionally takes forever to run on a local machine - jest.setTimeout(10000); - - beforeEach(() => { - collection = { - externalId: '124abc', - title: 'Collection of Collections', - slug: 'collection-of-collections', - imageUrl: 'http://placeimg.com/640/480/nature?random=494', - excerpt: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ', - intro: - ' Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' + - 'Nulla sed ultricies odio. Ut egestas urna neque, nec viverra risus' + - ' egestas id. Nulla eget felis quis enim blandit vehicula. Donec ac ' + - 'semper diam. Aenean eu sapien ornare, venenatis metus a, euismod metus.' + - ' Maecenas ullamcorper, nunc eu rhoncus luctus, ante augue lobortis nisi,' + - ' eu imperdiet est nisi non sapien. Pellentesque habitant morbi tristique' + - ' senectus et netus et malesuada fames ac turpis egestas.\n' + - '\n' + - 'Etiam vel maximus diam, hendrerit egestas augue. Etiam vel tincidunt ' + - 'mauris. Duis et elit quis nisl commodo sodales ut at dui. Aenean blandit' + - ' pellentesque aliquam. Mauris nec hendrerit lacus, et ultricies magna.' + - ' Praesent hendrerit eros luctus ligula facilisis, non sodales est ' + - 'suscipit. Nunc a lorem a metus venenatis euismod. Praesent id lectus' + - ' lobortis, ullamcorper ipsum in, eleifend sapien.', - language: CollectionLanguage.De, - status: CollectionStatus.Draft, - authors: [], - curationCategory: { externalId: 'cde-234', name: 'Food', slug: 'food' }, - }; - - authors = [ - { - externalId: '123abc', - name: 'Nigel Rutherford', - slug: 'nigel-rutherford', - imageUrl: 'http://placeimg.com/640/480/people?random=494', - bio: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - active: true, - }, - ]; - - curationCategories = [ - { - externalId: 'abc-123', - name: 'Business', - slug: 'business', - }, - { externalId: 'cde-234', name: 'Food', slug: 'food' }, - ]; - - iabCategories = [ - { - externalId: 'abc-345', - name: 'Education', - slug: 'education', - children: [ - { - externalId: '123-abc', - name: 'Language Learning', - slug: 'language-learning', - }, - ], - }, - ]; - - labels = [ - { externalId: '345-dfg', name: 'test-label-one' }, - { externalId: '789-yui', name: 'test-label-two' }, - ]; - - languages = Object.values(CollectionLanguage); - }); - - it('renders successfully', () => { - render( - , - ); - - // there is at least a form and nothing falls over - const form = screen.getByRole('form'); - expect(form).toBeInTheDocument(); - }); - - it('shows six action buttons by default', () => { - render( - , - ); - - const buttons = screen.getAllByRole('button'); - - // The seventh button is part of the MUI Autocomplete component - expect(buttons.length).toEqual(7); - }); - - it('only shows five buttons if not in edit mode', () => { - render( - , - ); - - const buttons = screen.getAllByRole('button'); - - // The sixth button is part of the MUI Autocomplete component - expect(buttons.length).toEqual(6); - }); - - it('displays collection information', () => { - render( - , - ); - - const titleField = screen.getByLabelText('Title'); - expect(titleField).toBeInTheDocument(); - - const slugField = screen.getByLabelText('Slug'); - expect(slugField).toBeInTheDocument(); - - const statusField = screen.getByLabelText('Status'); - expect(statusField).toBeInTheDocument(); - - const languageField = screen.getByLabelText('Language Code'); - expect(languageField).toBeInTheDocument(); - - const authorField = screen.getByLabelText('Author'); - expect(authorField).toBeInTheDocument(); - - const excerptField = screen.getByLabelText('Excerpt'); - expect(excerptField).toBeInTheDocument(); - - const introField = screen.getByLabelText('Intro'); - expect(introField).toBeInTheDocument(); - - const curationCategoryField = screen.getByLabelText('Curation Category'); - expect(curationCategoryField).toBeInTheDocument(); - - const iabParentCategoryField = screen.getByLabelText('IAB Parent Category'); - expect(iabParentCategoryField).toBeInTheDocument(); - - const iabChildCategoryField = screen.getByLabelText('IAB Child Category'); - expect(iabChildCategoryField).toBeInTheDocument(); - }); - - it('validates the "title" field', async () => { - render( - , - ); - - const titleField = screen.getByLabelText(/title/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(titleField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter a title for this collection/i), - ).toBeInTheDocument(); - expect( - screen.queryByText(/title must be at least 6 characters/i), - ).not.toBeInTheDocument(); - - // Submit a collection title that is too short (under 6 characters) - userEvent.type(titleField, 'All'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter a title for this collection/i), - ).not.toBeInTheDocument(); - expect( - screen.queryByText(/title must be at least 6 characters/i), - ).toBeInTheDocument(); - - // Submit a title that satisfies all the requirements - userEvent.type(titleField, 'All About Standing Desks'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter a title for this collection/i), - ).not.toBeInTheDocument(); - expect( - screen.queryByText(/title must be at least 6 characters/i), - ).not.toBeInTheDocument(); - }); - - it('validates the "slug" field', async () => { - render( - , - ); - - const slugField = screen.getByLabelText(/slug/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(slugField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a slug/i)).toBeInTheDocument(); - expect( - screen.queryByText(/slug must be at least 6 characters/i), - ).not.toBeInTheDocument(); - - // Submit a slug that is too short (under 6 characters) - userEvent.type(slugField, 'q-bee'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a slug/i)).not.toBeInTheDocument(); - expect( - screen.queryByText(/slug must be at least 6 characters/i), - ).toBeInTheDocument(); - - // Submit a slug that satisfies all the requirements - userEvent.type(slugField, 'queen-bee'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a slug/i)).not.toBeInTheDocument(); - expect( - screen.queryByText(/slug must be at least 6 characters/i), - ).not.toBeInTheDocument(); - }); - - it('suggests the slug correctly', async () => { - render( - , - ); - - // The "Suggest slug" button slugifies whatever is present in the "Title" field - const slugField = screen.getByLabelText(/slug/i); - const titleField = screen.getByLabelText(/title/i); - const suggestSlugButton = screen.getByText(/suggest slug/i); - - // Try with an empty field first - userEvent.clear(slugField); - userEvent.clear(titleField); - - await waitFor(() => { - userEvent.click(suggestSlugButton); - }); - - // Slugify button returned an empty string - expect(slugField).toHaveTextContent(''); - - // Try with an actual title - userEvent.type(titleField, 'A Very Long And Elaborate Collection Name'); - - await waitFor(() => { - userEvent.click(suggestSlugButton); - }); - - // Slugified version of the title appears in the slug input field - expect( - screen.getByDisplayValue('a-very-long-and-elaborate-collection-name'), - ).toBeInTheDocument(); - }); - - it('suggests a slug free of punctuation and other special characters', async () => { - render( - , - ); - - const slugField = screen.getByLabelText(/slug/i); - const titleField = screen.getByLabelText(/title/i); - const suggestSlugButton = screen.getByText(/suggest slug/i); - - // Clear the fields or the user event further on will add to the input - // rather than overwrite it - userEvent.clear(slugField); - userEvent.clear(titleField); - - userEvent.type( - titleField, - 'A !!!title ??? full ### of ```special~~~ chars!#@*^*#^.,;*_/', - ); - - await waitFor(() => { - userEvent.click(suggestSlugButton); - }); - - // Get a slug that is free - expect( - screen.getByDisplayValue('a-title-full-of-special-chars'), - ).toBeInTheDocument(); - }); - - it('has markdown preview tabs on two fields', () => { - render( - , - ); - - const writeTabs = screen.getAllByText(/write/i); - expect(writeTabs.length).toEqual(2); - - const previewTabs = screen.getAllByText(/preview/i); - expect(previewTabs.length).toEqual(2); - }); - - it('displays pre-existing labels for a collection', () => { - // Let's assign the two mock labels that we have to a collection - collection.labels = labels; - - render( - , - ); - - // Expect to see both of these labels on the screen - expect(screen.getByText(labels[0].name)).toBeInTheDocument(); - expect(screen.getByText(labels[1].name)).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/CollectionForm/CollectionForm.tsx b/src/collections/components/CollectionForm/CollectionForm.tsx deleted file mode 100644 index 4bc8e7793..000000000 --- a/src/collections/components/CollectionForm/CollectionForm.tsx +++ /dev/null @@ -1,443 +0,0 @@ -import React, { useState } from 'react'; -import { - Autocomplete, - Box, - FormHelperText, - Grid, - LinearProgress, - TextField, -} from '@mui/material'; -import slugify from 'slugify'; -import { FormikValues, useFormik } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { - Button, - FormikSelectField, - FormikTextField, - MarkdownPreview, - SharedFormButtons, - SharedFormButtonsProps, -} from '../../../_shared/components'; -import { getValidationSchema } from './CollectionForm.validation'; -import { config } from '../../../config'; -import { - Collection, - CollectionAuthor, - CollectionLanguage, - CollectionStatus, - CurationCategory, - IabCategory, - IabParentCategory, - Label, -} from '../../../api/generatedTypes'; -import { applyCurlyQuotes } from '../../../_shared/utils/applyCurlyQuotes'; -import { applyApTitleCase } from '../../../_shared/utils/applyApTitleCase'; - -interface CollectionFormProps { - /** - * An object with everything collection-related in it bar stories - */ - collection: Omit; - - /** - * A list of all collection authors - for the dropdown - */ - authors: CollectionAuthor[]; - - /** - * A list of curation categories - */ - curationCategories: CurationCategory[]; - - /** - * A list of IAB categories - */ - iabCategories: IabParentCategory[]; - - /** - * A list of available labels - */ - labels: Label[]; - - /** - * A list of all supported languages - */ - languages: CollectionLanguage[]; - - /** - * What do we do with the submitted data? - * - * Note: unlike most other onSubmit functions in this repository, - * this one consumes the value for one field (labels) directly, circumventing - * form validation. - * - * This is due to the fact that Formik cannot process objects as input values - * while `labels` must be an array of objects in order for the MUI Autocomplete - * component to work as intended. - */ - onSubmit: ( - values: FormikValues, - formikHelpers: FormikHelpers, - labels: Label[], - ) => void | Promise; -} - -/** - * A form for adding authors or editing information for existing authors - */ -export const CollectionForm: React.FC< - CollectionFormProps & SharedFormButtonsProps -> = (props): JSX.Element => { - const { - authors, - collection, - curationCategories, - iabCategories, - labels, - languages, - onSubmit, - editMode = true, - onCancel, - } = props; - - // get a list of author ids for the validation schema - const authorIds = authors.map((author: CollectionAuthor) => { - return author.externalId; - }); - - // if we're editing, grab the currently assigned author's external id - const authorExternalId = - collection.authors.length > 0 ? collection.authors[0].externalId : ''; - - // pull out external ids of all the available labels in a separate variable - const availableLabelExternalIds = labels.map((label) => label.externalId); - - // If this collection has any labels, let's keep track of them here - // to be able to show them in the form correctly - const [selectedLabels, setSelectedLabels] = useState( - // The API, via generated types, returns a `Maybe` type (Label [] | null), - // so type coalescing is required to keep the labels field happy. - (collection.labels as Label[]) ?? [], - ); - - // pull out external ids of all the selected labels in a separate variable - const selectedLabelsExternalIds = selectedLabels.map( - (label) => label.externalId, - ); - // Update labels if the user has made any changes - const handleLabelChange = (e: React.ChangeEvent, value: Label[]) => { - setSelectedLabels(value); - - formik.setFieldValue('labels', value); - }; - - /** - * Set up form validation - */ - const formik = useFormik({ - initialValues: { - title: collection.title ?? '', - slug: collection.slug ?? '', - excerpt: collection.excerpt ?? '', - intro: collection.intro ?? '', - labels: collection.labels ?? [], - language: collection.language ?? CollectionLanguage.En, - status: collection.status ?? CollectionStatus.Draft, - authorExternalId, - curationCategoryExternalId: collection.curationCategory?.externalId ?? '', - IABParentCategoryExternalId: - collection.IABParentCategory?.externalId ?? '', - IABChildCategoryExternalId: collection.IABChildCategory?.externalId ?? '', - }, - // We don't want to irritate users by displaying validation errors - // before they actually submit the form - validateOnBlur: false, - validateOnChange: false, - validationSchema: getValidationSchema( - authorIds, - selectedLabelsExternalIds, - availableLabelExternalIds, - ), - onSubmit: (values, formikHelpers) => { - onSubmit(values, formikHelpers, selectedLabels); - }, - }); - - /** - * Suggest a slug for the collection - works off the "title" field - */ - const suggestSlug = () => { - const newSlug = slugify(formik.values.title, config.slugify); - formik.setFieldValue('slug', newSlug); - }; - - const fixTitle = () => { - formik.setFieldValue( - 'title', - applyCurlyQuotes(applyApTitleCase(formik.values.title)), - ); - }; - - /** - * Work out which IAB child category to show when an IAB parent category is chosen - */ - const [iabChildrenCategories, setIabChildrenCategories] = useState< - IabCategory[] - >([]); - React.useEffect(() => { - // Determine which IAB parent category has been chosen - const currentIabParentCategory = iabCategories.find((category) => { - return category.externalId === formik.values.IABParentCategoryExternalId; - }); - - if (currentIabParentCategory) { - // Use its children as the dependent "IAB Child Category" - // dropdown options. - setIabChildrenCategories(currentIabParentCategory.children); - } else { - // No parent IAB category has been chosen - unset child categories - setIabChildrenCategories([]); - } - }, [ - formik.touched.IABParentCategoryExternalId, - formik.values.IABParentCategoryExternalId, - iabCategories, - ]); - - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - })} - - - {languages.map((language: CollectionLanguage) => { - return ( - - ); - })} - - - - - - - ); - })} - - - - - ); - })} - - - - ); - })} - - - - option.name} - isOptionEqualToValue={(option, value) => - option.externalId === value.externalId - } - value={selectedLabels} - filterSelectedOptions - renderInput={(params) => ( - - )} - /> - { - - {formik.errors?.labels && ( - {formik.errors?.labels} - )} - - } - - - - - - - - - - - - - - - - - - - - {formik.isSubmitting && ( - - - - )} - - - - - -
- ); -}; diff --git a/src/collections/components/CollectionForm/CollectionForm.validation.tsx b/src/collections/components/CollectionForm/CollectionForm.validation.tsx deleted file mode 100644 index c7b52295d..000000000 --- a/src/collections/components/CollectionForm/CollectionForm.validation.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import * as yup from 'yup'; -import { - CollectionLanguage, - CollectionStatus, -} from '../../../api/generatedTypes'; - -export const getValidationSchema = ( - authorIds: string[], - selectedLabelExternalIds: string[], - availableLabelExternalIds: string[], -) => { - return yup.object({ - title: yup - .string() - .required('Please enter a title for this collection') - .min(6), - slug: yup - .string() - .trim() - .required( - 'Please enter a slug or use the "Suggest slug" button to generate one from the collection title', - ) - .matches( - /^[a-z0-9-]+$/, - 'Slug can only contain lowercase alphanumeric characters and hyphens', - ) - .min(6), - excerpt: yup.string(), - intro: yup.string(), - language: yup - .mixed() - .oneOf(Object.values(CollectionLanguage)) - .required('Please choose a language'), - status: yup - .mixed() - .oneOf(Object.values(CollectionStatus)) - .required(), - labels: yup - .mixed() - .test( - 'hasValidLabels', - 'Please only select labels that are available for this collection', - () => { - return selectedLabelExternalIds.length === 0 - ? true - : selectedLabelExternalIds.some((label) => - availableLabelExternalIds.includes(label), - ); - }, - ), - authorExternalId: yup - .string() - .oneOf(authorIds) - .required('Please choose an author'), - curationCategoryExternalId: yup.string(), - IABParentCategoryExternalId: yup.string(), - // If an IAB parent category is chosen, require the IAB child category - // to be filled in as well. - IABChildCategoryExternalId: yup - .string() - .when('IABParentCategoryExternalId', { - is: (value: string) => value && value.length > 0, - then: yup - .string() - .required( - 'Please choose a child IAB category or leave both IAB categories blank', - ), - otherwise: yup.string(), - }), - }); -}; diff --git a/src/collections/components/CollectionInfo/CollectionInfo.test.tsx b/src/collections/components/CollectionInfo/CollectionInfo.test.tsx deleted file mode 100644 index a9627c715..000000000 --- a/src/collections/components/CollectionInfo/CollectionInfo.test.tsx +++ /dev/null @@ -1,288 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import userEvent from '@testing-library/user-event'; -import { CollectionInfo } from './CollectionInfo'; -import { - Collection, - CollectionLanguage, - CollectionStatus, -} from '../../../api/generatedTypes'; - -describe('The CollectionInfo component', () => { - let collection: Omit; - - beforeEach(() => { - collection = { - externalId: '124abc', - title: 'Hidden Histories of Presidential Medical Dramas', - slug: 'collection-slug', - imageUrl: 'https://placeimg.com/640/480/people?random=494', - excerpt: - 'There’s a long history of presidential ailments, including George Washington’s near-death encounter with the flu, Grover Cleveland’s secret tumor, and the clandestine suffering of John F. Kennedy. ', - intro: 'Intro text is generally longer than the excerpt.', - language: CollectionLanguage.De, - status: CollectionStatus.Draft, - authors: [{ externalId: '123-abc', name: 'Joe Bloggs', active: true }], - }; - }); - - it('shows basic collection information', () => { - render( - - - , - ); - - // The slug is present - const slug = screen.getByText(collection.slug); - expect(slug).toBeInTheDocument(); - - // There is no active link since it's still a draft collection - const link = screen.queryByRole('link'); - expect(link).not.toBeInTheDocument(); - - // The excerpt is present - const excerpt = screen.getByText(/presidential ailments/i); - expect(excerpt).toBeInTheDocument(); - - // Shows the correct status - const status = screen.getByText(/^draft/i); - expect(status).toBeInTheDocument(); - - // Shows the name of the author - const author = screen.getByText('Joe Bloggs'); - expect(author).toBeInTheDocument(); - }); - - it('shows "Published" status correctly', () => { - collection.status = CollectionStatus.Published; - - render( - - - , - ); - - // Shows 'Published' subtitle for a published collection - const published = screen.getByText(/^published/i); - expect(published).toBeInTheDocument(); - - // Doesn't show the other possible collection states - const draft = screen.queryByText(/^draft/i); - expect(draft).not.toBeInTheDocument(); - const review = screen.queryByText('/^review/i'); - expect(review).not.toBeInTheDocument(); - const archived = screen.queryByText(/^archived/i); - expect(archived).not.toBeInTheDocument(); - }); - - it('shows an active link for a published or "under review" collection', () => { - collection.status = CollectionStatus.Review; - - const { rerender } = render( - - - , - ); - - const link = screen.getByRole('link'); - expect(link).toBeInTheDocument(); - - expect(link.getAttribute('href')).toEqual( - `https://getpocket.com/collections/${collection.slug}`, - ); - - const textOnlySlug = screen.queryByText(collection.slug); - expect(textOnlySlug).not.toBeInTheDocument(); - - // Let's update the collection and make sure the active link is still there - collection.status = CollectionStatus.Published; - rerender( - - - , - ); - - // Test that the active link persists - const link2 = screen.getByRole('link'); - expect(link2).toBeInTheDocument(); - - expect(link2.getAttribute('href')).toEqual( - `https://getpocket.com/collections/${collection.slug}`, - ); - - const textOnlySlug2 = screen.queryByText(collection.slug); - expect(textOnlySlug2).not.toBeInTheDocument(); - }); - - it('shows language correctly', () => { - render( - - - , - ); - - expect(screen.getByText(/^de$/i)).toBeInTheDocument(); - }); - - it('shows label if curation category is set', () => { - collection.curationCategory = { - externalId: 'cde-234', - name: 'Food', - slug: 'food', - }; - - render( - - - , - ); - - expect(screen.getByText('Food')).toBeInTheDocument(); - }); - - it('omits label if curation category is not set', () => { - render( - - - , - ); - - expect(screen.queryByText('Food')).not.toBeInTheDocument(); - }); - - it('shows IAB label if IAB categories are set is set', () => { - collection.IABParentCategory = { - externalId: 'cde-234', - name: 'Careers', - slug: 'careers', - }; - - collection.IABChildCategory = { - externalId: 'cde-234', - name: 'Job Fairs', - slug: 'job-fairs', - }; - - render( - - - , - ); - - expect(screen.getByText('Careers → Job Fairs')).toBeInTheDocument(); - }); - - it('omits IAB label if IAB categories are not set', () => { - render( - - - , - ); - - expect(screen.queryByText('Careers → Job Fairs')).not.toBeInTheDocument(); - }); - - it('shows label if labels are set', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - ]; - - render( - - - , - ); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - }); - - it('shows 2 out of 3 labels, click on expand button and show one more label', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - { - externalId: 'label-3', - name: 'region-south-africa', - }, - ]; - - render( - - - , - ); - - // grab expand button, expect +1 as there are total of 3 labels - const expandButton = screen.getByRole('button', { - name: /\+ 1/i, - }); - expect(expandButton).toBeInTheDocument(); - - // click on the expand button - userEvent.click(expandButton); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - expect(screen.getByText('region-south-africa')).toBeInTheDocument(); - }); - - it('shows 2 out of 3 labels, one label is hidden', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - { - externalId: 'label-3', - name: 'region-south-africa', - }, - ]; - - render( - - - , - ); - - // grab expand button, expect +1 as there are total of 3 labels - const expandButton = screen.getByRole('button', { - name: /\+ 1/i, - }); - expect(expandButton).toBeInTheDocument(); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - // expand button not clicked, don't expect this label to shown - expect(screen.queryByText('region-south-africa')).not.toBeInTheDocument(); - }); - - it('omits label if labels are not set', () => { - render( - - - , - ); - - expect(screen.queryByText('region-east-africa')).not.toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/CollectionInfo/CollectionInfo.tsx b/src/collections/components/CollectionInfo/CollectionInfo.tsx deleted file mode 100644 index ed8be27c8..000000000 --- a/src/collections/components/CollectionInfo/CollectionInfo.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from 'react'; -import ReactMarkdown from 'react-markdown'; -import { Box, Chip, Typography } from '@mui/material'; -import AdUnitsIcon from '@mui/icons-material/AdUnits'; -import CategoryIcon from '@mui/icons-material/Category'; -import LanguageIcon from '@mui/icons-material/Language'; - -import { Collection, CollectionStatus } from '../../../api/generatedTypes'; -import { ChipLabelsList } from '../'; -import { flattenAuthors } from '../../../_shared/utils/flattenAuthors'; - -interface CollectionInfoProps { - /** - * An object with everything collection-related in it. - * Except for stories. We load them separately. - */ - collection: Omit; -} - -export const CollectionInfo: React.FC = ( - props, -): JSX.Element => { - const { collection } = props; - - const baseURL = 'https://getpocket.com/collections/'; - - // Link to a published collection or one under review on production. - const linkIsClickable = [ - CollectionStatus.Review, - CollectionStatus.Published, - ].includes(collection.status); - - return ( - <> - - {collection.status} ·{' '} - {flattenAuthors(collection.authors)} - - - } - />{' '} - {collection.curationCategory && ( - } - /> - )}{' '} - {collection.IABParentCategory && collection.IABChildCategory && ( - } - /> - )}{' '} - {collection && collection.labels && ( - - )} - - -

Slug

- - {linkIsClickable && ( - - {`${baseURL}${collection.slug}`} - - )} - {!linkIsClickable && ( - <> - {baseURL} - - {collection.slug} - - - )} - -

Excerpt

- - {collection.excerpt} - -

Intro

- - {collection.intro} - - - ); -}; diff --git a/src/collections/components/CollectionListCard/CollectionListCard.test.tsx b/src/collections/components/CollectionListCard/CollectionListCard.test.tsx deleted file mode 100644 index 8fa7f5294..000000000 --- a/src/collections/components/CollectionListCard/CollectionListCard.test.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter, Router } from 'react-router-dom'; -import { CollectionListCard } from './CollectionListCard'; -import { - Collection, - CollectionLanguage, - CollectionStatus, -} from '../../../api/generatedTypes'; -import { createMemoryHistory } from 'history'; -import { MockedProvider } from '@apollo/client/testing'; -import userEvent from '@testing-library/user-event'; - -describe('The CollectionListCard component', () => { - let collection: Omit; - - beforeEach(() => { - collection = { - externalId: '124abc', - title: 'Hidden Histories of Presidential Medical Dramas', - slug: 'collection-slug', - imageUrl: 'https://placeimg.com/640/480/people?random=494', - excerpt: - 'There’s a long history of presidential ailments, including George Washington’s near-death encounter with the flu, Grover Cleveland’s secret tumor, and the clandestine suffering of John F. Kennedy. ', - intro: - 'There’s a long history of presidential ailments, including George Washington’s near-death encounter with the flu, Grover Cleveland’s secret tumor, and the clandestine suffering of John F. Kennedy. ', - language: CollectionLanguage.De, - status: CollectionStatus.Published, - authors: [], - }; - }); - - it('shows basic collection information', () => { - render( - - - , - ); - - // The photo is present and the alt text is the collection title - const photo = screen.getByAltText(collection.title); - expect(photo).toBeInTheDocument(); - - // The link to the collection page is present and is well-formed - const linkToCollectionPage = screen.getByRole('link'); - expect(linkToCollectionPage).toBeInTheDocument(); - expect(linkToCollectionPage).toHaveAttribute( - 'href', - expect.stringContaining(collection.externalId), - ); - - // The excerpt is also present - const excerpt = screen.getByText(/presidential ailments/i); - expect(excerpt).toBeInTheDocument(); - }); - - it('shows "Published" status correctly', () => { - render( - - - , - ); - - // Shows 'Published' subtitle for a published collection - const published = screen.getByText(/^published/i); - expect(published).toBeInTheDocument(); - - // Doesn't show the other two possible collection states - const draft = screen.queryByText(/^draft/i); - expect(draft).not.toBeInTheDocument(); - const archived = screen.queryByText(/^archived/i); - expect(archived).not.toBeInTheDocument(); - }); - - it('shows language correctly', () => { - render( - - - , - ); - - expect(screen.getByText(/^de$/i)).toBeInTheDocument(); - }); - - it('shows label if curation category is set', () => { - collection.curationCategory = { - externalId: 'cde-234', - name: 'Food', - slug: 'food', - }; - - render( - - - , - ); - - expect(screen.getByText('Food')).toBeInTheDocument(); - }); - - it('omits label if curation category is not set', () => { - render( - - - , - ); - - expect(screen.queryByText('Food')).not.toBeInTheDocument(); - }); - - it('shows IAB label if IAB categories are set is set', () => { - collection.IABParentCategory = { - externalId: 'cde-234', - name: 'Careers', - slug: 'careers', - }; - - collection.IABChildCategory = { - externalId: 'cde-234', - name: 'Job Fairs', - slug: 'job-fairs', - }; - - render( - - - , - ); - - expect(screen.getByText('Careers → Job Fairs')).toBeInTheDocument(); - }); - - it('omits IAB label if IAB categories are not set', () => { - render( - - - , - ); - - expect(screen.queryByText('Careers → Job Fairs')).not.toBeInTheDocument(); - }); - - it('shows label if labels are set', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - ]; - - render( - - - , - ); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - }); - - it('shows 2 out of 3 labels, click on expand button and show one more label', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - { - externalId: 'label-3', - name: 'region-south-africa', - }, - ]; - - render( - - - , - ); - - // grab expand button, expect +1 as there are total of 3 labels - const expandButton = screen.getByRole('button', { - name: /\+ 1/i, - }); - expect(expandButton).toBeInTheDocument(); - - // click on the expand button - userEvent.click(expandButton); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - expect(screen.getByText('region-south-africa')).toBeInTheDocument(); - }); - - it('shows 2 out of 3 labels, one label is hidden', () => { - collection.labels = [ - { - externalId: 'label-1', - name: 'region-east-africa', - }, - { - externalId: 'label-2', - name: 'region-west-africa', - }, - { - externalId: 'label-3', - name: 'region-south-africa', - }, - ]; - - render( - - - , - ); - - // grab expand button, expect +1 as there are total of 3 labels - const expandButton = screen.getByRole('button', { - name: /\+ 1/i, - }); - expect(expandButton).toBeInTheDocument(); - - expect(screen.getByText('region-east-africa')).toBeInTheDocument(); - expect(screen.getByText('region-west-africa')).toBeInTheDocument(); - // expand button not clicked, don't expect this label to shown - expect(screen.queryByText('region-south-africa')).not.toBeInTheDocument(); - }); - - it('omits label if labels are not set', () => { - render( - - - , - ); - - expect(screen.queryByText('region-east-africa')).not.toBeInTheDocument(); - }); - - it("links to an individual collection's page", () => { - const history = createMemoryHistory({ - initialEntries: ['/collections/collections/'], - }); - - render( - - - - - , - ); - - // While the entire card is a giant link, we can click on - // anything we like within that link - i.e., the title of the collection - userEvent.click(screen.getByText(collection.title)); - expect(history.location.pathname).toEqual( - `/collections/collections/${collection.externalId}/`, - ); - - // Let's go back to the Collections page - history.goBack(); - expect(history.location.pathname).toEqual('/collections/collections/'); - - // And click on the image this time - userEvent.click(screen.getByRole('img')); - expect(history.location.pathname).toEqual( - `/collections/collections/${collection.externalId}/`, - ); - }); -}); diff --git a/src/collections/components/CollectionListCard/CollectionListCard.tsx b/src/collections/components/CollectionListCard/CollectionListCard.tsx deleted file mode 100644 index d6d85ae12..000000000 --- a/src/collections/components/CollectionListCard/CollectionListCard.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React from 'react'; -import { Link as RouterLink } from 'react-router-dom'; -import { Box, CardMedia, Chip, Grid, Typography } from '@mui/material'; -import LanguageIcon from '@mui/icons-material/Language'; -import CategoryIcon from '@mui/icons-material/Category'; -import AdUnitsIcon from '@mui/icons-material/AdUnits'; -import ReactMarkdown from 'react-markdown'; - -import { Collection } from '../../../api/generatedTypes'; -import { StyledCardLink, StyledListCard } from '../../../_shared/styled'; -import { ChipLabelsList } from '../'; -import { flattenAuthors } from '../../../_shared/utils/flattenAuthors'; - -interface CollectionListCardProps { - /** - * An object with everything collection-related in it. - * Except for stories. We don't need them in the card. - */ - collection: Omit; -} - -/** - * A compact card that displays collection information and links to the collection page. - * - * @param props - */ -export const CollectionListCard: React.FC = ( - props, -) => { - const { collection } = props; - return ( - - - - - 0 - ? collection.imageUrl - : '/placeholders/collectionSmall.svg' - } - alt={collection.title} - sx={{ - borderRadius: 1, - }} - /> - - - - {collection.title} - - - {collection.status} ·{' '} - {flattenAuthors(collection.authors)} - {' '} - - } - />{' '} - {collection.curationCategory && ( - } - /> - )}{' '} - {collection.IABParentCategory && collection.IABChildCategory && ( - } - /> - )}{' '} - {collection && collection.labels && ( - - )} - - - - {collection.excerpt ? collection.excerpt.substring(0, 100) : ''} - - - - - - - ); -}; diff --git a/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.test.tsx b/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.test.tsx deleted file mode 100644 index ba109e28f..000000000 --- a/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.test.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { - CollectionPartner, - CollectionPartnerAssociation, - CollectionPartnershipType, -} from '../../../api/generatedTypes'; -import { CollectionPartnerAssociationForm } from './CollectionPartnerAssociationForm'; - -describe('The CollectionPartnerAssociationForm component', () => { - let association: CollectionPartnerAssociation; - let partners: CollectionPartner[]; - const handleSubmit = jest.fn(); - - beforeEach(() => { - association = { - externalId: '123-abc', - type: CollectionPartnershipType.Partnered, - partner: { - externalId: 'cde-456', - name: 'Wellness Storm', - url: 'https://getpocket.com/', - imageUrl: 'https://www.test.com/image.png', - blurb: - "Star stuff harvesting star light the only home we've ever known " + - 'explorations finite but unbounded a mote of dust suspended in ' + - 'a sunbeam citizens of distant epochs. ', - }, - }; - - partners = [ - { - externalId: 'cde-456', - name: 'Wellness Storm', - url: 'https://getpocket.com/', - imageUrl: 'https://www.test.com/image.png', - blurb: - "Star stuff harvesting star light the only home we've ever known " + - 'explorations finite but unbounded a mote of dust suspended in ' + - 'a sunbeam citizens of distant epochs. ', - }, - { - externalId: '789-xyz', - name: 'The Cosmos Awaits', - url: 'https://www.example.com/', - imageUrl: 'https://placeimg.com/640/480/people?random=494', - blurb: - 'The sky calls to us vanquish the impossible hundreds of thousands' + - ' a very small stage in a vast cosmic arena white dwarf network' + - ' of wormholes?', - }, - { - externalId: '999-qwerty', - name: 'Billions upon billions', - url: 'https://www.example.com/', - imageUrl: 'https://placeimg.com/640/480/people?random=494', - blurb: - 'The softly dancing ship of the imagination from which we spring ' + - 'gathered by gravity with pretty stories for which there is little' + - ' good evidence and billions upon billions upon billions upon ' + - 'billions upon billions upon billions upon billions.', - }, - ]; - }); - - it('renders successfully', () => { - render( - , - ); - - // there is at least a form and nothing falls over - const form = screen.getByRole('form'); - expect(form).toBeInTheDocument(); - }); - - it('shows two action buttons by default', () => { - render( - , - ); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toEqual(2); - }); - - it('only shows one button if not in edit mode', () => { - render( - , - ); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toEqual(1); - }); - - it('has the requisite fields', () => { - render( - , - ); - - const typeField = screen.getByLabelText('Type'); - expect(typeField).toBeInTheDocument(); - - const partnerField = screen.getByLabelText('Partner'); - expect(partnerField).toBeInTheDocument(); - - const nameField = screen.getByLabelText('Name'); - expect(nameField).toBeInTheDocument(); - - const urlField = screen.getByLabelText('URL'); - expect(urlField).toBeInTheDocument(); - - const blurbField = screen.getByLabelText('Blurb'); - expect(blurbField).toBeInTheDocument(); - }); - - it('validates the "type" field', async () => { - render( - , - ); - - const typeField = screen.getByLabelText(/type/i) as HTMLSelectElement; - - // There is no apparent way to submit a non-existent option, so all that can - // be done here is switch to the other type and check that it works - await waitFor(() => { - userEvent.selectOptions(typeField, [CollectionPartnershipType.Sponsored]); - }); - - const chosenOption = screen.getByRole('option', { - name: CollectionPartnershipType.Sponsored, - }) as HTMLOptionElement; - expect(chosenOption.selected).toBe(true); - - // Check that the other option is no longer selected - const otherOption = screen.getByRole('option', { - name: CollectionPartnershipType.Partnered, - }) as HTMLOptionElement; - expect(otherOption.selected).toBe(false); - }); - - it('validates the "partner" field', async () => { - render( - , - ); - - const partnerField = screen.getByLabelText(/partner/i) as HTMLSelectElement; - - // There is no apparent way to submit a non-existent option, so all that can - // be done here is switch to the other type and check that it works - await waitFor(() => { - userEvent.selectOptions(partnerField, [partners[2].externalId]); - }); - - const chosenOption = screen.getByRole('option', { - name: partners[2].name, - }) as HTMLOptionElement; - expect(chosenOption.selected).toBe(true); - - // Check that the other option is no longer selected - const otherOption = screen.getByRole('option', { - name: association.partner.name, - }) as HTMLOptionElement; - expect(otherOption.selected).toBe(false); - }); - - it('validates the "name" field', async () => { - render( - , - ); - - const nameField = screen.getByLabelText(/name/i); - const saveButton = screen.getByText(/save/i); - - // Submit a name that is too short (cutoff is just two characters) - userEvent.clear(nameField); - userEvent.type(nameField, 'B'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter at least two characters/i), - ).toBeInTheDocument(); - - // Submit a longer name - userEvent.clear(nameField); - userEvent.type(nameField, 'Boots'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter at least two characters/i), - ).not.toBeInTheDocument(); - - // Since we're not modifying any other test data, and the default test data - // we have is more than enough for all the form inputs, the form should be - // submitted successfully - expect(handleSubmit).toHaveBeenCalled(); - }); - - it('validates the "url" field', async () => { - render( - , - ); - - const urlField = screen.getByLabelText(/url/i); - const saveButton = screen.getByText(/save/i); - - // Submit a URL that is too short (under ten characters) - userEvent.clear(urlField); - userEvent.type(urlField, 'https'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter at least 10 characters/i), - ).toBeInTheDocument(); - - // Submit a longer url - userEvent.clear(urlField); - userEvent.type(urlField, 'https://sample-website.com/'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter at least 10 characters/i), - ).not.toBeInTheDocument(); - - // Since we're not modifying any other test data, and the default test data - // we have is more than enough for all the form inputs, the form should be - // submitted successfully - expect(handleSubmit).toHaveBeenCalled(); - }); - - it('validates the "blurb" field', async () => { - render( - , - ); - - const blurbField = screen.getByLabelText(/blurb/i); - const saveButton = screen.getByText(/save/i); - - // Submit a description that is too short (under ten characters) - userEvent.clear(blurbField); - userEvent.type(blurbField, 'Something'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter at least 10 characters/i), - ).toBeInTheDocument(); - - // Submit a longer url - userEvent.clear(blurbField); - userEvent.type( - blurbField, - 'Hmm, this test tends to time out if this line is too long', - ); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter at least 10 characters/i), - ).not.toBeInTheDocument(); - - // Since we're not modifying any other test data, and the default test data - // we have is more than enough for all the form inputs, the form should be - // submitted successfully - expect(handleSubmit).toHaveBeenCalled(); - }); - - it('has markdown preview tabs on the blurb fields', () => { - render( - , - ); - const writeTabs = screen.getAllByText(/write/i); - expect(writeTabs.length).toEqual(1); - - const previewTabs = screen.getAllByText(/preview/i); - expect(previewTabs.length).toEqual(1); - }); -}); diff --git a/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.tsx b/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.tsx deleted file mode 100644 index 00d08928f..000000000 --- a/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import React from 'react'; -import { Grid, LinearProgress } from '@mui/material'; -import { FormikValues, useFormik } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { - FormikSelectField, - FormikTextField, - MarkdownPreview, - SharedFormButtons, - SharedFormButtonsProps, -} from '../../../_shared/components'; -import { - CollectionPartner, - CollectionPartnerAssociation, -} from '../../../api/generatedTypes'; -import { getValidationSchema } from './CollectionPartnerAssociationForm.validation'; - -interface AssociationFormProps { - /** - * An object with everything partner-related in it. - */ - association: CollectionPartnerAssociation; - - /** - * What do we do with the submitted data? - */ - onSubmit: (values: FormikValues, formikHelpers: FormikHelpers) => void; - - /** - * A list of partners for the dropdown - */ - partners: CollectionPartner[]; -} - -/** - * A form for adding a collection-partner association or editing data - * for an existing one. - */ -export const CollectionPartnerAssociationForm: React.FC< - AssociationFormProps & SharedFormButtonsProps -> = (props): JSX.Element => { - const { association, partners, onSubmit, editMode, onCancel } = props; - - // get a list of partner ids for the validation schema - const partnerIds = partners.map((partner: CollectionPartner) => { - return partner.externalId; - }); - - // if we're editing, grab the currently assigned partner's external id, - // otherwise use the first id from the partners array as the default value - const partnerExternalId = - association.partner.externalId.length > 0 - ? association.partner.externalId - : partnerIds[0]; - - /** - * Set up form validation - */ - const formik = useFormik({ - initialValues: { - partnerExternalId, - type: association.type, - name: association.name ?? '', - url: association.url ?? '', - blurb: association.blurb ?? '', - }, - // We don't want to irritate users by displaying validation errors - // before they actually submit the form - validateOnChange: false, - validateOnBlur: false, - validationSchema: getValidationSchema(partnerIds), - onSubmit: (values, formikHelpers) => { - onSubmit(values, formikHelpers); - }, - }); - - const types = ['PARTNERED', 'SPONSORED']; - - return ( -
- - - - {types.map((type) => { - return ( - - ); - })} - - - - - - {partners.map((partner: CollectionPartner) => { - return ( - - ); - })} - - - - -

Optional Overrides

-
- - - - - - - - - - - - - - - - {formik.isSubmitting && ( - - - - )} - - - - -
-
- ); -}; diff --git a/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.validation.tsx b/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.validation.tsx deleted file mode 100644 index 0b8f2afbb..000000000 --- a/src/collections/components/CollectionPartnerAssociationForm/CollectionPartnerAssociationForm.validation.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as yup from 'yup'; -import { CollectionPartnershipType } from '../../../api/generatedTypes'; - -/** - * Validation schema for the CollectionPartnerAssociation add/edit form - */ -export const getValidationSchema = (partnerIds: string[]) => { - return yup.object({ - partnerExternalId: yup.string().required().oneOf(partnerIds), - type: yup - .string() - .required() - .oneOf([ - CollectionPartnershipType.Partnered, - CollectionPartnershipType.Sponsored, - ]), - name: yup - .string() - .trim() - // This is more a sanity check than anything else - wouldn't want anyone to - // accidentally type something in this or the below fields and submit it - // without noticing. 'Name', 'URL', and 'Blurb' fields are optional, but if - // specified, will override the default Partner information so it's important - // they're checked. - .min(2, 'Please enter at least two characters or leave this field blank'), - url: yup - .string() - .trim() - .min(10, 'Please enter at least 10 characters or leave this field blank'), - blurb: yup - .string() - .trim() - .min(10, 'Please enter at least 10 characters or leave this field blank'), - }); -}; diff --git a/src/collections/components/CollectionPartnerAssociationInfo/CollectionPartnerAssociationInfo.tsx b/src/collections/components/CollectionPartnerAssociationInfo/CollectionPartnerAssociationInfo.tsx deleted file mode 100644 index 4f790d833..000000000 --- a/src/collections/components/CollectionPartnerAssociationInfo/CollectionPartnerAssociationInfo.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import React from 'react'; -import { - Box, - Button, - ButtonGroup, - Collapse, - Grid, - Paper, - Typography, -} from '@mui/material'; -import EditIcon from '@mui/icons-material/Edit'; -import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; -import ReactMarkdown from 'react-markdown'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { - CollectionPartnerAssociation, - CollectionPartnershipType, - useDeleteCollectionPartnerAssociationMutation, - useGetCollectionPartnersQuery, - useUpdateCollectionPartnerAssociationImageUrlMutation, - useUpdateCollectionPartnerAssociationMutation, -} from '../../../api/generatedTypes'; -import { HandleApiResponse } from '../../../_shared/components'; -import { CollectionPartnerAssociationForm, ImageUpload } from '../'; -import { useRunMutation, useToggle } from '../../../_shared/hooks'; -import { config } from '../../../config'; - -interface AssociationPreviewProps { - /** - * An object with everything related to an association between - * a collection and a partner. - */ - association: CollectionPartnerAssociation; - - /** - * A helper method that requests the partnership from the API - * whenever the cache needs updating, i.e. on deleting or updating - * the partnership. - */ - refetch: () => void; -} - -/** - * A simple component that shows collection-partner association information. - * - * @param props - * @constructor - */ -export const CollectionPartnerAssociationInfo: React.FC< - AssociationPreviewProps -> = (props): JSX.Element => { - const { association, refetch } = props; - const [showEditForm, toggleEditForm] = useToggle(); - - // Get a helper function that will execute a mutation and show notifications - const { runMutation } = useRunMutation(); - - // Load the partners for the dropdown in the partnership form - const { loading, error, data } = useGetCollectionPartnersQuery({ - variables: { perPage: config.pagination.valuesPerDropdown }, - }); - - // Prepare the "Delete association" mutate function - const [deleteAssociation] = useDeleteCollectionPartnerAssociationMutation(); - - // Delete the association when the user requests this action - const onDelete = (): void => { - runMutation( - deleteAssociation, - { - variables: { - externalId: association.externalId, - }, - }, - 'Partnership deleted successfully', - undefined, - undefined, - refetch, - ); - }; - - // prepare the "update association" mutation - const [updateAssociation] = useUpdateCollectionPartnerAssociationMutation(); - - // Update the association when the edit form is submitted - const onUpdate = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - const options = { - variables: { - externalId: association.externalId, - type: values.type, - partnerExternalId: values.partnerExternalId, - name: values.name ? values.name : null, - url: values.url ? values.url : null, - imageUrl: association.imageUrl, // we'll update it separately - blurb: values.blurb ? values.blurb : null, - }, - }; - - runMutation( - updateAssociation, - options, - 'Partnership updated successfully', - () => { - toggleEditForm(); - formikHelpers.setSubmitting(false); - }, - () => { - formikHelpers.setSubmitting(false); - }, - refetch, - ); - }; - - // prepare the "update story image url" mutation - const [updateImageUrl] = - useUpdateCollectionPartnerAssociationImageUrlMutation(); - - /** - * Save the S3 URL we get back from the API to the collection story record - */ - const handleImageUploadSave = (url: string): void => { - runMutation( - updateImageUrl, - { - variables: { - externalId: association.externalId, - imageUrl: url, - }, - }, - 'Image saved successfully', - ); - }; - - return ( - <> - - - - - - - {association.type === CollectionPartnershipType.Partnered - ? 'In partnership with ' - : 'Brought to you by '} - - {association.name ?? association.partner.name} - - - - - {association.blurb ?? association.partner.blurb} - - - - - - - - - - - - - - - -

Edit partnership

- {!data && } - {data && ( - - )} -
-
-
-
-
- - ); -}; diff --git a/src/collections/components/CollectionSearchForm/CollectionSearchForm.tsx b/src/collections/components/CollectionSearchForm/CollectionSearchForm.tsx deleted file mode 100644 index 367c761aa..000000000 --- a/src/collections/components/CollectionSearchForm/CollectionSearchForm.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import React, { useState } from 'react'; -import { - Autocomplete, - Box, - Grid, - LinearProgress, - TextField, -} from '@mui/material'; -import { Button, FormikSelectField } from '../../../_shared/components'; -import { FormikValues, useFormik } from 'formik'; -import { validationSchema } from './CollectionSearchForm.validation'; -import { Label } from '../../../api/generatedTypes'; - -interface CollectionSearchFormProps { - /** - * All the labels in the system we can filter by. - */ - labels: Label[]; - - /** - * What do we do with the submitted data? - */ - onSubmit: (values: FormikValues) => void; -} - -/** - * A form for searching collections. - */ -export const CollectionSearchForm: React.FC = ( - props, -): JSX.Element => { - const { labels, onSubmit } = props; - - // Keep track of selected labels - const [selectedLabels, setSelectedLabels] = useState([]); - - // Update labels if the user has made any changes - const handleLabelChange = (e: React.ChangeEvent, value: Label[]) => { - setSelectedLabels(value); - - // Explicitly set labels within the validation library, too - otherwise the value - // sent through is undefined - formik.setFieldValue('labels', value); - }; - - /** - * Set up form validation - */ - const formik = useFormik({ - initialValues: { - title: '', - author: '', - status: '', - labels: [], - filterRequired: '', - }, - // We don't want to irritate users by displaying validation errors - // before they actually submit the form - validateOnBlur: false, - validateOnChange: false, - validationSchema, - onSubmit: (values) => { - onSubmit(values); - formik.setSubmitting(false); - }, - }); - - return ( -
- - - - - - - - - - - - - - - - - - - - - option.name} - isOptionEqualToValue={(option, value) => - option.externalId === value.externalId - } - value={selectedLabels} - filterSelectedOptions - renderInput={(params) => ( - - )} - /> - - - {formik.errors && formik.errors.filterRequired && ( - -
- {formik.errors.filterRequired} -
-
- )} - - {formik.isSubmitting && ( - - - - )} - - - - - - - - -
-
- ); -}; diff --git a/src/collections/components/CollectionSearchForm/CollectionSearchForm.validation.tsx b/src/collections/components/CollectionSearchForm/CollectionSearchForm.validation.tsx deleted file mode 100644 index 2f156a4c8..000000000 --- a/src/collections/components/CollectionSearchForm/CollectionSearchForm.validation.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as yup from 'yup'; -import { CollectionStatus } from '../../../api/generatedTypes'; - -export const validationSchema = yup.object({ - title: yup.string().min(3), - author: yup.string().min(3), - labels: yup.array(), - status: yup.mixed().oneOf(Object.values(CollectionStatus)), - filterRequired: yup.bool().when(['title', 'author', 'status', 'labels'], { - is: (title: any, author: any, status: any, labels: any) => - !title && !author && !status && labels.length < 1, - then: yup.bool().required('At least one filter is required.'), - otherwise: yup.bool(), - }), -}); diff --git a/src/collections/components/ImageUpload/ImageUpload.tsx b/src/collections/components/ImageUpload/ImageUpload.tsx deleted file mode 100644 index 33c52ebe0..000000000 --- a/src/collections/components/ImageUpload/ImageUpload.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import React, { useState } from 'react'; -import { - Box, - Button as MuiButton, - Card, - CardActions, - CardMedia, - Grid, - Hidden, - LinearProgress, - Typography, -} from '@mui/material'; -import CloudUploadIcon from '@mui/icons-material/CloudUpload'; -import Dropzone, { FileWithPath } from 'react-dropzone'; -import { - FileUploadInfo, - Modal, - SharedFormButtons, -} from '../../../_shared/components'; -import { StyledDropzoneBox } from '../../../_shared/styled'; -import { curationPalette } from '../../../theme'; -import { useNotifications } from '../../../_shared/hooks'; -import { - Collection, - CollectionAuthor, - CollectionImageUploadInput, - CollectionPartner, - CollectionPartnerAssociation, - CollectionStory, - useImageUploadMutation, -} from '../../../api/generatedTypes'; - -interface ImageUploadProps { - /** - * Any entity with a customizable image - */ - entity: - | Omit - | CollectionAuthor - | CollectionPartner - | CollectionPartnerAssociation - | CollectionStory; - - /** - * A path to a placeholder image to show if no image is available - */ - placeholder: string; - - /** - * A method to call once the image is uploaded to S3 - this typically executes - * another mutation linking the freshly uploaded image to the entity - * - * @param url - */ - onImageSave: (url: string) => void; -} - -/** - * A component that displays an image for an entity such as a Collection or - * CollectionAuthor, or a placeholder if no image is available. - * - * On click, this component opens up a modal with a drag'n'droppable area - * where the user can upload a new image. - * - * @param props - * @constructor - */ -export const ImageUpload: React.FC = (props): JSX.Element => { - const { entity, placeholder, onImageSave } = props; - const { showNotification } = useNotifications(); - - // These state vars are used to show/hide the file upload modal and progress bar - const [imageUploadOpen, setImageUploadOpen] = useState(false); - const [uploadInfo, setUploadInfo] = useState(null); - const [uploadInProgress, setUploadInProgress] = useState(false); - - // This one is for the uploaded file itself - const [imageData, setImageData] = useState< - CollectionImageUploadInput | undefined - >(undefined); - - // And these are for figuring out whether to show the uploaded image - // or a placeholder - const [imageSrc, setImageSrc] = useState(entity.imageUrl); - const [hasImage, setHasImage] = useState( - !!(imageSrc && imageSrc.length > 0), - ); - - // Close the image upload modal when user clicks away or presses the "Cancel" button - const handleClose = () => { - setImageUploadOpen(false); - }; - - // prepare the upload to S3 mutation - const [uploadImage] = useImageUploadMutation(); - - // Process the file the user chose from their PC/mobile device, - // show file info to the user and set data to use in upload mutation - const onDrop = (acceptedFiles: FileWithPath[]) => { - const uploads = acceptedFiles.map((file: FileWithPath, index: number) => { - // Get the actual file - const reader = new FileReader(); - reader.readAsDataURL(file); - - // Load it - reader.onloadend = (e) => { - const contents = e.target?.result; - - // Load the contents of this file to an image element - const image = new Image() as HTMLImageElement; - if (typeof contents === 'string') { - image.src = contents; - - // Set the variables we'll use later when saving the file to S3 - image.onload = function () { - setImageData({ - image: file, - height: image.naturalHeight, - width: image.naturalWidth, - fileSizeBytes: file.size, - }); - }; - } - }; - - // Generate the output to show to the user - return ; - }); - // Set the output, once available, to a state variable to be used in the main component - setUploadInfo(uploads); - }; - - const onSave = () => { - setUploadInProgress(true); - - // Let's upload this thing to S3! - uploadImage({ - variables: imageData, - }) - .then((data) => { - setUploadInProgress(false); - - if (data.data && data.data.collectionImageUpload) { - setImageSrc(data.data.collectionImageUpload.url); - setImageUploadOpen(false); - setHasImage(true); - showNotification('Image successfully uploaded to S3', 'success'); - - // Pass the URL to the parent component function - onImageSave(data.data.collectionImageUpload.url); - } - }) - .catch((error) => { - setUploadInProgress(false); - showNotification(error.message, 'error'); - }); - }; - - return ( - <> - - { - setImageUploadOpen(true); - }} - /> - - { - setImageUploadOpen(true); - }} - > - - - Update image - - - - - - - - - - {({ getRootProps, getInputProps }) => ( - - - - Drag and drop an image here, or click to select one - - - {uploadInfo && uploadInfo.length > 0 ? ( - - Selected: - - ) : null} - {uploadInfo} - {uploadInProgress && ( - - - - )} - - - )} - - - - - - - - - - - ); -}; diff --git a/src/collections/components/LabelForm/LabelForm.test.tsx b/src/collections/components/LabelForm/LabelForm.test.tsx deleted file mode 100644 index 3c0e1f9ef..000000000 --- a/src/collections/components/LabelForm/LabelForm.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { MockedProvider } from '@apollo/client/testing'; -import { SnackbarProvider } from 'notistack'; -import { LabelForm } from './LabelForm'; -import { createLabel1SuccessMock } from '../../integration-test-mocks/createLabels'; - -describe('The LabelForm component', () => { - let mocks = []; - const onSubmit = jest.fn(); - const onCancel = jest.fn(); - - it('should render all the form fields and elements', () => { - render( - , - ); - - // Check if form input and buttons are rendered - const urlInputField = screen.getByLabelText(/label name/i); - expect(urlInputField).toBeInTheDocument(); - - const saveButton = screen.getByText(/save/i); - expect(saveButton).toBeInTheDocument(); - - const cancelButton = screen.getByText(/cancel/i); - expect(cancelButton).toBeInTheDocument(); - }); - - it('should render error message when no label name is provided', async () => { - render( - - - - - , - ); - - const saveButton = screen.getByText(/save/i); - - userEvent.click(saveButton); - const emptyInputError = await screen.findByText(/please add a label name/i); - - expect(emptyInputError).toBeInTheDocument(); - }); - - it('should successfully create a new label', async () => { - mocks = [createLabel1SuccessMock]; - render( - - - - - , - ); - - const saveButton = screen.getByText(/save/i); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter label name - userEvent.type(lableNameField, 'fake-label-1'); - - userEvent.click(saveButton); - await waitFor(() => expect(onSubmit).toHaveBeenCalled()); - }); -}); diff --git a/src/collections/components/LabelForm/LabelForm.tsx b/src/collections/components/LabelForm/LabelForm.tsx deleted file mode 100644 index e0e0bd8e6..000000000 --- a/src/collections/components/LabelForm/LabelForm.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import { Grid, LinearProgress } from '@mui/material'; -import { FormikHelpers, FormikValues, useFormik } from 'formik'; -import { - FormikTextField, - SharedFormButtons, - SharedFormButtonsProps, -} from '../../../_shared/components'; -import { Label } from '../../../api/generatedTypes'; - -import { validationSchema } from './LabelForm.validation'; - -interface LabelFormProps { - onSubmit: ( - values: FormikValues, - formikHelpers: FormikHelpers, - ) => void | Promise; - - // show/hide the loading bar on submissions - isLoaderShowing: boolean; - - /** - * An object with everything label-related in it. It is optional because it is only - * relevant when updating a label. - */ - label?: Label; -} - -/** - * This component houses all the logic and data that will be used in this form. - */ -export const LabelForm: React.FC = ( - props, -) => { - // de-structure props - const { onCancel, onSubmit, isLoaderShowing, label } = props; - - // set up formik object for this form - const formik = useFormik({ - initialValues: { - labelName: label?.name ?? '', - }, - validateOnBlur: false, - validateOnChange: false, - validationSchema, - onSubmit, - }); - - return ( -
- - - - - - {isLoaderShowing && } - - - -
- ); -}; diff --git a/src/collections/components/LabelForm/LabelForm.validation.tsx b/src/collections/components/LabelForm/LabelForm.validation.tsx deleted file mode 100644 index f6273cc80..000000000 --- a/src/collections/components/LabelForm/LabelForm.validation.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as yup from 'yup'; - -export const validationSchema = yup.object({ - labelName: yup - .string() - .trim() - .required('Please add a label name.') - .matches( - /^[a-z0-9-]+$/, - 'Label name can only contain lowercase alphanumeric characters and hyphens.', - ) - .min(2, 'Label name needs to be at least 2 characters.') - .max(255, 'Label name is too long, cannot exceed 255 characters.'), -}); diff --git a/src/collections/components/LabelFormConnector/LabelFormConnector.test.tsx b/src/collections/components/LabelFormConnector/LabelFormConnector.test.tsx deleted file mode 100644 index 853478d04..000000000 --- a/src/collections/components/LabelFormConnector/LabelFormConnector.test.tsx +++ /dev/null @@ -1,397 +0,0 @@ -import React from 'react'; -import { SnackbarProvider } from 'notistack'; -import { render, screen, waitFor } from '@testing-library/react'; -import { MockedProvider } from '@apollo/client/testing'; -import userEvent from '@testing-library/user-event'; -import { LabelFormConnector } from './LabelFormConnector'; -import { - createLabel1SuccessMock, - createLabel2SuccessMock, - createDuplicateLabelErrorMock, - createMinCharLabelErrorMock, - createBadCharLabelErrorMock, -} from '../../integration-test-mocks/createLabels'; -import { - updateLabel1SuccessMock, - updateDuplicateLabelErrorMock, - updateCollectionLabelAssociationErrorMock, -} from '../../integration-test-mocks/updateLabels'; -import { Label } from '../../../api/generatedTypes'; - -describe('LabelFormConnector', () => { - let mocks = []; - const toggleModal = jest.fn(); - - it('loads the form with all labels and buttons', async () => { - mocks = [createLabel1SuccessMock, createLabel2SuccessMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - expect(saveButton).toBeInTheDocument(); - - // grab cancel button - const cancelButton = screen.getByRole('button', { - name: /cancel/i, - }); - expect(cancelButton).toBeInTheDocument(); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - }); - - it('resolves in an error when empty label name', async () => { - mocks = [createLabel1SuccessMock, createLabel2SuccessMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // don't enter label name - userEvent.type(lableNameField, ' '); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - // should resolve in error - expect(screen.getByText('Please add a label name.')).toBeInTheDocument(); - }); - - it('resolves in a duplicate error when creating label', async () => { - mocks = [createDuplicateLabelErrorMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter label name which already exists - userEvent.type(lableNameField, 'fake-label-duplicate'); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - // should resolve in duplicate error - expect( - screen.getByText( - 'A label with the name "fake-label-duplicate" already exists', - ), - ).toBeInTheDocument(); - }); - - it('resolves in a duplicate error when updating label', async () => { - mocks = [updateDuplicateLabelErrorMock]; - const label: Label = { - externalId: 'duplicate-label', - name: 'fake-label-duplicate-update', - }; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - // the lableNameField should already be pre-filled so we don't neeed to enter a label name - expect(lableNameField).toBeInTheDocument(); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - // should resolve in duplicate error - expect( - screen.getByText( - 'A label with the name "fake-label-duplicate-update" already exists', - ), - ).toBeInTheDocument(); - }); - - it('resolves in a collection-label association error when updating label', async () => { - mocks = [updateCollectionLabelAssociationErrorMock]; - const label: Label = { - externalId: 'label-1', - name: 'fake-read-label', - }; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // first clear the pre-filled input - userEvent.clear(screen.getByLabelText(/label name/i)); - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter updated label name - userEvent.type(lableNameField, 'fake-obsessed-label'); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - // should resolve in duplicate error - expect( - screen.getByText( - 'Cannot update label; it is associated with at least one collection', - ), - ).toBeInTheDocument(); - }); - - it('resolves in a min length error', async () => { - mocks = [createMinCharLabelErrorMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter label name of one char - userEvent.type(lableNameField, 'a'); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - // should resolve in min length error - expect( - screen.getByText('Label name needs to be at least 2 characters.'), - ).toBeInTheDocument(); - }); - - it('resolves in a bad char error', async () => { - mocks = [createBadCharLabelErrorMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter label name with special char - userEvent.type(lableNameField, 'fake-label!'); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - // should resolve in min length error - expect( - screen.getByText( - 'Label name can only contain lowercase alphanumeric characters and hyphens.', - ), - ).toBeInTheDocument(); - }); - - it('resolves in no errors, save button works, label created', async () => { - mocks = [createLabel1SuccessMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter label name - userEvent.type(lableNameField, 'fake-label-1'); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - - await waitFor(() => expect(toggleModal).toHaveBeenCalled()); - await waitFor(() => expect(toggleModal).toHaveBeenCalledTimes(1)); - }); - - it('resolves in no errors, save button works, label updated', async () => { - mocks = [updateLabel1SuccessMock]; - const label: Label = { - externalId: 'label-1', - name: 'fake-old-label', - }; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab save button - const saveButton = screen.getByRole('button', { - name: /save/i, - }); - - // first clear the pre-filled input - userEvent.clear(screen.getByLabelText(/label name/i)); - // grab label field - const lableNameField = screen.getByLabelText(/label name/i); - expect(lableNameField).toBeInTheDocument(); - // enter updated label name - userEvent.type(lableNameField, 'fake-new-label'); - // click save button - await waitFor(() => { - userEvent.click(saveButton); - }); - - await waitFor(() => expect(toggleModal).toHaveBeenCalled()); - await waitFor(() => expect(toggleModal).toHaveBeenCalledTimes(1)); - }); - - it('cancel button works', async () => { - mocks = [createLabel1SuccessMock]; - - render( - - - - - , - ); - - // Wait for the form to load - await screen.findByRole('form'); - - // grab cancel button - const cancelButton = screen.getByRole('button', { - name: /cancel/i, - }); - expect(cancelButton).toBeInTheDocument(); - - await waitFor(() => { - userEvent.click(cancelButton); - }); - - await waitFor(() => expect(toggleModal).toHaveBeenCalled()); - await waitFor(() => expect(toggleModal).toHaveBeenCalledTimes(1)); - }); -}); diff --git a/src/collections/components/LabelFormConnector/LabelFormConnector.tsx b/src/collections/components/LabelFormConnector/LabelFormConnector.tsx deleted file mode 100644 index 10dc8583e..000000000 --- a/src/collections/components/LabelFormConnector/LabelFormConnector.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React, { useState } from 'react'; -import { useRunMutation } from '../../../_shared/hooks'; -import { LabelForm } from '../'; -import { FormikHelpers, FormikValues } from 'formik'; -import { - Label, - UpdateLabelInput, - useCreateLabelMutation, - useUpdateLabelMutation, -} from '../../../api/generatedTypes'; - -interface LabelFormConnectorProps { - /** - * Toggle the LabelFormConnectorProps to show/hide as necessary. - */ - toggleModal: VoidFunction; - - /** - * A helper function from Apollo Client that triggers a new API call to refetch - * the data for a given query. - */ - refetch: VoidFunction; - - /** - * An object with everything label-related in it. It is optional because it is only - * relevant when updating a label. - */ - label?: Label; - - /** - * Whether or not to run the createLabel mutation. - */ - runCreateLabelMutation?: boolean; - - /** - * Whether or not to run the updateLabel mutation. - */ - runUpdateLabelMutation?: boolean; -} - -/** - * Parent component for the LabelForm component - */ -export const LabelFormConnector: React.FC = ( - props, -): JSX.Element => { - const { - toggleModal, - refetch, - label, - runCreateLabelMutation, - runUpdateLabelMutation, - } = props; - - const [isLoaderShowing, setIsLoaderShowing] = useState(false); - const [createLabelMutation] = useCreateLabelMutation(); - const [updateLabelMutation] = useUpdateLabelMutation(); - - // Get a helper function that will execute each mutation, show standard notifications - // and execute any additional actions in a callback - const { runMutation } = useRunMutation(); - - const onSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - // if runCreateLabelMutation flag is true, createLabel - if (runCreateLabelMutation) { - runMutation( - createLabelMutation, - { variables: { name: values.labelName } }, - `"${values.labelName} has been created"`, - () => { - setIsLoaderShowing(false); - toggleModal(); - formikHelpers.setSubmitting(false); - }, - () => { - setIsLoaderShowing(false); - formikHelpers.setSubmitting(false); - }, - refetch, - ); - } - // if runUpdateLabelMutation flag is true and label data is passed, updateLabel - if (runUpdateLabelMutation && label) { - // construct the updateLabel input - const input: UpdateLabelInput = { - externalId: label.externalId, - name: values.labelName, - }; - runMutation( - updateLabelMutation, - { variables: { data: input } }, - `updated to "${values.labelName}"`, - () => { - setIsLoaderShowing(false); - toggleModal(); - formikHelpers.setSubmitting(false); - }, - () => { - setIsLoaderShowing(false); - formikHelpers.setSubmitting(false); - }, - refetch, - ); - } - setIsLoaderShowing(true); - }; - return ( - - ); -}; diff --git a/src/collections/components/LabelListCard/LabelListCard.test.tsx b/src/collections/components/LabelListCard/LabelListCard.test.tsx deleted file mode 100644 index 31f1b5403..000000000 --- a/src/collections/components/LabelListCard/LabelListCard.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { LabelListCard } from './LabelListCard'; -import { Label } from '../../../api/generatedTypes'; - -describe('The LabelListCard component', () => { - let label: Label; - - beforeEach(() => { - label = { - externalId: '124abc', - name: 'region-east-africa', - }; - }); - - it('shows label name and edit button', () => { - render( - - - , - ); - - const name = screen.getByText(/region-east-africa/i); - expect(name).toBeInTheDocument(); - - const editButton = screen.getByTestId('edit-label-button'); - expect(editButton).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/LabelListCard/LabelListCard.tsx b/src/collections/components/LabelListCard/LabelListCard.tsx deleted file mode 100644 index 910f06de3..000000000 --- a/src/collections/components/LabelListCard/LabelListCard.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { Button, ButtonGroup, Card, Grid, Typography } from '@mui/material'; -import EditIcon from '@mui/icons-material/Edit'; -import { Label } from '../../../api/generatedTypes'; -import { curationPalette } from '../../../theme'; -import { LabelModal } from '../../components'; -import { useToggle } from '../../../_shared/hooks'; - -interface LabelListCardProps { - /** - * An object with everything label-related in it. - */ - label: Label; - - /** - * A helper function from Apollo Client that triggers a new API call to refetch - * the data for a given query. - */ - refetch: VoidFunction; -} - -/** - * A compact card that displays label name. - * - * @param props - */ -export const LabelListCard: React.FC = (props) => { - const { label, refetch } = props; - const [labelModalOpen, toggleLabelModal] = useToggle(false); - return ( - - - - - {label.name} - - - - - - - - - ); -}; diff --git a/src/collections/components/LabelModal/LabelModal.test.tsx b/src/collections/components/LabelModal/LabelModal.test.tsx deleted file mode 100644 index 8e9d4f2f3..000000000 --- a/src/collections/components/LabelModal/LabelModal.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { SnackbarProvider } from 'notistack'; -import { MockedProvider } from '@apollo/client/testing'; -import { LabelModal } from './LabelModal'; - -describe('The LabelModal component', () => { - const toggleModal = jest.fn; - - it('should render the modal and the LabelForm component for adding a new label', () => { - render( - - - - - , - ); - - // using the modal heading to fetch it - const labelModal = screen.getByText(/add a new label/i); - - // fetching the form component that is rendered within this modal component - const labelForm = screen.getByLabelText(/label name/i); - - expect(labelModal).toBeInTheDocument(); - expect(labelForm).toBeInTheDocument(); - }); - - it('should render the modal and the LabelForm component for updating a label', () => { - render( - - - - - , - ); - - // using the modal heading to fetch it - const labelModal = screen.getByText(/edit label/i); - - // fetching the form component that is rendered within this modal component - const labelForm = screen.getByLabelText(/label name/i); - - expect(labelModal).toBeInTheDocument(); - expect(labelForm).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/LabelModal/LabelModal.tsx b/src/collections/components/LabelModal/LabelModal.tsx deleted file mode 100644 index 441cbd3a5..000000000 --- a/src/collections/components/LabelModal/LabelModal.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import { Grid } from '@mui/material'; -import { Modal } from '../../../_shared/components'; -import { LabelFormConnector } from '../'; -import { Label } from '../../../api/generatedTypes'; - -interface LabelModalProps { - /** - * Whether the modal is visible on the screen or not. - */ - isOpen: boolean; - - /** - * Toggle the LabelModalProps to show/hide as necessary. - */ - toggleModal: VoidFunction; - - /** - * Label Modal custom title. - */ - modalTitle: string; - - /** - * A helper function from Apollo Client that triggers a new API call to refetch - * the data for a given query. - */ - refetch: VoidFunction; - - /** - * An object with everything label-related in it. It is optional because it is only - * relevant when updating a label. - */ - label?: Label; - - /** - * Whether or not to run the createLabel mutation. - */ - runCreateLabelMutation?: boolean; - - /** - * Whether or not to run the updateLabel mutation. - */ - runUpdateLabelMutation?: boolean; -} - -/** - * Parent component for the LabelModal component - */ -export const LabelModal: React.FC = (props): JSX.Element => { - const { - isOpen, - toggleModal, - modalTitle, - refetch, - label, - runCreateLabelMutation, - runUpdateLabelMutation, - } = props; - - return ( - - - -

{modalTitle}

-
- - -
-
- ); -}; diff --git a/src/collections/components/PartnerForm/PartnerForm.test.tsx b/src/collections/components/PartnerForm/PartnerForm.test.tsx deleted file mode 100644 index 32583ac20..000000000 --- a/src/collections/components/PartnerForm/PartnerForm.test.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { PartnerForm } from './PartnerForm'; -import { CollectionPartner } from '../../../api/generatedTypes'; - -describe('The PartnerForm component', () => { - let partner: CollectionPartner; - const handleSubmit = jest.fn(); - - beforeEach(() => { - partner = { - externalId: '123-abc', - name: 'Configuration Captivation', - url: 'https://configuration-captivation.io', - imageUrl: 'http://placeimg.com/640/480/tech', - blurb: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - }; - }); - - it('renders successfully', () => { - render(); - - // there is at least a form and nothing falls over - const form = screen.getByRole('form'); - expect(form).toBeInTheDocument(); - }); - - it('shows three action buttons by default', () => { - render(); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toEqual(2); - }); - - it('only shows one button if cancel button is not requested', () => { - render( - , - ); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toEqual(1); - }); - - it('displays partner information', () => { - render(); - - const nameField = screen.getByLabelText('Name'); - expect(nameField).toBeInTheDocument(); - - const urlField = screen.getByLabelText('URL'); - expect(urlField).toBeInTheDocument(); - - const blurbField = screen.getByLabelText('Blurb'); - expect(blurbField).toBeInTheDocument(); - }); - - it('validates the "name" field', async () => { - render(); - - const nameField = screen.getByLabelText(/name/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(nameField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the name of the partner company/i), - ).toBeInTheDocument(); - expect( - screen.queryByText(/please enter at least two characters/i), - ).not.toBeInTheDocument(); - - // Submit a name that is too short (under two characters) - userEvent.type(nameField, 'B'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the name of the partner company/i), - ).not.toBeInTheDocument(); - expect( - screen.queryByText(/please enter at least two characters/i), - ).toBeInTheDocument(); - - // Submit a name that satisfies all the requirements - userEvent.type(nameField, 'Construction Contraption'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the name of the partner company/i), - ).not.toBeInTheDocument(); - expect( - screen.queryByText(/please enter at least two characters/i), - ).not.toBeInTheDocument(); - }); - - it('validates the URL field', async () => { - render(); - - const urlField = screen.getByLabelText(/url/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(urlField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a url/i)).toBeInTheDocument(); - expect( - screen.queryByText(/url must be at least 12 characters long/i), - ).not.toBeInTheDocument(); - - // Submit a URL that is too short (under 12 characters) - userEvent.type(urlField, 'http://'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a url/i)).not.toBeInTheDocument(); - expect( - screen.queryByText(/url must be at least 12 characters long/i), - ).toBeInTheDocument(); - - // Submit a URL that satisfies all the requirements - userEvent.type(urlField, 'http://construction-contraption.com'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect(screen.queryByText(/please enter a url/i)).not.toBeInTheDocument(); - expect( - screen.queryByText(/url must be at least 12 characters long/i), - ).not.toBeInTheDocument(); - }); - - it('requires the blurb field', async () => { - render(); - - const blurbField = screen.getByLabelText(/blurb/i); - const saveButton = screen.getByText(/save/i); - - // Submit an empty field - userEvent.clear(blurbField); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the blurb for the partner company/i), - ).toBeInTheDocument(); - - // Submit some text - userEvent.type(blurbField, 'Lorem ipsum'); - await waitFor(() => { - userEvent.click(saveButton); - }); - expect( - screen.queryByText(/please enter the blurb for the partner company/i), - ).not.toBeInTheDocument(); - }); - - it('has markdown preview tabs', () => { - render(); - - expect(screen.getByText(/write/i)).toBeInTheDocument(); - expect(screen.getByText(/preview/i)).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/PartnerForm/PartnerForm.tsx b/src/collections/components/PartnerForm/PartnerForm.tsx deleted file mode 100644 index d91b28ef7..000000000 --- a/src/collections/components/PartnerForm/PartnerForm.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react'; -import { Grid, LinearProgress } from '@mui/material'; -import { FormikValues, useFormik } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { - FormikTextField, - MarkdownPreview, - SharedFormButtons, - SharedFormButtonsProps, -} from '../../../_shared/components'; -import { validationSchema } from './PartnerForm.validation'; -import { CollectionPartner } from '../../../api/generatedTypes'; - -interface PartnerFormProps { - /** - * An object with everything partner-related in it. - */ - partner: CollectionPartner; - - /** - * What do we do with the submitted data? - */ - onSubmit: (values: FormikValues, formikHelpers: FormikHelpers) => void; -} - -/** - * A form for adding authors or editing information for existing authors - */ -export const PartnerForm: React.FC< - PartnerFormProps & SharedFormButtonsProps -> = (props): JSX.Element => { - const { partner, onSubmit, editMode, onCancel } = props; - - /** - * Set up form validation - */ - const formik = useFormik({ - initialValues: { - name: partner.name ?? '', - url: partner.url ?? '', - blurb: partner.blurb ?? '', - }, - // We don't want to irritate users by displaying validation errors - // before they actually submit the form - validateOnChange: false, - validateOnBlur: false, - validationSchema, - onSubmit: (values, formikHelpers) => { - onSubmit(values, formikHelpers); - }, - }); - - return ( -
- - - - - - - - - - - - - - - - {formik.isSubmitting && ( - - - - )} - - - - - -
- ); -}; diff --git a/src/collections/components/PartnerForm/PartnerForm.validation.tsx b/src/collections/components/PartnerForm/PartnerForm.validation.tsx deleted file mode 100644 index 097afbf58..000000000 --- a/src/collections/components/PartnerForm/PartnerForm.validation.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as yup from 'yup'; - -/** - * Validation schema for the Partner add/edit form - */ -export const validationSchema = yup.object({ - name: yup - .string() - .trim() - .required('Please enter the name of the partner company') - .min(2, 'Please enter at least two characters'), - url: yup - .string() - .trim() - .required('Please enter a URL') - .min(12, 'URL must be at least 12 characters long'), - blurb: yup - .string() - .trim() - .required('Please enter the blurb for the partner company'), -}); diff --git a/src/collections/components/PartnerInfo/PartnerInfo.test.tsx b/src/collections/components/PartnerInfo/PartnerInfo.test.tsx deleted file mode 100644 index 2517a8e7f..000000000 --- a/src/collections/components/PartnerInfo/PartnerInfo.test.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { PartnerInfo } from './PartnerInfo'; -import { CollectionPartner } from '../../../api/generatedTypes'; -import { MockedProvider } from '@apollo/client/testing'; - -describe('The PartnerInfo component', () => { - let partner: CollectionPartner; - - beforeEach(() => { - partner = { - externalId: '123-abc', - name: 'Conniption Constitution', - url: 'https://test.com/', - imageUrl: 'http://placeimg.com/640/480/tech', - blurb: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - }; - }); - - it('shows basic partner information', () => { - render( - - - - - , - ); - - // The link to the partner site is present - const link = screen.getByRole('link'); - expect(link).toBeInTheDocument(); - expect(link).toHaveAttribute('href', expect.stringContaining(partner.url)); - - // The partner blurb is present - const blurb = screen.getByText(/voluptatem est aut/i); - expect(blurb).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/PartnerInfo/PartnerInfo.tsx b/src/collections/components/PartnerInfo/PartnerInfo.tsx deleted file mode 100644 index 87554b01b..000000000 --- a/src/collections/components/PartnerInfo/PartnerInfo.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { Typography } from '@mui/material'; -import ReactMarkdown from 'react-markdown'; -import { CollectionPartner } from '../../../api/generatedTypes'; - -interface PartnerInfoProps { - /** - * An object with everything partner-related in it. - */ - partner: CollectionPartner; -} - -/** - * A simple component that shows partner information. The name of the partner - * and their accompanying image are rendered by other components - * on the Partner page. - * - * @param props - * @constructor - */ -export const PartnerInfo: React.FC = (props): JSX.Element => { - const { partner } = props; - - return ( - <> - - {partner.url} - - {partner.blurb} - - ); -}; diff --git a/src/collections/components/PartnerListCard/PartnerListCard.test.tsx b/src/collections/components/PartnerListCard/PartnerListCard.test.tsx deleted file mode 100644 index e9b4907d2..000000000 --- a/src/collections/components/PartnerListCard/PartnerListCard.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter, Router } from 'react-router-dom'; -import { PartnerListCard } from './PartnerListCard'; -import { CollectionPartner } from '../../../api/generatedTypes'; -import { MockedProvider } from '@apollo/client/testing'; -import userEvent from '@testing-library/user-event'; -import { createMemoryHistory } from 'history'; - -describe('The PartnerListCard component', () => { - let partner: CollectionPartner; - - beforeEach(() => { - partner = { - externalId: '123-abc', - name: 'Constellation Consolation', - url: 'https://getpocket.com/', - imageUrl: 'http://placeimg.com/640/480/tech', - blurb: - 'Incidunt corrupti earum. Quasi aut qui magnam eum. ' + - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. ' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - }; - }); - - it('shows basic partner information', () => { - render( - - - , - ); - - // The partner image is present and the alt text is the partner's name - const image = screen.getByAltText(partner.name); - expect(image).toBeInTheDocument(); - - // The link to the partner page is present and is well-formed - const link = screen.getByRole('link'); - expect(link).toBeInTheDocument(); - expect(link).toHaveAttribute( - 'href', - expect.stringContaining(partner.externalId), - ); - - // The blurb is also present - const blurb = screen.getByText(/voluptatem est aut/i); - expect(blurb).toBeInTheDocument(); - }); - - it("links to an individual partner's page", () => { - const history = createMemoryHistory({ - initialEntries: ['/collections/partners/'], - }); - - render( - - - - - , - ); - - // While the entire card is a giant link, we can click on - // anything we like within that link - i.e., the partners's name - userEvent.click(screen.getByText(partner.name)); - expect(history.location.pathname).toEqual( - `/collections/partners/${partner.externalId}/`, - ); - - // Let's go back to the Partners page - history.goBack(); - expect(history.location.pathname).toEqual('/collections/partners/'); - - // And click on the image this time - userEvent.click(screen.getByRole('img')); - expect(history.location.pathname).toEqual( - `/collections/partners/${partner.externalId}/`, - ); - }); -}); diff --git a/src/collections/components/PartnerListCard/PartnerListCard.tsx b/src/collections/components/PartnerListCard/PartnerListCard.tsx deleted file mode 100644 index 521a8e7bf..000000000 --- a/src/collections/components/PartnerListCard/PartnerListCard.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import ReactMarkdown from 'react-markdown'; -import { Link as RouterLink } from 'react-router-dom'; -import { CardMedia, Grid, Typography } from '@mui/material'; -import { CollectionPartner } from '../../../api/generatedTypes'; -import { StyledCardLink, StyledListCard } from '../../../_shared/styled'; - -interface PartnerListCardProps { - /** - * An object with everything partner-related in it. - */ - partner: CollectionPartner; -} - -/** - * A compact card that displays partner information and links to the partner page. - * - * @param props - */ -export const PartnerListCard: React.FC = (props) => { - const { partner } = props; - - // We pass the partner object along with the link so that when the user clicks - // on the card to go to an individual partner's page, the page is loaded instantly. - return ( - - - - - 0 - ? partner.imageUrl - : '/placeholders/collectionSmall.svg' - } - alt={partner.name} - sx={{ - borderRadius: 1, - }} - /> - - - - {partner.name} - - - {partner.url} - - - - {partner.blurb ? partner.blurb.substring(0, 100) : ''} - - - - - - - ); -}; diff --git a/src/collections/components/ReorderableCollectionStoryList/ReorderableCollectionStoryList.tsx b/src/collections/components/ReorderableCollectionStoryList/ReorderableCollectionStoryList.tsx deleted file mode 100644 index ef6421561..000000000 --- a/src/collections/components/ReorderableCollectionStoryList/ReorderableCollectionStoryList.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from 'react'; -import { Typography } from '@mui/material'; -import { - DragDropContext, - Draggable, - Droppable, - DropResult, -} from 'react-beautiful-dnd'; -import { StoryListCard } from '../'; -import { CollectionStory } from '../../../api/generatedTypes'; - -interface CollectionStoryListProps { - /** - * All the stories that belong to a collection - */ - stories: CollectionStory[]; - - /** - * - */ - showFromPartner: boolean; - - /** - * A callback function that will run a mutation every time a story's order - * in the list is updated - * @param result - */ - reorder: (result: DropResult) => void; - - /** - * A special Apollo Client helper that fetches updated query results - * from the API on demand - here we pass it on to StoryListCard - * that runs it if a story is deleted. - */ - refetch: () => void; -} - -/** - * This component displays a list of stories for a collection and allows users - * to drag and drop stories to change the order they appear in the collection. - * - * @param props - * @constructor - */ -export const ReorderableCollectionStoryList: React.FC< - CollectionStoryListProps -> = (props): JSX.Element => { - const { stories, showFromPartner, reorder, refetch } = props; - - return ( - - - {(provided) => ( - - {stories.map((story: CollectionStory, index: number) => { - return ( - - {(provided) => { - return ( - - - - ); - }} - - ); - })} - {provided.placeholder} - - )} - - - ); -}; diff --git a/src/collections/components/StoryCard/StoryCard.test.tsx b/src/collections/components/StoryCard/StoryCard.test.tsx deleted file mode 100644 index 966c8d120..000000000 --- a/src/collections/components/StoryCard/StoryCard.test.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { StoryCard } from './StoryCard'; -import { CollectionStory } from '../../../api/generatedTypes'; -import { flattenAuthors } from '../../../_shared/utils/flattenAuthors'; - -describe('The StoryCard component', () => { - let story: CollectionStory; - - beforeEach(() => { - story = { - externalId: '124abc', - title: 'Incidunt corrupti earum', - url: 'https://getpocket.com/', - imageUrl: 'http://placeimg.com/640/480/people?random=494', - excerpt: - 'Quia non dolores voluptatem est aut. Id officiis nulla est.\n \r' + - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit. \n \r' + - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - authors: [ - { name: 'Mary Shelley', sortOrder: 1 }, - { name: 'Percy Shelley', sortOrder: 2 }, - ], - publisher: 'Lackington, Hughes, Harding, Mavor, & Jones', - fromPartner: false, - }; - }); - - it('displays story information', () => { - render( - - - , - ); - - // The link to the story is present and well-formed - const linkToStory = screen.getByRole('link'); - expect(linkToStory).toBeInTheDocument(); - expect(linkToStory).toHaveAttribute( - 'href', - expect.stringMatching(story.url), - ); - expect(linkToStory).toHaveAttribute( - 'target', - expect.stringMatching('_blank'), - ); - - // the title is present - const title = screen.getByText(story.title); - expect(title).toBeInTheDocument(); - - // the authors are presented in a comma-separated list - const expectedAuthors = flattenAuthors(story.authors); - const authors = screen.getByText(expectedAuthors); - expect(authors).toBeInTheDocument(); - - // the excerpt is present - looking for a partial match here - // as the text is broken down by tags - const excerpt = screen.getByText(/quia non dolores voluptatem est aut/i); - expect(excerpt).toBeInTheDocument(); - - // the publisher is present - const publisher = screen.getByText(story.publisher!); - expect(publisher).toBeInTheDocument(); - - // The author bio is also present - const bio = screen.getByText(/voluptatem est aut/i); - expect(bio).toBeInTheDocument(); - }); - - it('uses Markdown to display the story excerpt', () => { - render( - - - , - ); - - // The sample excerpt has some line breaks - // and should be rendered as three paragraphs. - const firstParagraph = screen.getByText( - 'Quia non dolores voluptatem est aut. Id officiis nulla est.', - ); - expect(firstParagraph).toBeInTheDocument(); - - const secondParagraph = screen.getByText( - 'Harum et velit debitis. Quia assumenda commodi et dolor. ' + - 'Ut dicta veritatis perspiciatis suscipit.', - ); - expect(secondParagraph).toBeInTheDocument(); - - const thirdParagraph = screen.getByText( - 'Aspernatur reprehenderit laboriosam voluptates ut. Ut minus aut est.', - ); - expect(thirdParagraph).toBeInTheDocument(); - }); - - it('shows the middot symbol when author is present', () => { - render( - - - , - ); - - const middot = screen.getByText('\u00b7'); - expect(middot).toBeInTheDocument(); - }); - - it('omits the middot symbol if story has no authors', () => { - story.authors = []; - - render( - - - , - ); - - const middot = screen.queryByText('\u00b7'); - expect(middot).not.toBeInTheDocument(); - }); - - it('displays "From partner/sponsor" if a story is sponsored', () => { - story.fromPartner = true; - - render( - - - , - ); - - const fromPartner = screen.getByText(/from partner\/sponsor/i); - expect(fromPartner).toBeInTheDocument(); - }); -}); diff --git a/src/collections/components/StoryCard/StoryCard.tsx b/src/collections/components/StoryCard/StoryCard.tsx deleted file mode 100644 index d5c1030ad..000000000 --- a/src/collections/components/StoryCard/StoryCard.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import { Grid, Hidden, Typography } from '@mui/material'; -import CheckIcon from '@mui/icons-material/Check'; -import ReactMarkdown from 'react-markdown'; -import { CollectionStory } from '../../../api/generatedTypes'; -import { flattenAuthors } from '../../../_shared/utils/flattenAuthors'; - -interface StoryCardProps { - /** - * A story that belongs to one or more collections - */ - story: CollectionStory; -} - -/** - * A layout only component that displays Collection Story information such as title, authors, excerpt - */ -export const StoryCard: React.FC = (props): JSX.Element => { - const { story } = props; - - // Work out a comma-separated list of authors if there are any for this story - const displayAuthors = flattenAuthors(story.authors); - - // The · character is only needed if the story has authors as it separates - // the list of authors and the name of the publisher. - // There appears to be no way to display an HTML special character conditionally - // (well, except for setting innerHTML directly) other than assigning it to a variable - const middot = '\u00b7'; - - return ( - <> - - - {story.title} - - - - - {displayAuthors} - {displayAuthors.length > 0 && ` ${middot} `} - {story.publisher} - - {story.fromPartner && ( - - - - - - - From partner/sponsor - - - - )} - - - - {story.excerpt ? story.excerpt : ''} - - - - - ); -}; diff --git a/src/collections/components/StoryForm/StoryForm.tsx b/src/collections/components/StoryForm/StoryForm.tsx deleted file mode 100644 index b88b9239c..000000000 --- a/src/collections/components/StoryForm/StoryForm.tsx +++ /dev/null @@ -1,403 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { ApolloError } from '@apollo/client'; -import { - Box, - CardMedia, - CircularProgress, - FormControlLabel, - Grid, - LinearProgress, - Switch, - TextField, - Typography, -} from '@mui/material'; -import { FormikValues, useFormik } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { - Button, - FormikTextField, - MarkdownPreview, - SharedFormButtons, - SharedFormButtonsProps, -} from '../../../_shared/components'; -import { useNotifications } from '../../../_shared/hooks'; -import { validationSchema } from './StoryForm.validation'; -import { - CollectionStory, - useGetOpenGraphFieldsLazyQuery, - useGetStoryFromParserLazyQuery, -} from '../../../api/generatedTypes'; -import { flattenAuthors } from '../../../_shared/utils/flattenAuthors'; -import { applyApTitleCase } from '../../../_shared/utils/applyApTitleCase'; -import { applyCurlyQuotes } from '../../../_shared/utils/applyCurlyQuotes'; -import { TextSwitchLink } from '../TextSwitchLink/TextSwitchLink'; - -interface StoryFormProps { - /** - * An object with everything story-related in it. - */ - story: CollectionStory; - - /** - * What do we do with the submitted data? - */ - onSubmit: ( - values: FormikValues, - formikHelpers: FormikHelpers, - ) => void | Promise; - - /** - * Whether to show the full form or just the URL+Populate button - * one-line version. - */ - showAllFields: boolean; - - /** - * Whether to show the form in edit mode, that is, without the "Populate" button - * and without scrolling the form into view on rendering all the fields. - */ - editMode: boolean; - - /** - * Whether to show the 'fromPartner' field - it's only shown for stories - * within a sponsored/partnered collection. - */ - showFromPartner: boolean; -} - -export const StoryForm: React.FC = ( - props, -): JSX.Element => { - const { - story, - onCancel, - onSubmit, - showAllFields, - editMode, - showFromPartner, - } = props; - - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // Whether to show the full form or just the URL field with the "Populate" button - const [showOtherFields, setShowOtherFields] = - useState(showAllFields); - - // Whether to show the full form or just the URL field with the "Populate" button - const [ogExcerptText, setOgExcerptText] = useState(''); - const [parserItemExcerptText, setParserItemExcerptText] = - useState(''); - - // Listen for when the "Add Story" form opens up to show the rest of the fields - // and scroll to the bottom to bring the entire form into view. - useEffect(() => { - if (!editMode && showOtherFields) { - window.scrollTo({ - top: document.body.scrollHeight, - left: 0, - behavior: 'smooth', - }); - } - }, [showOtherFields, editMode]); - - // Which image do we show? - const [imageSrc, setImageSrc] = useState( - story.imageUrl ? story.imageUrl : '/placeholders/story.svg', - ); - - /** - * Set up form validation - */ - const formik = useFormik({ - initialValues: { - url: story.url ?? '', - title: story.title ?? '', - excerpt: story.excerpt ?? '', - authors: flattenAuthors(story.authors) ?? '', - publisher: story.publisher ?? '', - fromPartner: story.fromPartner ?? false, - }, - // We don't want to irritate users by displaying validation errors - // before they actually submit the form - validateOnChange: false, - validateOnBlur: false, - validationSchema, - onSubmit: (values: FormikValues, formikHelpers: FormikHelpers) => { - onSubmit(values, formikHelpers); - }, - }); - - const [getOGExcerpt] = useGetOpenGraphFieldsLazyQuery({ - fetchPolicy: 'no-cache', - onCompleted: ({ getOpenGraphFields }) => { - if (getOpenGraphFields?.description) { - const excerpt = getOpenGraphFields.description; - formik.setFieldValue('excerpt', excerpt); - setOgExcerptText(excerpt); - } - }, - onError: () => { - // Errors are silent as this is an optional service - }, - }); - - const [getStory, { loading: loadingStoryFromParser }] = - useGetStoryFromParserLazyQuery({ - fetchPolicy: 'no-cache', - onCompleted: (data) => { - // Rather than return errors if it can't parse a URL, the parser - // returns a null object instead - if (data.getItemByUrl) { - // This is the success path - - // If the parser returns multiple authors for the story, - // combine them in one comma-separated string - const commaSeparatedAuthors = data.getItemByUrl.authors - ?.map((author) => { - return author?.name; - }) - .join(', '); - - // set field values with data returned by the parser - formik.setFieldValue('authors', commaSeparatedAuthors); - - // make sure to use the 'resolvedUrl returned from the parser instead of the URL - // submitted by the user - formik.setFieldValue('url', data.getItemByUrl.resolvedUrl); - formik.setFieldValue('title', data.getItemByUrl.title); - formik.setFieldValue( - 'publisher', - data.getItemByUrl.domainMetadata?.name, - ); - setParserItemExcerptText(data.getItemByUrl.excerpt || ''); - if (!ogExcerptText) { - formik.setFieldValue('excerpt', data.getItemByUrl.excerpt); - } - - // Work out the image URL, if any - let imageUrl = ''; - - if ( - data.getItemByUrl.topImageUrl && - data.getItemByUrl.topImageUrl.length > 0 - ) { - // Use the publisher's preferred thumbnail image if it exists - imageUrl = data.getItemByUrl.topImageUrl; - setImageSrc(imageUrl); - } else { - // Try the images array - for YouTube, for example, this returns - // the correct thumbnail - if (data.getItemByUrl.images && data.getItemByUrl.images[0]) { - imageUrl = data.getItemByUrl.images[0].src!; - setImageSrc(imageUrl); - } else { - // Use the placeholder to display something on the frontend - // while the imageUrl field remains empty as set initially - setImageSrc('/placeholders/story.svg'); - } - } - - // Save the normalised imageUrl value in a hidden input field - // to upload to S3 later - formik.setFieldValue('imageUrl', imageUrl); - - // And we're done! - showNotification('Story parsed successfully', 'success'); - } else { - // This is the error path - showNotification(`Story couldn't be parsed`, 'error'); - } - // If this is used to add a story and only the URL is visible, - // show the other fields now that they contain something - // even if the parser can't process the URL at all. - setShowOtherFields(true); - }, - onError: (error: ApolloError) => { - // Show any other errors, i.e. cannot reach the API, etc. - showNotification(error.message, 'error'); - }, - }); - - const fetchStoryData = async () => { - // Make sure we don't send an empty string to the parser - await formik.setFieldTouched('url'); - await formik.validateField('url'); - - // NB: this check doesn't work on initial page load because - // formik.errors remains an empty object after validating - // this field for the first time and the request is still sent - // even though the field is empty and we see a validation error - // in the UI. Subsequent user interactions with the form work as expected. - // Marking this with a TODO to return to at some point in the future - if (!formik.errors.url) { - // Get story data from the parser. 'onComplete' callback specified - // in the prepared query above will fill in the form with the returned data - getStory({ - variables: { - url: formik.values.url, - }, - }); - getOGExcerpt({ - variables: { - url: formik.values.url, - }, - }); - } - }; - - const fixTitle = () => { - formik.setFieldValue( - 'title', - applyCurlyQuotes(applyApTitleCase(formik.values.title)), - ); - }; - - const fixExcerpt = () => { - formik.setFieldValue('excerpt', applyCurlyQuotes(formik.values.excerpt)); - }; - - return ( -
- - - - - - - {!editMode && ( - - - - )} - - - - {showOtherFields && ( - <> - - - - - - - - - - - - -
- - You can change this image in the list view. - -
- - - - - - - {showFromPartner && ( - - - } - label={'From Partner'} - labelPlacement="end" - /> - - )} - - - - - - - { - return formik.setFieldValue('excerpt', textToInsert); - }} - /> - - - - - - - {formik.isSubmitting && ( - - - - )} - - - - - )} -
- - - -
- ); -}; diff --git a/src/collections/components/StoryForm/StoryForm.validation.tsx b/src/collections/components/StoryForm/StoryForm.validation.tsx deleted file mode 100644 index f8e6afd53..000000000 --- a/src/collections/components/StoryForm/StoryForm.validation.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import * as yup from 'yup'; - -export const validationSchema = yup.object({ - url: yup.string().trim().required('Please enter a URL').min(12), - title: yup.string().trim().required('Please enter a title').min(3), - excerpt: yup.string().trim().required('Please enter an excerpt').min(12), - authors: yup - .string() - .trim() - .min( - 2, // minimum could be "AP" - 'Please enter one or more authors, separated by commas.' + - ' Please supply at least two characters or leave this field empty' + - ' if this story has no authors.', - ), - publisher: yup.string(), - fromPartner: yup.boolean(), -}); diff --git a/src/collections/components/StoryListCard/StoryListCard.tsx b/src/collections/components/StoryListCard/StoryListCard.tsx deleted file mode 100644 index 5b7b32aa6..000000000 --- a/src/collections/components/StoryListCard/StoryListCard.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import React from 'react'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { Box, Button, ButtonGroup, Collapse, Grid, Paper } from '@mui/material'; -import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; -import EditIcon from '@mui/icons-material/Edit'; -import { transformAuthors } from '../../../_shared/utils/transformAuthors'; -import { ImageUpload, StoryCard, StoryForm } from '../'; -import { useRunMutation, useToggle } from '../../../_shared/hooks'; -import { - CollectionStory, - useDeleteCollectionStoryMutation, - useUpdateCollectionStoryImageUrlMutation, - useUpdateCollectionStoryMutation, -} from '../../../api/generatedTypes'; -import { StyledListCard } from '../../../_shared/styled'; - -interface StoryListCardProps { - /** - * A story that belongs to one or more collections - */ - story: CollectionStory; - - /** - * A helper method that requests the list of stories from the API - * whenever the cache needs updating, i.e. after one of the stories - * was deleted. - */ - refetch: () => void; - - /** - * Whether to show in the edit form the 'From Partner' switch. - * This value comes from a collection partnership, and is not available - * from within the story object itself. - */ - showFromPartner: boolean; -} - -/** - * A Collection Story card component that is responsible for an awful lot of things: - * displaying all the controls for a story, i.e. image upload, edit and delete buttons, - * running the update/delete/upload new image mutations - the lot! - * - * @param props - */ -export const StoryListCard: React.FC = (props) => { - const { story, refetch, showFromPartner } = props; - const [showEditForm, toggleEditForm] = useToggle(); - - // Get a helper function that will execute a mutation and show notifications - const { runMutation } = useRunMutation(); - - // Prepare the "delete story" mutate function - const [deleteStory] = useDeleteCollectionStoryMutation(); - - // Delete the story when the user requests this action - const onDelete = (): void => { - runMutation( - deleteStory, - { - variables: { - externalId: story.externalId, - }, - }, - 'Story deleted successfully', - refetch, - ); - }; - - // 1. Prepare the "update story" mutation - const [updateStory] = useUpdateCollectionStoryMutation(); - // 2. Update the story when the user submits the form - const onUpdate = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - // prepare authors! They need to be an array of objects again - const authors = transformAuthors(values.authors); - - // Set out all the values that are going to be updated - const variables = { - externalId: story.externalId, - url: values.url, - title: values.title, - excerpt: values.excerpt, - publisher: values.publisher, - // NB: not updating the image here. Uploads are handled separately - imageUrl: story.imageUrl, - authors, - fromPartner: values.fromPartner, - }; - - // Run the mutation - runMutation( - updateStory, - { variables }, - 'Story updated successfully', - () => { - toggleEditForm(); - formikHelpers.setSubmitting(false); - }, - () => { - formikHelpers.setSubmitting(false); - }, - refetch, - ); - }; - - // prepare the "update story image url" mutation - const [updateStoryImageUrl] = useUpdateCollectionStoryImageUrlMutation(); - - /** - * Save the S3 URL we get back from the API to the collection story record - */ - const handleImageUploadSave = (url: string): void => { - runMutation( - updateStoryImageUrl, - { - variables: { - externalId: story.externalId, - imageUrl: url, - }, - }, - 'Image saved successfully', - ); - }; - - return ( - <> - - - - - - - - - - - - - - - - - - - - -

Edit Story

-
- -
-
-
-
- - ); -}; diff --git a/src/collections/components/TextSwitchLink/TextSwitchLink.tsx b/src/collections/components/TextSwitchLink/TextSwitchLink.tsx deleted file mode 100644 index df3472202..000000000 --- a/src/collections/components/TextSwitchLink/TextSwitchLink.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { TextSwitchLinkComponent } from './TextSwitchLinkComponent'; - -/** - * Options for choosing between two possible text descriptions in the 'excerpt' field - * By default OpenGraph description is used, but the user can restore the parser description - */ -export enum DescriptionTextStates { - // Means we don't have both Parser and Opengraph desription so there is no UI to show - IncompleteDescription, - CanInsertParserDesc, - CanRestoreOGDesc, - MatchingDescriptions, -} -export interface SetExceptTextInterface { - (textToInsert: string): void; -} - -interface TextSwitchLinkParams { - parserItemExcerptText: string; - ogExcerptText: string; - updateExcerptText: SetExceptTextInterface; -} - -export const TextSwitchLink = ({ - parserItemExcerptText, - ogExcerptText, - updateExcerptText, -}: TextSwitchLinkParams): JSX.Element | null => { - const manageDescriptionStateForServerInput = () => { - // Constructs the text insertion link state based on data from the server - if (ogExcerptText && parserItemExcerptText) { - if (ogExcerptText === parserItemExcerptText) { - // Both fields match, so No UI needed at all - setDescriptionState(DescriptionTextStates.MatchingDescriptions); - } else { - setDescriptionState(DescriptionTextStates.CanInsertParserDesc); - } - } else { - setDescriptionState(DescriptionTextStates.IncompleteDescription); - } - }; - - useEffect(manageDescriptionStateForServerInput, [ - ogExcerptText, - parserItemExcerptText, - ]); - - const [descriptionState, setDescriptionState] = - useState( - DescriptionTextStates.IncompleteDescription, - ); - - let nextDescriptionState = null; - let uiText = null; - let textToInsert = null; - - switch (descriptionState) { - case DescriptionTextStates.CanInsertParserDesc: - uiText = 'Use Parser Excerpt Instead'; - nextDescriptionState = DescriptionTextStates.CanRestoreOGDesc; - textToInsert = parserItemExcerptText; - break; - case DescriptionTextStates.CanRestoreOGDesc: - uiText = 'Restore OpenGraph Description'; - nextDescriptionState = DescriptionTextStates.CanInsertParserDesc; - textToInsert = ogExcerptText; - break; - default: - return null; - } - - const actionCallback = ( - textToInsert: string, - newDescriptionState: DescriptionTextStates, - ) => { - setDescriptionState(newDescriptionState); - updateExcerptText(textToInsert); - }; - - return ( - - ); -}; diff --git a/src/collections/components/TextSwitchLink/TextSwitchLinkComponent.tsx b/src/collections/components/TextSwitchLink/TextSwitchLinkComponent.tsx deleted file mode 100644 index 4f6fa8ea4..000000000 --- a/src/collections/components/TextSwitchLink/TextSwitchLinkComponent.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Link, styled, Tooltip } from '@mui/material'; -import React from 'react'; -import { DescriptionTextStates } from './TextSwitchLink'; - -const StyledLink = styled(Link)({ - cursor: 'pointer', -}); - -interface TextSwitchLinkComponentParams { - linkDescriptionText: string; - textToInsert: string; - nextDescriptionState: DescriptionTextStates; - actionCallback: any; -} - -export const TextSwitchLinkComponent = ({ - linkDescriptionText, - textToInsert, - nextDescriptionState, - actionCallback, -}: TextSwitchLinkComponentParams): JSX.Element => ( - - { - actionCallback(textToInsert, nextDescriptionState); - }} - sx={{ textDecoration: 'none' }} - > - {linkDescriptionText} - - -); diff --git a/src/collections/components/index.ts b/src/collections/components/index.ts deleted file mode 100644 index 243018d5f..000000000 --- a/src/collections/components/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -export { LabelForm } from './LabelForm/LabelForm'; -export { LabelFormConnector } from './LabelFormConnector/LabelFormConnector'; -export { LabelModal } from './LabelModal/LabelModal'; -export { AuthorForm } from './AuthorForm/AuthorForm'; -export { AuthorInfo } from './AuthorInfo/AuthorInfo'; -export { AuthorListCard } from './AuthorListCard/AuthorListCard'; -export { ChipLabelsList } from './ChipLabelsList/ChipLabelsList'; -export { CollectionForm } from './CollectionForm/CollectionForm'; -export { CollectionInfo } from './CollectionInfo/CollectionInfo'; -export { CollectionListCard } from './CollectionListCard/CollectionListCard'; -export { CollectionPartnerAssociationForm } from './CollectionPartnerAssociationForm/CollectionPartnerAssociationForm'; -export { CollectionPartnerAssociationInfo } from './CollectionPartnerAssociationInfo/CollectionPartnerAssociationInfo'; -export { CollectionSearchForm } from './CollectionSearchForm/CollectionSearchForm'; -export { ImageUpload } from './ImageUpload/ImageUpload'; -export { LabelListCard } from './LabelListCard/LabelListCard'; -export { PartnerForm } from './PartnerForm/PartnerForm'; -export { PartnerInfo } from './PartnerInfo/PartnerInfo'; -export { PartnerListCard } from './PartnerListCard/PartnerListCard'; -export { ReorderableCollectionStoryList } from './ReorderableCollectionStoryList/ReorderableCollectionStoryList'; -export { StoryCard } from './StoryCard/StoryCard'; -export { StoryForm } from './StoryForm/StoryForm'; -export { StoryListCard } from './StoryListCard/StoryListCard'; diff --git a/src/collections/integration-test-mocks/createLabels.ts b/src/collections/integration-test-mocks/createLabels.ts deleted file mode 100644 index ddb58dcd0..000000000 --- a/src/collections/integration-test-mocks/createLabels.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { createLabel } from '../../api/mutations/createLabel'; - -export const createLabel1SuccessMock = { - request: { - query: createLabel, - variables: { - name: 'fake-label-1', - }, - }, - result: { - data: { - createLabel: { - externalId: 'a1', - name: 'fake-label-1', - }, - }, - }, -}; - -export const createLabel2SuccessMock = { - request: { - query: createLabel, - variables: { - name: 'fake-label-2', - }, - }, - result: { - data: { - createLabel: { - externalId: 'a2', - name: 'fake-label-2', - }, - }, - }, -}; - -export const createDuplicateLabelErrorMock = { - request: { - query: createLabel, - variables: { - name: 'fake-label-duplicate', - }, - }, - error: new Error( - 'A label with the name "fake-label-duplicate" already exists', - ), -}; - -export const createMinCharLabelErrorMock = { - request: { - query: createLabel, - variables: { - name: 'a', - }, - }, - error: new Error('Label name needs to be at least 2 characters.'), -}; - -export const createBadCharLabelErrorMock = { - request: { - query: createLabel, - variables: { - name: 'fake-label!', - }, - }, - error: new Error( - 'Label name can only contain lowercase alphanumeric characters and hyphens.', - ), -}; diff --git a/src/collections/integration-test-mocks/updateLabels.ts b/src/collections/integration-test-mocks/updateLabels.ts deleted file mode 100644 index f48c91e11..000000000 --- a/src/collections/integration-test-mocks/updateLabels.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { updateLabel } from '../../api/mutations/updateLabel'; - -export const updateLabel1SuccessMock = { - request: { - query: updateLabel, - variables: { - data: { - externalId: 'label-1', - name: 'fake-new-label', - }, - }, - }, - result: { - data: { - updateLabel: { - externalId: 'label-1', - name: 'fake-new-label', - }, - }, - }, -}; - -export const updateDuplicateLabelErrorMock = { - request: { - query: updateLabel, - variables: { - data: { - externalId: 'duplicate-label', - name: 'fake-label-duplicate-update', - }, - }, - }, - error: new Error( - 'A label with the name "fake-label-duplicate-update" already exists', - ), -}; - -//Cannot update label; it is associated with at least one collection -export const updateCollectionLabelAssociationErrorMock = { - request: { - query: updateLabel, - variables: { - data: { - externalId: 'label-1', - name: 'fake-obsessed-label', - }, - }, - }, - error: new Error( - 'Cannot update label; it is associated with at least one collection', - ), -}; diff --git a/src/collections/pages/AddAuthorPage/AddAuthorPage.test.tsx b/src/collections/pages/AddAuthorPage/AddAuthorPage.test.tsx deleted file mode 100644 index e5225c1e6..000000000 --- a/src/collections/pages/AddAuthorPage/AddAuthorPage.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { MockedProvider, MockedResponse } from '@apollo/client/testing'; -import { ApolloError } from '@apollo/client'; -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { AddAuthorPage } from './AddAuthorPage'; -import { - CreateCollectionAuthorDocument, - CreateCollectionAuthorInput, -} from '../../../api/generatedTypes'; - -describe('The AddAuthor page', () => { - let addAuthorVariables: CreateCollectionAuthorInput; - - beforeEach(() => { - addAuthorVariables = { - name: 'Test Name', - slug: 'test-name', - bio: - "I'm baby tousled tbh 8-bit poke farm-to-table poutine occupy " + - "you probably haven't heard of them lomo chillwave. ", - imageUrl: '', - }; - }); - - xit('shows an error if saving an author was unsuccessful', async () => { - const mocksWithError: MockedResponse[] = [ - { - request: { - query: CreateCollectionAuthorDocument, - variables: addAuthorVariables, - }, - error: new ApolloError({ - networkError: new Error('An error occurred.'), - }), - }, - ]; - - render( - - - , - ); - - // fill out the form - userEvent.type(screen.getByLabelText(/full name/i), 'Test Name'); - userEvent.type(screen.getByLabelText(/slug/i), 'test-name'); - - // submit it - await waitFor(() => { - userEvent.click(screen.getByText(/save/i)); - }); - - // get response - expect(screen.getByText(/an error occurred/i)).toBeInTheDocument(); - }); -}); diff --git a/src/collections/pages/AddAuthorPage/AddAuthorPage.tsx b/src/collections/pages/AddAuthorPage/AddAuthorPage.tsx deleted file mode 100644 index f47394089..000000000 --- a/src/collections/pages/AddAuthorPage/AddAuthorPage.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, Paper } from '@mui/material'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { AuthorForm } from '../../components'; -import { useNotifications } from '../../../_shared/hooks'; -import { - CollectionAuthor, - GetInitialCollectionFormDataDocument, - useCreateCollectionAuthorMutation, -} from '../../../api/generatedTypes'; - -export const AddAuthorPage: React.FC = (): JSX.Element => { - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // This is used to redirect the user to the full author page once - // the record is added successfully - const history = useHistory(); - - // Provide a default author object for the form - const author: CollectionAuthor = { - externalId: '', - name: '', - slug: '', - bio: '', - imageUrl: '', - active: true, - }; - - // prepare the "add new author" mutation - // has to be done at the top level of the component because it's a hook - const [addAuthor] = useCreateCollectionAuthorMutation(); - - /** - * Collect form data and send it to the API - */ - const handleSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - addAuthor({ - variables: { - name: values.name, - slug: values.slug, - bio: values.bio, - imageUrl: '', - active: values.active, - }, - refetchQueries: [ - // The lookup query for collection form dropdowns needs a refresh. - // Since it contains a call to `getCollectionAuthors`, we don't need to - // refresh it separately. - { - query: GetInitialCollectionFormDataDocument, - }, - ], - }) - .then(({ data }) => { - // Success! Move on to the author page to be able to upload a photo, etc. - history.push( - `/collections/authors/${data?.createCollectionAuthor?.externalId}/`, - { - author: data?.createCollectionAuthor, - }, - ); - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - }; - - return ( - <> - -

Add an Author

-

You will be able to add a photo on the next page.

-
- - - - - - - ); -}; diff --git a/src/collections/pages/AddCollectionPage/AddCollectionPage.tsx b/src/collections/pages/AddCollectionPage/AddCollectionPage.tsx deleted file mode 100644 index 110b21913..000000000 --- a/src/collections/pages/AddCollectionPage/AddCollectionPage.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, Paper } from '@mui/material'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { HandleApiResponse } from '../../../_shared/components'; -import { CollectionForm } from '../../components'; -import { useNotifications } from '../../../_shared/hooks'; -import { - Collection, - CollectionLanguage, - CollectionStatus, - GetCollectionsDocument, - Label, - useCreateCollectionMutation, - useGetInitialCollectionFormDataQuery, -} from '../../../api/generatedTypes'; -import { config } from '../../../config'; - -export const AddCollectionPage: React.FC = (): JSX.Element => { - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // This is used to redirect the user to the full collection page once - // the record is added successfully - const history = useHistory(); - - // Provide a default collection object for the form - const collection: Collection = { - externalId: '', - title: '', - slug: '', - excerpt: '', - intro: '', - imageUrl: '', - labels: [], - language: CollectionLanguage.En, - status: CollectionStatus.Draft, - authors: [], - stories: [], - }; - - // Load data for all the dropdowns in the add collection form - const { loading, error, data } = useGetInitialCollectionFormDataQuery({ - variables: { page: 1, perPage: config.pagination.valuesPerDropdown }, - }); - - // prepare the "add new collection" mutation - // has to be done at the top level of the component because it's a hook - const [addCollection] = useCreateCollectionMutation(); - - /** - * Collect form data and send it to the API - */ - const handleSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - labels: Label[], - ): void => { - addCollection({ - variables: { - data: { - title: values.title, - slug: values.slug, - excerpt: values.excerpt, - intro: values.intro, - status: values.status, - authorExternalId: values.authorExternalId, - curationCategoryExternalId: values.curationCategoryExternalId, - IABParentCategoryExternalId: values.IABParentCategoryExternalId, - IABChildCategoryExternalId: values.IABChildCategoryExternalId, - labelExternalIds: labels.map((value: Label) => value.externalId), - language: values.language, - }, - }, - // make sure the relevant Collections tab is updated - // when we add a new collection - refetchQueries: [ - { - query: GetCollectionsDocument, - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Draft, - }, - }, - ], - }) - .then(({ data }) => { - // Success! Move on to the author page to be able to upload a photo, etc. - history.push( - `/collections/collections/${data?.createCollection?.externalId}/`, - { - collection: data?.createCollection, - }, - ); - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - }; - - return ( - <> - -

Add a Collection

-

You will be able to add a hero image on the next page.

-
- - - {!data && } - - {data && - data.getCollectionAuthors && - data.getCurationCategories && - data.getIABCategories && - data.getLanguages && - data.labels && ( - - )} - - - - ); -}; diff --git a/src/collections/pages/AddPartnerPage/AddPartnerPage.tsx b/src/collections/pages/AddPartnerPage/AddPartnerPage.tsx deleted file mode 100644 index 171366003..000000000 --- a/src/collections/pages/AddPartnerPage/AddPartnerPage.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, Paper } from '@mui/material'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { PartnerForm } from '../../components'; -import { useNotifications } from '../../../_shared/hooks'; -import { - CollectionPartner, - GetCollectionPartnersDocument, - useCreateCollectionPartnerMutation, -} from '../../../api/generatedTypes'; -import { config } from '../../../config'; - -export const AddPartnerPage: React.FC = (): JSX.Element => { - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // This is used to redirect the user to the full author page once - // the record is added successfully - const history = useHistory(); - - // Provide a default collection partner object for the form - const partner: CollectionPartner = { - externalId: '', - name: '', - url: '', - imageUrl: '', - blurb: '', - }; - - // prepare the "add new collection partner" mutation - const [addCollectionPartner] = useCreateCollectionPartnerMutation(); - - /** - * Collect form data and send it to the API - */ - const handleSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - addCollectionPartner({ - variables: { - name: values.name, - url: values.url, - blurb: values.blurb, - // Images are added separately, on the individual Partner pages - imageUrl: '', - }, - refetchQueries: [ - // make sure the Partners page is updated when we add a new partner - { - query: GetCollectionPartnersDocument, - variables: { perPage: config.pagination.partnersPerPage }, - }, - ], - }) - .then(({ data }) => { - // Success! Move on to the author page to be able to upload a photo, etc. - history.push( - `/collections/partners/${data?.createCollectionPartner?.externalId}/`, - { - partner: data?.createCollectionPartner, - }, - ); - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - }; - - return ( - <> - -

Add a Partner

-

You will be able to add an image on the next page.

-
- - - - - - - ); -}; diff --git a/src/collections/pages/AuthorListPage/AuthorListPage.tsx b/src/collections/pages/AuthorListPage/AuthorListPage.tsx deleted file mode 100644 index b5c2d1ddc..000000000 --- a/src/collections/pages/AuthorListPage/AuthorListPage.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { Box } from '@mui/material'; -import { - Button, - HandleApiResponse, - LoadMore, -} from '../../../_shared/components'; -import { AuthorListCard } from '../../components'; -import { - CollectionAuthor, - useGetAuthorsQuery, -} from '../../../api/generatedTypes'; -import { useFetchMoreResults } from '../../../_shared/hooks'; -import { config } from '../../../config'; - -/** - * Author List Page - */ -export const AuthorListPage = (): JSX.Element => { - const [loading, reloading, error, data, updateData] = useFetchMoreResults( - useGetAuthorsQuery, - { - variables: { - perPage: config.pagination.authorsPerPage, - page: 1, - }, - }, - ); - - return ( - <> - - -

Authors

-
- - - -
- - {!data && } - - {data && - data.getCollectionAuthors.authors.map((author: CollectionAuthor) => { - return ; - })} - - {data && ( - - )} - - ); -}; diff --git a/src/collections/pages/AuthorPage/AuthorPage.tsx b/src/collections/pages/AuthorPage/AuthorPage.tsx deleted file mode 100644 index e0b4edb5d..000000000 --- a/src/collections/pages/AuthorPage/AuthorPage.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import React, { useState } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; -import { Box, Button, Collapse, Grid, Paper, Typography } from '@mui/material'; -import EditIcon from '@mui/icons-material/Edit'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { HandleApiResponse, ScrollToTop } from '../../../_shared/components'; -import { AuthorForm, AuthorInfo, ImageUpload } from '../../components'; -import { useNotifications } from '../../../_shared/hooks'; -import { - CollectionAuthor, - GetInitialCollectionFormDataDocument, - useGetAuthorByIdQuery, - useUpdateCollectionAuthorImageUrlMutation, - useUpdateCollectionAuthorMutation, -} from '../../../api/generatedTypes'; - -interface AuthorPageProps { - author?: CollectionAuthor; -} - -export const AuthorPage = (): JSX.Element => { - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // prepare the "update author" mutation - // has to be done at the top level of the component because it's a hook - const [updateAuthor] = useUpdateCollectionAuthorMutation(); - - // And this one is only used to set the image url once the we know the S3 link - const [updateAuthorImageUrl] = useUpdateCollectionAuthorImageUrlMutation(); - - /** - * If an Author object was passed to the page from one of the other app pages, - * let's extract it from the routing. - */ - const location = useLocation(); - - const [author, setAuthor] = useState( - location.state?.author - ? // Deep clone a read-only object that comes from the routing - JSON.parse(JSON.stringify(location.state?.author)) - : undefined, - ); - - /** - * If the user came directly to this page (i.e., via a bookmarked page), - * fetch the author info from the the API. - */ - const params = useParams<{ id: string }>(); - const { loading, error, data } = useGetAuthorByIdQuery({ - variables: { - id: params.id, - }, - // Skip query if author object was delivered via the routing - // This is needed because hooks can only be called at the top level - // of the component. - skip: typeof author === 'object', - }); - - if (data) { - //Author is a read only object when returned from Apollo, if we want to - // update it we have to stringify and then parse it - setAuthor(JSON.parse(JSON.stringify(data.getCollectionAuthor))); - } - - const [showEditForm, setShowEditForm] = useState(false); - - const toggleEditForm = (): void => { - setShowEditForm(!showEditForm); - }; - - /** - * Collect form data and send it to the API. - * Update components on page if updates have been saved successfully - */ - const handleSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - updateAuthor({ - variables: { - externalId: author!.externalId, - name: values.name, - slug: values.slug, - bio: values.bio, - active: values.active, - }, - refetchQueries: [ - // The lookup query for collection form dropdowns needs a refresh. - // Since it contains a call to `getCollectionAuthors`, we don't need to - // refresh it separately. - { - query: GetInitialCollectionFormDataDocument, - }, - ], - }) - .then(({ data }) => { - showNotification('Author updated successfully!', 'success'); - - if (author) { - author.bio = data?.updateCollectionAuthor?.bio; - author.name = data?.updateCollectionAuthor?.name!; - author.active = data?.updateCollectionAuthor.active!; - } - toggleEditForm(); - formikHelpers.setSubmitting(false); - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - }; - - /** - * Save the S3 URL we get back from the API to the author record - */ - const handleImageUploadSave = (url: string): void => { - updateAuthorImageUrl({ - variables: { - externalId: author!.externalId, - imageUrl: url, - }, - }) - .then(({ data }) => { - if (author) { - author.imageUrl = data?.updateCollectionAuthorImageUrl?.imageUrl; - showNotification(`Image saved to "${author!.name}"`, 'success'); - } - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - }); - }; - - return ( - <> - - {!data && } - {author && ( - <> - - -

- {author.name} - - Author - -

-
- - - -
- - - - - - - - - - - - - -

Edit Author

-
- -
-
-
- - )} - {/*

Collections by this author

*/} - - ); -}; diff --git a/src/collections/pages/CollectionListPage/CollectionListPage.tsx b/src/collections/pages/CollectionListPage/CollectionListPage.tsx deleted file mode 100644 index 83a6446f9..000000000 --- a/src/collections/pages/CollectionListPage/CollectionListPage.tsx +++ /dev/null @@ -1,264 +0,0 @@ -import React, { useState } from 'react'; -import { Link, useLocation } from 'react-router-dom'; -import { Box } from '@mui/material'; -import { - Button, - CustomTabType, - HandleApiResponse, - LoadMore, - TabPanel, - TabSet, -} from '../../../_shared/components'; -import { CollectionListCard } from '../../components'; -import { - Collection, - CollectionStatus, - useGetCollectionsQuery, -} from '../../../api/generatedTypes'; -import { config } from '../../../config'; -import { useFetchMoreResults } from '../../../_shared/hooks'; - -/** - * Collection List Page - */ -export const CollectionListPage = (): JSX.Element => { - const { pathname } = useLocation(); - - /** - * Set the value of the active tab to path name - drafts tab by default - */ - const [value, setValue] = useState( - pathname ?? '/collections/collections/drafts/', - ); - - // switch to active tab when user clicks on tab heading - const handleChange = ( - event: React.ChangeEvent, - newValue: string, - ): void => { - setValue(newValue); - }; - - // Load draft collections - const [loading, reloading, error, data, fetchMoreDraftCollections] = - useFetchMoreResults(useGetCollectionsQuery, { - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Draft, - }, - }); - - // Load collections under review - const [ - loadingReview, - reloadingReview, - errorReview, - dataReview, - fetchMoreReviewCollections, - ] = useFetchMoreResults(useGetCollectionsQuery, { - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Review, - }, - }); - - // Load published collections - const [ - loadingPublished, - reloadingPublished, - errorPublished, - dataPublished, - fetchMorePublishedCollections, - ] = useFetchMoreResults(useGetCollectionsQuery, { - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Published, - }, - }); - - // Load archived collections - const [ - loadingArchived, - reloadingArchived, - errorArchived, - dataArchived, - fetchMoreArchivedCollections, - ] = useFetchMoreResults(useGetCollectionsQuery, { - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Archived, - }, - }); - - // Define the set of tabs that we're going to show on this page - const tabs: CustomTabType[] = [ - { - label: 'Drafts', - pathname: '/collections/collections/drafts/', - count: data?.searchCollections.pagination.totalResults, - hasLink: true, - }, - { - label: 'Review', - pathname: '/collections/collections/review/', - count: dataReview?.searchCollections.pagination.totalResults, - hasLink: true, - }, - { - label: 'Published', - pathname: '/collections/collections/published/', - count: dataPublished?.searchCollections.pagination.totalResults, - hasLink: true, - }, - { - label: 'Archived', - pathname: '/collections/collections/archived/', - count: dataArchived?.searchCollections.pagination.totalResults, - hasLink: true, - }, - ]; - - return ( - <> - - -

Collections

-
- - - -
- - - - - - - {!data && } - - {data && - data.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - - {data && ( - - )} - - - - {!dataReview && ( - - )} - - {dataReview && - dataReview.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - - {dataReview && ( - - )} - - - - {!dataPublished && ( - - )} - - {dataPublished && - dataPublished.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - - {dataPublished && ( - - )} - - - - {!dataArchived && ( - - )} - - {dataArchived && - dataArchived.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - - {dataArchived && ( - - )} - - - ); -}; diff --git a/src/collections/pages/CollectionPage/CollectionPage.tsx b/src/collections/pages/CollectionPage/CollectionPage.tsx deleted file mode 100644 index 83bdb6313..000000000 --- a/src/collections/pages/CollectionPage/CollectionPage.tsx +++ /dev/null @@ -1,753 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; -import { - Box, - Button, - ButtonGroup, - Collapse, - Grid, - Paper, - Typography, -} from '@mui/material'; -import EditIcon from '@mui/icons-material/Edit'; -import AddIcon from '@mui/icons-material/Add'; -import { DropResult } from 'react-beautiful-dnd'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { HandleApiResponse, ScrollToTop } from '../../../_shared/components'; -import { - CollectionForm, - CollectionInfo, - CollectionPartnerAssociationForm, - CollectionPartnerAssociationInfo, - ImageUpload, - ReorderableCollectionStoryList, - StoryForm, -} from '../../components'; -import { - Collection, - CollectionPartnerAssociation, - CollectionPartnershipType, - CollectionStatus, - CollectionStory, - GetCollectionByExternalIdDocument, - GetCollectionsDocument, - Label, - useCreateCollectionPartnerAssociationMutation, - useCreateCollectionStoryMutation, - useGetCollectionByExternalIdQuery, - useGetCollectionPartnerAssociationLazyQuery, - useGetCollectionPartnersQuery, - useGetCollectionStoriesQuery, - useGetInitialCollectionFormDataQuery, - useImageUploadMutation, - useUpdateCollectionImageUrlMutation, - useUpdateCollectionMutation, - useUpdateCollectionStoryImageUrlMutation, - useUpdateCollectionStorySortOrderMutation, -} from '../../../api/generatedTypes'; -import { - useNotifications, - useRunMutation, - useToggle, -} from '../../../_shared/hooks'; -import { transformAuthors } from '../../../_shared/utils/transformAuthors'; -import { config } from '../../../config'; - -interface CollectionPageProps { - collection?: Omit; -} - -export const CollectionPage = (): JSX.Element => { - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // Set up toggles for form visibility - const [showEditForm, toggleEditForm] = useToggle(); - const [showPartnershipForm, togglePartnershipForm] = useToggle(); - - // Get a helper function that will execute each mutation, show standard notifications - // and execute any additional actions in a callback - const { runMutation } = useRunMutation(); - - // And this one is only used to set the image url once we know the S3 link - const [updateCollectionImageUrl] = useUpdateCollectionImageUrlMutation(); - - // prepare the "create story" mutation - const [createStory] = useCreateCollectionStoryMutation(); - - // prepare the "update story image url" mutation - const [updateStoryImageUrl] = useUpdateCollectionStoryImageUrlMutation(); - - // prepare the "update story sort order" mutation - const [updateStorySortOrder] = useUpdateCollectionStorySortOrderMutation(); - - // prepare the upload to S3 mutation - const [uploadImage] = useImageUploadMutation(); - - /** - * If a Collection object was passed to the page from one of the other app pages, - * let's extract it from the routing. - */ - const location = useLocation(); - - const [collection, setCollection] = useState< - Omit | undefined - >( - location.state?.collection - ? // Deep clone a read-only object that comes from the routing - JSON.parse(JSON.stringify(location.state?.collection)) - : undefined, - ); - - /** - * If the user came directly to this page (i.e., via a bookmarked page), - * fetch the Collection info from the API. - */ - const params = useParams<{ id: string }>(); - const { loading, error, data } = useGetCollectionByExternalIdQuery({ - variables: { - externalId: params.id, - }, - // Skip query if collection object was delivered via the routing - // This is needed because hooks can only be called at the top level - // of the component. - skip: typeof collection === 'object', - }); - - if (data) { - //Collection is a read only object when returned from Apollo, if we want to - // update it we have to stringify and then parse it - setCollection(JSON.parse(JSON.stringify(data.getCollection))); - } - - // Load data for all the dropdowns in the edit collection form - const { - loading: initialCollectionFormLoading, - error: initialCollectionFormError, - data: initialCollectionFormData, - } = useGetInitialCollectionFormDataQuery({ - variables: { page: 1, perPage: 1000 }, - }); - - // Load collection stories - deliberately in a separate query - const { - loading: storiesLoading, - error: storiesError, - data: storiesData, - refetch: refetchStories, - } = useGetCollectionStoriesQuery({ - variables: { - id: params.id, - }, - // This setting lets us switch this query to manual cache updates only - // so that on reordering stories they (stories) don't snap back - // after the first mutation has run - fetchPolicy: 'no-cache', - }); - - if (!storiesData) { - // We need to fetch these stories if they're absent from the cache - refetchStories(); - } - - // Let's keep stories in state to be able to reorder them with drag'n'drop - const [stories, setStories] = useState( - undefined, - ); - // And update the state variable when data is loaded - useEffect(() => { - setStories(storiesData?.getCollection?.stories); - }, [storiesData]); - - // for adding new stories, keep track of what the next story order value should be - const [storySortOrder, setStorySortOrder] = useState(1); - useEffect(() => { - if (stories && stories.length > 0) { - const lastStorySortOrder = stories[stories.length - 1].sortOrder ?? 0; - setStorySortOrder(lastStorySortOrder + 1); - } - }, [stories]); - - // Prepare a query to fetch the collection-partner association, if one exists - const [ - loadAssociation, - { - loading: associationLoading, - error: associationError, - data: associationData, - refetch: refetchAssociation, - }, - ] = useGetCollectionPartnerAssociationLazyQuery(); - - // Load the association once collection data is ready - useEffect(() => { - if (collection) { - loadAssociation({ - variables: { externalId: collection.externalId }, - }); - } - }, [collection, loadAssociation]); - - // 1. Prepare the "update collection" mutation - const [updateCollection] = useUpdateCollectionMutation(); - - // 3. Update the story when the user submits the form - const onCollectionUpdate = ( - values: FormikValues, - formikHelpers: FormikHelpers, - labels: Label[], - ): void => { - const options = { - variables: { - data: { - externalId: collection!.externalId, - title: values.title, - slug: values.slug, - excerpt: values.excerpt, - intro: values.intro, - status: values.status, - authorExternalId: values.authorExternalId, - curationCategoryExternalId: values.curationCategoryExternalId, - IABParentCategoryExternalId: values.IABParentCategoryExternalId, - IABChildCategoryExternalId: values.IABChildCategoryExternalId, - labelExternalIds: labels.map((value: Label) => value.externalId), - language: values.language, - }, - }, - refetchQueries: [ - { - query: GetCollectionByExternalIdDocument, - variables: { - externalId: collection!.externalId, - }, - }, - { - query: GetCollectionsDocument, - // Must have all the required variables for the query to be executed. - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Draft, - }, - }, - { - query: GetCollectionsDocument, - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Review, - }, - }, - { - query: GetCollectionsDocument, - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Published, - }, - }, - { - query: GetCollectionsDocument, - variables: { - page: 1, - perPage: config.pagination.collectionsPerPage, - status: CollectionStatus.Archived, - }, - }, - ], - }; - - const successCallback = (data: any): void => { - if (collection) { - // update our collection object with the data that is brought back - // by the mutation - collection.title = data?.updateCollection?.title!; - collection.slug = data?.updateCollection?.slug!; - collection.excerpt = data?.updateCollection?.excerpt; - collection.intro = data?.updateCollection?.intro; - collection.status = data?.updateCollection?.status!; - collection.authors = data?.updateCollection?.authors!; - collection.curationCategory = data?.updateCollection?.curationCategory!; - collection.IABParentCategory = - data?.updateCollection?.IABParentCategory; - collection.IABChildCategory = data?.updateCollection?.IABChildCategory; - collection.labels = data?.updateCollection?.labels; - collection.language = data?.updateCollection?.language!; - - toggleEditForm(); - formikHelpers.setSubmitting(false); - } - }; - - const errorCallback = (): void => { - formikHelpers.setSubmitting(false); - }; - - runMutation( - updateCollection, - options, - 'Collection successfully updated.', - successCallback, - errorCallback, - ); - }; - - /** - * Save the S3 URL we get back from the API to the collection record - */ - const handleImageUploadSave = (url: string): void => { - updateCollectionImageUrl({ - variables: { - externalId: collection!.externalId, - imageUrl: url, - }, - refetchQueries: [ - { - query: GetCollectionByExternalIdDocument, - variables: { - externalId: collection!.externalId, - }, - }, - ], - }) - .then(({ data }) => { - if (collection) { - collection.imageUrl = data?.updateCollectionImageUrl?.imageUrl!; - showNotification( - `Image saved to "${collection.title.substring(0, 50)}..."`, - 'success', - ); - } - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - }); - }; - - // make sure we regenerate the 'Add Story' form each time a new story - // has been added - const [addStoryFormKey, setAddStoryFormKey] = useState(1); - - /** - * Save a new story - a multi-step process - * @param values - * @param formikHelpers - */ - const handleCreateStorySubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - // First, let's save the new story - // Prepare authors. They need to be an array of objects again - const authors = transformAuthors(values.authors); - - // Save the new story with the S3 URL - createStory({ - variables: { - collectionExternalId: params.id, - url: values.url, - title: values.title, - excerpt: values.excerpt, - publisher: values.publisher, - imageUrl: '', - authors, - sortOrder: storySortOrder, - fromPartner: values.fromPartner, - }, - }) - .then((data) => { - showNotification( - `Added "${data.data?.createCollectionStory?.title.substring( - 0, - 50, - )}..."`, - 'success', - ); - - // If the parser returned an image, let's upload it to S3 - // First, side-step CORS issues that prevent us from downloading - // the image directly from the publisher - const parserImageUrl = - 'https://pocket-image-cache.com/x/filters:no_upscale():format(jpg)/' + - encodeURIComponent(values.imageUrl); - - // Get the file - fetch(parserImageUrl) - .then((res) => res.blob()) - .then((blob) => { - // Upload the file to S3 - uploadImage({ - variables: { - image: blob, - height: 0, - width: 0, - fileSizeBytes: blob.size, - }, - }) - .then((imgUploadData) => { - if ( - imgUploadData.data && - imgUploadData.data.collectionImageUpload - ) { - // Don't show a notification about a successful S3 upload just yet - - // that's just too many. Wait until we save the url to show another - // success message - updateStoryImageUrl({ - variables: { - externalId: data.data?.createCollectionStory?.externalId!, - imageUrl: imgUploadData.data.collectionImageUpload.url, - }, - }) - .then(() => { - showNotification( - 'Image uploaded to S3 and linked to story', - 'success', - ); - // manually refresh the cache - refetchStories(); - formikHelpers.setSubmitting(false); - }) - .catch((error) => { - // manually refresh the cache - refetchStories(); - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - } - }) - .catch((error) => { - // manually refresh the cache - refetchStories(); - showNotification(error.message, 'error'); - }); - }) - .catch((error: Error) => { - showNotification( - 'Could not process image - file may be too large.\n' + - `(Original error: ${error.message})`, - 'error', - ); - // manually refresh the cache - refetchStories(); - formikHelpers.setSubmitting(false); - }); - - setAddStoryFormKey(addStoryFormKey + 1); - formikHelpers.setSubmitting(false); - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - }; - - // provide an empty story object for the 'Add story' form - const emptyStory: CollectionStory = { - externalId: '', - url: '', - title: '', - excerpt: null, - authors: [ - { - name: '', - sortOrder: 0, - }, - ], - publisher: null, - fromPartner: false, - imageUrl: null, - sortOrder: null, - }; - - // Provide a default collection-partnership association for the 'Add Partnership form - const emptyAssociation: CollectionPartnerAssociation = { - externalId: '', - type: CollectionPartnershipType.Partnered, - name: '', - url: '', - imageUrl: '', - blurb: '', - partner: { - externalId: '', - name: '', - url: '', - imageUrl: '', - blurb: '', - }, - }; - - // Load the partners for the dropdown in the partnership form - const { - loading: partnersLoading, - error: partnersError, - data: partnersData, - } = useGetCollectionPartnersQuery({ - variables: { perPage: 1000 }, - }); - - const [createAssociation] = useCreateCollectionPartnerAssociationMutation(); - - const handleCreateAssociationSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - const options = { - variables: { - type: values.type, - partnerExternalId: values.partnerExternalId, - collectionExternalId: collection!.externalId, - name: values.name ? values.name : null, - url: values.url ? values.url : null, - blurb: values.blurb ? values.blurb : null, - }, - }; - runMutation( - createAssociation, - options, - 'Partnership created successfully', - () => { - formikHelpers.setSubmitting(false); - togglePartnershipForm(); - }, - () => { - formikHelpers.setSubmitting(false); - }, - refetchAssociation, - ); - }; - - /** - * Save the new sort order of stories - * @param result - */ - const reorderStories = (result: DropResult) => { - // if a story was dragged out of the list, let it snap back to where it was - // without an error in the console - if (!result.destination) return; - - // save the new order of stories to state - const reorderedStories = Array.from(stories!); - const [story] = reorderedStories.splice(result.source.index, 1); - reorderedStories.splice(result.destination.index, 0, story); - setStories(reorderedStories); - - // save the new order of stories to the database - reorderedStories.forEach((story: CollectionStory, index: number) => { - const newSortOrder = index + 1; - - // update each affected story with the new sort order - if (story.sortOrder !== newSortOrder) { - runMutation( - updateStorySortOrder, - { - variables: { - externalId: story.externalId, - sortOrder: newSortOrder, - }, - }, - `Order updated for "${story.title.substring(0, 50)}..."`, - ); - } - }); - }; - - return ( - <> - - {!data && } - {collection && ( - <> - - -

- {collection.title} - - Collection - -

-
- - - - - -
- - - - - - - - - - - - -

Edit Collection

-
- {!initialCollectionFormData && ( - - )} - - {initialCollectionFormData && - initialCollectionFormData.getCollectionAuthors && - initialCollectionFormData.getCurationCategories && - initialCollectionFormData.getIABCategories && - initialCollectionFormData.getLanguages && ( - - )} -
-
-
- - - - -

Partnership

-
- {associationData && - !associationData.getCollectionPartnerAssociationForCollection && ( - - - - - - )} -
- - {!associationData && ( - - )} - - {associationData && - associationData.getCollectionPartnerAssociationForCollection && - refetchAssociation && - refetchStories && ( - { - refetchAssociation(); - refetchStories(); - }} - /> - )} - - {partnersData && ( - - - - - -

Add Partnership

- {!partnersData && ( - - )} - {partnersData && ( - - )} -
-
-
-
-
- )} -
- - -

Stories

- {!storiesData && ( - - )} - {stories && associationData && ( - - )} -
- - - -

Add Story

-
- {associationData && ( - { - setAddStoryFormKey(addStoryFormKey + 1); - }} - onSubmit={handleCreateStorySubmit} - story={emptyStory} - editMode={false} - showAllFields={false} - showFromPartner={ - associationData.getCollectionPartnerAssociationForCollection !== - null - } - /> - )} -
-
- - )} - - ); -}; diff --git a/src/collections/pages/CollectionSearchPage/CollectionSearchPage.tsx b/src/collections/pages/CollectionSearchPage/CollectionSearchPage.tsx deleted file mode 100644 index 91a00c558..000000000 --- a/src/collections/pages/CollectionSearchPage/CollectionSearchPage.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react'; -import { Box, Paper } from '@mui/material'; -import { FormikValues } from 'formik'; -import { HandleApiResponse, ScrollToTop } from '../../../_shared/components'; -import { CollectionListCard, CollectionSearchForm } from '../../components'; -import { - Label, - useLabelsQuery, - useSearchCollectionsLazyQuery, -} from '../../../api/generatedTypes'; - -export const CollectionSearchPage: React.FC = (): JSX.Element => { - // TODO: Get all available labels and pass them onto the form - - // prepare the query for executing in the handleSubmit callback below - const [searchCollections, { loading, error, data }] = - useSearchCollectionsLazyQuery( - // We need to make sure search results are never served from the cache. - // Otherwise, this page is broken as we have a type policy on the - // 'searchCollections' query. - { fetchPolicy: 'no-cache' }, - ); - - const { data: labelData } = useLabelsQuery(); - - /** - * Collect form data and send it to the API. - * Update components on page if updates have been saved successfully - */ - const handleSubmit = (values: FormikValues): void => { - const searchVars: any = { - author: values.author, - title: values.title, - }; - - // author and title are strings and can be blank, but status must be a - // CollectionStatus enum - if (values.status !== '') { - searchVars['status'] = values.status; - } - - if (values.labels.length > 0) { - searchVars['labelExternalIds'] = values.labels.map( - (value: Label) => value.externalId, - ); - } - - // execute the search - searchCollections({ - variables: { filters: searchVars }, - }); - }; - - return ( - <> - - -

Search Collections

-
- - - {labelData && ( - - )} - - - - - {!data && } - - {data && - data.searchCollections && - !data.searchCollections.collections.length && ( -
No results!
- )} - - {data && - data.searchCollections && - data.searchCollections.collections.map((collection) => { - return ( - - ); - })} -
-
- - ); -}; diff --git a/src/collections/pages/CollectionsHomePage/CollectionsHomePage.tsx b/src/collections/pages/CollectionsHomePage/CollectionsHomePage.tsx deleted file mode 100644 index e34ec86c0..000000000 --- a/src/collections/pages/CollectionsHomePage/CollectionsHomePage.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { Box, Button } from '@mui/material'; -import { - Collection, - CollectionStatus, - useGetCollectionsQuery, -} from '../../../api/generatedTypes'; -import { HandleApiResponse } from '../../../_shared/components'; -import { CollectionListCard } from '../../components'; - -/** - * Home Page - */ -export const CollectionsHomePage = (): JSX.Element => { - // Load a few of the most recent draft collections - const { loading, error, data } = useGetCollectionsQuery({ - variables: { page: 1, perPage: 3, status: CollectionStatus.Draft }, - }); - - // Load some most recently updated collections that are under review - const { - loading: loadingReview, - error: errorReview, - data: dataReview, - } = useGetCollectionsQuery({ - variables: { page: 1, perPage: 3, status: CollectionStatus.Review }, - }); - - // Load a few of the most recently published collections - const { - loading: loadingPublished, - error: errorPublished, - data: dataPublished, - } = useGetCollectionsQuery({ - variables: { page: 1, perPage: 3, status: CollectionStatus.Published }, - }); - - return ( - <> -

- Latest Draft Collections{' '} - - - -

- {!data && } - {data && - data.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - -

- Latest Review Collections{' '} - - - -

- {!dataReview && ( - - )} - {dataReview && - dataReview.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - -

- Latest Published Collections{' '} - - - -

- {!dataPublished && ( - - )} - {dataPublished && - dataPublished.searchCollections.collections.map( - (collection: Omit) => { - return ( - - ); - }, - )} - - ); -}; diff --git a/src/collections/pages/CollectionsLandingPage/CollectionsLandingPage.tsx b/src/collections/pages/CollectionsLandingPage/CollectionsLandingPage.tsx deleted file mode 100644 index 87989a6f0..000000000 --- a/src/collections/pages/CollectionsLandingPage/CollectionsLandingPage.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React from 'react'; -import { Route, Switch, useRouteMatch } from 'react-router-dom'; -import { ApolloProvider } from '@apollo/client'; - -import { client } from '../../../api/client'; -import { - HeaderConnector, - MenuLink, - PageNotFound, -} from '../../../_shared/components'; -import { StyledContainer } from '../../../_shared/styled'; - -import { - AddAuthorPage, - AddCollectionPage, - AddPartnerPage, - AuthorListPage, - AuthorPage, - CollectionListPage, - CollectionPage, - CollectionSearchPage, - CollectionsHomePage, - LabelListPage, - PartnerListPage, - PartnerPage, -} from '../'; - -/** - * Collections landing page - */ -export const CollectionsLandingPage = (): JSX.Element => { - // Get the base path (/collections) - const { path } = useRouteMatch(); - - const menuLinks: MenuLink[] = [ - { - text: 'Collections', - url: `${path}/collections/drafts/`, - }, - { - text: 'Authors', - url: `${path}/authors/`, - }, - { - text: 'Labels', - url: `${path}/labels/`, - }, - { - text: 'Partners', - url: `${path}/partners/`, - }, - { - text: 'Search', - url: `${path}/search/`, - }, - ]; - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/src/collections/pages/LabelListPage/LabelListPage.tsx b/src/collections/pages/LabelListPage/LabelListPage.tsx deleted file mode 100644 index 2841f5b7e..000000000 --- a/src/collections/pages/LabelListPage/LabelListPage.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { Box } from '@mui/material'; -import { Button, HandleApiResponse } from '../../../_shared/components'; -import { useToggle } from '../../../_shared/hooks'; -import { LabelModal, LabelListCard } from '../../components'; -import { Label, useLabelsQuery } from '../../../api/generatedTypes'; - -/** - * Label List Page - */ -export const LabelListPage = (): JSX.Element => { - /** - * Keeps track of whether the "Add Label/Edit Label" modal is open or not. - */ - const [labelModalOpen, toggleLabelModal] = useToggle(false); - const { loading, error, data, refetch } = useLabelsQuery({}); - return ( - <> - - -

Labels

-
- - - -
- {!data && } - - {data && - data.labels.map((label: Label) => { - return ( - - ); - })} - - ); -}; diff --git a/src/collections/pages/PartnerListPage/PartnerListPage.tsx b/src/collections/pages/PartnerListPage/PartnerListPage.tsx deleted file mode 100644 index a895a836d..000000000 --- a/src/collections/pages/PartnerListPage/PartnerListPage.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { Box } from '@mui/material'; -import { - Button, - HandleApiResponse, - LoadMore, -} from '../../../_shared/components'; -import { PartnerListCard } from '../../components'; -import { - CollectionPartner, - useGetCollectionPartnersQuery, -} from '../../../api/generatedTypes'; -import { config } from '../../../config'; -import { useFetchMoreResults } from '../../../_shared/hooks'; - -/** - * Partner List Page - */ -export const PartnerListPage = (): JSX.Element => { - // Load partners - const [loading, reloading, error, data, updateData] = useFetchMoreResults( - useGetCollectionPartnersQuery, - { - variables: { - perPage: config.pagination.partnersPerPage, - page: 1, - }, - }, - ); - - return ( - <> - - -

Partners

-
- - - -
- - {!data && } - - {data && - data.getCollectionPartners?.partners.map( - (partner: CollectionPartner) => { - return ( - - ); - }, - )} - - {data && ( - - )} - - ); -}; diff --git a/src/collections/pages/PartnerPage/PartnerPage.tsx b/src/collections/pages/PartnerPage/PartnerPage.tsx deleted file mode 100644 index 880a332bf..000000000 --- a/src/collections/pages/PartnerPage/PartnerPage.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import React, { useState } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; -import { Box, Button, Collapse, Grid, Paper, Typography } from '@mui/material'; -import EditIcon from '@mui/icons-material/Edit'; -import { FormikValues } from 'formik'; -import { FormikHelpers } from 'formik/dist/types'; -import { HandleApiResponse, ScrollToTop } from '../../../_shared/components'; -import { ImageUpload, PartnerForm, PartnerInfo } from '../../components'; -import { useNotifications } from '../../../_shared/hooks'; -import { - CollectionPartner, - GetCollectionPartnersDocument, - useGetCollectionPartnerQuery, - useUpdateCollectionPartnerImageUrlMutation, - useUpdateCollectionPartnerMutation, -} from '../../../api/generatedTypes'; -import { config } from '../../../config'; - -interface PartnerPageProps { - partner?: CollectionPartner; -} - -export const PartnerPage = (): JSX.Element => { - // Prepare state vars and helper methods for API notifications - const { showNotification } = useNotifications(); - - // prepare the "update author" mutation - // has to be done at the top level of the component because it's a hook - const [updateCollectionPartner] = useUpdateCollectionPartnerMutation(); - - // And this one is only used to set the image url once the we know the S3 link - const [updatePartnerImageUrl] = useUpdateCollectionPartnerImageUrlMutation(); - - /** - * If a Collection Partner object was passed to the page from one of the other app pages, - * let's extract it from the routing. - */ - const location = useLocation(); - - const [partner, setPartner] = useState( - location.state?.partner - ? // Deep clone a read-only object that comes from the routing - JSON.parse(JSON.stringify(location.state?.partner)) - : undefined, - ); - - /** - * If the user came directly to this page (i.e., via a bookmarked page), - * fetch the partner info from the the API. - */ - const params = useParams<{ id: string }>(); - const { loading, error, data } = useGetCollectionPartnerQuery({ - variables: { - id: params.id, - }, - // Skip query if partner object was delivered via the routing - // This is needed because hooks can only be called at the top level - // of the component. - skip: typeof partner === 'object', - }); - - if (data) { - //Partner is a read only object when returned from Apollo, if we want to - // update it we have to stringify and then parse it - setPartner(JSON.parse(JSON.stringify(data.getCollectionPartner))); - } - - const [showEditForm, setShowEditForm] = useState(false); - - const toggleEditForm = (): void => { - setShowEditForm(!showEditForm); - }; - - /** - * Collect form data and send it to the API. - * Update components on page if updates have been saved successfully - */ - const handleSubmit = ( - values: FormikValues, - formikHelpers: FormikHelpers, - ): void => { - updateCollectionPartner({ - variables: { - externalId: partner!.externalId, - name: values.name, - url: values.url, - blurb: values.blurb, - }, - refetchQueries: [ - // make sure the Partners page is updated when we update a partner - { - query: GetCollectionPartnersDocument, - variables: { perPage: config.pagination.collectionsPerPage }, - }, - ], - }) - .then(({ data }) => { - showNotification('Partner updated successfully!', 'success'); - - if (partner) { - partner.name = data?.updateCollectionPartner?.name!; - partner.url = data?.updateCollectionPartner?.url!; - partner.blurb = data?.updateCollectionPartner.blurb!; - } - toggleEditForm(); - formikHelpers.setSubmitting(false); - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - formikHelpers.setSubmitting(false); - }); - }; - - /** - * Save the S3 URL we get back from the API to the author record - */ - const handleImageUploadSave = (url: string): void => { - updatePartnerImageUrl({ - variables: { - externalId: partner!.externalId, - imageUrl: url, - }, - refetchQueries: [ - // make sure the Partners page is updated when we update a partner's image - { - query: GetCollectionPartnersDocument, - variables: { perPage: config.pagination.partnersPerPage }, - }, - ], - }) - .then(({ data }) => { - if (partner) { - partner.imageUrl = data?.updateCollectionPartnerImageUrl?.imageUrl; - showNotification(`Image saved for "${partner!.name}"`, 'success'); - } - }) - .catch((error: Error) => { - showNotification(error.message, 'error'); - }); - }; - - return ( - <> - - {!data && } - {partner && ( - <> - - -

- {partner.name} - - Partner - -

-
- - - -
- - - - - - - - - - - - - -

Edit Partner

-
- -
-
-
- - )} - - ); -}; diff --git a/src/collections/pages/index.ts b/src/collections/pages/index.ts deleted file mode 100644 index 06f5ac31d..000000000 --- a/src/collections/pages/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -export { CollectionsLandingPage } from './CollectionsLandingPage/CollectionsLandingPage'; -export { CollectionsHomePage } from './CollectionsHomePage/CollectionsHomePage'; - -export { AuthorListPage } from './AuthorListPage/AuthorListPage'; -export { AuthorPage } from './AuthorPage/AuthorPage'; -export { AddAuthorPage } from './AddAuthorPage/AddAuthorPage'; - -export { CollectionListPage } from './CollectionListPage/CollectionListPage'; -export { CollectionPage } from './CollectionPage/CollectionPage'; -export { CollectionSearchPage } from './CollectionSearchPage/CollectionSearchPage'; -export { AddCollectionPage } from './AddCollectionPage/AddCollectionPage'; - -export { LabelListPage } from './LabelListPage/LabelListPage'; - -export { PartnerListPage } from './PartnerListPage/PartnerListPage'; -export { PartnerPage } from './PartnerPage/PartnerPage'; -export { AddPartnerPage } from './AddPartnerPage/AddPartnerPage'; diff --git a/src/config/index.ts b/src/config/index.ts index 5799b8948..0afb89559 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -36,9 +36,6 @@ export const config = { }, environment: process.env.NODE_ENV, pagination: { - authorsPerPage: 200, - collectionsPerPage: 30, - partnersPerPage: 30, // To display four items in a row, 32 is a better choice than 30 which gives us 7 1/2 rows. curatedItemsPerPage: 32, rejectedItemsPerPage: 32, diff --git a/src/curated-corpus/components/ApprovedItemListCard/ApprovedItemListCard.test.tsx b/src/curated-corpus/components/ApprovedItemListCard/ApprovedItemListCard.test.tsx index 81fbef797..edf0301b0 100644 --- a/src/curated-corpus/components/ApprovedItemListCard/ApprovedItemListCard.test.tsx +++ b/src/curated-corpus/components/ApprovedItemListCard/ApprovedItemListCard.test.tsx @@ -5,7 +5,6 @@ import { ApprovedCorpusItem } from '../../../api/generatedTypes'; import { ApprovedItemListCard } from './ApprovedItemListCard'; import { flattenAuthors } from '../../../_shared/utils/flattenAuthors'; import { getTestApprovedItem } from '../../helpers/approvedItem'; -import { ScheduledSurfaces } from '../../helpers/definitions'; import userEvent from '@testing-library/user-event'; import { ThemeProvider } from '@mui/material/styles'; import theme from '../../../theme'; diff --git a/src/curated-corpus/components/CustomSectionTable/CustomSectionTable.test.tsx b/src/curated-corpus/components/CustomSectionTable/CustomSectionTable.test.tsx index a2ffeca1d..430fe6437 100644 --- a/src/curated-corpus/components/CustomSectionTable/CustomSectionTable.test.tsx +++ b/src/curated-corpus/components/CustomSectionTable/CustomSectionTable.test.tsx @@ -29,8 +29,8 @@ describe('CustomSectionTable', () => { createdAt: 1704067200, updatedAt: 1704067200, scheduledSurfaceGuid: 'NEW_TAB_EN_US', - followable: true, allowAds: true, + followable: true, }; const mockSectionScheduled: Section = { diff --git a/src/curated-corpus/components/EditCustomSectionModal/EditCustomSectionModal.test.tsx b/src/curated-corpus/components/EditCustomSectionModal/EditCustomSectionModal.test.tsx index 5c7dbdaf5..ae59c487d 100644 --- a/src/curated-corpus/components/EditCustomSectionModal/EditCustomSectionModal.test.tsx +++ b/src/curated-corpus/components/EditCustomSectionModal/EditCustomSectionModal.test.tsx @@ -30,8 +30,8 @@ describe('EditCustomSectionModal', () => { categories: ['IAB1'], }, sectionItems: [], - followable: true, allowAds: true, + followable: true, }; beforeEach(() => { diff --git a/src/curated-corpus/components/ScheduleHistoryModal/ScheduleHistoryModal.test.tsx b/src/curated-corpus/components/ScheduleHistoryModal/ScheduleHistoryModal.test.tsx index d1a6586fc..9e239d4f7 100644 --- a/src/curated-corpus/components/ScheduleHistoryModal/ScheduleHistoryModal.test.tsx +++ b/src/curated-corpus/components/ScheduleHistoryModal/ScheduleHistoryModal.test.tsx @@ -4,7 +4,6 @@ import { MemoryRouter } from 'react-router-dom'; import { ApprovedCorpusItem } from '../../../api/generatedTypes'; import { ScheduleHistoryModal } from './ScheduleHistoryModal'; import { getTestApprovedItem } from '../../helpers/approvedItem'; -import { ScheduledSurfaces } from '../../helpers/definitions'; import { ThemeProvider } from '@mui/material/styles'; import theme from '../../../theme'; diff --git a/src/curated-corpus/components/SectionDetails/SectionDetails.test.tsx b/src/curated-corpus/components/SectionDetails/SectionDetails.test.tsx index 9f0f071e9..df0d21b92 100644 --- a/src/curated-corpus/components/SectionDetails/SectionDetails.test.tsx +++ b/src/curated-corpus/components/SectionDetails/SectionDetails.test.tsx @@ -46,8 +46,13 @@ describe('The SectionDetails component', () => { updatedAt: Date.now(), createSource: ActivitySource.Ml, scheduledSurfaceGuid: 'NEW_TAB_EN_US', +<<<<<<< HEAD followable: true, allowAds: true, +======= + allowAds: true, + followable: true, +>>>>>>> a4b0cc5 (feat(delete collections): remove collections from the tool) }, // disabled section { @@ -69,8 +74,13 @@ describe('The SectionDetails component', () => { updatedAt: Date.now(), createSource: ActivitySource.Ml, scheduledSurfaceGuid: 'NEW_TAB_EN_US', +<<<<<<< HEAD followable: true, allowAds: true, +======= + allowAds: true, + followable: true, +>>>>>>> a4b0cc5 (feat(delete collections): remove collections from the tool) }, ]; diff --git a/src/curated-corpus/helpers/definitions.ts b/src/curated-corpus/helpers/definitions.ts index 07bc54f28..9dc18fa8c 100644 --- a/src/curated-corpus/helpers/definitions.ts +++ b/src/curated-corpus/helpers/definitions.ts @@ -1,7 +1,6 @@ // Here we keep sets of options for curating items import { ApprovedCorpusItem, - CollectionStoryAuthor, CorpusItemAuthor, CorpusLanguage, CuratedStatus, @@ -74,7 +73,7 @@ export type ApprovedItemFromProspect = Omit< 'language' | 'authors' > & { language: CorpusLanguage | undefined; - authors: CollectionStoryAuthor[] | CorpusItemAuthor[]; + authors: CorpusItemAuthor[]; }; // List of scheduled surfaces used to show the readable name by mapping it to it's corresponding guid diff --git a/src/curated-corpus/integration-test-mocks/getSectionsWithSectionItems.ts b/src/curated-corpus/integration-test-mocks/getSectionsWithSectionItems.ts index 9345be622..0029574ab 100644 --- a/src/curated-corpus/integration-test-mocks/getSectionsWithSectionItems.ts +++ b/src/curated-corpus/integration-test-mocks/getSectionsWithSectionItems.ts @@ -113,8 +113,13 @@ const createSection = ( sectionItems, createdAt: 1704067200, updatedAt: 1704067200, +<<<<<<< HEAD followable: true, allowAds: true, +======= + allowAds: true, + followable: true, +>>>>>>> a4b0cc5 (feat(delete collections): remove collections from the tool) }); /** From dd04b05ac1be536c92ee976115489dfa06cb81b0 Mon Sep 17 00:00:00 2001 From: Jonathan Petto Date: Mon, 23 Feb 2026 16:49:16 -0600 Subject: [PATCH 2/2] review fixes: - delete more unused files and imports - delete unused config --- package-lock.json | 934 ------------------ package.json | 4 - .../components/LoadMore/LoadMore.test.tsx | 77 -- src/_shared/components/LoadMore/LoadMore.tsx | 58 -- .../MarkdownPreview/MarkdownPreview.test.tsx | 50 - .../MarkdownPreview/MarkdownPreview.tsx | 77 -- .../ScrollToTop/ScrollToTop.test.tsx | 24 - .../components/ScrollToTop/ScrollToTop.tsx | 21 - src/_shared/components/Tab/Tab.test.tsx | 26 - src/_shared/components/Tab/Tab.tsx | 67 -- .../TabNavigation/TabNavigation.test.tsx | 61 -- .../TabNavigation/TabNavigation.tsx | 47 - .../components/TabPanel/TabPanel.test.tsx | 21 - src/_shared/components/TabPanel/TabPanel.tsx | 55 -- src/_shared/components/TabSet/TabSet.test.tsx | 30 - src/_shared/components/TabSet/TabSet.tsx | 76 -- src/_shared/components/index.ts | 8 - .../useFetchMoreResults.ts | 74 -- src/_shared/styled/StyledCardLink.tsx | 11 - src/_shared/styled/StyledChipComponent.tsx | 15 - src/_shared/styled/index.ts | 2 - src/config/index.ts | 9 - .../SectionDetails/SectionDetails.test.tsx | 10 - .../getSectionsWithSectionItems.ts | 5 - 24 files changed, 1762 deletions(-) delete mode 100644 src/_shared/components/LoadMore/LoadMore.test.tsx delete mode 100644 src/_shared/components/LoadMore/LoadMore.tsx delete mode 100644 src/_shared/components/MarkdownPreview/MarkdownPreview.test.tsx delete mode 100644 src/_shared/components/MarkdownPreview/MarkdownPreview.tsx delete mode 100644 src/_shared/components/ScrollToTop/ScrollToTop.test.tsx delete mode 100644 src/_shared/components/ScrollToTop/ScrollToTop.tsx delete mode 100644 src/_shared/components/Tab/Tab.test.tsx delete mode 100644 src/_shared/components/Tab/Tab.tsx delete mode 100644 src/_shared/components/TabNavigation/TabNavigation.test.tsx delete mode 100644 src/_shared/components/TabNavigation/TabNavigation.tsx delete mode 100644 src/_shared/components/TabPanel/TabPanel.test.tsx delete mode 100644 src/_shared/components/TabPanel/TabPanel.tsx delete mode 100644 src/_shared/components/TabSet/TabSet.test.tsx delete mode 100644 src/_shared/components/TabSet/TabSet.tsx delete mode 100644 src/_shared/hooks/useFetchMoreResults/useFetchMoreResults.ts delete mode 100644 src/_shared/styled/StyledCardLink.tsx delete mode 100644 src/_shared/styled/StyledChipComponent.tsx diff --git a/package-lock.json b/package-lock.json index 551b457d5..6b1a87f61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "@svgr/webpack": "^8.1.0", "@types/apollo-upload-client": "18.0.0", "@types/luxon": "2.0.9", - "@types/react-beautiful-dnd": "13.1.4", "@types/react-dom": "17.0.20", "@types/react-router-dom": "5.3.3", "apollo-upload-client": "18.0.1", @@ -75,11 +74,9 @@ "prompts": "2.4.0", "react": "17.0.2", "react-app-polyfill": "^2.0.0", - "react-beautiful-dnd": "13.1.1", "react-dev-utils": "^12.0.1", "react-dom": "17.0.2", "react-dropzone": "11.5.3", - "react-markdown": "6.0.3", "react-oauth2-pkce": "2.0.7", "react-refresh": "^0.14.2", "react-router-dom": "5.3.4", @@ -87,7 +84,6 @@ "resolve-url-loader": "^5.0.0", "sass-loader": "^12.6.0", "semver": "7.6.0", - "slugify": "1.6.6", "stream-browserify": "^3.0.0", "style-loader": "^3.3.4", "terser-webpack-plugin": "^5.3.14", @@ -2939,37 +2935,6 @@ "integrity": "sha512-+EQE8xZhRM/hsY0CDTVyayMDDY5ihc4MqXCrPxooKw19yAzUIC6uUqsZeaOFNL9YKTNxYKrJP5DFgE8o5xRCOw==", "license": "MIT" }, - "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -5890,18 +5855,6 @@ } } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -6984,16 +6937,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/apollo-upload-client": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/@types/apollo-upload-client/-/apollo-upload-client-18.0.0.tgz", @@ -7154,33 +7097,12 @@ "integrity": "sha512-4sd7uDB0OVZmwH2wD6w7Qlpr2P5Pn8C9IGwnaq9aiiBDD3Lou7CwFjjkJTDYCDsEvk9zxAtmv9TaMg1lt/YJfA==", "license": "MIT" }, - "node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, "node_modules/@types/history": { "version": "4.7.11", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", "license": "MIT" }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", - "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", - "license": "MIT", - "dependencies": { - "hoist-non-react-statics": "^3.3.0" - }, - "peerDependencies": { - "@types/react": "*" - } - }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -7335,15 +7257,6 @@ "integrity": "sha512-ZuzIc7aN+i2ZDMWIiSmMdubR9EMMSTdEzF6R+FckP4p6xdnOYKqknTo/k+xXQvciSXlNGIwA4OPU5X7JIFzYdA==", "license": "MIT" }, - "node_modules/@types/mdast": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", - "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -7409,15 +7322,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-beautiful-dnd": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.4.tgz", - "integrity": "sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-dom": { "version": "17.0.20", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz", @@ -7427,18 +7331,6 @@ "@types/react": "^17" } }, - "node_modules/@types/react-redux": { - "version": "7.1.34", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz", - "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==", - "license": "MIT", - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, "node_modules/@types/react-router": { "version": "5.1.20", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", @@ -7561,12 +7453,6 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, - "node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, "node_modules/@types/uuid": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", @@ -7938,32 +7824,6 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", @@ -7977,217 +7837,6 @@ "darwin" ] }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -9395,16 +9044,6 @@ "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, - "node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -10041,36 +9680,6 @@ "node": ">=10" } }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chardet": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", @@ -10345,16 +9954,6 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "license": "MIT" }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -10699,15 +10298,6 @@ "postcss": "^8.4" } }, - "node_modules/css-box-model": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", - "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", - "license": "MIT", - "dependencies": { - "tiny-invariant": "^1.0.6" - } - }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -13487,12 +13077,6 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, "node_modules/extract-files": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz", @@ -15364,12 +14948,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", - "license": "MIT" - }, "node_modules/inquirer": { "version": "8.2.7", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", @@ -15444,30 +15022,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -15570,29 +15124,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -15671,16 +15202,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -15769,16 +15290,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -15917,15 +15428,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -18600,78 +18102,12 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", - "license": "MIT", - "dependencies": { - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", - "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", - "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "license": "CC0-1.0" }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "license": "MIT" - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -18693,12 +18129,6 @@ "node": ">= 4.0.0" } }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT" - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -18750,26 +18180,6 @@ "node": ">= 0.6" } }, - "node_modules/micromark": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", - "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -19595,24 +19005,6 @@ "node": ">= 0.10" } }, - "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "license": "MIT", - "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -21428,19 +20820,6 @@ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", "license": "MIT" }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -21572,12 +20951,6 @@ "performance-now": "^2.1.0" } }, - "node_modules/raf-schd": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", - "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==", - "license": "MIT" - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -21672,26 +21045,6 @@ "asap": "~2.0.6" } }, - "node_modules/react-beautiful-dnd": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", - "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", - "deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.9.2", - "css-box-model": "^1.2.0", - "memoize-one": "^5.1.1", - "raf-schd": "^4.0.2", - "react-redux": "^7.2.0", - "redux": "^4.0.4", - "use-memo-one": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8.5 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -21861,41 +21214,6 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, - "node_modules/react-markdown": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.3.tgz", - "integrity": "sha512-kQbpWiMoBHnj9myLlmZG9T1JdoT/OEyHK7hqM6CqFT14MAkgWiWBUYijLyBmxbntaN6dCDicPcUhWhci1QYodg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.3", - "comma-separated-tokens": "^1.0.0", - "prop-types": "^15.7.2", - "property-information": "^5.3.0", - "react-is": "^17.0.0", - "remark-parse": "^9.0.0", - "remark-rehype": "^8.0.0", - "space-separated-tokens": "^1.1.0", - "style-to-object": "^0.3.0", - "unified": "^9.0.0", - "unist-util-visit": "^2.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/react-markdown/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, "node_modules/react-oauth2-pkce": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/react-oauth2-pkce/-/react-oauth2-pkce-2.0.7.tgz", @@ -21913,37 +21231,6 @@ "react-dom": ">=16.8.0" } }, - "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" - }, - "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/react-redux/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -22150,15 +21437,6 @@ "node": ">=8" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -22287,32 +21565,6 @@ "invariant": "^2.2.4" } }, - "node_modules/remark-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", - "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^0.8.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz", - "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==", - "license": "MIT", - "dependencies": { - "mdast-util-to-hast": "^10.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remedial": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", @@ -23250,15 +22502,6 @@ "node": ">=8" } }, - "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -23336,16 +22579,6 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "license": "MIT" }, - "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -23808,15 +23041,6 @@ "webpack": "^5.0.0" } }, - "node_modules/style-to-object": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", - "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.1.1" - } - }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -24328,16 +23552,6 @@ "tslib": "2" } }, - "node_modules/trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -24734,24 +23948,6 @@ "node": ">=4" } }, - "node_modules/unified": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", - "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", - "license": "MIT", - "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -24764,88 +23960,6 @@ "node": ">=8" } }, - "node_modules/unist-builder": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", - "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-generated": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", - "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", - "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -25098,15 +24212,6 @@ "braces": "^3.0.2" } }, - "node_modules/use-memo-one": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", - "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -25193,36 +24298,6 @@ "node": ">= 0.8" } }, - "node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -26178,15 +25253,6 @@ "dev": true, "license": "MIT" }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 79f9a8cbf..3bda2746a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "@svgr/webpack": "^8.1.0", "@types/apollo-upload-client": "18.0.0", "@types/luxon": "2.0.9", - "@types/react-beautiful-dnd": "13.1.4", "@types/react-dom": "17.0.20", "@types/react-router-dom": "5.3.3", "apollo-upload-client": "18.0.1", @@ -70,11 +69,9 @@ "prompts": "2.4.0", "react": "17.0.2", "react-app-polyfill": "^2.0.0", - "react-beautiful-dnd": "13.1.1", "react-dev-utils": "^12.0.1", "react-dom": "17.0.2", "react-dropzone": "11.5.3", - "react-markdown": "6.0.3", "react-oauth2-pkce": "2.0.7", "react-refresh": "^0.14.2", "react-router-dom": "5.3.4", @@ -82,7 +79,6 @@ "resolve-url-loader": "^5.0.0", "sass-loader": "^12.6.0", "semver": "7.6.0", - "slugify": "1.6.6", "stream-browserify": "^3.0.0", "style-loader": "^3.3.4", "terser-webpack-plugin": "^5.3.14", diff --git a/src/_shared/components/LoadMore/LoadMore.test.tsx b/src/_shared/components/LoadMore/LoadMore.test.tsx deleted file mode 100644 index 102eae1b1..000000000 --- a/src/_shared/components/LoadMore/LoadMore.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import { LoadMore } from './LoadMore'; -import userEvent from '@testing-library/user-event'; - -describe('The LoadMore component', () => { - let buttonDisabled: boolean; - let loadMore: () => void; - let showSpinner: boolean; - - beforeEach(() => { - buttonDisabled = false; - loadMore = jest.fn(); - showSpinner = false; - }); - - it('renders with props', () => { - render( - , - ); - - expect(screen.getByText(/load more results/i)).toBeInTheDocument(); - }); - - it('disables button if requested', () => { - buttonDisabled = true; - - render( - , - ); - - const button = screen.getByRole('button') as HTMLButtonElement; - - // Helpfully, Material UI doesn't actually use the 'disabled' attribute - // on a button element - instead, it adds a couple of 'disabled' classes to it. - expect(button.getAttribute('class')).toContain('disabled'); - }); - - it('shows the loading spinner if requested', () => { - showSpinner = true; - - render( - , - ); - expect(screen.getByRole('progressbar')).toBeInTheDocument(); - }); - - it('calls the "loadMore" method on click', async () => { - render( - , - ); - - const button = screen.getByRole('button') as HTMLButtonElement; - - await waitFor(() => { - userEvent.click(button); - }); - - expect(loadMore).toHaveBeenCalled(); - }); -}); diff --git a/src/_shared/components/LoadMore/LoadMore.tsx b/src/_shared/components/LoadMore/LoadMore.tsx deleted file mode 100644 index 73e661689..000000000 --- a/src/_shared/components/LoadMore/LoadMore.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { Box, CircularProgress } from '@mui/material'; -import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; -import { Button } from '../'; - -interface LoadMoreProps { - /** - * Whether the button is disabled - it *should* be disabled if no more results - * exist for a query. - */ - buttonDisabled: boolean; - - /** - * A function to execute when the button is clicked. - */ - loadMore: () => void; - - /** - * Whether to show a loading spinner as part of the button label - * to indicate request status. - */ - showSpinner: boolean; -} - -/** - * A 'Load More Results' button, placed below a pageful of results, such as - * published collections. - * - * @param props - * @constructor - */ -export const LoadMore: React.FC = (props): JSX.Element => { - const { buttonDisabled, loadMore, showSpinner } = props; - - return ( - - - - ); -}; diff --git a/src/_shared/components/MarkdownPreview/MarkdownPreview.test.tsx b/src/_shared/components/MarkdownPreview/MarkdownPreview.test.tsx deleted file mode 100644 index a32288060..000000000 --- a/src/_shared/components/MarkdownPreview/MarkdownPreview.test.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { MarkdownPreview } from './MarkdownPreview'; - -describe('The MarkdownPreview component', () => { - const markdownSource = - '* Lists\n' + - '* [ ] todo\n' + - '* [x] done\n' + - '\n' + - 'A table:\n' + - '\n' + - '| a | b |\n' + - '| - | - |\n' + - '\n' + - '## A heading\n' + - '\n' + - '```\n' + - 'a code block\n' + - '```'; - - it('renders the default tab successfully', () => { - render( - -