Skip to content

Commit

Permalink
Merge pull request #2476 from NDLANO/refactor/replace-async-tags-with…
Browse files Browse the repository at this point in the history
…-tag-selector

refactor: replace AsyncSearchTags with TagSelector
  • Loading branch information
Jonas-C authored Oct 9, 2024
2 parents 9645636 + 90ae23d commit 1d3fe36
Show file tree
Hide file tree
Showing 20 changed files with 448 additions and 254 deletions.
133 changes: 0 additions & 133 deletions src/components/Dropdown/asyncDropdown/AsyncSearchTags.tsx

This file was deleted.

This file was deleted.

27 changes: 27 additions & 0 deletions src/components/Form/SearchTagsContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2024-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { forwardRef } from "react";
import { useTranslation } from "react-i18next";
import { ComboboxContent, ComboboxContentProps, ComboboxPositioner, Spinner, Text } from "@ndla/primitives";

interface Props extends ComboboxContentProps {
isFetching: boolean;
hits: number;
}

export const SearchTagsContent = forwardRef<HTMLDivElement, Props>(({ isFetching, hits, children, ...props }) => {
const { t } = useTranslation();
return (
<ComboboxPositioner>
<ComboboxContent {...props}>
{isFetching ? <Spinner /> : hits ? children : <Text>{t("dropdown.numberHits", { hits: 0 })}</Text>}
</ComboboxContent>
</ComboboxPositioner>
);
});
44 changes: 44 additions & 0 deletions src/components/Form/SearchTagsTagSelectorInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2024-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { forwardRef } from "react";
import { CloseLine } from "@ndla/icons/action";
import { ArrowDownShortLine } from "@ndla/icons/common";
import { IconButton, InputContainer } from "@ndla/primitives";
import { HStack } from "@ndla/styled-system/jsx";
import {
TagSelectorClearTrigger,
TagSelectorControl,
TagSelectorInput,
TagSelectorTrigger,
TagSelectorInputProps,
} from "@ndla/ui";

interface Props extends TagSelectorInputProps {}

export const SearchTagsTagSelectorInput = forwardRef<HTMLInputElement, Props>((props, ref) => {
return (
<HStack gap="3xsmall">
<TagSelectorControl asChild>
<InputContainer>
<TagSelectorInput {...props} ref={ref} />
<TagSelectorClearTrigger asChild>
<IconButton variant="clear">
<CloseLine />
</IconButton>
</TagSelectorClearTrigger>
</InputContainer>
</TagSelectorControl>
<TagSelectorTrigger asChild>
<IconButton variant="secondary">
<ArrowDownShortLine />
</IconButton>
</TagSelectorTrigger>
</HStack>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import {
IConceptSearchResult,
INewConcept,
IUpdatedConcept,
ITagsSearchResult,
IConceptSummary,
} from "@ndla/types-backend/concept-api";
import { IArticle } from "@ndla/types-backend/draft-api";
Expand All @@ -53,7 +52,6 @@ interface Props {
addConcept: (concept: IConceptSummary | IConcept) => void;
concept?: IConcept;
createConcept: (createdConcept: INewConcept) => Promise<IConcept>;
fetchSearchTags: (input: string, language: string) => Promise<ITagsSearchResult>;
handleRemove: () => void;
onClose: () => void;
locale: string;
Expand All @@ -80,7 +78,6 @@ const ConceptModalContent = ({
updateConcept,
createConcept,
concept,
fetchSearchTags,
conceptArticles,
conceptType,
}: Props) => {
Expand Down Expand Up @@ -241,7 +238,6 @@ const ConceptModalContent = ({
subjects={subjects}
upsertProps={upsertProps}
language={locale}
fetchConceptTags={fetchSearchTags}
concept={concept}
conceptArticles={conceptArticles}
initialTitle={selectedText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ const InlineWrapper = (props: Props) => {
const nodeText = Node.string(element).trim();
const [isEditing, setIsEditing] = useState(element.isFirstEdit);
const locale = useArticleLanguage();
const { concept, subjects, loading, fetchSearchTags, conceptArticles, createConcept, updateConcept } =
useFetchConceptData(parseInt(element.data.contentId), locale);
const { concept, subjects, loading, conceptArticles, createConcept, updateConcept } = useFetchConceptData(
parseInt(element.data.contentId),
locale,
);

const visualElementQuery = useConceptVisualElement(concept?.id!, concept?.visualElement?.visualElement!, locale, {
enabled: !!concept?.id && !!concept?.visualElement?.visualElement.length,
Expand Down Expand Up @@ -258,7 +260,6 @@ const InlineWrapper = (props: Props) => {
subjects={subjects}
handleRemove={handleRemove}
selectedText={nodeText}
fetchSearchTags={fetchSearchTags}
createConcept={createConcept}
updateConcept={updateConcept}
conceptArticles={conceptArticles}
Expand Down
94 changes: 73 additions & 21 deletions src/containers/AudioUploader/components/AudioMetaData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,85 @@
*
*/

import { FieldProps, useFormikContext } from "formik";
import { useFormikContext } from "formik";
import { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { createListCollection } from "@ark-ui/react";
import { CheckLine } from "@ndla/icons/editor";
import {
ComboboxItem,
ComboboxItemIndicator,
ComboboxItemText,
FieldErrorMessage,
FieldHelper,
FieldRoot,
Input,
} from "@ndla/primitives";
import { TagSelectorLabel, TagSelectorRoot, useTagSelectorTranslations } from "@ndla/ui";
import { AudioFormikType } from "./AudioForm";
import AsyncSearchTags from "../../../components/Dropdown/asyncDropdown/AsyncSearchTags";
import FormikField from "../../../components/FormikField";
import { fetchSearchTags } from "../../../modules/audio/audioApi";
import { SearchTagsContent } from "../../../components/Form/SearchTagsContent";
import { SearchTagsTagSelectorInput } from "../../../components/Form/SearchTagsTagSelectorInput";
import { FormField } from "../../../components/FormField";
import { useAudioSearchTags } from "../../../modules/audio/audioQueries";
import useDebounce from "../../../util/useDebounce";

const AudioMetaData = () => {
const {
values: { language, tags },
} = useFormikContext<AudioFormikType>();
const { values } = useFormikContext<AudioFormikType>();
const { t } = useTranslation();
const [inputQuery, setInputQuery] = useState<string>("");
const debouncedQuery = useDebounce(inputQuery, 300);
const tagSelectorTranslations = useTagSelectorTranslations();
const searchTagsQuery = useAudioSearchTags(
{
input: debouncedQuery,
language: values.language,
},
{
enabled: !!debouncedQuery.length,
placeholderData: (prev) => prev,
},
);

const collection = useMemo(() => {
return createListCollection({
items: searchTagsQuery.data?.results ?? [],
itemToValue: (item) => item,
itemToString: (item) => item,
});
}, [searchTagsQuery.data?.results]);

return (
<>
<FormikField name="tags" label={t("form.tags.label")} obligatory description={t("form.tags.description")}>
{({ field, form }: FieldProps<string[], string[]>) => (
<AsyncSearchTags
multiSelect
language={language}
initialTags={tags}
field={field}
form={form}
fetchTags={fetchSearchTags}
/>
)}
</FormikField>
</>
<FormField name="tags">
{({ field, meta, helpers }) => (
<FieldRoot invalid={!!meta.error}>
<TagSelectorRoot
collection={collection}
value={field.value}
onValueChange={(details) => helpers.setValue(details.value)}
translations={tagSelectorTranslations}
inputValue={inputQuery}
onInputValueChange={(details) => setInputQuery(details.inputValue)}
>
<TagSelectorLabel>{t("form.tags.label")}</TagSelectorLabel>
<FieldErrorMessage>{meta.error}</FieldErrorMessage>
<FieldHelper>{t("form.tags.description")}</FieldHelper>
<SearchTagsTagSelectorInput asChild>
<Input placeholder={t("form.tags.searchPlaceholder")} />
</SearchTagsTagSelectorInput>
<SearchTagsContent isFetching={searchTagsQuery.isFetching} hits={collection.items.length}>
{collection.items.map((item) => (
<ComboboxItem key={item} item={item}>
<ComboboxItemText>{item}</ComboboxItemText>
<ComboboxItemIndicator asChild>
<CheckLine />
</ComboboxItemIndicator>
</ComboboxItem>
))}
</SearchTagsContent>
</TagSelectorRoot>
</FieldRoot>
)}
</FormField>
);
};

Expand Down
Loading

0 comments on commit 1d3fe36

Please sign in to comment.