diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 80688a44..6631bd81 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -68,6 +68,13 @@ const preview: Preview = { }, }, }, + a11y: { + config: { + // Add a global 2-second delay for accessibility tests to make sure + // all animations are completed before the tests run + delay: 3000, + }, + }, }, decorators: [ // 👇 Defining the decorator in the preview file applies it to all stories diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index 59a38ab5..83027b17 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -3,10 +3,15 @@ import { checkA11y, injectAxe } from "axe-playwright" module.exports = { async preVisit(page) { await injectAxe(page) + // Add a delay to wait for all animations to finish + await page.waitForTimeout(2000) }, async postVisit(page) { + // Ensure all animations are complete before running accessibility checks + await page.waitForTimeout(2000) + await checkA11y(page, "#storybook-root", { - axeOptions: {}, + axeOptions: { preload: true }, detailedReport: true, detailedReportOptions: { html: true, diff --git a/components/pages/searchPageLayout/SearchPageLayout.tsx b/components/pages/searchPageLayout/SearchPageLayout.tsx index f52f4dfc..86922bcb 100644 --- a/components/pages/searchPageLayout/SearchPageLayout.tsx +++ b/components/pages/searchPageLayout/SearchPageLayout.tsx @@ -3,15 +3,16 @@ import { motion, useInView } from "framer-motion" import { useEffect, useRef } from "react" +import SearchResults, { + SearchResultsSkeleton, +} from "@/components/pages/searchPageLayout/SearchResults" +import { useSearchDataAndLoadingStates } from "@/components/pages/searchPageLayout/helper" +import SearchFiltersDesktop, { + SearchFiltersDesktopSkeleton, +} from "@/components/shared/searchFilters/SearchFiltersDesktop" import SearchFiltersMobile from "@/components/shared/searchFilters/SearchFiltersMobile" import useSearchMachineActor from "@/lib/machines/search/useSearchMachineActor" -import SearchFiltersDesktop, { - SearchFiltersDesktopGhost, -} from "../../shared/searchFilters/SearchFiltersDesktop" -import SearchResults, { SearchResultsGhost } from "./SearchResults" -import { useSearchDataAndLoadingStates } from "./helper" - const SearchPageLayout = () => { const loadMoreRef = useRef(null) const isInView = useInView(loadMoreRef) @@ -62,15 +63,17 @@ const SearchPageLayout = () => { ) : ( <> -
{/* */}
+
{/* */}
- +
)}
- {isNoSearchResult &&

Ingen søgeresultat

} + {isNoSearchResult && ( +

Ingen søgeresultat

+ )} {data.search && data.search.pages.map( (works, i) => @@ -85,7 +88,7 @@ const SearchPageLayout = () => { ) )} - {(isLoadingMoreResults || isLoadingResults) && } + {(isLoadingMoreResults || isLoadingResults) && }
) : ( diff --git a/components/pages/searchPageLayout/SearchResults.tsx b/components/pages/searchPageLayout/SearchResults.tsx index 8a27f121..0a39ea4e 100644 --- a/components/pages/searchPageLayout/SearchResults.tsx +++ b/components/pages/searchPageLayout/SearchResults.tsx @@ -2,7 +2,7 @@ import React from "react" -import WorkCard, { WorkCardGhost } from "@/components/shared/workCard/WorkCard" +import WorkCard, { WorkCardSkeleton } from "@/components/shared/workCard/WorkCard" import { WorkTeaserSearchPageFragment } from "@/lib/graphql/generated/fbi/graphql" type SearchResultProps = { @@ -21,14 +21,14 @@ const SearchResults = ({ works }: SearchResultProps) => { ) } -export const SearchResultsGhost = () => { - const ghostItems = Array.from({ length: 6 }) +export const SearchResultsSkeleton = () => { + const skeletonItems = Array.from({ length: 6 }) return (
- {ghostItems.map((_, index) => ( + {skeletonItems.map((_, index) => (
- +
))}
diff --git a/components/pages/workPageLayout/WorkPageButtons.tsx b/components/pages/workPageLayout/WorkPageButtons.tsx index 4db28661..4f162369 100644 --- a/components/pages/workPageLayout/WorkPageButtons.tsx +++ b/components/pages/workPageLayout/WorkPageButtons.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react" +import { isAudioBook, isEbook, isPodcast } from "@/components/pages/workPageLayout/helper" import { Button } from "@/components/shared/button/Button" import Player from "@/components/shared/publizonPlayer/PublizonPlayer" import ResponsiveDialog from "@/components/shared/responsiveDialog/ResponsiveDialog" @@ -7,8 +8,6 @@ import SmartLink from "@/components/shared/smartLink/SmartLink" import { resolveUrl } from "@/lib/helpers/helper.routes" import { useSelectedManifestationStore } from "@/store/selectedManifestation.store" -import { isAudioBook, isEbook, isPodcast } from "./helper" - export type WorkPageButtonsProps = { workId: string } diff --git a/components/pages/workPageLayout/WorkPageHeader.tsx b/components/pages/workPageLayout/WorkPageHeader.tsx index 79758479..f1c3e95c 100644 --- a/components/pages/workPageLayout/WorkPageHeader.tsx +++ b/components/pages/workPageLayout/WorkPageHeader.tsx @@ -4,6 +4,14 @@ import { motion } from "framer-motion" import { useRouter, useSearchParams } from "next/navigation" import React, { useEffect, useState } from "react" +import WorkPageButtons from "@/components/pages/workPageLayout/WorkPageButtons" +import { + addMaterialTypeIconToSelectOption, + findInitialSliderValue, + getManifestationByMaterialType, + getManifestationLanguageIsoCode, + getWorkMaterialTypes, +} from "@/components/pages/workPageLayout/helper" import { Badge } from "@/components/shared/badge/Badge" import { CoverPicture } from "@/components/shared/coverPicture/CoverPicture" import SlideSelect, { SlideSelectOption } from "@/components/shared/slideSelect/SlideSelect" @@ -16,15 +24,6 @@ import { GetCoverCollectionSizesItem } from "@/lib/rest/cover-service-api/genera import { useGetV1ProductsIdentifier } from "@/lib/rest/publizon-api/generated/publizon" import { useSelectedManifestationStore } from "@/store/selectedManifestation.store" -import WorkPageButtons from "./WorkPageButtons" -import { - addMaterialTypeIconToSelectOption, - findInitialSliderValue, - getManifestationByMaterialType, - getManifestationLanguageIsoCode, - getWorkMaterialTypes, -} from "./helper" - type WorkPageHeaderProps = { work: WorkFullWorkPageFragment } diff --git a/components/pages/workPageLayout/WorkPageLayout.tsx b/components/pages/workPageLayout/WorkPageLayout.tsx index 5b53b07d..7c7b8aca 100644 --- a/components/pages/workPageLayout/WorkPageLayout.tsx +++ b/components/pages/workPageLayout/WorkPageLayout.tsx @@ -5,12 +5,18 @@ import { notFound } from "next/navigation" import { useSearchParams } from "next/navigation" import React, { useEffect } from "react" +import WorkPageHeader from "@/components/pages/workPageLayout/WorkPageHeader" +import { + getBestRepresentation, + getManifestationByMaterialType, +} from "@/components/pages/workPageLayout/helper" +import { ButtonSkeleton } from "@/components/shared/button/Button" +import { CoverPictureSkeleton } from "@/components/shared/coverPicture/CoverPicture" +import InfoBox from "@/components/shared/infoBox/InfoBox" +import { SlideSelectSkeleton } from "@/components/shared/slideSelect/SlideSelect" import { GetMaterialQuery, useGetMaterialQuery } from "@/lib/graphql/generated/fbi/graphql" import { useSelectedManifestationStore } from "@/store/selectedManifestation.store" -import WorkPageHeader from "./WorkPageHeader" -import { getBestRepresentation, getManifestationByMaterialType } from "./helper" - type WorkPageLayoutProps = { workId: string dehydratedQueryData: GetMaterialQuery | undefined @@ -23,8 +29,6 @@ function WorkPageLayout({ workId, dehydratedQueryData }: WorkPageLayoutProps) { queryFn: useGetMaterialQuery.fetcher({ wid: workId }), initialData: dehydratedQueryData, }) - const pageContainerClasses = - "content-container my-grid-gap-2 flex-row flex-wrap space-y-grid-gap-2 lg:space-y-grid-gap-1 lg:my-grid-gap-half" const { selectedManifestation, setSelectedManifestation } = useSelectedManifestationStore() // Cleanup at unmount @@ -52,7 +56,11 @@ function WorkPageLayout({ workId, dehydratedQueryData }: WorkPageLayoutProps) { }, [selectedManifestation]) if (isLoading && !data) { - return
Loading...
+ return ( +
+ +
+ ) } if (!data || !data.work) { @@ -60,12 +68,35 @@ function WorkPageLayout({ workId, dehydratedQueryData }: WorkPageLayoutProps) { } return ( -
+
- {/* */} + {/* */}
) } +export const WorkPageSkeleton = () => { + return ( +
+
+
+ +
+
+ +
+
+
+
+
+
+
+ + +
+
+ ) +} + export default WorkPageLayout diff --git a/components/shared/badge/Badge.stories.tsx b/components/shared/badge/Badge.stories.tsx new file mode 100644 index 00000000..c336594c --- /dev/null +++ b/components/shared/badge/Badge.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from "@storybook/react" + +import { darkModeDecorator } from "@/.storybook/decorators" +import { Badge } from "@/components/shared/badge/Badge" + +const meta = { + title: "components/Badge", + component: Badge, + parameters: { + layout: "centered", + }, + argTypes: { + variant: { + control: { type: "select", options: ["default", "outline", "destructive", "blue-title"] }, + }, + }, + args: { + variant: "blue-title", + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + render: args => Badge, +} + +export const DarkMode: Story = { + decorators: [darkModeDecorator], + render: args => Badge, +} diff --git a/components/shared/badge/Badge.tsx b/components/shared/badge/Badge.tsx index 08cf50ec..292e9fe2 100644 --- a/components/shared/badge/Badge.tsx +++ b/components/shared/badge/Badge.tsx @@ -12,7 +12,7 @@ const badgeVariants = cva( default: "bg-foreground text-background", outline: "bg-secondary border-1 border border-foreground text-foreground", destructive: "bg-destructive text-destructive-foreground", - "blue-title": "bg-blue-title", + "blue-title": "bg-blue-title text-blue-title-dark", }, }, defaultVariants: { diff --git a/components/shared/badge/BadgeButton.tsx b/components/shared/badge/BadgeButton.tsx index f5a66785..09675084 100644 --- a/components/shared/badge/BadgeButton.tsx +++ b/components/shared/badge/BadgeButton.tsx @@ -25,14 +25,14 @@ const BadgeButton = ({ ) diff --git a/components/shared/button/Button.stories.tsx b/components/shared/button/Button.stories.tsx index 06ddb5ab..3a808757 100644 --- a/components/shared/button/Button.stories.tsx +++ b/components/shared/button/Button.stories.tsx @@ -13,7 +13,6 @@ const meta = { parameters: { layout: "centered", }, - tags: ["autodocs"], } satisfies Meta export default meta diff --git a/components/shared/button/Button.tsx b/components/shared/button/Button.tsx index c864653d..6b9a673a 100644 --- a/components/shared/button/Button.tsx +++ b/components/shared/button/Button.tsx @@ -59,7 +59,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean - ariaLabel: string + ariaLabel?: string } const Button = React.forwardRef( @@ -70,7 +70,7 @@ const Button = React.forwardRef( className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props} - aria-label={ariaLabel} + aria-label={ariaLabel || ""} /> ) } @@ -78,4 +78,21 @@ const Button = React.forwardRef( Button.displayName = "Button" -export { Button, buttonVariants } +type ButtonSkeletonProps = { + size?: "sm" | "md" | "lg" +} + +const ButtonSkeleton = ({ size }: ButtonSkeletonProps) => { + return ( +
+ ) +} + +export { Button, buttonVariants, ButtonSkeleton } diff --git a/components/shared/coverPicture/CoverPicture.stories.tsx b/components/shared/coverPicture/CoverPicture.stories.tsx new file mode 100644 index 00000000..19b63351 --- /dev/null +++ b/components/shared/coverPicture/CoverPicture.stories.tsx @@ -0,0 +1,102 @@ +import type { Meta, StoryObj } from "@storybook/react" + +import { darkModeDecorator } from "@/.storybook/decorators" +import { CoverPicture } from "@/components/shared/coverPicture/CoverPicture" + +const defaultArgs = { + src: "https://res.cloudinary.com/dandigbib/image/upload/v1544470826/saxo.dk/9788762722880.jpg", + lowResSrc: + "https://res.cloudinary.com/dandigbib/image/upload/t_ddb_cover_xxs/v1544470826/saxo.dk/9788762722880.jpg", + alt: "Cover picture alt text", +} + +const meta = { + title: "components/CoverPicture", + component: CoverPicture, + parameters: { + layout: "centered", + }, + argTypes: { + lowResSrc: { + control: { type: "text" }, + }, + src: { + control: { type: "text" }, + }, + alt: { + control: { type: "text" }, + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: defaultArgs, + render: args => ( +
+ +
+ ), +} + +export const DarkMode: Story = { + args: defaultArgs, + decorators: [darkModeDecorator], + render: args => ( +
+ +
+ ), +} + +export const WithTilt: Story = { + args: { ...defaultArgs, withTilt: true }, + render: args => ( +
+ +
+ ), +} + +export const WithTiltDarkMode: Story = { + args: { ...defaultArgs, withTilt: true }, + decorators: [darkModeDecorator], + render: args => ( +
+ +
+ ), +} + +export const WithoutCover: Story = { + args: { ...defaultArgs, src: "", lowResSrc: "" }, + render: args => ( +
+ +
+ ), +} + +export const WithoutCoverDarkMode: Story = { + args: { ...defaultArgs, src: "", lowResSrc: "" }, + decorators: [darkModeDecorator], + render: args => ( +
+ +
+ ), +} diff --git a/components/shared/coverPicture/CoverPicture.tsx b/components/shared/coverPicture/CoverPicture.tsx index 151f3e48..d1e4af27 100644 --- a/components/shared/coverPicture/CoverPicture.tsx +++ b/components/shared/coverPicture/CoverPicture.tsx @@ -4,10 +4,9 @@ import Image from "next/image" import React, { useEffect, useRef, useState } from "react" import Tilt from "react-parallax-tilt" +import Icon from "@/components/shared/icon/Icon" import { cn } from "@/lib/helpers/helper.cn" -import Icon from "../icon/Icon" - type CoverPictureProps = { lowResSrc: string src: string @@ -95,8 +94,12 @@ export const CoverPicture = ({ animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="flex w-full flex-col items-center justify-center"> - -

Billede kunne ikke vises

+ +

Billede kunne ikke vises

)}
@@ -131,3 +134,7 @@ const CoverPictureTiltWrapper = ({
) } + +export const CoverPictureSkeleton = () => { + return
+} diff --git a/components/shared/fonts/Fonts.stories.tsx b/components/shared/fonts/Fonts.stories.tsx index 0a52f43b..9f21daa1 100644 --- a/components/shared/fonts/Fonts.stories.tsx +++ b/components/shared/fonts/Fonts.stories.tsx @@ -11,7 +11,6 @@ const meta = { parameters: { layout: "centered", }, - tags: ["autodocs"], } satisfies Meta export default meta diff --git a/components/shared/infoBox/InfoBox.tsx b/components/shared/infoBox/InfoBox.tsx new file mode 100644 index 00000000..96714af8 --- /dev/null +++ b/components/shared/infoBox/InfoBox.tsx @@ -0,0 +1,62 @@ +import { motion } from "framer-motion" +import React from "react" + +import InfoBoxItem from "@/components/shared/infoBox/InfoBoxItem" +import { getSeriesInfo } from "@/components/shared/infoBox/helper" +import { WorkFullWorkPageFragment } from "@/lib/graphql/generated/fbi/graphql" +import { useSelectedManifestationStore } from "@/store/selectedManifestation.store" + +type InfoBoxProps = { + work: WorkFullWorkPageFragment +} + +const InfoBox = ({ work }: InfoBoxProps) => { + const { selectedManifestation } = useSelectedManifestationStore() + + return ( + +
+

Beskrivelse

+
+
+ {!work.abstract || + (!work.abstract.length && ( +

Værket har desværre ingen beskrivelse.

+ ))} + {work.abstract && + work.abstract.length && + work.abstract.map(abstract => ( +

+ {abstract} +

+ ))} +
+
+ {!!selectedManifestation?.audience && ( + age.display)} + /> + )} + {!!selectedManifestation && ( + + )} + {!!selectedManifestation?.subjects.all && + selectedManifestation?.subjects.all.length > 0 && ( + subject.display)} + isButtons + /> + )} +
+
+
+
+ ) +} + +export default InfoBox diff --git a/components/shared/infoBox/InfoBoxItem.tsx b/components/shared/infoBox/InfoBoxItem.tsx new file mode 100644 index 00000000..9073a2ac --- /dev/null +++ b/components/shared/infoBox/InfoBoxItem.tsx @@ -0,0 +1,39 @@ +import { useRouter } from "next/navigation" +import React from "react" + +import { Button } from "@/components/shared/button/Button" + +type InfoBoxItemProps = { + term: string + description: string[] + isButtons?: boolean +} + +const InfoBoxItem = ({ term, description, isButtons = false }: InfoBoxItemProps) => { + const router = useRouter() + + const handleClick = (text: string) => { + router.push(`/search?q=${text}`, { + scroll: true, + }) + } + + return ( + <> +
+
{term}
+ {!isButtons &&
{description.join(", ")}
} + {isButtons && ( +
+ {description.map(item => ( + + ))} +
+ )} + + ) +} + +export default InfoBoxItem diff --git a/components/shared/infoBox/helper.ts b/components/shared/infoBox/helper.ts new file mode 100644 index 00000000..f461efb3 --- /dev/null +++ b/components/shared/infoBox/helper.ts @@ -0,0 +1,7 @@ +import { ManifestationWorkPageFragment } from "@/lib/graphql/generated/fbi/graphql" + +export const getSeriesInfo = (manifestation: ManifestationWorkPageFragment) => { + return manifestation.series.map( + series => `${series.numberInSeries ? series.numberInSeries + " i " : ""}${series.title}` + ) +} diff --git a/components/shared/searchFilters/SearchFiltersColumn.tsx b/components/shared/searchFilters/SearchFiltersColumn.tsx index 5a5d5068..2454c69b 100644 --- a/components/shared/searchFilters/SearchFiltersColumn.tsx +++ b/components/shared/searchFilters/SearchFiltersColumn.tsx @@ -106,15 +106,15 @@ const SearchFiltersColumn = ({ facet, isLast }: SearchFiltersColumnProps) => { ) } -export const SearchFiltersColumnGhost = () => { +export const SearchFiltersColumnSkeleton = () => { return (
-
+
-
-
-
-
+
+
+
+
) diff --git a/components/shared/searchFilters/SearchFiltersDesktop.tsx b/components/shared/searchFilters/SearchFiltersDesktop.tsx index 5cfe3fbb..f00396f3 100644 --- a/components/shared/searchFilters/SearchFiltersDesktop.tsx +++ b/components/shared/searchFilters/SearchFiltersDesktop.tsx @@ -4,7 +4,7 @@ import React from "react" import { SearchFacetFragment } from "@/lib/graphql/generated/fbi/graphql" -import SearchFiltersColumn, { SearchFiltersColumnGhost } from "./SearchFiltersColumn" +import SearchFiltersColumn, { SearchFiltersColumnSkeleton } from "./SearchFiltersColumn" const SearchFiltersDesktop = ({ facets }: { facets: SearchFacetFragment[] }) => { return ( @@ -17,13 +17,13 @@ const SearchFiltersDesktop = ({ facets }: { facets: SearchFacetFragment[] }) => ) } -export const SearchFiltersDesktopGhost = () => { - const ghostItems = Array.from({ length: 5 }) +export const SearchFiltersDesktopSkeleton = () => { + const skeletonItems = Array.from({ length: 5 }) return (
- {ghostItems.map((_, index) => ( - + {skeletonItems.map((_, index) => ( + ))}
) diff --git a/components/shared/searchInput/SearchInput.tsx b/components/shared/searchInput/SearchInput.tsx index 81c942ed..e54316c3 100644 --- a/components/shared/searchInput/SearchInput.tsx +++ b/components/shared/searchInput/SearchInput.tsx @@ -55,10 +55,9 @@ const SearchInput = ({ className, placeholder }: SearchInputProps) => { + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: defaultArgs, + render: args => ( +
+ +
+ ), +} + +export const DarkMode: Story = { + args: defaultArgs, + decorators: [darkModeDecorator], + render: args => ( +
+ +
+ ), +} diff --git a/components/shared/slideSelect/SlideSelect.tsx b/components/shared/slideSelect/SlideSelect.tsx index 29b97c54..1a255b17 100644 --- a/components/shared/slideSelect/SlideSelect.tsx +++ b/components/shared/slideSelect/SlideSelect.tsx @@ -28,7 +28,9 @@ const SlideSelect = ({ options, initialOption, onOptionSelect }: SlideSelectProp }, [initialOption, options]) return ( -
+
{/* Animated black background */} { setSelected(index) onOptionSelect(option) }} variant="transparent" - classNames={cn("z-slide-select w-28", selected === index && "text-background")}> - {!!option.icon && } - {option.render} + classNames={cn("z-slide-select min-w-28", selected === index && "text-background")}> + {!!option.icon && ( + + )} + {option.render} ) })} @@ -69,4 +73,8 @@ const SlideSelect = ({ options, initialOption, onOptionSelect }: SlideSelectProp ) } +export const SlideSelectSkeleton = () => { + return
+} + export default SlideSelect diff --git a/components/shared/workCard/WorkCard.tsx b/components/shared/workCard/WorkCard.tsx index 7560df31..7a9c1304 100644 --- a/components/shared/workCard/WorkCard.tsx +++ b/components/shared/workCard/WorkCard.tsx @@ -91,18 +91,18 @@ const WorkCard = ({ work }: WorkCardProps) => { ) } -export const WorkCardGhost = () => { +export const WorkCardSkeleton = () => { return (
-
+
-
-
+
+
) diff --git a/lib/graphql/fragments/manifestation.fbi.graphql b/lib/graphql/fragments/manifestation.fbi.graphql index 1768e158..6f6625d5 100644 --- a/lib/graphql/fragments/manifestation.fbi.graphql +++ b/lib/graphql/fragments/manifestation.fbi.graphql @@ -59,6 +59,25 @@ fragment ManifestationLanguages on Manifestation { } } +fragment ManifestationDescription on Manifestation { + audience { + generalAudience + ages { + display + } + libraryRecommendation + } + series { + numberInSeries + title + } + subjects { + all { + display + } + } +} + # Bigger chunks for specific pages (built from above fragments) ---------------- fragment ManifestationSearchPageTeaser on Manifestation { @@ -72,4 +91,5 @@ fragment ManifestationWorkPage on Manifestation { ...ManifestationAccess ...ManifestationTitles ...ManifestationLanguages + ...ManifestationDescription } diff --git a/lib/graphql/fragments/work.fbi.graphql b/lib/graphql/fragments/work.fbi.graphql index a0dcfa0e..de204c2d 100644 --- a/lib/graphql/fragments/work.fbi.graphql +++ b/lib/graphql/fragments/work.fbi.graphql @@ -38,6 +38,10 @@ fragment WorkPublicationYear on Work { } } +fragment WorkDescription on Work { + abstract +} + # Bigger chunks for specific pages --------------------------------------------- fragment WorkTeaserSearchPage on Work { @@ -62,6 +66,7 @@ fragment WorkFullWorkPage on Work { ...WorkCreators ...WorkMaterialTypes ...WorkPublicationYear + ...WorkDescription manifestations { all { ...ManifestationWorkPage diff --git a/lib/graphql/generated/fbi/graphql.tsx b/lib/graphql/generated/fbi/graphql.tsx index 6dcba078..829b2eea 100644 --- a/lib/graphql/generated/fbi/graphql.tsx +++ b/lib/graphql/generated/fbi/graphql.tsx @@ -229,6 +229,12 @@ export type ComplexSearchSuggestion = { __typename?: 'ComplexSearchSuggestion'; /** The suggested term which can be searched for */ term: Scalars['String']['output']; + /** + * A unique identifier for tracking user interactions with this suggestion. + * It is generated in the response and should be included in subsequent + * API calls when this suggestion is selected. + */ + traceId: Scalars['String']['output']; /** The type of suggestion */ type: Scalars['String']['output']; /** A work related to the term */ @@ -374,6 +380,12 @@ export type DidYouMean = { query: Scalars['String']['output']; /** A probability score between 0-1 indicating how relevant the query is */ score: Scalars['Float']['output']; + /** + * A unique identifier for tracking user interactions with this didYouMean value. + * It is generated in the response and should be included in subsequent + * API calls when this manifestation is selected. + */ + traceId: Scalars['String']['output']; }; export type DigitalArticleService = { @@ -476,6 +488,12 @@ export type FacetValue = { score?: Maybe; /** A value of a facet field */ term: Scalars['String']['output']; + /** + * A unique identifier for tracking user interactions with this facet value. + * It is generated in the response and should be included in subsequent + * API calls when this manifestation is selected. + */ + traceId: Scalars['String']['output']; }; export type FictionNonfiction = { @@ -780,8 +798,12 @@ export type Manifestation = { languages?: Maybe; /** Details about the latest printing of this manifestation */ latestPrinting?: Maybe; + /** Identification of the local id of this manifestation */ + localId?: Maybe; /** Tracks on music album, sheet music content, or articles/short stories etc. in this manifestation */ manifestationParts?: Maybe; + /** Field for presenting bibliographic records in MARC format */ + marc?: Maybe; /** The type of material of the manifestation based on bibliotek.dk types */ materialTypes: Array; /** Notes about the manifestation */ @@ -804,6 +826,8 @@ export type Manifestation = { review?: Maybe; /** Series for this manifestation */ series: Array; + /** Material that can be identified as sheet music */ + sheetMusicCategories?: Maybe; /** Information about on which shelf in the library this manifestation can be found */ shelfmark?: Maybe; /** The source of the manifestation, e.g. own library catalogue (Bibliotekskatalog) or online source e.g. Filmstriben, Ebook Central, eReolen Global etc. */ @@ -814,6 +838,12 @@ export type Manifestation = { tableOfContents?: Maybe; /** Different kinds of titles for this work */ titles: ManifestationTitles; + /** + * A unique identifier for tracking user interactions with this manifestation. + * It is generated in the response and should be included in subsequent + * API calls when this manifestation is selected. + */ + traceId: Scalars['String']['output']; /** id of the manifestaion unit */ unit?: Maybe; /** Universes for this manifestation */ @@ -902,6 +932,33 @@ export type Manifestations = { mostRelevant: Array; }; +export type Marc = { + __typename?: 'Marc'; + /** Gets the MARC record collection for the given record identifier, containing either standalone or head and/or section and volume records. */ + getMarcByRecordId?: Maybe; +}; + + +export type MarcGetMarcByRecordIdArgs = { + recordId: Scalars['String']['input']; +}; + +export type MarcRecord = { + __typename?: 'MarcRecord'; + /** The library agency */ + agencyId: Scalars['String']['output']; + /** The bibliographic record identifier */ + bibliographicRecordId: Scalars['String']['output']; + /** The MARC record collection content as marcXchange XML string */ + content: Scalars['String']['output']; + /** The serialization format of the MARC record content. Defaults to 'marcXchange' */ + contentSerializationFormat: Scalars['String']['output']; + /** Flag indicating whether or not the record is deleted */ + deleted: Scalars['Boolean']['output']; + /** The marc record identifier */ + id: Scalars['String']['output']; +}; + export type MaterialType = { __typename?: 'MaterialType'; /** jed 1.1 - the general materialtype */ @@ -1077,6 +1134,14 @@ export type MoodTagRecommendResponse = { work: Work; }; +export type MusicalExercise = { + __typename?: 'MusicalExercise'; + /** The types of instrument 'schools' intended to practise with */ + display: Array; + /** Information whether material is intended for practising and in combination with an instrument */ + forExercise: Scalars['Boolean']['output']; +}; + export type Mutation = { __typename?: 'Mutation'; elba: ElbaServices; @@ -1106,6 +1171,7 @@ export enum NoteTypeEnum { Dissertation = 'DISSERTATION', Edition = 'EDITION', EstimatedPlayingTimeForGames = 'ESTIMATED_PLAYING_TIME_FOR_GAMES', + ExpectedPublicationDate = 'EXPECTED_PUBLICATION_DATE', Frequency = 'FREQUENCY', MusicalEnsembleOrCast = 'MUSICAL_ENSEMBLE_OR_CAST', NotSpecified = 'NOT_SPECIFIED', @@ -1214,6 +1280,8 @@ export type Query = { localSuggest: LocalSuggestResponse; manifestation?: Maybe; manifestations: Array>; + /** Field for presenting bibliographic records in MARC format */ + marc: Marc; mood: MoodQueries; ors: OrsQuery; /** Get recommendations */ @@ -1597,6 +1665,8 @@ export type Series = { seriesId?: Maybe; /** The title of the series */ title: Scalars['String']['output']; + /** Traceid for tracking */ + traceId: Scalars['String']['output']; /** WorkTypes for the series */ workTypes: Array; }; @@ -1615,6 +1685,20 @@ export type Setting = SubjectInterface & { type: SubjectTypeEnum | '%future added value'; }; +export type SheetMusicCategory = { + __typename?: 'SheetMusicCategory'; + /** The types of chamber music material covers */ + chamberMusicTypes: Array; + /** The types of choir material covers */ + choirTypes: Array; + /** The types of instruments material covers */ + instruments: Array; + /** Material intended to practice with */ + musicalExercises?: Maybe; + /** The types of orchestra material covers */ + orchestraTypes: Array; +}; + export type Shelfmark = { __typename?: 'Shelfmark'; /** A postfix to the shelfmark, eg. 99.4 Christensen, Inger. f. 1935 */ @@ -1714,6 +1798,12 @@ export type Suggestion = { __typename?: 'Suggestion'; /** The suggested term which can be searched for */ term: Scalars['String']['output']; + /** + * A unique identifier for tracking user interactions with this suggestion. + * It is generated in the response and should be included in subsequent + * API calls when this suggestion is selected. + */ + traceId: Scalars['String']['output']; /** The type of suggestion: creator, subject or title */ type: SuggestionTypeEnum | '%future added value'; /** A work related to the term */ @@ -1811,6 +1901,12 @@ export type Universe = { series: Array; /** Literary/movie universe this work is part of e.g. Wizarding World, Marvel Cinematic Universe */ title: Scalars['String']['output']; + /** + * A unique identifier for tracking user interactions with this universe. + * It is generated in the response and should be included in subsequent + * API calls when this work is selected. + */ + traceId: Scalars['String']['output']; /** An id that identifies a universe. */ universeId?: Maybe; /** work types that are in this universe */ @@ -1866,6 +1962,8 @@ export type Work = { mainLanguages: Array; /** Details about the manifestations of this work */ manifestations: Manifestations; + /** Field for presenting bibliographic records in MARC format */ + marc?: Maybe; /** The type of material of the manifestation based on bibliotek.dk types */ materialTypes: Array; /** Relations to other manifestations */ @@ -1875,6 +1973,12 @@ export type Work = { /** Subjects for this work */ subjects: SubjectContainer; titles: WorkTitles; + /** + * A unique identifier for tracking user interactions with this work. + * It is generated in the response and should be included in subsequent + * API calls when this work is selected. + */ + traceId: Scalars['String']['output']; /** Literary/movie universes this work is part of, e.g. Wizarding World, Marvel Universe */ universes: Array; /** Unique identification of the work based on work-presentation id e.g work-of:870970-basis:54029519 */ @@ -1936,9 +2040,11 @@ export type ManifestationTitlesFragment = { __typename?: 'Manifestation', titles export type ManifestationLanguagesFragment = { __typename?: 'Manifestation', languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null }; +export type ManifestationDescriptionFragment = { __typename?: 'Manifestation', audience?: { __typename?: 'Audience', generalAudience: Array, libraryRecommendation?: string | null, ages: Array<{ __typename?: 'Range', display: string }> } | null, series: Array<{ __typename?: 'Series', numberInSeries?: string | null, title: string }>, subjects: { __typename?: 'SubjectContainer', all: Array<{ __typename?: 'Corporation', display: string } | { __typename?: 'Mood', display: string } | { __typename?: 'NarrativeTechnique', display: string } | { __typename?: 'Person', display: string } | { __typename?: 'Setting', display: string } | { __typename?: 'SubjectText', display: string } | { __typename?: 'SubjectWithRating', display: string } | { __typename?: 'TimePeriod', display: string }> } }; + export type ManifestationSearchPageTeaserFragment = { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }> }; -export type ManifestationWorkPageFragment = { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null }; +export type ManifestationWorkPageFragment = { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null, audience?: { __typename?: 'Audience', generalAudience: Array, libraryRecommendation?: string | null, ages: Array<{ __typename?: 'Range', display: string }> } | null, series: Array<{ __typename?: 'Series', numberInSeries?: string | null, title: string }>, subjects: { __typename?: 'SubjectContainer', all: Array<{ __typename?: 'Corporation', display: string } | { __typename?: 'Mood', display: string } | { __typename?: 'NarrativeTechnique', display: string } | { __typename?: 'Person', display: string } | { __typename?: 'Setting', display: string } | { __typename?: 'SubjectText', display: string } | { __typename?: 'SubjectWithRating', display: string } | { __typename?: 'TimePeriod', display: string }> } }; export type WorkAccessFragment = { __typename?: 'Work', workId: string, manifestations: { __typename?: 'Manifestations', all: Array<{ __typename?: 'Manifestation', access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }> }> } }; @@ -1950,9 +2056,11 @@ export type WorkCreatorsFragment = { __typename?: 'Work', creators: Array<{ __ty export type WorkPublicationYearFragment = { __typename?: 'Work', workYear?: { __typename?: 'PublicationYear', display: string } | null }; +export type WorkDescriptionFragment = { __typename?: 'Work', abstract?: Array | null }; + export type WorkTeaserSearchPageFragment = { __typename?: 'Work', workId: string, manifestations: { __typename?: 'Manifestations', all: Array<{ __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }> }>, bestRepresentation: { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }> } }, titles: { __typename?: 'WorkTitles', full: Array, original?: Array | null }, creators: Array<{ __typename: 'Corporation', display: string } | { __typename: 'Person', display: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, workYear?: { __typename?: 'PublicationYear', display: string } | null }; -export type WorkFullWorkPageFragment = { __typename?: 'Work', workId: string, manifestations: { __typename?: 'Manifestations', all: Array<{ __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null }>, bestRepresentation: { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null } }, titles: { __typename?: 'WorkTitles', full: Array, original?: Array | null }, creators: Array<{ __typename: 'Corporation', display: string } | { __typename: 'Person', display: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, workYear?: { __typename?: 'PublicationYear', display: string } | null }; +export type WorkFullWorkPageFragment = { __typename?: 'Work', workId: string, abstract?: Array | null, manifestations: { __typename?: 'Manifestations', all: Array<{ __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null, audience?: { __typename?: 'Audience', generalAudience: Array, libraryRecommendation?: string | null, ages: Array<{ __typename?: 'Range', display: string }> } | null, series: Array<{ __typename?: 'Series', numberInSeries?: string | null, title: string }>, subjects: { __typename?: 'SubjectContainer', all: Array<{ __typename?: 'Corporation', display: string } | { __typename?: 'Mood', display: string } | { __typename?: 'NarrativeTechnique', display: string } | { __typename?: 'Person', display: string } | { __typename?: 'Setting', display: string } | { __typename?: 'SubjectText', display: string } | { __typename?: 'SubjectWithRating', display: string } | { __typename?: 'TimePeriod', display: string }> } }>, bestRepresentation: { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null, audience?: { __typename?: 'Audience', generalAudience: Array, libraryRecommendation?: string | null, ages: Array<{ __typename?: 'Range', display: string }> } | null, series: Array<{ __typename?: 'Series', numberInSeries?: string | null, title: string }>, subjects: { __typename?: 'SubjectContainer', all: Array<{ __typename?: 'Corporation', display: string } | { __typename?: 'Mood', display: string } | { __typename?: 'NarrativeTechnique', display: string } | { __typename?: 'Person', display: string } | { __typename?: 'Setting', display: string } | { __typename?: 'SubjectText', display: string } | { __typename?: 'SubjectWithRating', display: string } | { __typename?: 'TimePeriod', display: string }> } } }, titles: { __typename?: 'WorkTitles', full: Array, original?: Array | null }, creators: Array<{ __typename: 'Corporation', display: string } | { __typename: 'Person', display: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, workYear?: { __typename?: 'PublicationYear', display: string } | null }; export type SearchWithPaginationQueryVariables = Exact<{ q: SearchQueryInput; @@ -1979,7 +2087,7 @@ export type GetMaterialQueryVariables = Exact<{ }>; -export type GetMaterialQuery = { __typename?: 'Query', work?: { __typename?: 'Work', workId: string, manifestations: { __typename?: 'Manifestations', all: Array<{ __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null }>, bestRepresentation: { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null } }, titles: { __typename?: 'WorkTitles', full: Array, original?: Array | null }, creators: Array<{ __typename: 'Corporation', display: string } | { __typename: 'Person', display: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, workYear?: { __typename?: 'PublicationYear', display: string } | null } | null }; +export type GetMaterialQuery = { __typename?: 'Query', work?: { __typename?: 'Work', workId: string, abstract?: Array | null, manifestations: { __typename?: 'Manifestations', all: Array<{ __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null, audience?: { __typename?: 'Audience', generalAudience: Array, libraryRecommendation?: string | null, ages: Array<{ __typename?: 'Range', display: string }> } | null, series: Array<{ __typename?: 'Series', numberInSeries?: string | null, title: string }>, subjects: { __typename?: 'SubjectContainer', all: Array<{ __typename?: 'Corporation', display: string } | { __typename?: 'Mood', display: string } | { __typename?: 'NarrativeTechnique', display: string } | { __typename?: 'Person', display: string } | { __typename?: 'Setting', display: string } | { __typename?: 'SubjectText', display: string } | { __typename?: 'SubjectWithRating', display: string } | { __typename?: 'TimePeriod', display: string }> } }>, bestRepresentation: { __typename?: 'Manifestation', pid: string, identifiers: Array<{ __typename?: 'Identifier', type: IdentifierTypeEnum, value: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, access: Array<{ __typename: 'AccessUrl', origin: string, url: string, loginRequired: boolean } | { __typename: 'DigitalArticleService', issn: string } | { __typename: 'Ereol', origin: string, url: string, canAlwaysBeLoaned: boolean } | { __typename: 'InfomediaService', id: string } | { __typename: 'InterLibraryLoan', loanIsPossible: boolean }>, titles: { __typename?: 'ManifestationTitles', identifyingAddition?: string | null, full: Array }, languages?: { __typename?: 'Languages', main?: Array<{ __typename?: 'Language', display: string, isoCode: string }> | null } | null, audience?: { __typename?: 'Audience', generalAudience: Array, libraryRecommendation?: string | null, ages: Array<{ __typename?: 'Range', display: string }> } | null, series: Array<{ __typename?: 'Series', numberInSeries?: string | null, title: string }>, subjects: { __typename?: 'SubjectContainer', all: Array<{ __typename?: 'Corporation', display: string } | { __typename?: 'Mood', display: string } | { __typename?: 'NarrativeTechnique', display: string } | { __typename?: 'Person', display: string } | { __typename?: 'Setting', display: string } | { __typename?: 'SubjectText', display: string } | { __typename?: 'SubjectWithRating', display: string } | { __typename?: 'TimePeriod', display: string }> } } }, titles: { __typename?: 'WorkTitles', full: Array, original?: Array | null }, creators: Array<{ __typename: 'Corporation', display: string } | { __typename: 'Person', display: string }>, materialTypes: Array<{ __typename?: 'MaterialType', materialTypeGeneral: { __typename?: 'GeneralMaterialType', display: string, code: GeneralMaterialTypeCodeEnum } }>, workYear?: { __typename?: 'PublicationYear', display: string } | null } | null }; export const SearchFacetFragmentDoc = ` @@ -2109,6 +2217,11 @@ ${WorkCreatorsFragmentDoc} ${WorkMaterialTypesFragmentDoc} ${WorkPublicationYearFragmentDoc} ${ManifestationSearchPageTeaserFragmentDoc}`; +export const WorkDescriptionFragmentDoc = ` + fragment WorkDescription on Work { + abstract +} + `; export const ManifestationTitlesFragmentDoc = ` fragment ManifestationTitles on Manifestation { titles { @@ -2127,6 +2240,26 @@ export const ManifestationLanguagesFragmentDoc = ` } } `; +export const ManifestationDescriptionFragmentDoc = ` + fragment ManifestationDescription on Manifestation { + audience { + generalAudience + ages { + display + } + libraryRecommendation + } + series { + numberInSeries + title + } + subjects { + all { + display + } + } +} + `; export const ManifestationWorkPageFragmentDoc = ` fragment ManifestationWorkPage on Manifestation { ...ManifestationIdentifiers @@ -2134,12 +2267,14 @@ export const ManifestationWorkPageFragmentDoc = ` ...ManifestationAccess ...ManifestationTitles ...ManifestationLanguages + ...ManifestationDescription } ${ManifestationIdentifiersFragmentDoc} ${ManifestationCoverFragmentDoc} ${ManifestationAccessFragmentDoc} ${ManifestationTitlesFragmentDoc} -${ManifestationLanguagesFragmentDoc}`; +${ManifestationLanguagesFragmentDoc} +${ManifestationDescriptionFragmentDoc}`; export const WorkFullWorkPageFragmentDoc = ` fragment WorkFullWorkPage on Work { workId @@ -2147,6 +2282,7 @@ export const WorkFullWorkPageFragmentDoc = ` ...WorkCreators ...WorkMaterialTypes ...WorkPublicationYear + ...WorkDescription manifestations { all { ...ManifestationWorkPage @@ -2160,6 +2296,7 @@ export const WorkFullWorkPageFragmentDoc = ` ${WorkCreatorsFragmentDoc} ${WorkMaterialTypesFragmentDoc} ${WorkPublicationYearFragmentDoc} +${WorkDescriptionFragmentDoc} ${ManifestationWorkPageFragmentDoc}`; export const SearchWithPaginationDocument = ` query searchWithPagination($q: SearchQueryInput!, $offset: Int!, $limit: PaginationLimitScalar!, $filters: SearchFiltersInput) { diff --git a/lib/mocks/work/work.mock.ts b/lib/mocks/work/work.mock.ts new file mode 100644 index 00000000..55b94e7b --- /dev/null +++ b/lib/mocks/work/work.mock.ts @@ -0,0 +1,179 @@ +import { WorkFullWorkPageFragment } from "@/lib/graphql/generated/fbi/graphql" + +const workMock = { + workId: "work-of:870970-basis:28412932", + titles: { + full: ["Den vingede hest Skar"], + original: ["Skor the winged stallion"], + }, + creators: [ + { + display: "Adam Blade", + __typename: "Person", + }, + ], + materialTypes: [ + { + materialTypeGeneral: { + display: "e-bøger", + code: "EBOOKS", + }, + }, + { + materialTypeGeneral: { + display: "lydbøger", + code: "AUDIO_BOOKS", + }, + }, + ], + workYear: { + display: "2008", + }, + manifestations: { + all: [ + { + pid: "870970-basis:38634097", + identifiers: [ + { + type: "PUBLIZON", + value: "9788762735934", + }, + { + type: "ISBN", + value: "9788762735934", + }, + ], + materialTypes: [ + { + materialTypeGeneral: { + display: "lydbøger", + code: "AUDIO_BOOKS", + }, + }, + ], + access: [ + { + __typename: "Ereol", + origin: "eReolen Go", + url: "https://ereolengo.dk/ting/object/870970-basis:38634097", + canAlwaysBeLoaned: false, + }, + { + __typename: "Ereol", + origin: "eReolen", + url: "https://ereolen.dk/ting/object/870970-basis:38634097", + canAlwaysBeLoaned: false, + }, + ], + titles: { + identifyingAddition: null, + full: ["Den vingede hest Skar"], + }, + languages: { + main: [ + { + display: "dansk", + isoCode: "dan", + }, + ], + }, + }, + { + pid: "870970-basis:51579623", + identifiers: [ + { + type: "PUBLIZON", + value: "9788762722880", + }, + { + type: "ISBN", + value: "9788762722880", + }, + ], + materialTypes: [ + { + materialTypeGeneral: { + display: "e-bøger", + code: "EBOOKS", + }, + }, + ], + access: [ + { + __typename: "Ereol", + origin: "eReolen", + url: "https://ereolen.dk/ting/object/870970-basis:51579623", + canAlwaysBeLoaned: false, + }, + { + __typename: "Ereol", + origin: "eReolen Go", + url: "https://ereolengo.dk/ting/object/870970-basis:51579623", + canAlwaysBeLoaned: false, + }, + ], + titles: { + identifyingAddition: null, + full: ["Den vingede hest Skar"], + }, + languages: { + main: [ + { + display: "dansk", + isoCode: "dan", + }, + ], + }, + }, + ], + bestRepresentation: { + pid: "870970-basis:38634097", + identifiers: [ + { + type: "PUBLIZON", + value: "9788762735934", + }, + { + type: "ISBN", + value: "9788762735934", + }, + ], + materialTypes: [ + { + materialTypeGeneral: { + display: "lydbøger", + code: "AUDIO_BOOKS", + }, + }, + ], + access: [ + { + __typename: "Ereol", + origin: "eReolen Go", + url: "https://ereolengo.dk/ting/object/870970-basis:38634097", + canAlwaysBeLoaned: false, + }, + { + __typename: "Ereol", + origin: "eReolen", + url: "https://ereolen.dk/ting/object/870970-basis:38634097", + canAlwaysBeLoaned: false, + }, + ], + titles: { + identifyingAddition: null, + full: ["Den vingede hest Skar"], + }, + languages: { + main: [ + { + display: "dansk", + isoCode: "dan", + }, + ], + }, + }, + }, +} as WorkFullWorkPageFragment + +export default workMock diff --git a/package.json b/package.json index 703f7d04..d43783e1 100644 --- a/package.json +++ b/package.json @@ -45,9 +45,9 @@ "lucide-react": "^0.446.0", "next": "15.0.3", "next-drupal": "^2.0.0-beta.0", + "openid-client": "^6.1.3", "react": "^19.0.0", "react-dom": "^19.0.0", - "openid-client": "^6.1.3", "react-parallax-tilt": "^1.7.252", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", @@ -66,14 +66,14 @@ "@graphql-codegen/typescript-react-query": "^6.1.0", "@next/eslint-plugin-next": "15.0.3", "@parcel/watcher": "^2.4.1", - "@storybook/addon-a11y": "^8.4.2", + "@storybook/addon-a11y": "^8.4.7", "@storybook/addon-essentials": "^8.3.6", "@storybook/addon-interactions": "^8.3.6", "@storybook/addon-links": "^8.3.6", "@storybook/addon-onboarding": "^8.3.6", "@storybook/blocks": "^8.3.6", "@storybook/nextjs": "^8.4.0", - "@storybook/react": "^8.3.6", + "@storybook/react": "^8.4.7", "@storybook/test": "^8.3.6", "@storybook/test-runner": "^0.19.1", "@svgr/webpack": "^8.1.0", diff --git a/styles/globals.css b/styles/globals.css index aad9b212..b8f3ba8a 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -67,9 +67,11 @@ --beige: hsla(36, 45%, 98%, 1); --beige-overlay: hsla(42, 25%, 35%, 0.05); + --beige-skeleton: hsla(42, 25%, 35%, 0.1); --blue: hsla(228, 100%, 10%, 1); --blue-overlay: hsla(238, 54%, 89%, 0.09); + --blue-skeleton: hsla(238, 54%, 89%, 0.18); --border: hsla(48, 4.4%, 77.8%, 1); --reader-grey: hsla(0, 0%, 94.1%, 1); @@ -82,7 +84,9 @@ --content-orange: hsla(34, 100%, 72%, 1); --content-blue: hsla(202, 100%, 72%, 1); - --blue-title: hsla(202, 100%, 60%, 1); + /* These two blues provide JUST enough color contrast to pass wcag2.1aa */ + --blue-title: hsla(202, 100%, 76%, 1); + --blue-title-dark: hsla(202, 60%, 30%, 1); /* CONTENT COLORS */ --content-pink-100: 320 100% 91%; @@ -104,6 +108,7 @@ /* DYNAMIC COLORS */ --background: var(--beige); --background-overlay: var(--beige-overlay); + --background-skeleton: var(--beige-skeleton); --foreground: var(--black); /* TRANSITION DURATIONS */ @@ -181,7 +186,7 @@ --typo-button-sm-size: 12px; --typo-button-sm-weight: 500; --typo-button-sm-line-height: 12px; - --typo-button-sm-letter-spacing: 0.1em; + --typo-button-sm-letter-spacing: 0.04em; --typo-link-size: 14px; --typo-link-weight: 400; @@ -193,9 +198,9 @@ --typo-tag-lg-line-height: 12px; --typo-tag-lg-letter-spacing: 0.05em; - --typo-tag-sm-size: 8px; + --typo-tag-sm-size: 9px; --typo-tag-sm-weight: 500; - --typo-tag-sm-line-height: 8px; + --typo-tag-sm-line-height: 9px; --typo-tag-sm-letter-spacing: 0; --typo-caption-size: 10px; @@ -284,7 +289,7 @@ --typo-button-sm-size: 12px; --typo-button-sm-weight: 500; --typo-button-sm-line-height: 12px; - --typo-button-sm-letter-spacing: 0.1em; + --typo-button-sm-letter-spacing: 0.04em; --typo-link-size: 14px; --typo-link-weight: 400; @@ -311,7 +316,9 @@ .dark-mode { --background: var(--blue); --background-overlay: var(--blue-overlay); + --background-skeleton: var(--blue-skeleton); --foreground: var(--beige); + --shadow-add-contrast: hsla(0, 0%, 0%, 0.5); } .content-container { diff --git a/tailwind.config.ts b/tailwind.config.ts index 324d6845..27f36066 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -193,8 +193,10 @@ export const extendedTheme = { background: { DEFAULT: "var(--background)", overlay: "var(--background-overlay)", + skeleton: "var(--background-skeleton)", }, foreground: "var(--foreground)", + blue: "var(--blue)", border: "var(--border)", "reader-grey": "var(--reader-grey)", content: { @@ -203,7 +205,10 @@ export const extendedTheme = { orange: "var(--content-orange)", purple: "var(--content-purple)", }, - "blue-title": "var(--blue-title)", + "blue-title": { + DEFAULT: "var(--blue-title)", + dark: "var(--blue-title-dark)", + }, // shadcn colors card: { DEFAULT: "var(--card)", @@ -344,6 +349,9 @@ const config: Config = { ".hyphens-none": { hyphens: "none" }, }) }, + function ({ addVariant }: { addVariant: (name: string, generator: string) => void }) { + addVariant("not-first", "&:not(:first-child)") + }, ], } export default config diff --git a/yarn.lock b/yarn.lock index 04bbeea3..8025bc5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3704,12 +3704,12 @@ "@stoplight/yaml-ast-parser" "0.0.50" tslib "^2.2.0" -"@storybook/addon-a11y@^8.4.2": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-8.4.4.tgz#4506067cbe4d0366d3c3f8e96ddf641a651c9816" - integrity sha512-xXNOG4Bw/v8rg2Zq/ZJnZSLWfpfkfnZjn0sQVLOe5JcDxavkh5o+WvQ6Tc2w/kK/ophCd7nbTotywrtdQYGNKw== +"@storybook/addon-a11y@^8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-8.4.7.tgz#0073090d8d4e0748249317a292ac27dc2c2b9ef2" + integrity sha512-GpUvXp6n25U1ZSv+hmDC+05BEqxWdlWjQTb/GaboRXZQeMBlze6zckpVb66spjmmtQAIISo0eZxX1+mGcVR7lA== dependencies: - "@storybook/addon-highlight" "8.4.4" + "@storybook/addon-highlight" "8.4.7" axe-core "^4.2.0" "@storybook/addon-actions@8.4.4": @@ -3777,6 +3777,13 @@ dependencies: "@storybook/global" "^5.0.0" +"@storybook/addon-highlight@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-8.4.7.tgz#06b9752977e38884007e9446f9a2b0c04c873229" + integrity sha512-whQIDBd3PfVwcUCrRXvCUHWClXe9mQ7XkTPCdPo4B/tZ6Z9c6zD8JUHT76ddyHivixFLowMnA8PxMU6kCMAiNw== + dependencies: + "@storybook/global" "^5.0.0" + "@storybook/addon-interactions@^8.3.6": version "8.4.4" resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-8.4.4.tgz#6c005ba711effc281273fd6336525052122ea42c" @@ -3872,11 +3879,6 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.6.0" -"@storybook/components@8.4.4": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.4.4.tgz#487c5d89061519bd3f8f3dcb7cbf052d032ea744" - integrity sha512-0BSZVmsk23C0BSRKx3liZSVQFXtoF86XQFdNQxjrXIwdHIEN7TcL3DwcxeVUU5ilGp7HeDgAydGNIPGgTeEe6g== - "@storybook/components@8.4.7": version "8.4.7" resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.4.7.tgz#09eeffa07aa672ad3966ca1764a43003731b1d30" @@ -3957,11 +3959,6 @@ "@storybook/global" "^5.0.0" "@vitest/utils" "^2.1.1" -"@storybook/manager-api@8.4.4": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.4.4.tgz#ea3e134d7cc5a91ef82d95601206eb839e34c631" - integrity sha512-rmNPcbEyzakEHoaecUbhkW7WWOkyZ0z7ywH4d5/s0ZuQS57Px2N+ZLVgRJwYK+YNHiJYqDf1BTln9YJ/Mt1L6Q== - "@storybook/manager-api@8.4.7": version "8.4.7" resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.4.7.tgz#4e13debf645c9300d7d6d49195e720d0c7ecd261" @@ -4031,16 +4028,16 @@ tsconfig-paths "^4.2.0" webpack "5" -"@storybook/preview-api@8.4.4", "@storybook/preview-api@^8.0.0": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.4.tgz#bcfde521927406f9c37744f5cc959c54bf268a92" - integrity sha512-iZrWQcjRBqBHFdDXVxGpw6mHBZMCMYqhWXdyJ0d1S2y3PwcfOjkcXlQ1UiAenFHlA6dKrcYw8luKUQTL9bKReA== - "@storybook/preview-api@8.4.7": version "8.4.7" resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.7.tgz#85e01a97f4182b974581765d725f6c7a7d190013" integrity sha512-0QVQwHw+OyZGHAJEXo6Knx+6/4er7n2rTDE5RYJ9F2E2Lg42E19pfdLlq2Jhoods2Xrclo3wj6GWR//Ahi39Eg== +"@storybook/preview-api@^8.0.0": + version "8.4.4" + resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.4.tgz#bcfde521927406f9c37744f5cc959c54bf268a92" + integrity sha512-iZrWQcjRBqBHFdDXVxGpw6mHBZMCMYqhWXdyJ0d1S2y3PwcfOjkcXlQ1UiAenFHlA6dKrcYw8luKUQTL9bKReA== + "@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0": version "1.0.6--canary.9.0c3f3b7.0" resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz#7f10f3c641f32e4513a8b6ffb5036933e7059534" @@ -4064,7 +4061,7 @@ resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-8.4.7.tgz#f0dd5bbf2fc185def72d9d08a11c8de22f152c2a" integrity sha512-6bkG2jvKTmWrmVzCgwpTxwIugd7Lu+2btsLAqhQSzDyIj2/uhMNp8xIMr/NBDtLgq3nomt9gefNa9xxLwk/OMg== -"@storybook/react@8.4.7": +"@storybook/react@8.4.7", "@storybook/react@^8.4.7": version "8.4.7" resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.4.7.tgz#e2cf62b3c1d8e4bfe5eff82ced07ec473d4e4fd1" integrity sha512-nQ0/7i2DkaCb7dy0NaT95llRVNYWQiPIVuhNfjr1mVhEP7XD090p0g7eqUmsx8vfdHh2BzWEo6CoBFRd3+EXxw== @@ -4076,18 +4073,6 @@ "@storybook/react-dom-shim" "8.4.7" "@storybook/theming" "8.4.7" -"@storybook/react@^8.3.6": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.4.4.tgz#f456a904e1afea780b08cdfc6827618e8badf250" - integrity sha512-92lGnRcAI2qW6zH8GMBScyXmOS1ANI8ZuSP4ExQj+lGsCrAr7PBr0wuHy3wIn1YyAvQGPUn/mpYrmMz08c2HfA== - dependencies: - "@storybook/components" "8.4.4" - "@storybook/global" "^5.0.0" - "@storybook/manager-api" "8.4.4" - "@storybook/preview-api" "8.4.4" - "@storybook/react-dom-shim" "8.4.4" - "@storybook/theming" "8.4.4" - "@storybook/test-runner@^0.19.1": version "0.19.1" resolved "https://registry.yarnpkg.com/@storybook/test-runner/-/test-runner-0.19.1.tgz#b0a94bd09d9914f47e23d11779690ffc5b5164a7" @@ -4144,11 +4129,6 @@ "@vitest/expect" "2.0.5" "@vitest/spy" "2.0.5" -"@storybook/theming@8.4.4": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.4.4.tgz#3d209a235bf2b6f5543f1eebe78244f0104811b5" - integrity sha512-iq4yt3Fx35ZV5owNC//E6G+QPV19xHHVN2Ugi3p7KOSFK3chuXX9mxZ1rfir+t+U30a5EPOEnlsY3/1LXn7aTw== - "@storybook/theming@8.4.7": version "8.4.7" resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.4.7.tgz#c308f6a883999bd35e87826738ab8a76515932b5"