From 4c3be9ed6f20228b871b9e3c6ccbd770103e2f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20S=C3=B8rlie?= Date: Wed, 22 Jan 2025 15:36:50 +0100 Subject: [PATCH] Split learningpath embed into multiple files for readability --- src/components/Learningpath/Learningpath.tsx | 52 +-- .../Learningpath/LearningpathEmbed.tsx | 310 ------------------ .../Learningpath/components/ArticleStep.tsx | 124 +++++++ .../Learningpath/components/EmbedStep.tsx | 53 +++ .../Learningpath/components/ExternalStep.tsx | 52 +++ .../components/LearningpathStep.tsx | 231 +++++++++++++ .../components/LearningpathStepTitle.tsx | 35 ++ .../Learningpath/components/TextStep.tsx | 46 +++ src/graphqlTypes.ts | 74 ++--- src/messages/messagesEN.ts | 3 + src/messages/messagesNB.ts | 3 + src/messages/messagesNN.ts | 3 + src/messages/messagesSE.ts | 3 + 13 files changed, 602 insertions(+), 387 deletions(-) delete mode 100644 src/components/Learningpath/LearningpathEmbed.tsx create mode 100644 src/components/Learningpath/components/ArticleStep.tsx create mode 100644 src/components/Learningpath/components/EmbedStep.tsx create mode 100644 src/components/Learningpath/components/ExternalStep.tsx create mode 100644 src/components/Learningpath/components/LearningpathStep.tsx create mode 100644 src/components/Learningpath/components/LearningpathStepTitle.tsx create mode 100644 src/components/Learningpath/components/TextStep.tsx diff --git a/src/components/Learningpath/Learningpath.tsx b/src/components/Learningpath/Learningpath.tsx index 078d20691..f7788539b 100644 --- a/src/components/Learningpath/Learningpath.tsx +++ b/src/components/Learningpath/Learningpath.tsx @@ -9,9 +9,7 @@ import { useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { gql } from "@apollo/client"; -import { transform } from "@ndla/article-converter"; import { ArrowDownShortLine, ArrowLeftLine, ArrowRightLine } from "@ndla/icons"; -import { getLicenseByAbbreviation } from "@ndla/licenses"; import { AccordionItem, AccordionItemContent, @@ -24,10 +22,8 @@ import { } from "@ndla/primitives"; import { SafeLinkButton } from "@ndla/safelink"; import { styled } from "@ndla/styled-system/jsx"; -import { ArticleContent, ArticleHeader, ArticleWrapper, ContentTypeBadge, HomeBreadcrumb, LicenseLink } from "@ndla/ui"; +import { ContentTypeBadge, HomeBreadcrumb } from "@ndla/ui"; import { contains } from "@ndla/util"; -import LastLearningpathStepInfo from "./LastLearningpathStepInfo"; -import LearningpathEmbed, { EmbedPageContent } from "./LearningpathEmbed"; import LearningpathMenu from "./LearningpathMenu"; import type { LearningpathContext } from "./learningpathUtils"; import { @@ -35,18 +31,19 @@ import { GQLLearningpath_LearningpathStepFragment, GQLLearningpathPage_NodeFragment, } from "../../graphqlTypes"; -import { Breadcrumb as BreadcrumbType } from "../../interfaces"; +import { Breadcrumb } from "../../interfaces"; import { routes, toLearningPath } from "../../routeHelpers"; import FavoriteButton from "../Article/FavoritesButton"; import { PageContainer } from "../Layout/PageContainer"; import AddResourceToFolderModal from "../MyNdla/AddResourceToFolderModal"; +import { LearningpathStep } from "./components/LearningpathStep"; interface Props { learningpath: GQLLearningpath_LearningpathFragment; learningpathStep: GQLLearningpath_LearningpathStepFragment; resource?: GQLLearningpathPage_NodeFragment; skipToContentId?: string; - breadcrumbItems: BreadcrumbType[]; + breadcrumbItems: Breadcrumb[]; resourcePath?: string; context?: LearningpathContext; } @@ -190,7 +187,7 @@ const Learningpath = ({ breadcrumbItems, context = "default", }: Props) => { - const { t, i18n } = useTranslation(); + const { t } = useTranslation(); const [accordionValue, setAccordionValue] = useState(); const accordionRef = useRef(null); @@ -273,39 +270,14 @@ const Learningpath = ({ {context === "default" && {menu}} - {(!!learningpathStep.description || !!learningpathStep.showTitle) && ( - - - {!!learningpathStep.showTitle && ( - - - {learningpathStep.title} - - - - )} - - {!!learningpathStep.description &&
{transform(learningpathStep.description, {})}
} -
-
-
- )} - - - + skipToContentId={skipToContentId} + learningpathStep={learningpathStep} + /> {/* TODO: How should this handle long titles on smaller screens? */} {previousStep ? ( @@ -362,7 +334,7 @@ Learningpath.fragments = { ...LearningpathMenu_LearningpathStep } ${LearningpathMenu.fragments.step} - ${LearningpathEmbed.fragments.learningpathStep} + ${LearningpathStep.fragments.learningpathStep} `, learningpath: gql` fragment Learningpath_Learningpath on Learningpath { diff --git a/src/components/Learningpath/LearningpathEmbed.tsx b/src/components/Learningpath/LearningpathEmbed.tsx deleted file mode 100644 index d21d60fe1..000000000 --- a/src/components/Learningpath/LearningpathEmbed.tsx +++ /dev/null @@ -1,310 +0,0 @@ -/** - * Copyright (c) 2019-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 { ReactNode, useId, useMemo } from "react"; -import { useTranslation } from "react-i18next"; -import { useLocation } from "react-router-dom"; -import { gql, useQuery } from "@apollo/client"; -import { PageContent } from "@ndla/primitives"; -import { styled } from "@ndla/styled-system/jsx"; -import { ArticleContent, ArticleTitle, ArticleWrapper, ExternalEmbed } from "@ndla/ui"; -import LearningpathIframe from "./LearningpathIframe"; -import config from "../../config"; -import { - GQLLearningpathEmbed_LearningpathStepFragment, - GQLLearningpathStepQuery, - GQLLearningpathStepQueryVariables, -} from "../../graphqlTypes"; -import { supportedLanguages } from "../../i18n"; -import { Breadcrumb } from "../../interfaces"; -import { getArticleScripts } from "../../util/getArticleScripts"; -import { getContentType } from "../../util/getContentType"; -import getStructuredDataFromArticle, { structuredArticleDataFragment } from "../../util/getStructuredDataFromArticle"; -import { transformArticle } from "../../util/transformArticle"; -import Article from "../Article"; -import { CreatedBy } from "../Article/CreatedBy"; -import { ContentPlaceholder } from "../ContentPlaceholder"; -import { DefaultErrorMessage } from "../DefaultErrorMessage"; - -export const EmbedPageContent = styled(PageContent, { - base: { - background: "background.default", - tablet: { - border: "1px solid", - borderColor: "stroke.subtle", - boxShadow: "small", - borderRadius: "xsmall", - }, - }, -}); - -const urlIsNDLAApiUrl = (url: string) => /^(http|https):\/\/(ndla-frontend|www).([a-zA-Z]+.)?api.ndla.no/.test(url); -const urlIsNDLAEnvUrl = (url: string) => /^(http|https):\/\/(www.)?([a-zA-Z]+.)?ndla.no/.test(url); -const urlIsLocalNdla = (url: string) => /^http:\/\/(proxy.ndla-local|localhost):30017/.test(url); -const urlIsNDLAUrl = (url: string) => urlIsNDLAApiUrl(url) || urlIsNDLAEnvUrl(url) || urlIsLocalNdla(url); - -const regex = new RegExp(`\\/(${supportedLanguages.join("|")})($|\\/)`, ""); - -const getIdFromIframeUrl = (_url: string): [string | undefined, string | undefined] => { - const url = _url.split("/article-iframe")?.[1]?.replace(regex, "")?.replace("article/", "")?.split("?")?.[0]; - - if (url?.includes("/")) { - const [taxId, articleId] = url.split("/"); - if (parseInt(articleId!)) { - return [taxId, articleId]; - } - } else if (url && parseInt(url)) { - return [undefined, url]; - } - return [undefined, undefined]; -}; - -interface Props { - learningpathStep: GQLLearningpathEmbed_LearningpathStepFragment; - skipToContentId?: string; - breadcrumbItems: Breadcrumb[]; - subjectId?: string; - children?: ReactNode; -} -const LearningpathEmbed = ({ learningpathStep, skipToContentId, subjectId, breadcrumbItems, children }: Props) => { - const { t, i18n } = useTranslation(); - const location = useLocation(); - const [taxId, articleId] = - !learningpathStep.resource && learningpathStep.embedUrl?.url - ? getIdFromIframeUrl(learningpathStep.embedUrl.url) - : [undefined, undefined]; - - const fallbackId = useId(); - - const shouldUseConverter = - !!articleId && - !learningpathStep.resource?.article && - learningpathStep.embedUrl && - urlIsNDLAUrl(learningpathStep.embedUrl?.url); - - const { data, loading } = useQuery( - learningpathStepQuery, - { - variables: { - articleId: articleId ?? learningpathStep.resource?.article?.id.toString() ?? "", - resourceId: taxId ?? "", - includeResource: !!taxId, - transformArgs: { - path: location.pathname, - subjectId, - prettyUrl: true, - }, - }, - skip: - !!learningpathStep.resource?.article || - !articleId || - (!learningpathStep.embedUrl && !learningpathStep.resource), - }, - ); - - const url = !learningpathStep.resource?.url ? data?.node?.url : undefined; - const contentUrl = url ? `${config.ndlaFrontendDomain}${url}` : undefined; - - const [article, scripts] = useMemo(() => { - const article = learningpathStep.resource?.article ? learningpathStep.resource.article : data?.article; - if (!article) return [undefined, undefined]; - return [ - transformArticle(article, i18n.language, { - path: `${config.ndlaFrontendDomain}/article/${article.id}`, - subject: subjectId, - articleLanguage: article.language, - contentType: getContentType(learningpathStep.resource), - }), - getArticleScripts(article, i18n.language), - ]; - }, [data?.article, i18n.language, learningpathStep.resource, subjectId]); - - if ( - (!learningpathStep || (!learningpathStep.resource && (!learningpathStep?.embedUrl || !learningpathStep?.oembed))) && - learningpathStep.embedUrl?.embedType !== "external" - ) { - return null; - } - const { embedUrl, oembed } = learningpathStep; - const isExternalLink = embedUrl?.embedType === "external"; - if ( - (!learningpathStep.resource && - !shouldUseConverter && - embedUrl && - (embedUrl.embedType === "oembed" || embedUrl.embedType === "iframe") && - oembed && - oembed.html) || - isExternalLink - ) { - if (urlIsNDLAUrl(embedUrl.url) && oembed) { - return ; - } - - return ( - - - - -
- -
-
-
-
- ); - } - - if (loading) { - return ( - - - - ); - } - - if (!article || !scripts) { - return null; - } - - const learningpathStepResource = learningpathStep.resource ?? data; - const resource = learningpathStep.resource ?? data?.node; - const stepArticle = learningpathStepResource?.article; - - if (!stepArticle) { - return ; - } - - return ( - - {!!article?.metaDescription && } - {scripts.map((script) => ( - -
- {children} - {!!url && } -
-
- ); -}; - -const articleFragment = gql` - fragment LearningpathEmbed_Article on Article { - id - metaDescription - created - updated - articleType - requiredLibraries { - name - url - mediaType - } - ...StructuredArticleData - ...Article_Article - } - ${structuredArticleDataFragment} - ${Article.fragments.article} -`; - -LearningpathEmbed.fragments = { - article: articleFragment, - learningpathStep: gql` - fragment LearningpathEmbed_LearningpathStep on LearningpathStep { - id - title - description - introduction - opengraph { - title - description - url - } - resource { - id - url - resourceTypes { - id - name - } - article { - ...LearningpathEmbed_Article - } - } - embedUrl { - embedType - url - } - oembed { - html - width - height - type - version - } - } - ${articleFragment} - ${structuredArticleDataFragment} - ${Article.fragments.article} - `, -}; - -const learningpathStepQuery = gql` - query learningpathStep( - $articleId: String! - $resourceId: String! - $includeResource: Boolean! - $transformArgs: TransformedArticleContentInput - ) { - article(id: $articleId) { - oembed - ...LearningpathEmbed_Article - } - node(id: $resourceId) @include(if: $includeResource) { - id - url - resourceTypes { - id - name - } - } - } - ${articleFragment} -`; - -export default LearningpathEmbed; diff --git a/src/components/Learningpath/components/ArticleStep.tsx b/src/components/Learningpath/components/ArticleStep.tsx new file mode 100644 index 000000000..abfae15dd --- /dev/null +++ b/src/components/Learningpath/components/ArticleStep.tsx @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2025-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 { ReactNode, useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { useLocation } from "react-router-dom"; +import { useQuery } from "@apollo/client"; +import { BaseStepProps, learningpathStepQuery, EmbedPageContent } from "./LearningpathStep"; +import config from "../../../config"; +import { GQLLearningpathStepQuery, GQLLearningpathStepQueryVariables } from "../../../graphqlTypes"; +import { Breadcrumb } from "../../../interfaces"; +import { getArticleScripts } from "../../../util/getArticleScripts"; +import { getContentType } from "../../../util/getContentType"; +import getStructuredDataFromArticle from "../../../util/getStructuredDataFromArticle"; +import { transformArticle } from "../../../util/transformArticle"; +import Article from "../../Article"; +import { CreatedBy } from "../../Article/CreatedBy"; +import { ContentPlaceholder } from "../../ContentPlaceholder"; +import { DefaultErrorMessage } from "../../DefaultErrorMessage"; + +interface ArticleStepProps extends BaseStepProps { + breadcrumbItems: Breadcrumb[]; + subjectId?: string; + taxId?: string; + articleId?: string; + children?: ReactNode; +} + +export const ArticleStep = ({ + learningpathStep, + skipToContentId, + subjectId, + breadcrumbItems, + articleId, + taxId, + children, +}: ArticleStepProps) => { + const { t, i18n } = useTranslation(); + const location = useLocation(); + + const { data, loading } = useQuery( + learningpathStepQuery, + { + variables: { + articleId: articleId ?? learningpathStep.resource?.article?.id.toString() ?? "", + resourceId: taxId ?? "", + includeResource: !!taxId, + transformArgs: { + path: location.pathname, + subjectId, + prettyUrl: true, + }, + }, + skip: + !!learningpathStep.resource?.article || + !articleId || + (!learningpathStep.embedUrl && !learningpathStep.resource), + }, + ); + + const url = !learningpathStep.resource?.url ? data?.node?.url : undefined; + const contentUrl = url ? `${config.ndlaFrontendDomain}${url}` : undefined; + + const [article, scripts] = useMemo(() => { + const article = learningpathStep.resource?.article ? learningpathStep.resource.article : data?.article; + if (!article) return [undefined, undefined]; + return [ + transformArticle(article, i18n.language, { + path: `${config.ndlaFrontendDomain}/article/${article.id}`, + subject: subjectId, + articleLanguage: article.language, + contentType: getContentType(learningpathStep.resource), + }), + getArticleScripts(article, i18n.language), + ]; + }, [data?.article, i18n.language, learningpathStep.resource, subjectId]); + + if (loading) { + return ( + + + + ); + } + + if (!article || !scripts) { + return null; + } + + const learningpathStepResource = learningpathStep.resource ?? data; + const resource = learningpathStep.resource ?? data?.node; + const stepArticle = learningpathStepResource?.article; + + if (!stepArticle) { + return ; + } + + return ( + + {!!article?.metaDescription && } + {scripts.map((script) => ( + +
+ {children} + {!!url && } +
+
+ ); +}; diff --git a/src/components/Learningpath/components/EmbedStep.tsx b/src/components/Learningpath/components/EmbedStep.tsx new file mode 100644 index 000000000..91f411cb0 --- /dev/null +++ b/src/components/Learningpath/components/EmbedStep.tsx @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2025-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 { useId } from "react"; +import { ArticleWrapper, ArticleTitle, ArticleContent, ExternalEmbed } from "@ndla/ui"; +import { GQLLearningpathEmbed_LearningpathStepFragment } from "../../../graphqlTypes"; +import LearningpathIframe, { urlIsNDLAUrl } from "../LearningpathIframe"; +import { EmbedPageContent } from "./LearningpathStep"; + +interface EmbedStepProps { + url: string; + title: string; + oembed: GQLLearningpathEmbed_LearningpathStepFragment["oembed"]; + skipToContentId?: string; +} + +export const EmbedStep = ({ skipToContentId, url, title, oembed }: EmbedStepProps) => { + const fallbackId = useId(); + + if (urlIsNDLAUrl(url) && oembed?.html) { + return ; + } + + return ( + + + + +
+ +
+
+
+
+ ); +}; diff --git a/src/components/Learningpath/components/ExternalStep.tsx b/src/components/Learningpath/components/ExternalStep.tsx new file mode 100644 index 000000000..b10f9fd74 --- /dev/null +++ b/src/components/Learningpath/components/ExternalStep.tsx @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2025-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 { useId } from "react"; +import { useTranslation } from "react-i18next"; +import { InformationLine } from "@ndla/icons"; +import { MessageBox } from "@ndla/primitives"; +import { styled } from "@ndla/styled-system/jsx"; +import { ArticleContent, ArticleTitle, ArticleWrapper, ResourceBox } from "@ndla/ui"; +import { BaseStepProps, EmbedPageContent } from "./LearningpathStep"; + +const StyledEmbedPageContent = styled(EmbedPageContent, { + base: { + paddingBlock: "medium", + }, +}); +interface Props extends BaseStepProps {} + +export const ExternalStep = ({ learningpathStep, skipToContentId }: Props) => { + const { t } = useTranslation(); + const fallbackId = useId(); + return ( + + + + {t("learningpathPage.externalWarning")} + + + + +
+ +
+
+
+
+ ); +}; diff --git a/src/components/Learningpath/components/LearningpathStep.tsx b/src/components/Learningpath/components/LearningpathStep.tsx new file mode 100644 index 000000000..d4a9c36e0 --- /dev/null +++ b/src/components/Learningpath/components/LearningpathStep.tsx @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2019-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 { gql } from "@apollo/client"; +import { PageContent } from "@ndla/primitives"; +import { styled } from "@ndla/styled-system/jsx"; +import { + GQLLearningpath_LearningpathStepFragment, + GQLLearningpath_LearningpathFragment, + GQLLearningpathPage_NodeFragment, +} from "../../../graphqlTypes"; +import { supportedLanguages } from "../../../i18n"; +import { structuredArticleDataFragment } from "../../../util/getStructuredDataFromArticle"; +import Article from "../../Article"; +import LastLearningpathStepInfo from "../LastLearningpathStepInfo"; +import { ArticleStep } from "./ArticleStep"; +import { EmbedStep } from "./EmbedStep"; +import { ExternalStep } from "./ExternalStep"; +import { LearningpathStepTitle } from "./LearningpathStepTitle"; +import { TextStep } from "./TextStep"; +import { Breadcrumb } from "../../../interfaces"; + +export const EmbedPageContent = styled(PageContent, { + base: { + background: "background.default", + tablet: { + border: "1px solid", + borderColor: "stroke.subtle", + boxShadow: "small", + borderRadius: "xsmall", + }, + }, +}); + +const urlIsNDLAApiUrl = (url: string) => /^(http|https):\/\/(ndla-frontend|www).([a-zA-Z]+.)?api.ndla.no/.test(url); +const urlIsNDLAEnvUrl = (url: string) => /^(http|https):\/\/(www.)?([a-zA-Z]+.)?ndla.no/.test(url); +const urlIsLocalNdla = (url: string) => /^http:\/\/(proxy.ndla-local|localhost):30017/.test(url); +const urlIsNDLAUrl = (url: string) => urlIsNDLAApiUrl(url) || urlIsNDLAEnvUrl(url) || urlIsLocalNdla(url); + +const regex = new RegExp(`\\/(${supportedLanguages.join("|")})($|\\/)`, ""); + +const getIdFromIframeUrl = (_url: string): [string | undefined, string | undefined] => { + const url = _url.split("/article-iframe")?.[1]?.replace(regex, "")?.replace("article/", "")?.split("?")?.[0]; + + if (url?.includes("/")) { + const [taxId, articleId] = url.split("/"); + if (parseInt(articleId!)) { + return [taxId, articleId]; + } + } else if (url && parseInt(url)) { + return [undefined, url]; + } + return [undefined, undefined]; +}; + +interface Props { + learningpath: GQLLearningpath_LearningpathFragment; + learningpathStep: GQLLearningpath_LearningpathStepFragment; + resource?: GQLLearningpathPage_NodeFragment; + skipToContentId?: string; + breadcrumbItems: Breadcrumb[]; + subjectId?: string; +} + +export interface BaseStepProps { + learningpathStep: GQLLearningpath_LearningpathStepFragment; + skipToContentId?: string; +} + +export const LearningpathStep = ({ + learningpath, + learningpathStep, + breadcrumbItems, + subjectId, + skipToContentId, + resource, +}: Props) => { + const [taxId, articleId] = + !learningpathStep.resource && learningpathStep.embedUrl?.url + ? getIdFromIframeUrl(learningpathStep.embedUrl.url) + : [undefined, undefined]; + + const shouldUseConverter = + !!articleId && + !learningpathStep.resource?.article && + !!learningpathStep.embedUrl && + urlIsNDLAUrl(learningpathStep.embedUrl?.url); + + if ( + !shouldUseConverter && + !learningpathStep.resource && + learningpathStep.oembed?.html && + (learningpathStep.embedUrl?.embedType === "oembed" || learningpathStep.embedUrl?.embedType === "iframe") + ) { + return ( + <> + + + + ); + } + + if (learningpathStep.resource) { + return ( + <> + + + + + + ); + } + + if (learningpathStep.description && learningpathStep.introduction) { + return ( + + ); + } + + if (learningpathStep.opengraph) { + return ; + } + + return ; +}; + +export const articleFragment = gql` + fragment LearningpathEmbed_Article on Article { + id + metaDescription + created + updated + articleType + requiredLibraries { + name + url + mediaType + } + ...StructuredArticleData + ...Article_Article + } + ${structuredArticleDataFragment} + ${Article.fragments.article} +`; + +LearningpathStep.fragments = { + article: articleFragment, + learningpathStep: gql` + fragment LearningpathEmbed_LearningpathStep on LearningpathStep { + id + title + description + introduction + opengraph { + title + description + url + } + resource { + id + url + resourceTypes { + id + name + } + article { + ...LearningpathEmbed_Article + } + } + embedUrl { + embedType + url + } + oembed { + html + width + height + type + version + } + } + ${articleFragment} + ${structuredArticleDataFragment} + ${Article.fragments.article} + `, +}; + +export const learningpathStepQuery = gql` + query learningpathStep( + $articleId: String! + $resourceId: String! + $includeResource: Boolean! + $transformArgs: TransformedArticleContentInput + ) { + article(id: $articleId) { + oembed + ...LearningpathEmbed_Article + } + node(id: $resourceId) @include(if: $includeResource) { + id + url + resourceTypes { + id + name + } + } + } + ${articleFragment} +`; diff --git a/src/components/Learningpath/components/LearningpathStepTitle.tsx b/src/components/Learningpath/components/LearningpathStepTitle.tsx new file mode 100644 index 000000000..0fa794153 --- /dev/null +++ b/src/components/Learningpath/components/LearningpathStepTitle.tsx @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2025-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 { useTranslation } from "react-i18next"; +import { transform } from "@ndla/article-converter"; +import { getLicenseByAbbreviation } from "@ndla/licenses"; +import { Heading } from "@ndla/primitives"; +import { ArticleWrapper, ArticleContent, ArticleHeader, LicenseLink } from "@ndla/ui"; +import { BaseStepProps, EmbedPageContent } from "./LearningpathStep"; + +interface DescriptionStepProps extends BaseStepProps {} + +export const LearningpathStepTitle = ({ learningpathStep, skipToContentId }: DescriptionStepProps) => { + const { i18n } = useTranslation(); + return learningpathStep.showTitle || learningpathStep.description ? ( + + + {!!learningpathStep.showTitle && ( + + {learningpathStep.title} + + + )} + + {!!learningpathStep.description &&
{transform(learningpathStep.description, {})}
} +
+
+
+ ) : null; +}; diff --git a/src/components/Learningpath/components/TextStep.tsx b/src/components/Learningpath/components/TextStep.tsx new file mode 100644 index 000000000..2c1fc7329 --- /dev/null +++ b/src/components/Learningpath/components/TextStep.tsx @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2025-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 { useId } from "react"; +import { useTranslation } from "react-i18next"; +import { transform } from "@ndla/article-converter"; +import { ArticleWrapper, ArticleTitle, ArticleContent, ArticleFooter, ArticleByline } from "@ndla/ui"; +import { BaseStepProps, EmbedPageContent } from "./LearningpathStep"; +import { GQLLearningpath_LearningpathFragment } from "../../../graphqlTypes"; + +interface TextStepProps extends BaseStepProps { + learningpath: GQLLearningpath_LearningpathFragment; +} + +export const TextStep = ({ learningpathStep, learningpath, skipToContentId }: TextStepProps) => { + const fallbackId = useId(); + + const { i18n } = useTranslation(); + + const updatedDate = new Date(learningpath.lastUpdated); + const dateFormatter = Intl.DateTimeFormat(i18n.language); + + return ( + + + + + {learningpathStep.description ?
{transform(learningpathStep.description, {})}
: null} +
+ + + +
+
+ ); +}; diff --git a/src/graphqlTypes.ts b/src/graphqlTypes.ts index d19d677ce..851f27b94 100644 --- a/src/graphqlTypes.ts +++ b/src/graphqlTypes.ts @@ -2644,6 +2644,43 @@ export type GQLLearningpath_LearningpathFragment = { __typename?: "Learningpath"; } & GQLLearningpathMenu_LearningpathFragment; +export type GQLLearningpathMenu_LearningpathFragment = { + __typename?: "Learningpath"; + id: number; + title: string; + lastUpdated: string; + copyright: { + __typename?: "LearningpathCopyright"; + license: { __typename?: "License"; license: string }; + contributors: Array<{ __typename?: "Contributor"; type: string; name: string }>; + }; + learningsteps: Array<{ __typename?: "LearningpathStep"; id: number; title: string }>; +}; + +export type GQLLearningpathMenu_LearningpathStepFragment = { + __typename?: "LearningpathStep"; + id: number; + seqNo: number; +}; + +export type GQLLearningpathStepQueryVariables = Exact<{ + articleId: Scalars["String"]["input"]; + resourceId: Scalars["String"]["input"]; + includeResource: Scalars["Boolean"]["input"]; + transformArgs?: InputMaybe; +}>; + +export type GQLLearningpathStepQuery = { + __typename?: "Query"; + article?: { __typename?: "Article"; oembed?: string } & GQLLearningpathEmbed_ArticleFragment; + node?: { + __typename?: "Node"; + id: string; + url?: string; + resourceTypes?: Array<{ __typename?: "ResourceType"; id: string; name: string }>; + }; +}; + export type GQLLearningpathEmbed_ArticleFragment = { __typename?: "Article"; id: number; @@ -2680,43 +2717,6 @@ export type GQLLearningpathEmbed_LearningpathStepFragment = { }; }; -export type GQLLearningpathStepQueryVariables = Exact<{ - articleId: Scalars["String"]["input"]; - resourceId: Scalars["String"]["input"]; - includeResource: Scalars["Boolean"]["input"]; - transformArgs?: InputMaybe; -}>; - -export type GQLLearningpathStepQuery = { - __typename?: "Query"; - article?: { __typename?: "Article"; oembed?: string } & GQLLearningpathEmbed_ArticleFragment; - node?: { - __typename?: "Node"; - id: string; - url?: string; - resourceTypes?: Array<{ __typename?: "ResourceType"; id: string; name: string }>; - }; -}; - -export type GQLLearningpathMenu_LearningpathFragment = { - __typename?: "Learningpath"; - id: number; - title: string; - lastUpdated: string; - copyright: { - __typename?: "LearningpathCopyright"; - license: { __typename?: "License"; license: string }; - contributors: Array<{ __typename?: "Contributor"; type: string; name: string }>; - }; - learningsteps: Array<{ __typename?: "LearningpathStep"; id: number; title: string }>; -}; - -export type GQLLearningpathMenu_LearningpathStepFragment = { - __typename?: "LearningpathStep"; - id: number; - seqNo: number; -}; - export type GQLSubjectLinks_SubjectPageFragment = { __typename?: "SubjectPage"; buildsOn: Array<{ __typename?: "SubjectLink"; name?: string; url?: string }>; diff --git a/src/messages/messagesEN.ts b/src/messages/messagesEN.ts index bebebc07c..df771b8ee 100644 --- a/src/messages/messagesEN.ts +++ b/src/messages/messagesEN.ts @@ -344,6 +344,9 @@ const messages = { accordionTitle: "Learning path content", learningsteps: "Steps", stepCompleted: "Completed", + externalWarning: + "This learning path has been developed by an external teacher, who holds the editorial responsibility. Please note that it may contain texts and links that do not originate from ndla.no.", + externalLink: "Open in a new window", }, movedResourcePage: { title: "The page has moved, but you can find it here:", diff --git a/src/messages/messagesNB.ts b/src/messages/messagesNB.ts index dbded502f..69a6cb265 100644 --- a/src/messages/messagesNB.ts +++ b/src/messages/messagesNB.ts @@ -345,6 +345,9 @@ const messages = { accordionTitle: "Innhold i læringssti", learningsteps: "Læringssteg", stepCompleted: "Fullført", + externalWarning: + "Denne læringsstien er utarbeidet av en ekstern lærer, som har det redaksjonelle ansvaret. Vær oppmerksom på at den kan inneholde tekster og lenker som ikke kommer fra ndla.no.", + externalLink: "Åpne i nytt vindu", }, movedResourcePage: { title: "Siden har flyttet, men du finner den her:", diff --git a/src/messages/messagesNN.ts b/src/messages/messagesNN.ts index dd9e48d26..0513dee31 100644 --- a/src/messages/messagesNN.ts +++ b/src/messages/messagesNN.ts @@ -345,6 +345,9 @@ const messages = { accordionTitle: "Innhald i læringssti", learningsteps: "Læringssteg", stepCompleted: "Fullført", + externalWarning: + "Denne læringsstien er utarbeidd av ein ekstern lærar, som har det redaksjonelle ansvaret. Ver merksam på at ho kan innehalde tekstar og lenkjer som ikkje kjem frå ndla.no.", + externalLink: "Åpne i nytt vindauge", }, movedResourcePage: { title: "Sida har flytta, men du finn ho her:", diff --git a/src/messages/messagesSE.ts b/src/messages/messagesSE.ts index 685d63065..5fee51a9b 100644 --- a/src/messages/messagesSE.ts +++ b/src/messages/messagesSE.ts @@ -339,6 +339,9 @@ const messages = { accordionTitle: "Innhold i læringssti", learningsteps: "Læringssteg", stepCompleted: "Fullført", + externalWarning: + "Denne læringsstien er utarbeidet av en ekstern lærer, som har det redaksjonelle ansvaret. Vær oppmerksom på at den kan inneholde tekster og lenker som ikke kommer fra ndla.no.", + externalLink: "Åpne i nytt vindu", }, movedResourcePage: { title: "Siden har flyttet, men du finner den her:",