Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sveltekit viewer layout #479

Merged
merged 19 commits into from
Mar 26, 2024
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ tests/fixtures/development.json
.vscode
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
*.log

# sveltekit
/build
Expand Down
30 changes: 27 additions & 3 deletions src/lib/api/documents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, test as base, expect } from "vitest";
import { APP_URL, IMAGE_WIDTHS_ENTRIES } from "@/config/config.js";

import * as documents from "./documents";
import type { Document } from "./types";
import type { Document, sizes } from "./types";

type Use<T> = (value: T) => Promise<void>;

Expand Down Expand Up @@ -51,7 +51,9 @@ describe("document helper methods", () => {
test("pageImageUrl", ({ document }) => {
const page = 1;
IMAGE_WIDTHS_ENTRIES.forEach(([size, width]) => {
expect(documents.pageImageUrl(document, page, size)).toStrictEqual(
expect(
documents.pageImageUrl(document, page, size as sizes),
).toStrictEqual(
new URL(
`documents/${document.id}/pages/${document.slug}-p${page}-${size}.gif`,
document.asset_url,
Expand Down Expand Up @@ -87,5 +89,27 @@ describe("document helper methods", () => {
);
});

test.todo("userOrgString");
test("userOrgString", async ({ document }) => {
// user + org expanded
expect(documents.userOrgString(document)).toStrictEqual(
"Chris Amico (NewsHour)",
);

// user and org not expanded
const d2 = (await import("./fixtures/documents/document.json")) as Document;
expect(documents.userOrgString(d2)).toStrictEqual("");

// user, but no org
const d3 = { ...document, organization: 1 };
expect(documents.userOrgString(d3)).toStrictEqual("Chris Amico");
});

test("pdfUrl", ({ document }) => {
expect(documents.pdfUrl(document)).toStrictEqual(
new URL(
`documents/${document.id}/${document.slug}.pdf`,
document.asset_url,
),
);
});
});
56 changes: 32 additions & 24 deletions src/lib/api/documents.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/** API helpers related to documents.
* Lots of duplicated code here that should get consolidated at some point.
*/
import type { Document, sizes } from "./types";

import { error } from "@sveltejs/kit";
import { APP_URL, BASE_API_URL } from "@/config/config.js";

import { DEFAULT_EXPAND } from "@/api/common.js";
import { isOrg } from "@/api/types/orgAndUser";
import { APP_URL, BASE_API_URL } from "@/config/config.js";
import { isErrorCode } from "../utils";

/** Search documents */
Expand Down Expand Up @@ -37,7 +40,7 @@ export async function get(
fetch: typeof globalThis.fetch,
): Promise<Document> {
const endpoint = new URL(`documents/${id}.json`, BASE_API_URL);
const expand = ["user", "organization", "projects", "revisions"];
const expand = ["user", "organization", "projects", "revisions", "sections"];
endpoint.searchParams.set("expand", expand.join(","));

const resp = await fetch(endpoint, { credentials: "include" });
Expand Down Expand Up @@ -74,7 +77,7 @@ export async function process() {}
* @param {import('./types').Document} document
* @returns {URL}
*/
export function canonicalUrl(document) {
export function canonicalUrl(document: Document): URL {
const path = `/documents/${document.id}-${document.slug}/`;
return new URL(path, APP_URL);
}
Expand All @@ -88,18 +91,16 @@ export function canonicalUrl(document) {
* @param {number} page
* @returns {URL}
*/
export function canonicalPageUrl(document, page) {
export function canonicalPageUrl(document: Document, page: number): URL {
return new URL(`/documents/${document.id}/pages/${page}/`, APP_URL);
}

/**
* Generate the hash for a path, without the host or path
*
* @export
* @param {number} page
* @returns {URL}
*/
export function pageHashUrl(page) {
export function pageHashUrl(page: number): string {
return `#document/p${page}`;
}

Expand All @@ -111,7 +112,7 @@ export function pageHashUrl(page) {
* @param {number} page
* @returns {URL}
*/
export function pageUrl(document, page) {
export function pageUrl(document: Document, page: number): URL {
return new URL(pageHashUrl(page), canonicalUrl(document));
}

Expand All @@ -124,7 +125,11 @@ export function pageUrl(document, page) {
* @param {import('./types').sizes} size
* @returns {URL}
*/
export function pageImageUrl(document, page, size) {
export function pageImageUrl(
document: Document,
page: number,
size: sizes,
): URL {
return new URL(
`documents/${document.id}/pages/${document.slug}-p${page}-${size}.gif`,
document.asset_url,
Expand All @@ -135,11 +140,8 @@ export function pageImageUrl(document, page, size) {
* Asset URL for page text
*
* @export
* @param {import('./types').Document} document
* @param {number} page
* @returns {URL}
*/
export function textUrl(document, page) {
export function textUrl(document: Document, page: number): URL {
return new URL(
`documents/${document.id}/pages/${document.slug}-p${page}.txt`,
document.asset_url,
Expand All @@ -150,10 +152,8 @@ export function textUrl(document, page) {
* Asset URL for JSON text
*
* @export
* @param {import('./types').Document} document
* @returns {URL}
*/
export function jsonUrl(document) {
export function jsonUrl(document: Document): URL {
return new URL(
`documents/${document.id}/${document.slug}.txt.json`,
document.asset_url,
Expand All @@ -164,33 +164,41 @@ export function jsonUrl(document) {
* Asset URL for text positions
*
* @export
* @param {import('./types').Document} document
* @param {number} page
* @returns {URL}
*/
export function selectableTextUrl(document, page) {
export function selectableTextUrl(document: Document, page: number): URL {
return new URL(
`documents/${document.id}/pages/${document.slug}-p${page}.position.json`,
document.asset_url,
);
}

/**
* Generate URL for the PDF version of a document.
* This will always be a PDF, regardless of the original file type.
*
* @export
*/
export function pdfUrl(document: Document): URL {
return new URL(
`documents/${document.id}/${document.slug}.pdf`,
document.asset_url,
);
}

/**
* Generate a user (organization) string
*
* @export
* @param {import('./types').Document} document
* @returns {string}
*/
export function userOrgString(document) {
export function userOrgString(document: Document): string {
// we have an org and user
if (isOrg(document.organization) && typeof document.user === "object") {
return `${document.user.name} (${document.organization.name})`;
}

// just a user
if (typeof document.user === "object") {
return document.user;
return document.user.name;
}

// nothing, so return nothing
Expand Down
17 changes: 14 additions & 3 deletions src/lib/api/embed.test.js → src/lib/api/embed.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
import type { Document, Note, OEmbed } from "./types";
import { test, describe, expect } from "vitest";

import { APP_URL, BASE_API_URL } from "@/config/config.js";
import { embedUrl, getEmbed } from "./embed.js";
import { embedUrl, getEmbed } from "./embed";

import * as documents from "./documents";
import * as notes from "./notes.js";

import document from "./fixtures/documents/document.json";
import note from "./fixtures/notes/note.json";
import oembed from "./fixtures/oembed.json";

describe("embed tests", () => {
test("embedUrl", () => {
// document
const docUrl = documents.canonicalUrl(document);
const docUrl = documents.canonicalUrl(document as Document);
expect(embedUrl(docUrl)).toStrictEqual(
new URL(`oembed/?url=${docUrl.toString()}`, BASE_API_URL),
);

// note
const noteUrl = notes.noteUrl(document, note);
const noteUrl = notes.noteUrl(document as Document, note as Note);
expect(embedUrl(noteUrl)).toStrictEqual(
new URL(`oembed/?url=${noteUrl.toString()}`, BASE_API_URL),
);
});

test.todo("getEmbed");
/* need to sort out mocking first
test("getEmbed", async () => {
const url =
"https://www.documentcloud.org/documents/282753-lefler-thesis.html";
const result = await getEmbed(url);

expect(result).toEqual(oembed);
});
*/
});
13 changes: 7 additions & 6 deletions src/lib/api/embed.js → src/lib/api/embed.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// api utilities for embeds

import type { OEmbed } from "./types";
import { BASE_API_URL } from "@/config/config.js";

/**
* Generate an oembed URL for a given DocumentCloud URL
*
* @export
* @param {URL | string} url
* @returns {URL}
*/
export function embedUrl(url) {
export function embedUrl(url: URL | string): URL {
return new URL(`oembed/?url=${url.toString()}`, BASE_API_URL);
}

/**
* Fetch embed code from the OEmbed API endpoint
*
* @export
* @param {URL | string} url
* @returns {import('./types').OEmbed}
*/
export async function getEmbed(url) {}
export async function getEmbed(url: URL | string): Promise<OEmbed> {
const endpoint = embedUrl(url);

return fetch(endpoint, { credentials: "include" }).then((r) => r.json());
}
4 changes: 4 additions & 0 deletions src/lib/api/fixtures/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import meFixture from "../fixtures/users/me.json";
import projectFixture from "../fixtures/projects/project.json";
import projDocsPage1 from "../fixtures/projects/project-documents-expanded.json";
import projDocsPage2 from "../fixtures/projects/project-documents-2.json";
import oembedFixture from "../fixtures/oembed.json";

import { BASE_API_URL } from "@/config/config.js";

Expand Down Expand Up @@ -34,6 +35,7 @@ const urls = {
info: u("projects/"),
documents: u("projects/*/documents/"),
},
oembed: u("oembed/"),
};

export const me = {
Expand All @@ -48,3 +50,5 @@ export const projects = {
pageHandler(projDocsPage1, projDocsPage2),
),
};

export const oembed = rest.get(urls.oembed, dataHandler(oembedFixture));
11 changes: 11 additions & 0 deletions src/lib/api/fixtures/oembed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "1.0",
"provider_name": "DocumentCloud",
"provider_url": "https://www.documentcloud.org",
"cache_age": 300,
"title": "Lefler Thesis",
"width": 700,
"height": 905,
"html": "<iframe\n src=\"https://embed.documentcloud.org/documents/282753-lefler-thesis/?embed=1\"\n title=\"Lefler Thesis (Hosted by DocumentCloud)\"\n width=\"700\"\n height=\"905\"\n style=\"border: 1px solid #aaa;\"\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-forms allow-popups-to-escape-sandbox\"\n></iframe>\n",
"type": "rich"
}
2 changes: 1 addition & 1 deletion src/lib/api/projects.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, test, expect } from "vitest";

import * as projects from "./projects.js";
import * as projects from "./projects";

describe("project tests", () => {
test.todo("projects.get");
Expand Down
File renamed without changes.
File renamed without changes.
26 changes: 26 additions & 0 deletions src/lib/components/common/Metadata.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script lang="ts">
export let key: string;
export let value: string;
</script>

<div class="metadata">
<slot name="icon" />
<dl class="text">
<dt>{key}</dt>
<dd>{value}</dd>
</dl>
</div>

<style>
.metadata {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}

.text {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
23 changes: 23 additions & 0 deletions src/lib/components/common/stories/Metadata.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script context="module" lang="ts">
import { Story } from "@storybook/addon-svelte-csf";
import { Tag16 } from "svelte-octicons";

import Metadata from "../Metadata.svelte";

export const meta = {
title: "Components / Common / Metadata",
component: Metadata,
tags: ["autodocs"],
parameters: { layout: "centered" },
};
</script>

<Story name="default">
<Metadata key="Last Updated" value="March 21, 2024" />
</Story>

<Story name="icon">
<Metadata key="Last Updated" value="March 21, 2024">
<Tag16 slot="icon" />
</Metadata>
</Story>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { informSize } from "@/embed/iframeSizer.js";
import { pageImageUrl } from "@/lib/api/documents";
import * as notes from "$lib/api/notes.js";
import { embedUrl } from "$lib/api/embed.js";
import { embedUrl } from "$lib/api/embed";
import { canonicalNoteUrl, noteUrl } from "$lib/api/notes.js";
import { pageSizesFromSpec } from "@/api/pageSize.js";
import { IMAGE_WIDTHS_MAP } from "@/config/config.js";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
textUrl,
userOrgString,
} from "@/lib/api/documents";
import { embedUrl } from "$lib/api/embed.js";
import { embedUrl } from "$lib/api/embed";

export let data;

Expand Down
Loading
Loading