Skip to content

Commit

Permalink
Merge branch 'main' into DDFBRA-217-opdater-unilogin-integration-til-…
Browse files Browse the repository at this point in the history
…at-bruge-seneste-stil-anvisninger
  • Loading branch information
spaceo committed Nov 21, 2024
2 parents 1bc978c + 399492f commit f327445
Show file tree
Hide file tree
Showing 30 changed files with 2,980 additions and 123 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/accessibility-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Workflow name
name: "Accessibility Test"

# Event for the workflow
on: push

# List of jobs
jobs:
# Run interaction and accessibility tests
accessibility:
name: Accessibility tests

runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
# Make sure the actual branch is checked out when running on pull requests.
with:
ref: ${{ github.head_ref }}
fetch-depth: 0 # 👈 Required to retrieve git history

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"

- name: Install dependencies
run: yarn

- name: Install Playwright
run: npx playwright install --with-deps

- name: Build Storybook
run: yarn build-storybook --quiet

- name: Serve Storybook and run tests
run: |
export FORCE_COLOR=1
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --port 6006 --silent" \
"npx wait-on tcp:6006 && yarn test-storybook"
2 changes: 1 addition & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:
run: yarn install --frozen-lockfile

- name: Run unit tests
run: yarn test:unit
run: yarn test:unit:once
1 change: 1 addition & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const config: StorybookConfig = {
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
"@storybook/addon-a11y",
],
framework: {
name: "@storybook/nextjs",
Expand Down
17 changes: 17 additions & 0 deletions .storybook/test-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { checkA11y, injectAxe } from "axe-playwright"

module.exports = {
async preVisit(page) {
await injectAxe(page)
},
async postVisit(page) {
await checkA11y(page, "#storybook-root", {
axeOptions: {},
detailedReport: true,
detailedReportOptions: {
html: true,
},
verbose: true,
})
},
}
3 changes: 2 additions & 1 deletion __tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { expect, test, vi } from "vitest"
import { expect, test } from "vitest"

import goConfig from "@/lib/config/config"
import MissingConfigurationError from "@/lib/config/errors/MissingConfigurationError"

test("That an error is thrown if we ask for unknown config", async () => {
// @ts-ignore
expect(() => goConfig("unknown.thingy")).toThrowError(MissingConfigurationError)
})

Expand Down
5 changes: 5 additions & 0 deletions __tests__/refresh-token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IronSession, getIronSession } from "iron-session"
import { testApiHandler } from "next-test-api-route-handler"
import { afterAll, beforeAll, beforeEach, expect, test, vi } from "vitest"

// @ts-ignore
import * as tokenRefreshHandler from "@/app/auth/token/refresh/route"
import { TSessionData, accessTokenShouldBeRefreshed } from "@/lib/session/session"

Expand All @@ -24,6 +25,7 @@ afterAll(() => {
})

beforeEach(() => {
// @ts-ignore
getIronSession.mockResolvedValue({
isLoggedIn: true,
})
Expand All @@ -41,6 +43,7 @@ const sessionThatShouldBeRefreshed = () => ({

test("That the refresh endpoint redirects to the frontpage if there is no active session", async () => {
// Simulate an anonymous session.
// @ts-ignore
getIronSession.mockResolvedValue({
isLoggedIn: false,
})
Expand All @@ -57,6 +60,7 @@ test("That the refresh endpoint redirects to the frontpage if there is no active

test("That the refresh endpoint redirects to the given endpoint after refreshing token", async () => {
// This is an authorized session that should be refreshed.
// @ts-ignore
getIronSession.mockResolvedValue(sessionThatShouldBeRefreshed())

await testApiHandler({
Expand All @@ -69,6 +73,7 @@ test("That the refresh endpoint redirects to the given endpoint after refreshing
})

// This is an authorized session that should NOT be refreshed.
// @ts-ignore
getIronSession.mockResolvedValue({
isLoggedIn: true,
expires: add(new Date(), { seconds: 300 }),
Expand Down
3 changes: 3 additions & 0 deletions __tests__/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const facets = {
} as const

beforeEach(() => {
// @ts-ignore
;(goConfig as jest.Mock).mockImplementation((key: string) => {
if (key === "search.item.limit") {
return 9 // Mocked return value for "search.item.limit"
Expand All @@ -49,6 +50,7 @@ beforeEach(() => {

describe("Facet functionality", () => {
it("getMachineNames should return all the facet machine names", () => {
// @ts-ignore
goConfig.mockReturnValue(facets)
const machineNames = getFacetMachineNames()
expect(machineNames).toStrictEqual([
Expand Down Expand Up @@ -109,6 +111,7 @@ describe("Facet functionality", () => {
})

it("getFacetTranslation should give a translated facet when given a facet machine name", () => {
// @ts-ignore
const translation = getFacetTranslation("LIX")
expect(translation).toBe("Lix")
})
Expand Down
6 changes: 3 additions & 3 deletions __tests__/url.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { expect, test, vi } from "vitest"
import { expect, test } from "vitest"

import { resolveUrl } from "../lib/helpers/helper.routes"

test("That resolveUrl can return a work url", async () => {
const workUrl = resolveUrl({ type: "work", routeParams: { id: 123 } })
const workUrl = resolveUrl({ type: "work", routeParams: { wid: 123 } })
expect(workUrl).toBe("/work/123")
})

test("That resolveUrl can return a work url with a manifestation type", async () => {
const workUrl = resolveUrl({
type: "work",
routeParams: { id: 123 },
routeParams: { wid: 123 },
queryParams: { audio: "true" },
})
expect(workUrl).toBe("/work/123?audio=true")
Expand Down
4 changes: 3 additions & 1 deletion app/user/profile/LogoutButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const LogoutButton = () => {

return (
<>
<Button onClick={() => router.push("/auth/logout")}>Log ud</Button>
<Button onClick={() => router.push("/auth/logout")} ariaLabel="Log ud">
Log ud
</Button>
<h2 className="mt-5 text-typo-heading-5">Debugging:</h2>
<div className="mt-3">
<pre>{JSON.stringify(session, null, 2)}</pre>
Expand Down
29 changes: 29 additions & 0 deletions app/work/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { HydrationBoundary, dehydrate } from "@tanstack/react-query"
import React from "react"

import WorkPageLayout from "@/components/pages/workPageLayout/WorkPageLayout"
import getQueryClient from "@/lib/getQueryClient"
import { useGetMaterialQuery } from "@/lib/graphql/generated/fbi/graphql"

function Page({ params: { id } }: { params: { id: string } }) {
const queryClient = getQueryClient()

const decodedWid = decodeURIComponent(id)

queryClient.prefetchQuery({
queryKey: useGetMaterialQuery.getKey({ wid: decodedWid }),
queryFn: useGetMaterialQuery.fetcher({ wid: decodedWid }),
})

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<div>
<WorkPageLayout wid={decodedWid} />
<pre>{JSON.stringify(id, null, 2)}</pre>
Page
</div>
</HydrationBoundary>
)
}

export default Page
2 changes: 1 addition & 1 deletion components/global/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function Header() {
<DarkModeToggle />
</div>
<div className="flex-0 flex justify-end space-x-4 pr-1">
<Button variant="icon" aria-label="Tilgå hjælpesiden">
<Button variant="icon" ariaLabel="Tilgå hjælpesiden">
<Icon className="h-[24px] w-[24px]" name="question-mark" />
</Button>
<Suspense fallback={<p>Loading...</p>}>
Expand Down
6 changes: 4 additions & 2 deletions components/global/header/ProfileButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const HeaderButton = ({
asChild?: boolean
// TODO: Dynamic aria-label.
}) => (
<Button onClick={onClick} variant="icon" aria-label="Login / Tilgå profilsiden" asChild={asChild}>
<Button onClick={onClick} variant="icon" ariaLabel="Login / Tilgå profilsiden" asChild={asChild}>
<Icon className="h-[24px] w-[24px]" name="profile" />
</Button>
)
Expand Down Expand Up @@ -55,7 +55,9 @@ function ProfileButton() {
Log ind med UNI•Login
</SheetDescription>
<div>
<Button onClick={() => router.push("/auth/login/unilogin")}>LOG IND</Button>
<Button onClick={() => router.push("/auth/login/unilogin")} ariaLabel="Log ind">
LOG IND
</Button>
</div>
</div>
</SheetHeader>
Expand Down
21 changes: 21 additions & 0 deletions components/pages/workPageLayout/WorkPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client"

import { useQuery } from "@tanstack/react-query"
import React from "react"

import { useGetMaterialQuery } from "@/lib/graphql/generated/fbi/graphql"

function WorkPageLayout({ wid }: { wid: string }) {
const data = useQuery({
queryKey: useGetMaterialQuery.getKey({ wid }),
queryFn: useGetMaterialQuery.fetcher({ wid }),
})

return (
<div>
<pre>{JSON.stringify({ data }, null, 2)}</pre>
</div>
)
}

export default WorkPageLayout
12 changes: 10 additions & 2 deletions components/shared/badge/BadgeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ type BadgeButtonProps = {
isActive?: boolean
classNames?: string
children: React.ReactNode
ariaLabel: string
}

const BadgeButton = ({ onClick, isActive = false, classNames, children }: BadgeButtonProps) => {
const BadgeButton = ({
onClick,
isActive = false,
classNames,
children,
ariaLabel,
}: BadgeButtonProps) => {
return (
<button
onClick={onClick}
className={cn(
`focus-visible h-[28px] w-auto self-start whitespace-nowrap rounded-full bg-background-overlay px-4
py-2 text-typo-caption hover:animate-wiggle`,
isActive && "bg-foreground text-background",
classNames
classNames,
ariaLabel
)}>
{children}
</button>
Expand Down
9 changes: 9 additions & 0 deletions components/shared/button/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const Default: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "default",
},
Expand All @@ -48,6 +49,7 @@ export const Small: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "sm",
},
Expand All @@ -66,6 +68,7 @@ export const SmallDark: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "sm",
},
Expand All @@ -85,6 +88,7 @@ export const Medium: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "md",
},
Expand All @@ -103,6 +107,7 @@ export const MediumDark: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "md",
},
Expand All @@ -122,6 +127,7 @@ export const Large: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "lg",
},
Expand All @@ -140,6 +146,7 @@ export const LargeDark: Story = {
},
},
args: {
ariaLabel: "Prøv Lydbogen",
variant: "default",
size: "lg",
},
Expand All @@ -154,6 +161,7 @@ export const LargeDark: Story = {
export const IconStory: Story = {
name: "Icon",
args: {
ariaLabel: "Tilgå hjælpesiden",
variant: "icon",
},
render: args => (
Expand All @@ -166,6 +174,7 @@ export const IconStory: Story = {
export const IconStoryDark: Story = {
name: "Icon dark",
args: {
ariaLabel: "Tilgå hjælpesiden",
variant: "icon",
},
decorators: [darkModeDecorator],
Expand Down
10 changes: 8 additions & 2 deletions components/shared/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@ export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
ariaLabel: string
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
({ className, ariaLabel, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props} />
<Comp
className={cn(buttonVariants({ variant, size }), className)}
ref={ref}
{...props}
aria-label={ariaLabel}
/>
)
}
)
Expand Down
Loading

0 comments on commit f327445

Please sign in to comment.