From dd1133d7cc6ed269581a566097780a4d7eac3b83 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 17 May 2026 09:38:10 +0200 Subject: [PATCH] test(17-01): add failing test + view model + message bubble CSS (RED) - Append .message-row/.message-own/.message-other/.message-bubble/.message-meta CSS classes to app.css - Create discussion_view.go with DiscussionMessageView, DiscussionTabData, NewDiscussionTabData - Create discussion_view_test.go with TestChatMainContentRendersBubbleClasses (RED: compile error expected) --- go-backend/internal/web/ui/app.css | 52 +++++++++++++++++++ .../internal/web/views/discussion_view.go | 47 +++++++++++++++++ .../web/views/discussion_view_test.go | 28 ++++++++++ 3 files changed, 127 insertions(+) create mode 100644 go-backend/internal/web/views/discussion_view.go create mode 100644 go-backend/internal/web/views/discussion_view_test.go diff --git a/go-backend/internal/web/ui/app.css b/go-backend/internal/web/ui/app.css index 9056499..dc93167 100644 --- a/go-backend/internal/web/ui/app.css +++ b/go-backend/internal/web/ui/app.css @@ -1894,3 +1894,55 @@ td.text-right .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-danger padding: 1.25rem; } } + +/* ── Message bubbles ── */ + +.message-row { + padding: 0.75rem 1rem; +} + +.message-own { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.message-other { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.message-bubble { + border: 1px solid var(--color-border-subtle); + border-radius: 0.25rem 0.75rem 0.75rem 0.75rem; + max-width: 70%; + padding: 0.75rem 1rem; + white-space: pre-wrap; + word-break: break-word; +} + +.message-own .message-bubble { + background: rgba(128, 78, 236, 0.10); + border-radius: 0.75rem 0.75rem 0.25rem 0.75rem; +} + +.message-other .message-bubble { + background: var(--color-surface-default); +} + +.message-meta { + display: flex; + gap: 0.5rem; + margin-bottom: 0.25rem; + font-size: 0.875rem; + color: var(--color-text-muted); +} + +.message-own .message-meta { + justify-content: flex-end; +} + +.message-meta .message-author { + font-weight: 600; +} diff --git a/go-backend/internal/web/views/discussion_view.go b/go-backend/internal/web/views/discussion_view.go new file mode 100644 index 0000000..5afe346 --- /dev/null +++ b/go-backend/internal/web/views/discussion_view.go @@ -0,0 +1,47 @@ +package views + +// DiscussionMessageView holds the data for a single chat message row. +type DiscussionMessageView struct { + Author string + Timestamp string + Body string + IsOwn bool +} + +// DiscussionTabData holds the full data set for the discussion tab. +type DiscussionTabData struct { + Messages []DiscussionMessageView +} + +// NewDiscussionTabData returns a DiscussionTabData populated with hardcoded demo +// messages that alternate IsOwn values for visual verification of bubble styles. +func NewDiscussionTabData() DiscussionTabData { + return DiscussionTabData{ + Messages: []DiscussionMessageView{ + { + Author: "you@xtablo.com", + Timestamp: "09:14", + Body: "Hey, any update on the design review?", + IsOwn: true, + }, + { + Author: "other@xtablo.com", + Timestamp: "09:17", + Body: "Just finished — sharing the file in the tablo now.", + IsOwn: false, + }, + { + Author: "you@xtablo.com", + Timestamp: "09:19", + Body: "Perfect, I'll take a look this afternoon.", + IsOwn: true, + }, + { + Author: "other@xtablo.com", + Timestamp: "09:22", + Body: "Sounds good. Let me know if you need changes.", + IsOwn: false, + }, + }, + } +} diff --git a/go-backend/internal/web/views/discussion_view_test.go b/go-backend/internal/web/views/discussion_view_test.go new file mode 100644 index 0000000..e39de9b --- /dev/null +++ b/go-backend/internal/web/views/discussion_view_test.go @@ -0,0 +1,28 @@ +package views + +import ( + "strings" + "testing" +) + +func TestChatMainContentRendersBubbleClasses(t *testing.T) { + data := DiscussionTabData{ + Messages: []DiscussionMessageView{ + {Author: "you@xtablo.com", Timestamp: "09:14", Body: "Hello", IsOwn: true}, + {Author: "other@xtablo.com", Timestamp: "09:17", Body: "Hi there", IsOwn: false}, + }, + } + + html := renderViewToString(t, ChatMainContent(data)) + + for _, want := range []string{ + `class="ui-card"`, + `message-own`, + `message-other`, + `message-bubble`, + } { + if !strings.Contains(html, want) { + t.Fatalf("expected %q in rendered HTML, got:\n%s", want, html) + } + } +}