From 3198065d40dd4e7330992559f54e8427d0c164eb Mon Sep 17 00:00:00 2001 From: Jonas Carlsen Date: Fri, 1 Nov 2024 16:01:03 +0100 Subject: [PATCH] refactor: primitives in ContentField --- .../ArticlePage/components/ContentField.tsx | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/src/containers/ArticlePage/components/ContentField.tsx b/src/containers/ArticlePage/components/ContentField.tsx index 3a02871276..6362b17091 100644 --- a/src/containers/ArticlePage/components/ContentField.tsx +++ b/src/containers/ArticlePage/components/ContentField.tsx @@ -12,27 +12,30 @@ import { useTranslation } from "react-i18next"; import { DragVertical, Link } from "@ndla/icons/editor"; import { Button, + ComboboxLabel, DialogBody, DialogContent, DialogHeader, DialogRoot, DialogTitle, DialogTrigger, - Heading, } from "@ndla/primitives"; import { styled } from "@ndla/styled-system/jsx"; import { IArticle, IArticleSummary, IRelatedContentLink } from "@ndla/types-backend/draft-api"; import ContentLink from "./ContentLink"; +import { GenericComboboxInput, GenericComboboxItemContent } from "../../../components/abstractions/Combobox"; import { DialogCloseButton } from "../../../components/DialogCloseButton"; import DndList from "../../../components/DndList"; import { DragHandle } from "../../../components/DraggableItem"; -import AsyncDropdown from "../../../components/Dropdown/asyncDropdown/AsyncDropdown"; -import FieldHeader from "../../../components/Field/FieldHeader"; +import { GenericSearchCombobox } from "../../../components/Form/GenericSearchCombobox"; import ListResource from "../../../components/Form/ListResource"; +import { FormContent } from "../../../components/FormikForm"; import { ConvertedRelatedContent, RelatedContent } from "../../../interfaces"; -import { fetchDraft, searchDrafts } from "../../../modules/draft/draftApi"; +import { fetchDraft } from "../../../modules/draft/draftApi"; +import { useSearchDrafts } from "../../../modules/draft/draftQueries"; import handleError from "../../../util/handleError"; import { routes } from "../../../util/routeHelpers"; +import { usePaginatedQuery } from "../../../util/usePaginatedQuery"; import { ArticleFormType } from "../../FormikForm/articleFormHooks"; const StyledList = styled("ul", { @@ -52,11 +55,22 @@ interface Props { form: FormikHelpers; } +const isDraftApiType = (relatedContent: ConvertedRelatedContent): relatedContent is IArticle => + (relatedContent as IArticle).id !== undefined; + const ContentField = ({ field, form }: Props) => { const { t, i18n } = useTranslation(); + const { query, delayedQuery, setQuery, page, setPage } = usePaginatedQuery(); const [relatedContent, setRelatedContent] = useState([]); const [showAddExternal, setShowAddExternal] = useState(false); + const selectedItems = useMemo(() => relatedContent.filter(isDraftApiType), [relatedContent]); + + const searchQuery = useSearchDrafts( + { query: delayedQuery, language: i18n.language, page }, + { placeholderData: (prev) => prev }, + ); + useEffect(() => { (async () => { const promises = field.value.map | IRelatedContentLink>((element) => { @@ -72,11 +86,15 @@ const ContentField = ({ field, form }: Props) => { const onAddArticleToList = async (article: IArticleSummary) => { try { - const newArticle = await fetchDraft(article.id, i18n.language); - const temp = [...relatedContent, newArticle]; - if (newArticle) { - setRelatedContent(temp); - updateFormik(field, temp); + if (selectedItems.some((a) => a.id === article.id)) { + const newRelatedContent = relatedContent.filter((a) => isDraftApiType(a) && a.id !== article.id); + setRelatedContent(newRelatedContent); + updateFormik(field, newRelatedContent); + } else { + const newArticle = await fetchDraft(article.id, i18n.language); + const newRelatedContent = relatedContent.concat(newArticle); + setRelatedContent(newRelatedContent); + updateFormik(field, newRelatedContent); } } catch (e) { handleError(e); @@ -104,25 +122,12 @@ const ContentField = ({ field, form }: Props) => { }); }; - const searchForArticles = async (query: string, page?: number) => { - return searchDrafts({ - query, - page, - language: i18n.language, - }); - }; - const addExternalLink = (title: string, url: string) => { const temp = [...relatedContent, { title, url }]; setRelatedContent(temp); updateFormik(field, temp); }; - const isDraftApiType = (relatedContent: ConvertedRelatedContent): relatedContent is IArticle => - (relatedContent as IArticle).id !== undefined; - - const selectedItems = relatedContent.filter(isDraftApiType); - const releatedContentDndItems = useMemo( () => relatedContent @@ -134,8 +139,25 @@ const ContentField = ({ field, form }: Props) => { ); return ( - <> - + + article.id.toString())} + closeOnSelect={false} + selectionBehavior="preserve" + onValueChange={(details) => onAddArticleToList(details.items[0])} + items={searchQuery.data?.results ?? []} + itemToString={(item) => item.title.title} + itemToValue={(item) => item.id.toString()} + inputValue={query} + onInputValueChange={(details) => setQuery(details.inputValue)} + isSuccess={searchQuery.isSuccess} + paginationData={searchQuery.data} + onPageChange={(details) => setPage(details.page)} + renderItem={(item) => } + > + {t("form.relatedContent.articlesTitle")} + + { onDragEnd={(_, newArray) => onUpdateElements(newArray)} /> - event.stopPropagation()} - onChange={onAddArticleToList} - multiSelect - disableSelected - clearInputField - showPagination - /> setShowAddExternal(open)}> @@ -203,7 +212,7 @@ const ContentField = ({ field, form }: Props) => { - + ); };