Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions content/banner/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Tempor enim non proident amet aliqua et ipsum [fugiat](https://github.com/remarkjs/react-markdown?tab=readme-ov-file) laborum in nostrud. Quis cupidatat commodo non irure. Cupidatat culpa mollit duis officia pariatur elit ullamco proident ad aliqua esse nulla enim amet.
1 change: 1 addition & 0 deletions content/banner/sv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**[sv]** Tempor enim non proident amet aliqua et ipsum [fugiat](https://github.com/remarkjs/react-markdown?tab=readme-ov-file) laborum in nostrud. Quis cupidatat commodo non irure. Cupidatat culpa mollit duis officia pariatur elit ullamco proident ad aliqua esse nulla enim amet.
20 changes: 20 additions & 0 deletions docs/banner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Disclaimer banner

Adding a dismissable banner on top of all pages, with translatable markdown content.

### How to manage the content

To **enable** the disclaimer banner:

* Navigate to `/content/banner` in your repository
* Add the English content to `en.md`
* Add the Swedish content to `sv.md`
* Commit your changes to `main` (production) or `staging` (staging environment)

Note that markdown is supported.

To **disable** the banner, simply clear the contents of the files.

Screenshot:
![alt text](disclaimer-banner.png)

Binary file added docs/banner/disclaimer-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const nextConfig: NextConfig = {
images: {
remotePatterns: imagesUrl,
},

outputFileTracingIncludes: {
"/*": ["content/**/*"],
},
};

export default withNextIntl(nextConfig);
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@
"@types/papaparse": "^5.5.0",
"@types/react": "^19.1.13",
"@types/react-dom": "^19",
"axe-core": "^4.11.1",
"cypress": "^15.9.0",
"cypress-axe": "^1.7.0",
"dotenv": "^17.2.3",
"eslint": "^9",
"eslint-config-next": "15.2.4",
"lightningcss": "^1.28.1",
"tailwindcss": "^4.1.3",
"typescript": "^5",
"axe-core": "^4.11.1",
"cypress": "^15.9.0",
"cypress-axe": "^1.7.0",
"dotenv": "^17.2.3"
"typescript": "^5"
}
}
15 changes: 13 additions & 2 deletions src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { Metadata } from "next";
import { buildLocalizedMetadata } from "@/lib/seo";
import MatomoTracker from "@/components/analytics/matomo";
import SkipToContent from "@/components/layout/SkipToContent";
import DisclaimerBanner from "@/components/layout/DisclaimerBanner";
import { getMarkdownContent } from "@/lib/markdown";

const mainFont = Open_Sans({
weight: ["300", "400", "500", "600", "700"],
Expand Down Expand Up @@ -45,18 +47,27 @@ export default async function RootLayout({

const messages = await getMessages();

const bannerContent: string | null = await getMarkdownContent(
`banner/${locale}.md`,
);

return (
<html lang={locale}>
<body
className={`${mainFont.className} antialiased min-h-screen flex flex-col`}
>
<Suspense>
<MatomoTracker />


<NextIntlClientProvider locale={locale} messages={messages}>
<MatomoTracker />
<SkipToContent />
<DisclaimerBanner content={bannerContent} />
<Header />
<QueryProvider>
<main className="flex-grow" id="main-content">{children}</main>
<main className="flex-grow" id="main-content">
{children}
</main>
</QueryProvider>
<Footer />
</NextIntlClientProvider>
Expand Down
50 changes: 50 additions & 0 deletions src/components/layout/DisclaimerBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import { X } from "lucide-react";
import { useState } from "react";
import Container from "../ui/container";
import MarkdownRender from "../ui/markdown";


export default function DisclaimerBanner({
content = "",
}: {
content: string | null;
}) {
const [show, setShow] = useState<boolean | null>(true);

const handleDismiss = () => {
setShow(false);
};

if (!show) return null;

return (
content && (
<div className="bg-gray-900 py-2.5 ">
<Container>
<div className="flex gap-x-6 bg-gray-900 ">
<div className="block">
<div
className="text-white text-sm/6"
>
<MarkdownRender content={content} />
</div>
</div>

<div className="flex flex-1 h-fit w-fit justify-end">
<button
type="button"
className=" p-3 focus-visible:-outline-offset-4 text-white cursor-pointer"
onClick={handleDismiss}
>
<span className="sr-only">Dismiss</span>
<X />
</button>
</div>
</div>
</Container>
</div>
)
);
}
2 changes: 1 addition & 1 deletion src/components/layout/PageHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { useTranslations } from "next-intl";
import Image from "next/image";
import homeGraphic from "@/assets/hero-home-bg.svg";
import navGraphic from "@/assets/hero-nav-bg.svg";
import useIsClamped from "@/hooks/clamped";
import { Button } from "../ui/button";
import { useIsClamped } from "@/hooks/element";

export type BreadcrumbItemProps = {
title: string;
Expand Down
9 changes: 6 additions & 3 deletions src/components/package/dataset/DatasetResource.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
"use client";

import { Resource } from "@/schemas/ckan";
import MarkdownRender from "@/components/ui/markdown";
import { formatFileSize } from "@/lib/utils";
import { Link } from "@/i18n/navigation";
import React from "react";
import { RESOURCE_COLORS, supportsPreview } from "@/lib/resource";
import { getTranslations } from "next-intl/server";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { EyeIcon } from "lucide-react";
import { useTranslations } from "next-intl";

export default async function DatasetResources({
export default function DatasetResources({
resources,
dataset,
organization,
Expand All @@ -18,7 +21,7 @@ export default async function DatasetResources({
dataset: string;
organization: string;
}) {
const t = await getTranslations();
const t = useTranslations();

return (
<div className="space-y-5">
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export default function MarkdownRender({
props.children
) : (
<pre
className="bg-gray-100 rounded p-2 overflow-x-auto"
className="bg-gray-100 text-foreground rounded p-2 overflow-x-auto"
{...props}
/>
),
Expand Down
36 changes: 0 additions & 36 deletions src/hooks/clamped.ts

This file was deleted.

36 changes: 35 additions & 1 deletion src/hooks/element.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useCallback, useEffect, useRef, useState } from "react";
"use client"

import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";

export function useElementWidth() {
const [width, setWidth] = useState(0);
Expand Down Expand Up @@ -27,3 +29,35 @@ export function useElementWidth() {

return { ref, width };
}

export function useIsClamped(deps: unknown[] = []) {
const ref = useRef<HTMLDivElement | null>(null);
const [isClamped, setIsClamped] = useState(false);

const measure = () => {
const el = ref.current;
if (!el) return;
const clamped = el.scrollHeight > el.clientHeight + 1;
setIsClamped(clamped);
};

useLayoutEffect(() => {
measure();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);

useEffect(() => {
const el = ref.current;
if (!el) return;

// Re-measure on resize + font load/layout changes
const ro = new ResizeObserver(() => measure());
ro.observe(el);

return () => ro.disconnect();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);

return { ref, isClamped };
}

19 changes: 19 additions & 0 deletions src/lib/markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import fs from "fs";
import path from "path";

export async function getMarkdownContent(fileName: string): Promise<string> {
try {
const filePath = path.join(process.cwd(), "content", fileName);
const content = fs.readFileSync(filePath, "utf8");
return content;
} catch (error) {
console.error(`Error reading Markdown file ${fileName}:`, error);
return "";
}
}

export const extractTitle = (content:string) => {
const match = content?.match(/^#+\s*(.*?)(?=\r?\n|$)/);
const firstHeading = match ? match[1] : null;
return firstHeading;
};