Skip to content

Commit

Permalink
Add FileDetails.svelte (#9)
Browse files Browse the repository at this point in the history
* use as const to fix TS errors

* add FileDetails.svelte

* add FileDetails.test.ts

* remove pr trigger from gh-pages.yml workflow
  • Loading branch information
janosh committed May 20, 2023
1 parent c144da2 commit 0182e75
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 35 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: GitHub Pages

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:
Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@
"test": "vitest"
},
"dependencies": {
"@sveltejs/kit": "^1.16.3",
"@sveltejs/kit": "^1.18.0",
"highlight.js": "^11.8.0",
"svelte": "^3.59.1"
},
"devDependencies": {
"@sveltejs/adapter-static": "^2.0.2",
"@sveltejs/package": "^2.0.2",
"@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.59.5",
"@vitest/coverage-c8": "^0.31.0",
"eslint": "^8.40.0",
"@typescript-eslint/eslint-plugin": "^5.59.6",
"@typescript-eslint/parser": "^5.59.6",
"@vitest/coverage-c8": "^0.31.1",
"eslint": "^8.41.0",
"eslint-plugin-svelte3": "^4.0.0",
"hastscript": "^7.2.0",
"jsdom": "^22.0.0",
Expand All @@ -42,8 +43,8 @@
"svelte-preprocess": "^5.0.3",
"svelte2tsx": "^0.6.14",
"typescript": "5.0.4",
"vite": "^4.3.5",
"vitest": "^0.31.0"
"vite": "^4.3.8",
"vitest": "^0.31.1"
},
"keywords": [
"svelte",
Expand Down
57 changes: 57 additions & 0 deletions src/lib/FileDetails.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import hljs from 'highlight.js/lib/common'
import 'highlight.js/styles/vs2015.css'
export let files: {
title: string
content: string
language?: string
node?: HTMLDetailsElement | null
}[] = []
export let toggle_all_btn_title: string = 'Toggle all'
export let default_lang: string = 'typescript'
function toggle_all() {
const any_open = files.some((file) => file.node?.open)
for (const file of files) {
if (!file.node) continue
file.node.open = !any_open
}
files = [...files] // trigger reactivity
}
</script>

{#if files?.length > 1}
<button on:click={toggle_all} title={toggle_all_btn_title}>
{files.some((file) => file.node?.open) ? 'Close' : 'Open'} all
</button>
{/if}

<ol>
{#each files as file, idx (file.title)}
{@const { title, content, language = default_lang } = file}
<li>
<details bind:this={file.node}>
<summary>
<slot name="title" {idx} {...file}>
<code>{title.split('/').at(-1)}</code>
</slot>
</summary>

<pre><code>{@html hljs.highlight(content, { language }).value}</code></pre>
</details>
</li>
{/each}
</ol>

<style>
button {
float: right;
}
ol {
padding: 0 1em;
}
ol > li {
margin: 1ex 0;
}
</style>
3 changes: 2 additions & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ export { default as CodeExample } from './CodeExample.svelte'
export { default as CodeLinks } from './CodeLinks.svelte'
export { default as Confetti } from './Confetti.svelte'
export { default as CopyButton } from './CopyButton.svelte'
export { default as FileDetails } from './FileDetails.svelte'
export { default as GitHubCorner } from './GitHubCorner.svelte'
export { default as Icon } from './Icon.svelte'
export { default as PrevNext } from './PrevNext.svelte'
export { default as RadioButtons } from './RadioButtons.svelte'
export { default as Slider } from './Slider.svelte'
export * from './stores'
export { default as Toggle } from './Toggle.svelte'
export { default as Tooltip } from './Tooltip.svelte'
export * from './stores'

export function get_bg_color(elem: HTMLElement | null): string {
// recurse up the DOM tree to find the first non-transparent background color
Expand Down
2 changes: 1 addition & 1 deletion tests/CodeLinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test.each([[true], [`src/lib/CodeLinks.svelte`]])(`CodeLinks`, (file) => {
).toBeInstanceOf(HTMLAnchorElement)
})

test.each([[`_blank`], [`_self`]])(
test.each([[`_blank`], [`_self`]] as const)(
`applies target=%s to all links`,
(target) => {
new CodeLinks({ target: document.body, props: { target } })
Expand Down
121 changes: 121 additions & 0 deletions tests/FileDetails.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { FileDetails } from '$lib'
import { tick } from 'svelte'
import { expect, test } from 'vitest'
import { doc_query } from '.'

const files = [
{ title: `file1`, content: `content1` },
{ title: `file2`, content: `content2` },
]
const click = new MouseEvent(`click`)

test(`FileDetails renders all files passed`, async () => {
new FileDetails({ target: document.body, props: { files } })
await tick()

const details = document.querySelectorAll(`li > details`)
expect(details.length).toBe(files.length)

// check items are wrapped in ordered list
const ol = doc_query(`ol`)
expect(ol.children.length).toBe(files.length)
})

test(`FileDetails renders file titles and contents`, async () => {
new FileDetails({ target: document.body, props: { files } })
await tick()

const titles = document.querySelectorAll(`summary`)
expect(titles.length).toBe(files.length)

const contents = document.querySelectorAll(`pre > code`)

for (const [idx, src] of contents.entries()) {
expect(src.textContent).toBe(files[idx].content)
}
})

test(`FileDetails opens and closes files when the toggle button is clicked`, async () => {
const files = [
{ title: `file1`, content: `content1`, open: false },
{ title: `file2`, content: `content2`, open: false },
]

new FileDetails({
target: document.body,
props: { files },
})
await tick()

const details = document.querySelectorAll(`details`)
for (const detail of details) {
expect(detail.open).toBe(false)
}

const btn = doc_query(`button`)
btn.dispatchEvent(click)
await tick()

for (const detail of details) {
expect(detail.open).toBe(true)
}

btn.dispatchEvent(click)
await tick()

for (const detail of details) {
expect(detail.open).toBe(false)
}
})

test(`toggle all button opens and closes all details`, async () => {
const files = [
{ title: `file1`, content: `content1`, open: false },
{ title: `file2`, content: `content2`, open: false },
{ title: `file3`, content: `content3`, open: false },
]

const toggle_all_btn_title = `toggle all`
new FileDetails({
target: document.body,
props: { files, toggle_all_btn_title },
})

await tick()

const details = document.querySelectorAll(`details`)

const btn = doc_query(`button[title='${toggle_all_btn_title}']`)

// open all details
btn.dispatchEvent(click)
await tick()

// all details elements should now be open
for (const detail of details) {
expect(detail.open).toBe(true)
}

// Click the toggle button again to close all details
btn.dispatchEvent(click)
await tick()

// all details elements should now be closed
for (const detail of details) {
expect(detail.open).toBe(false)
}

// Open some of the details
details[0].open = true
details[1].open = true
await tick()

// Click the toggle button to close all details
btn.dispatchEvent(click)
await tick()

// All details elements should now be closed
for (const detail of details) {
expect(detail.open).toBe(false)
}
})
43 changes: 22 additions & 21 deletions tests/GitHubCorner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,26 @@ import { repository } from '$root/package.json'
import { expect, test } from 'vitest'
import { doc_query } from '.'

test.each([[`top-left`], [`top-right`], [`bottom-left`], [`bottom-right`]])(
`GitHubCorner`,
(corner) => {
const props = {
href: repository,
title: `Fancy words`,
aria_label: `Click here for riches`,
target: `_blank` as const,
corner,
style: `z-index: 42;`,
}
const { href, title, aria_label, target } = props
// @ts-expect-error - vitest poorly infers corner type
new GitHubCorner({ target: document.body, props })
test.each([
[`top-left`],
[`top-right`],
[`bottom-left`],
[`bottom-right`],
] as const)(`GitHubCorner`, (corner) => {
const props = {
href: repository,
title: `Fancy words`,
aria_label: `Click here for riches`,
target: `_blank`,
corner,
style: `z-index: 42;`,
} as const
const { href, title, aria_label, target } = props
new GitHubCorner({ target: document.body, props })

expect(
doc_query(
`a[href='${href}'][target='${target}'][title='${title}'][aria-label='${aria_label}']`
)
).toBeInstanceOf(HTMLAnchorElement)
}
)
expect(
doc_query(
`a[href='${href}'][target='${target}'][title='${title}'][aria-label='${aria_label}']`
)
).toBeInstanceOf(HTMLAnchorElement)
})
4 changes: 1 addition & 3 deletions tests/stores.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { StorageType } from '$lib'
import {
local_store,
persisted_store,
Expand All @@ -18,8 +17,7 @@ test.each([
[session_store, `sessionStorage`],
[persisted_store, `localStorage`],
[persisted_store, `sessionStorage`],
// @ts-expect-error vitest bad inference
])(`store tests`, (store_factory, type: StorageType) => {
] as const)(`store tests`, (store_factory, type) => {
const testName = `test_${type}`
const initialValue = `initial_${type}`
const newValue = `new_${type}`
Expand Down

0 comments on commit 0182e75

Please sign in to comment.