Files
audi-app/design-system/scripts/smoke.mjs
T
tobias e1d570992e Initialer Import: HA-Panel, Design-System, Companion-App
Drei zusammengehörige Teile in einem Repository:

- homeassistant/  Das fertige, im Einsatz befindliche Home-Assistant-Panel
  (panel_custom Custom Element + pyscript-Backend). Echte Fahrzeug- und
  Personendaten (fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl,
  Tankbelege) bleiben per .gitignore außen vor; die anonymisierte Vorlage
  fahrzeugprofil.example.json ist mit dabei.

- design-system/  Eigenständige React-Komponentenbibliothek (@audi-dash/ui),
  die die visuelle Sprache des Panels nachbildet - ohne Audi-Markenzeichen
  und ohne die lizenzierte Hausschrift. Dient als Grundlage für Claude
  Design. War bis hierher ein eigenes Repository und ist in dieses
  eingeschmolzen worden.

- companion-app/  Datenschicht der neuen App DataMetric360 (iOS/Android via
  Capacitor, zusätzlich als Iframe im HA-Dashboard). Noch ohne Oberfläche:
  REST- und WebSocket-Zugriff auf Home Assistant plus Warteschlange für
  Änderungen ohne Netz. Ersetzt das eingespritzte hass-Objekt, das nur
  innerhalb des HA-Frontends existiert.

Dazu die Projektdokumentation: SPECIFICATION.md (Ist-Stand des Panels),
COMPANION_APP_ARCHITECTURE.md (Architekturentscheidungen der neuen App),
AUDIT_2026-08-10.md, DESIGN_BRIEF_DATAMETRIC360.md und der ursprüngliche
Bauauftrag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 00:20:05 +02:00

72 lines
2.5 KiB
JavaScript

import * as React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import * as UI from "../dist/index.js";
const h = React.createElement;
const noop = () => {};
const cases = [
{ name: "Pill", el: h(UI.Pill, {}, "Text") },
{ name: "IconButton", el: h(UI.IconButton, { "aria-label": "x" }, "*") },
{ name: "Fig", el: h(UI.Fig, { value: 42, unit: "km" }) },
{ name: "StatusRow", el: h(UI.StatusRow, { status: "ok", title: "Titel", subtitle: "Sub" }) },
{ name: "ActionButton", el: h(UI.ActionButton, {}, "Aktion") },
{ name: "Switch", el: h(UI.Switch, { checked: true, onChange: noop }) },
{
name: "Seg",
el: h(UI.Seg, {
options: [{ value: "a", label: "A" }, { value: "b", label: "B" }],
value: "a",
onChange: noop,
}),
},
{
name: "RowList",
el: h(UI.RowList, { items: [{ key: 1, label: "L", value: "V", subValue: "SV" }] }),
},
{ name: "LeafRow", el: h(UI.LeafRow, { title: "T", value: "V" }) },
{ name: "Tile", el: h(UI.Tile, {}, "Inhalt") },
{ name: "Tile (button)", el: h(UI.Tile, { variant: "button", chevron: true, onClick: noop }, "Klick") },
{ name: "Feld", el: h(UI.Feld, { label: "L", value: "V" }) },
{ name: "ProgressRing", el: h(UI.ProgressRing, { percent: 42, unit: "%" }) },
{ name: "ProgressBar (single)", el: h(UI.ProgressBar, { percent: 50 }) },
{
name: "ProgressBar (segments)",
el: h(UI.ProgressBar, { segments: [{ value: 3, label: "A" }, { value: 1, label: "B" }] }),
},
{
name: "StatGrid",
el: h(UI.StatGrid, { items: [{ value: 1, caption: "C" }, { value: 2, caption: "C2" }] }),
},
{ name: "Accordion", el: h(UI.Accordion, { title: "T", defaultOpen: true }, "Body") },
{ name: "SwipeRow", el: h(UI.SwipeRow, { onDelete: noop }, "Zeile") },
{
name: "TabBar",
el: h(UI.TabBar, {
items: [{ key: "a", label: "A", icon: h("svg") }],
activeKey: "a",
onChange: noop,
}),
},
{
name: "Popup",
el: h(UI.Popup, { open: true, onClose: noop }, h(UI.PopupMenuItem, {}, "Item")),
},
{ name: "ImagePlaceholder", el: h(UI.ImagePlaceholder, { alt: "a", label: "Label" }) },
];
let failed = 0;
for (const c of cases) {
try {
const html = renderToStaticMarkup(c.el);
if (!html || html.length === 0) throw new Error("empty output");
console.log(`PASS ${c.name}`);
} catch (err) {
failed++;
console.log(`FAIL ${c.name}: ${err.message}`);
}
}
console.log(`\n${cases.length - failed}/${cases.length} passed`);
if (failed > 0) process.exit(1);