All files / src/templates/sections timeline.ts

100% Statements 9/9
100% Branches 4/4
100% Functions 2/2
100% Lines 8/8

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69                                                                                9x   8x   8x   9x 9x 9x     9x                   8x              
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
 
/**
 * @module Templates/Sections/Timeline
 * @description Timeline section builder for legislative or procedural events.
 * Split out of `section-builders.ts` (Refactor 8/8).
 */
 
import { escapeHTML } from '../../utils/file-utils.js';
import type { LanguageCode } from '../../types/index.js';
import { getLocalizedString, TIMELINE_HEADINGS } from '../../constants/languages.js';
 
/**
 * A single item in a legislative or procedural timeline.
 */
export interface TimelineItem {
  /** Date label (e.g. "2026-03-15" or human-readable) */
  date: string;
  /** Short event label */
  label: string;
  /** Optional extended description */
  description?: string | undefined;
}
 
/**
 * Build an HTML timeline section for legislative or procedural events.
 *
 * Renders an ordered list of dated events. Each item includes a date badge
 * and a label. An optional description is included as visible text when
 * provided. Empty items array returns an empty string.
 *
 * @param items - Ordered list of {@link TimelineItem} events to render.
 * @param lang - Language code used for the section heading.
 * @returns HTML string for the timeline `<section>`, or empty string when items is empty.
 */
export function buildTimelineSection(
  items: ReadonlyArray<TimelineItem>,
  lang: LanguageCode
): string {
  if (items.length === 0) return '';
 
  const heading = escapeHTML(getLocalizedString(TIMELINE_HEADINGS, lang));
 
  const listItems = items
    .map((item) => {
      const safeDate = escapeHTML(item.date);
      const safeLabel = escapeHTML(item.label);
      const descPart = item.description
        ? `<span class="timeline-description">${escapeHTML(item.description)}</span>`
        : '';
      return (
        `<li class="timeline-item">` +
        `<span class="timeline-date">${safeDate}</span>` +
        `<span class="timeline-label">${safeLabel}</span>` +
        descPart +
        `</li>`
      );
    })
    .join('\n      ');
 
  return `<section class="timeline-section" aria-label="${heading}">
  <h2>${heading}</h2>
  <ol class="timeline-list" role="list">
      ${listItems}
  </ol>
</section>`;
}