From ed632ad3fb6bd0e16963507c081ea9a64acb5958 Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Fri, 12 Jun 2026 14:55:38 +0200 Subject: [PATCH] :zap: Episodes (no paging) --- src/api/episodes.ts | 7 ++-- src/app-episodes/index.css.ts | 12 ++---- src/app-episodes/index.ts | 8 +--- .../c-episode-list-card/index.css.ts | 41 ++++++++++--------- .../c-episode-list-card/index.test.ts | 17 ++++---- src/components/c-episode-list-card/index.ts | 24 ++++++++--- 6 files changed, 59 insertions(+), 50 deletions(-) diff --git a/src/api/episodes.ts b/src/api/episodes.ts index 5f6c950..0e61120 100644 --- a/src/api/episodes.ts +++ b/src/api/episodes.ts @@ -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 { diff --git a/src/app-episodes/index.css.ts b/src/app-episodes/index.css.ts index 03c6b9b..4d3b393 100644 --- a/src/app-episodes/index.css.ts +++ b/src/app-episodes/index.css.ts @@ -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; } diff --git a/src/app-episodes/index.ts b/src/app-episodes/index.ts index 6219a1a..b024f8a 100644 --- a/src/app-episodes/index.ts +++ b/src/app-episodes/index.ts @@ -43,12 +43,8 @@ export class AppEpisodes extends LitElement { } return html` - - +

Episodes

+ ${this.episodes.map((episode) => html``)} `; } } diff --git a/src/components/c-episode-list-card/index.css.ts b/src/components/c-episode-list-card/index.css.ts index d27f3b2..f785f8a 100644 --- a/src/components/c-episode-list-card/index.css.ts +++ b/src/components/c-episode-list-card/index.css.ts @@ -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; + } `; diff --git a/src/components/c-episode-list-card/index.test.ts b/src/components/c-episode-list-card/index.test.ts index 665248f..bb7ab21 100644 --- a/src/components/c-episode-list-card/index.test.ts +++ b/src/components/c-episode-list-card/index.test.ts @@ -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: "Test content", + feedId: "2", + feedName: "Test Feed", }; it("renders correctly with episode data", async () => { const el = await fixture(html``); - 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("Test content"); }); it("renders empty when no episode is provided", async () => { diff --git a/src/components/c-episode-list-card/index.ts b/src/components/c-episode-list-card/index.ts index 1c8e0ae..f7a19ca 100644 --- a/src/components/c-episode-list-card/index.ts +++ b/src/components/c-episode-list-card/index.ts @@ -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` - -
- ${this.episode.title} - -
-
+

+ ${title} +

+ + ${feedName} +
+ ${templateContent(template)} +
`; } }