Repository: NDLANO/ndla-frontend
Pull request #2469: Add link to preview in step
Author: login: gunnarvelle
Created: 2025-04-04T09:52:26Z | Merged: 2025-04-07T10:52:56Z

Gjør det mulig å sjekke at artikkelen du har søkt opp til stien din faktisk er den du leita etter.
![image](https://github.com/user-attachments/assets/e3c93f5f-3dd6-40e0-904f-90bf9f02b492)



Diff:
diff --git a/src/containers/MyNdla/Learningpath/components/FolderStepForm.tsx b/src/containers/MyNdla/Learningpath/components/FolderStepForm.tsx
index 4140325da5..7c713ed2ba 100644
--- a/src/containers/MyNdla/Learningpath/components/FolderStepForm.tsx
+++ b/src/containers/MyNdla/Learningpath/components/FolderStepForm.tsx
@@ -9,14 +9,16 @@
 import { t } from "i18next";
 import { useEffect, useState } from "react";
 import { useFormContext } from "react-hook-form";
-import { DeleteBinLine } from "@ndla/icons";
+import { DeleteBinLine, ExternalLinkLine } from "@ndla/icons";
 import { FieldHelper, FieldLabel, FieldRoot, IconButton, Text } from "@ndla/primitives";
 import { styled } from "@ndla/styled-system/jsx";
+import { linkOverlay } from "@ndla/styled-system/patterns";
 import { FolderResourcePicker } from "./FolderResourcePicker";
 import config from "../../../../config";
 import { useFetchOembed } from "../learningpathQueries";
 import { FolderResource } from "./folderTypes";
 import { ResourceFormValues } from "./ResourceStepForm";
+import { StepSafeLink } from "./StepSafeLink";
 
 const TextWrapper = styled("div", {
   base: {
@@ -26,12 +28,6 @@ const TextWrapper = styled("div", {
   },
 });
 
-const StyledText = styled(Text, {
-  base: {
-    textDecoration: "underline",
-  },
-});
-
 const ResourceWrapper = styled("div", {
   base: {
     display: "flex",
@@ -41,6 +37,13 @@ const ResourceWrapper = styled("div", {
     gap: "medium",
     justifyContent: "space-between",
     backgroundColor: "background.default",
+    position: "relative",
+  },
+});
+
+const StyledIconButton = styled(IconButton, {
+  base: {
+    position: "relative",
   },
 });
 
@@ -97,13 +100,18 @@ export const FolderStepForm = () => {
       ) : (
         <ResourceWrapper>
           <TextWrapper>
-            <StyledText fontWeight="bold">{resource.title}</StyledText>
+            <StepSafeLink to={resource.path} target="_blank" css={linkOverlay.raw()}>
+              <Text fontWeight="bold">
+                {resource.title}
+                <ExternalLinkLine size="small" />
+              </Text>
+            </StepSafeLink>
             <PathText textStyle="label.small" color="text.subtle">
               {config.ndlaFrontendDomain}
               {resource.path}
             </PathText>
           </TextWrapper>
-          <IconButton
+          <StyledIconButton
             id="remove-resource"
             aria-label={t("myNdla.learningpath.form.delete")}
             title={t("myNdla.learningpath.form.delete")}
@@ -111,7 +119,7 @@ export const FolderStepForm = () => {
             variant="tertiary"
           >
             <DeleteBinLine />
-          </IconButton>
+          </StyledIconButton>
         </ResourceWrapper>
       )}
     </FieldRoot>
diff --git a/src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx b/src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
index d521ca3738..fb9927d835 100644
--- a/src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
+++ b/src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
@@ -9,12 +9,14 @@
 import { useEffect, useState } from "react";
 import { useFormContext } from "react-hook-form";
 import { useTranslation } from "react-i18next";
-import { DeleteBinLine } from "@ndla/icons";
+import { DeleteBinLine, ExternalLinkLine } from "@ndla/icons";
 import { FieldLabel, FieldHelper, FieldRoot, IconButton, Text } from "@ndla/primitives";
 import { HStack, styled } from "@ndla/styled-system/jsx";
+import { linkOverlay } from "@ndla/styled-system/patterns";
 import { ContentTypeBadge } from "@ndla/ui";
 import { ResourceData } from "./folderTypes";
 import { ResourcePicker } from "./ResourcePicker";
+import { StepSafeLink } from "./StepSafeLink";
 import { contentTypeMapping } from "../../../../util/getContentType";
 
 export interface ResourceFormValues {
@@ -87,6 +89,7 @@ const ResourceWrapper = styled("div", {
     justifyContent: "space-between",
     boxShadow: "xsmall",
     backgroundColor: "background.default",
+    position: "relative",
   },
 });
 
@@ -102,6 +105,12 @@ const CrumbText = styled(Text, {
   },
 });
 
+const StyledIconButton = styled(IconButton, {
+  base: {
+    position: "relative",
+  },
+});
+
 interface ResourceContentProps {
   onRemove: () => void;
   selectedResource: ResourceData;
@@ -115,7 +124,12 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
   return (
     <ResourceWrapper>
       <TextWrapper>
-        <Text>{selectedResource.title}</Text>
+        <StepSafeLink to={selectedResource.url} target="_blank" css={linkOverlay.raw()}>
+          <Text fontWeight="bold">
+            {selectedResource.title}
+            <ExternalLinkLine size="small" />
+          </Text>
+        </StepSafeLink>
         {!!selectedResource.breadcrumbs && (
           <CrumbText
             textStyle="label.small"
@@ -129,7 +143,7 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
       </TextWrapper>
       <StyledHStack gap="medium">
         <ContentTypeBadge contentType={contentType} />
-        <IconButton
+        <StyledIconButton
           id="remove-resource"
           aria-label={t("myNdla.learningpath.form.delete")}
           title={t("myNdla.learningpath.form.delete")}
@@ -137,7 +151,7 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
           onClick={onRemove}
         >
           <DeleteBinLine />
-        </IconButton>
+        </StyledIconButton>
       </StyledHStack>
     </ResourceWrapper>
   );
diff --git a/src/containers/MyNdla/Learningpath/components/StepSafeLink.tsx b/src/containers/MyNdla/Learningpath/components/StepSafeLink.tsx
new file mode 100644
index 0000000000..c8d4279fcd
--- /dev/null
+++ b/src/containers/MyNdla/Learningpath/components/StepSafeLink.tsx
@@ -0,0 +1,24 @@
+/**
+ * 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 { SafeLink } from "@ndla/safelink";
+import { styled } from "@ndla/styled-system/jsx";
+
+export const StepSafeLink = styled(SafeLink, {
+  base: {
+    display: "inline",
+    color: "text.default",
+    textDecoration: "underline",
+    _hover: {
+      textDecoration: "none",
+    },
+    _focusVisible: {
+      textDecoration: "none",
+    },
+  },
+});


Reviews:
user: login: katrinewi
body: Hva med samme oppførsel for folder form også? 😄 
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T10:23:26Z
user: login: Jonas-C
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T09:42:29Z
user: login: Jonas-C
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T10:14:39Z
user: login: Jonas-C
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T10:48:11Z

Review comments:
diff_hunk: @@ -115,7 +116,12 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
   return (
     <ResourceWrapper>
       <TextWrapper>
-        <Text>{selectedResource.title}</Text>
+        <SafeLink to={selectedResource.url} target="_blank">
path: src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
user: login: katrinewi
body: Skulle vi kanskje brukt samme styling som i masthead search? bare med ExternalLinkLine-ikonet i tillegg kanskje?


created_at: 2025-04-04T10:22:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -115,17 +139,22 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
   return (
     <ResourceWrapper>
       <TextWrapper>
-        <Text>{selectedResource.title}</Text>
-        {!!selectedResource.breadcrumbs && (
-          <CrumbText
-            textStyle="label.small"
-            color="text.subtle"
-            css={{ textAlign: "start" }}
-            aria-label={`${t("breadcrumb.breadcrumb")}: ${selectedResource.breadcrumbs.join(", ")}`}
-          >
-            {selectedResource.breadcrumbs.join(" > ")}
-          </CrumbText>
-        )}
+        <StyledSafeLink to={selectedResource.url} target="_blank">
path: src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
user: login: Jonas-C
body: Må nok legge `position: "relative"` på `ResourceWrapper` også.
created_at: 2025-04-07T09:41:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -115,17 +139,22 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
   return (
     <ResourceWrapper>
       <TextWrapper>
-        <Text>{selectedResource.title}</Text>
-        {!!selectedResource.breadcrumbs && (
-          <CrumbText
-            textStyle="label.small"
-            color="text.subtle"
-            css={{ textAlign: "start" }}
-            aria-label={`${t("breadcrumb.breadcrumb")}: ${selectedResource.breadcrumbs.join(", ")}`}
-          >
-            {selectedResource.breadcrumbs.join(" > ")}
-          </CrumbText>
-        )}
+        <StyledSafeLink to={selectedResource.url} target="_blank">
+          <StyledText fontWeight="bold">
+            {selectedResource.title}
+            <ExternalLinkLine size="small" />
+          </StyledText>
+          {!!selectedResource.breadcrumbs && (
+            <CrumbText
+              textStyle="label.small"
+              color="text.subtle"
+              css={{ textAlign: "start" }}
+              aria-label={`${t("breadcrumb.breadcrumb")}: ${selectedResource.breadcrumbs.join(", ")}`}
+            >
+              {selectedResource.breadcrumbs.join(" > ")}
+            </CrumbText>
+          )}
path: src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
user: login: Jonas-C
body: Dette trenger vel ikke å ligge i lenken
created_at: 2025-04-07T09:41:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -115,17 +139,22 @@ export const ResourceContent = ({ onRemove, selectedResource }: ResourceContentP
   return (
     <ResourceWrapper>
       <TextWrapper>
-        <Text>{selectedResource.title}</Text>
-        {!!selectedResource.breadcrumbs && (
-          <CrumbText
-            textStyle="label.small"
-            color="text.subtle"
-            css={{ textAlign: "start" }}
-            aria-label={`${t("breadcrumb.breadcrumb")}: ${selectedResource.breadcrumbs.join(", ")}`}
-          >
-            {selectedResource.breadcrumbs.join(" > ")}
-          </CrumbText>
-        )}
+        <StyledSafeLink to={selectedResource.url} target="_blank">
+          <StyledText fontWeight="bold">
+            {selectedResource.title}
+            <ExternalLinkLine size="small" />
+          </StyledText>
+          {!!selectedResource.breadcrumbs && (
+            <CrumbText
+              textStyle="label.small"
+              color="text.subtle"
+              css={{ textAlign: "start" }}
+              aria-label={`${t("breadcrumb.breadcrumb")}: ${selectedResource.breadcrumbs.join(", ")}`}
+            >
+              {selectedResource.breadcrumbs.join(" > ")}
+            </CrumbText>
+          )}
path: src/containers/MyNdla/Learningpath/components/ResourceStepForm.tsx
user: login: Jonas-C
body: Hvis vi flytter denne trenger vi heller ikke å style text
created_at: 2025-04-07T09:42:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,25 @@
+/**
+ * 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 { SafeLink } from "@ndla/safelink";
+import { styled } from "@ndla/styled-system/jsx";
+
+export const StepSafeLink = styled(SafeLink, {
+  base: {
+    display: "inline",
+    color: "text.default",
+    textStyle: "label.small",
path: src/containers/MyNdla/Learningpath/components/StepSafeLink.tsx
user: login: Jonas-C
body: Tror ikke textStyle gjør noe her, i og med at vi bruker `<Text />` inne i lenken. Kan enten fjerne det, eller droppe å bruke `Text` og heller sette `fontWeight` direkte i denne.
created_at: 2025-04-07T10:14:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: Jonas-C
created_at: 2025-04-07T06:37:23Z
body: Skal vi gjøre hele kortet klikkbart, eller holder det at lenken er klikkbar?
author_association: CONTRIBUTOR
user: login: gunnarvelle
created_at: 2025-04-07T08:12:22Z
body: > Skal vi gjøre hele kortet klikkbart, eller holder det at lenken er klikkbar?

Fortell meg det du. Eg tenkte det var nok at lenka var klikkbar, men er fort gjort å gjøre kortet klikkbart.
author_association: MEMBER