Episodes (no paging)

This commit is contained in:
2026-06-12 14:55:38 +02:00
parent 5a0a2de0f0
commit ed632ad3fb
6 changed files with 59 additions and 50 deletions
+4 -3
View File
@@ -3,9 +3,10 @@ import { fetchAPI } from "./fetchAPI.js";
export interface Episode {
id: string;
title: string;
published: string; // ISO string or similar
audio_url?: string;
description?: string;
content: string; // HTML
date: number;
feedId: string;
feedName: string;
}
export interface FeedEntriesResponse {
+3 -9
View File
@@ -4,16 +4,10 @@ export const styles = css`
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
--width: 33ch;
/* internal */
margin-inline: max(7.5ch, calc(50% - var(--width)));
}
.episode-list {
padding: 0;
margin: 0;
width: 100%;
max-width: 600px;
}
.error {
color: red;
}
-4
View File
@@ -43,12 +43,8 @@ export class AppEpisodes extends LitElement {
}
return html`
<div class="logo">
<h1>Episodes</h1>
</div>
<ul class="episode-list">
${this.episodes.map((episode) => html`<c-episode-list-card .episode=${episode}></c-episode-list-card>`)}
</ul>
`;
}
}
+22 -19
View File
@@ -2,27 +2,30 @@ import { css } from "lit";
export const styles = css`
:host {
list-style: none;
padding: 1rem;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
display: grid;
grid-template-areas:
"title title"
"date feed"
"content content";
grid-template-rows:
fit-content
fit-content
1fr;
}
.info {
display: flex;
flex-direction: column;
h2 {
grid-area: title;
text-wrap: balance;
text-wrap: pretty;
}
.title {
font-weight: bold;
text-decoration: none;
color: var(--primary-color, #007bff);
}
time {
font-size: 0.85rem;
time, .feed {
color: #666;
}
time { grid-area: date; }
.feed { grid-area: feed; }
.content {
grid-area: content;
max-height: 3.5lh;
overflow: auto;
font-size: .9em;
}
`;
@@ -7,20 +7,23 @@ describe("EpisodeListItem", () => {
const mockEpisode: Episode = {
id: "1",
title: "Test Episode",
published: "2023-01-01T00:00:00Z",
audio_url: "http://example.com/audio.mp3",
date: 1781258812000,
content: "<b>Test content</b>",
feedId: "2",
feedName: "Test Feed",
};
it("renders correctly with episode data", async () => {
const el = await fixture(html`<episode-list-card .episode=${mockEpisode}></episode-list-card>`);
const title = el.shadowRoot!.querySelector(".title");
const time = el.shadowRoot!.querySelector("time");
const audioLink = el.shadowRoot!.querySelector(".audio-link");
const title = el.shadowRoot!.querySelector("h2");
expect(title?.textContent).to.equal("Test Episode");
const time = el.shadowRoot!.querySelector("time");
expect(time?.textContent).to.contain("2023");
expect(audioLink).to.exist;
expect(audioLink?.getAttribute("href")).to.equal("http://example.com/audio.mp3");
const feed = el.shadowRoot!.querySelector(".feed");
expect(feed?.textContent).to.equal("Test Feed");
const content = el.shadowRoot!.querySelector(".content");
expect(content?.innerHTML.trim()).to.equal("<b>Test content</b>");
});
it("renders empty when no episode is provided", async () => {
+17 -5
View File
@@ -2,6 +2,7 @@ import { LitElement, html } from "lit";
import { property, customElement } from "lit/decorators.js";
import { type Episode } from "@/api/episodes.js";
import { styles } from "./index.css.js";
import { templateContent } from "lit/directives/template-content.js";
@customElement("c-episode-list-card")
export class EpisodeListCard extends LitElement {
@@ -11,13 +12,24 @@ export class EpisodeListCard extends LitElement {
override render() {
if (!this.episode) return html``;
const { id, title, content, date, feedId, feedName }= this.episode;
const dateString = new Date(date).toLocaleDateString();
const template = document.createElement("template");
try {
// @ts-expect-error 2551
template.setHTML(content);
} catch (e) {
template.innerText = content;
}
return html`
<a href="/episodes/${this.episode.id}">
<div class="info">
${this.episode.title}
<time datetime="${this.episode.published}">${new Date(this.episode.published).toLocaleDateString()}</time>
<h2>
<a href="/episodes/${id}">${title}</a>
</h2>
<time datetime="${date}">${dateString}</time>
<a class="feed" href="/feeds/${feedId}">${feedName}</a>
<div class="content">
${templateContent(template)}
</div>
</a>
`;
}
}