feat/SW-3108 external links

* feat(SW-3108): Added external link options to shortcuts
* feat(SW-3108): Added external link options to header

Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-08-28 07:25:17 +00:00
parent 1a10afdbad
commit fd48f86c90
8 changed files with 213 additions and 98 deletions

View File

@@ -27,6 +27,11 @@ fragment Shortcuts on Shortcuts {
title
two_column_list
shortcuts {
is_contentstack_link
external_link {
href
title
}
open_in_new_tab
text
linkConnection {

View File

@@ -33,6 +33,7 @@ query GetHeader($locale: String!) {
top_link {
logged_in {
icon
is_contentstack_link
title
linkConnection {
edges {
@@ -52,9 +53,14 @@ query GetHeader($locale: String!) {
}
}
}
external_link {
href
title
}
}
logged_out {
icon
is_contentstack_link
title
linkConnection {
edges {
@@ -74,9 +80,14 @@ query GetHeader($locale: String!) {
}
}
}
external_link {
href
title
}
}
}
menu_items {
is_contentstack_link
title
linkConnection {
edges {
@@ -96,7 +107,12 @@ query GetHeader($locale: String!) {
}
}
}
external_link {
href
title
}
see_all_link {
is_contentstack_link
title
linkConnection {
edges {
@@ -116,10 +132,15 @@ query GetHeader($locale: String!) {
}
}
}
external_link {
href
title
}
}
submenu {
title
links {
is_contentstack_link
title
linkConnection {
edges {
@@ -139,6 +160,10 @@ query GetHeader($locale: String!) {
}
}
}
external_link {
href
title
}
}
}
cardConnection {

View File

@@ -7,6 +7,10 @@ import {
import { Lang } from "@scandic-hotels/common/constants/language"
import { logger } from "@scandic-hotels/common/logger"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import {
nullableStringUrlValidator,
nullableStringValidator,
} from "@scandic-hotels/common/utils/zod/stringValidator"
import { discriminatedUnion } from "../../../utils/discriminatedUnion"
import {
@@ -97,7 +101,7 @@ export const validateCurrentHeaderConfigSchema = z
edges: z.array(
z.object({
node: z.object({
description: z.string().optional().nullable(),
description: z.string().nullish(),
dimension: z.object({
height: z.number(),
width: z.number(),
@@ -106,8 +110,8 @@ export const validateCurrentHeaderConfigSchema = z
system: z.object({
uid: z.string(),
}),
title: z.string().optional().default(""),
url: z.string().optional().default(""),
title: nullableStringValidator,
url: nullableStringUrlValidator,
}),
})
),
@@ -178,7 +182,7 @@ const validateAppDownload = z.object({
edges: z.array(
z.object({
node: z.object({
description: z.string().optional().nullable(),
description: z.string().nullish(),
dimension: z.object({
height: z.number(),
width: z.number(),
@@ -220,7 +224,7 @@ export const validateCurrentFooterConfigSchema = z.object({
edges: z.array(
z.object({
node: z.object({
description: z.string().optional().nullable(),
description: z.string().nullish(),
dimension: z.object({
height: z.number(),
width: z.number(),
@@ -248,7 +252,7 @@ export const validateCurrentFooterConfigSchema = z.object({
edges: z.array(
z.object({
node: z.object({
description: z.string().optional().nullable(),
description: z.string().nullish(),
dimension: z.object({
height: z.number(),
width: z.number(),
@@ -561,6 +565,51 @@ export const headerRefsSchema = z
}
})
const internalOrExternalLinkSchema = z
.object({
is_contentstack_link: z.boolean().nullish(),
external_link: z
.object({
href: nullableStringUrlValidator,
title: z.string().nullish(),
})
.nullish(),
title: nullableStringValidator,
linkConnection: z.object({
edges: z.array(
z.object({
node: linkUnionSchema.transform((data) => {
const link = transformPageLink(data)
if (link) {
return link
}
return data
}),
})
),
}),
})
.transform(
({ is_contentstack_link, external_link, linkConnection, title }) => {
if (is_contentstack_link !== false && linkConnection.edges.length) {
const linkRef = linkConnection.edges[0].node
return {
title: title || linkRef.title,
url: linkRef.url,
}
} else if (is_contentstack_link === false && external_link?.href) {
return {
title: title || external_link.title || "",
url: external_link.href,
}
} else {
return {
title,
}
}
}
)
const linkSchema = z
.object({
linkConnection: z.object({
@@ -590,7 +639,7 @@ const linkSchema = z
})
const titleSchema = z.object({
title: z.string().optional().default(""),
title: nullableStringValidator,
})
/**
@@ -605,7 +654,7 @@ const linkAndTitleSchema = z.intersection(linkSchema, titleSchema)
*/
export const menuItemSchema = z
.intersection(
linkAndTitleSchema,
internalOrExternalLinkSchema,
z
.object({
cardConnection: z.object({
@@ -615,11 +664,11 @@ export const menuItemSchema = z
})
),
}),
see_all_link: linkAndTitleSchema,
see_all_link: internalOrExternalLinkSchema,
submenu: z.array(
z.object({
links: z.array(linkAndTitleSchema),
title: z.string().optional().default(""),
links: z.array(internalOrExternalLinkSchema),
title: nullableStringValidator,
})
),
})
@@ -636,11 +685,13 @@ export const menuItemSchema = z
}
})
)
.transform((data) => {
.transform(({ title, url, card, seeAllLink, submenu }) => {
return {
...data,
link: data.submenu.length ? null : data.link,
seeAllLink: data.submenu.length ? data.seeAllLink : null,
title,
link: submenu.length ? null : { url },
seeAllLink: submenu.length ? seeAllLink : null,
card,
submenu,
}
})
@@ -652,7 +703,7 @@ enum IconName {
}
const topLinkItemSchema = z.intersection(
linkAndTitleSchema,
internalOrExternalLinkSchema,
z.object({
icon: z
.enum(["loyalty", "info", "offer"])

View File

@@ -15,37 +15,65 @@ export const shortcutsBlockSchema = z.object({
two_column_list: z.boolean().nullable().default(false),
shortcuts: z
.array(
z.object({
open_in_new_tab: z.boolean(),
text: z.string().optional().default(""),
linkConnection: z.object({
edges: z.array(
z.object({
node: linkUnionSchema.transform((data) => {
const link = transformPageLink(data)
if (link) {
return link
}
return data
}),
z
.object({
is_contentstack_link: z.boolean().nullish(),
external_link: z
.object({
href: z.string().nullish().default(""),
title: z.string().nullish(),
})
),
}),
})
)
.transform((data) => {
return data
.filter((node) => node.linkConnection.edges.length)
.map((node) => {
const link = node.linkConnection.edges[0].node
return {
openInNewTab: node.open_in_new_tab,
text: node.text,
title: link.title,
url: link.url,
}
.nullish(),
open_in_new_tab: z.boolean(),
text: z.string().optional().default(""),
linkConnection: z.object({
edges: z.array(
z.object({
node: linkUnionSchema.transform((data) => {
const link = transformPageLink(data)
if (link) {
return link
}
return data
}),
})
),
}),
})
}),
.transform(
({
is_contentstack_link,
external_link,
linkConnection,
open_in_new_tab,
text,
}) => {
if (
is_contentstack_link !== false &&
linkConnection.edges.length
) {
const linkRef = linkConnection.edges[0].node
return {
openInNewTab: open_in_new_tab,
text: text || linkRef.title,
url: linkRef.url,
}
} else if (
is_contentstack_link === false &&
external_link?.href
) {
return {
openInNewTab: open_in_new_tab,
text: text || external_link.title || "",
url: external_link.href,
}
} else {
return null
}
}
)
)
.transform((data) => data.filter((item) => !!item)),
})
.transform(({ two_column_list, ...rest }) => {
return {