Skip to content

Commit

Permalink
Merge pull request #2523 from NDLANO/refactor/combobox-in-about-film
Browse files Browse the repository at this point in the history
refactor: use combobox in about film article
  • Loading branch information
Jonas-C authored Oct 17, 2024
2 parents 8b7c48e + b6b44f7 commit c4e8ce4
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ const DisclaimerForm = ({ initialData, onOpenChange, onSave }: DisclaimerFormPro
title={item.title.title}
description={item.metaDescription?.metaDescription}
image={item.metaImage}
useFallbackImage
/>
)}
>
Expand Down
9 changes: 5 additions & 4 deletions src/components/abstractions/Combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ interface GenericComboboxItemProps {
title: string;
description?: string;
fallbackImageElement?: ReactNode;
useFallbackImage?: boolean;
image?: {
url?: string;
alt?: string;
Expand All @@ -99,12 +100,12 @@ const StyledListItemRoot = styled(ListItemRoot, {
});

export const GenericComboboxItemContent = forwardRef<HTMLDivElement, GenericComboboxItemProps & ListItemProps>(
({ title, image, description, fallbackImageElement, ...props }, ref) => (
({ title, image, description, fallbackImageElement, useFallbackImage, ...props }, ref) => (
<StyledListItemRoot context="list" ref={ref} {...props}>
{!!image && (
{(!!image || useFallbackImage) && (
<ListItemImage
src={image.url ?? ""}
alt={image.alt ?? ""}
src={image?.url ?? ""}
alt={image?.alt ?? ""}
fallbackElement={fallbackImageElement ?? <ImageLine />}
/>
)}
Expand Down
4 changes: 3 additions & 1 deletion src/containers/ArticlePage/components/ConceptsField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ const ConceptsField = ({ field, form }: Props) => {
paginationData={searchQuery.data}
onInputValueChange={(details) => setQuery(details.inputValue)}
onPageChange={(details) => setPage(details.page)}
renderItem={(item) => <GenericComboboxItemContent title={item.title.title} image={item.metaImage} />}
renderItem={(item) => (
<GenericComboboxItemContent title={item.title.title} image={item.metaImage} useFallbackImage />
)}
>
<ComboboxLabel>{t("form.relatedConcepts.articlesTitle")}</ComboboxLabel>
<GenericComboboxInput placeholder={t("form.relatedConcepts.placeholder")} isFetching={searchQuery.isFetching} />
Expand Down
15 changes: 1 addition & 14 deletions src/containers/NdlaFilm/components/NdlaFilmAccordionPanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,6 @@ const SubjectpageAccordionPanels = ({ errors, selectedLanguage }: ComponentProps
},
});
};
const onUpdateArticle = (
field: FieldProps<FormikValues>["field"],
form: FormikHelpers<FormikValues>,
article?: string,
) => {
form.setFieldTouched(field.name, true, false);
field.onChange({
target: {
name: field.name,
value: article,
},
});
};

return (
<FormAccordions defaultOpen={["slideshow", "themes"]}>
Expand All @@ -69,7 +56,7 @@ const SubjectpageAccordionPanels = ({ errors, selectedLanguage }: ComponentProps
</PageContent>
</FormAccordion>
<FormAccordion id="article" title={t("ndlaFilm.editor.moreInfoHeader")} hasError={false}>
<NdlaFilmArticle fieldName={"article"} onUpdateArticle={onUpdateArticle} />
<NdlaFilmArticle fieldName={"article"} />
</FormAccordion>
<FormAccordion
id="slideshow"
Expand Down
116 changes: 56 additions & 60 deletions src/containers/NdlaFilm/components/NdlaFilmArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,37 @@
*
*/

import { useField, useFormikContext } from "formik";
import { useCallback, useEffect, useState } from "react";
import { useField } from "formik";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import styled from "@emotion/styled";
import { IconButtonV2 } from "@ndla/button";
import { colors, spacing } from "@ndla/core";
import { DeleteForever } from "@ndla/icons/editor";
import { IArticleSummaryV2, IArticleV2 } from "@ndla/types-backend/article-api";
import AsyncDropdown from "../../../components/Dropdown/asyncDropdown/AsyncDropdown";
import FieldHeader from "../../../components/Field/FieldHeader";
import { getArticle, searchArticles } from "../../../modules/article/articleApi";
import { ComboboxLabel, FieldHelper, FieldRoot } from "@ndla/primitives";
import { IArticleV2 } from "@ndla/types-backend/article-api";
import { GenericComboboxInput, GenericComboboxItemContent } from "../../../components/abstractions/Combobox";
import { GenericSearchCombobox } from "../../../components/Form/GenericSearchCombobox";
import ListResource from "../../../components/Form/ListResource";
import { getArticle } from "../../../modules/article/articleApi";
import { useArticleSearch } from "../../../modules/article/articleQueries";
import { getUrnFromId, getIdFromUrn } from "../../../util/ndlaFilmHelpers";
import { toEditFrontPageArticle } from "../../../util/routeHelpers";
import { routes } from "../../../util/routeHelpers";
import useDebounce from "../../../util/useDebounce";

interface Props {
fieldName: string;
onUpdateArticle: Function;
}

const ArticleElement = styled.div`
align-items: center;
background: ${colors.brand.greyLighter};
display: flex;
justify-content: space-between;
margin: ${spacing.normal} 0;
padding: ${spacing.small};
`;

const NdlaFilmArticle = ({ fieldName, onUpdateArticle }: Props) => {
const NdlaFilmArticle = ({ fieldName }: Props) => {
const { t } = useTranslation();
const form = useFormikContext();
const [field] = useField<string>(fieldName);
const [field, _, helpers] = useField<string | null>(fieldName);
const [selectedArticle, setSelectedArticle] = useState<undefined | IArticleV2>(undefined);
const [query, setQuery] = useState("");
const [page, setPage] = useState(1);
const delayedQuery = useDebounce(query, 300);

const searchQuery = useArticleSearch({
articleTypes: ["frontpage-article"],
page,
query: delayedQuery,
});

useEffect(() => {
const initSelectedArticle = async () => {
Expand All @@ -53,43 +50,42 @@ const NdlaFilmArticle = ({ fieldName, onUpdateArticle }: Props) => {
initSelectedArticle();
}, [field.value]);

const onSearch = useCallback((query: string, page?: number) => {
return searchArticles({ articleTypes: ["frontpage-article"], page, query });
}, []);

return (
<>
<FieldHeader title={t("ndlaFilm.editor.moreInfoTitle")} subTitle={t("ndlaFilm.editor.moreInfoSubTitle")} />
{selectedArticle && (
<ArticleElement>
<Link to={toEditFrontPageArticle(selectedArticle.id, selectedArticle.title.language)}>
{selectedArticle.title.title}
</Link>
<IconButtonV2
aria-label={t("ndlaFilm.editor.removeArticleFromMoreInformation")}
variant="ghost"
title={t("ndlaFilm.editor.removeArticleFromMoreInformation")}
colorTheme="danger"
data-testid="elementListItemDeleteButton"
onClick={() => onUpdateArticle(field, form, null)}
>
<DeleteForever />
</IconButtonV2>
</ArticleElement>
<FieldRoot>
<GenericSearchCombobox
items={searchQuery.data?.results ?? []}
itemToString={(item) => item.title.title}
itemToValue={(item) => getUrnFromId(item.id.toString())}
inputValue={query}
isSuccess={searchQuery.isSuccess}
paginationData={searchQuery.data}
onInputValueChange={(details) => setQuery(details.inputValue)}
onPageChange={(details) => setPage(details.page)}
value={field.value ? [field.value?.toString()] : undefined}
onValueChange={(details) => helpers.setValue(getUrnFromId(details.items[0].id))}
renderItem={(item) => (
<GenericComboboxItemContent
title={item.title.title}
description={item.metaDescription?.metaDescription}
image={item.metaImage}
useFallbackImage
/>
)}
>
<ComboboxLabel>{t("ndlaFilm.editor.moreInfoTitle")}</ComboboxLabel>
<FieldHelper>{t("ndlaFilm.editor.moreInfoSubTitle")}</FieldHelper>
<GenericComboboxInput placeholder={t("frontpageForm.search")} isFetching={searchQuery.isFetching} />
</GenericSearchCombobox>
{!!selectedArticle && (
<ListResource
title={selectedArticle.title.title}
metaImage={selectedArticle.metaImage}
url={routes.frontpage.edit(selectedArticle.id, selectedArticle.title.language)}
onDelete={() => helpers.setValue(null)}
removeElementTranslation={t("ndlaFilm.editor.removeArticleFromMoreInformation")}
/>
)}
<AsyncDropdown<IArticleSummaryV2>
idField="id"
labelField="title"
placeholder={t("frontpageForm.search")}
apiAction={onSearch}
disableSelected
onChange={(article: IArticleSummaryV2) => onUpdateArticle(field, form, getUrnFromId(article.id))}
startOpen={!field.value}
showPagination
initialSearch={!field.value}
clearInputField
/>
</>
</FieldRoot>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/containers/Podcast/components/PodcastSeries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const PodcastSeries = () => {
title={item.title.title}
description={item.description.description}
image={item.coverPhoto}
useFallbackImage
/>
)}
>
Expand Down

0 comments on commit c4e8ce4

Please sign in to comment.