Skip to content

Commit 1e7ec07

Browse files
committed
Add a label to toggle buttons to match odyssey labels more easily
1 parent 5865f3e commit 1e7ec07

6 files changed

Lines changed: 104 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2025-11-07]
9+
10+
### Added
11+
- Added optional `label` attribute to `odyssey_toggle_button` and `odyssey_toggle_button_input` components
12+
- When a label is provided, the toggle button is automatically wrapped in a fieldset with proper label styling
13+
- Follows the same pattern as Phoenix core input components for consistency
14+
- Maintains 100% backward compatibility - existing usage without labels continues to work unchanged
15+
- Works seamlessly with both standalone and form-integrated usage
16+
817
## [2025-11-04]
918

1019
### Added

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ In your `assets/css/app.css` file, add the Odyssey assets to your Tailwind sourc
189189
@source "../css";
190190
@source "../js";
191191
@source "../../lib/your_app_web";
192+
@import "../../deps/peek_app_sdk/assets/odyssey/odyssey_web_components.css";
192193
@source "../../deps/peek_app_sdk/assets"; /* Add this line */
193194
```
194195

demo/lib/demo_web/live/odyssey_showcase_live.ex

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ defmodule DemoWeb.OdysseyShowcaseLive do
2323
|> assign(:toggle_selection, "Minutes")
2424
|> assign(:icon_toggle_selection, "Minutes")
2525
|> assign(:status_selection, "Active")
26+
|> assign(:size_selection, "Medium")
2627
|> assign(:channel_selection, :email)
2728
|> assign(:form_data, form_data)
2829
|> assign(:form, to_form(form_data, as: "form"))
@@ -72,6 +73,12 @@ defmodule DemoWeb.OdysseyShowcaseLive do
7273
{:noreply, socket}
7374
end
7475

76+
@impl true
77+
def handle_event("change_size", %{"value" => option}, socket) do
78+
socket = assign(socket, :size_selection, option)
79+
{:noreply, socket}
80+
end
81+
7582
@impl true
7683
def handle_event("change_channel", %{"value" => option}, socket) do
7784
socket = assign(socket, :channel_selection, option)
@@ -199,6 +206,17 @@ defmodule DemoWeb.OdysseyShowcaseLive do
199206
<p class="text-sm text-gray-600 mt-2">Selected: <strong>{@status_selection}</strong></p>
200207
</div>
201208
209+
<div>
210+
<h3 class="text-lg font-medium mb-2">Toggle Button with Label</h3>
211+
<.odyssey_toggle_button
212+
options={["Small", "Medium", "Large"]}
213+
selected={@size_selection}
214+
on_change="change_size"
215+
label="Size"
216+
/>
217+
<p class="text-sm text-gray-600 mt-2">Selected: <strong>{@size_selection}</strong></p>
218+
</div>
219+
202220
<div>
203221
<h3 class="text-lg font-medium mb-2">Toggle Buttons with Icons</h3>
204222
<.odyssey_toggle_button

lib/peek_app_sdk/ui/odyssey/toggle_button.ex

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ defmodule PeekAppSDK.UI.Odyssey.ToggleButton do
4747
attr(:selected, :string, required: false, doc: "the currently selected option (not needed when using field)")
4848
attr(:on_change, :string, required: false, doc: "event name to fire on change (not needed when using field)")
4949
attr(:field, Phoenix.HTML.FormField, required: false, doc: "optional form field for automatic form integration")
50+
attr(:label, :string, required: false, doc: "optional label to wrap the toggle button in a fieldset")
5051
attr(:phx_target, :any, required: false, doc: "optional phx-target for LiveComponent integration")
5152
attr(:rest, :global)
5253

@@ -66,8 +67,27 @@ defmodule PeekAppSDK.UI.Odyssey.ToggleButton do
6667

6768
# Standalone mode - regular component without form integration
6869
def odyssey_toggle_button(assigns) do
69-
assigns = assign_new(assigns, :phx_target, fn -> nil end)
70+
assigns =
71+
assigns
72+
|> assign_new(:phx_target, fn -> nil end)
73+
|> assign_new(:label, fn -> nil end)
74+
75+
~H"""
76+
<%= if @label do %>
77+
<fieldset class="fieldset mb-2">
78+
<label>
79+
<span class="label mb-1">{@label}</span>
80+
<.do_odyssey_toggle_button {assigns} />
81+
</label>
82+
</fieldset>
83+
<% else %>
84+
<.do_odyssey_toggle_button {assigns} />
85+
<% end %>
86+
"""
87+
end
7088

89+
# Private function component for the button group to avoid duplication
90+
defp do_odyssey_toggle_button(assigns) do
7191
~H"""
7292
<div class="inline-flex rounded-lg" role="group">
7393
<button

lib/peek_app_sdk/ui/odyssey/toggle_button_input_component.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ defmodule PeekAppSDK.UI.Odyssey.ToggleButtonInputComponent do
6969
selected={@selected}
7070
on_change="odyssey_toggle_button_change"
7171
phx_target={@myself}
72+
label={assigns[:label]}
7273
{@rest}
7374
/>
7475
</div>

test/peek_app_sdk/ui/odyssey_test.exs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,59 @@ defmodule PeekAppSDK.UI.OdysseyTest do
127127
# The Hours button should have the selected styling
128128
assert html =~ "bg-gray-50 text-blue-600"
129129
end
130+
131+
test "renders without fieldset when no label is provided" do
132+
html =
133+
render_component(&PeekAppSDK.UI.Odyssey.odyssey_toggle_button/1, %{
134+
options: ["Minutes", "Hours", "Days"],
135+
selected: "Minutes",
136+
on_change: "change_time_unit"
137+
})
138+
139+
refute html =~ "<fieldset"
140+
refute html =~ "class=\"fieldset mb-2\""
141+
assert html =~ "class=\"inline-flex rounded-lg\""
142+
end
143+
144+
test "renders with fieldset and label when label is provided" do
145+
html =
146+
render_component(&PeekAppSDK.UI.Odyssey.odyssey_toggle_button/1, %{
147+
options: ["Minutes", "Hours", "Days"],
148+
selected: "Minutes",
149+
on_change: "change_time_unit",
150+
label: "Time Unit"
151+
})
152+
153+
assert html =~ "<fieldset"
154+
assert html =~ "class=\"fieldset mb-2\""
155+
assert html =~ "<label>"
156+
assert html =~ "<span class=\"label mb-1\">Time Unit</span>"
157+
assert html =~ "class=\"inline-flex rounded-lg\""
158+
end
159+
160+
test "form integration passes through label to toggle_button component" do
161+
form_data = %{"channel" => :email}
162+
form = to_form(form_data, as: "form")
163+
164+
html =
165+
render_component(&PeekAppSDK.UI.Odyssey.odyssey_toggle_button/1, %{
166+
field: form[:channel],
167+
label: "Communication Channel",
168+
options: [
169+
%{label: "Email", value: :email},
170+
%{label: "Text Message", value: :sms}
171+
]
172+
})
173+
174+
assert html =~ "<fieldset"
175+
assert html =~ "class=\"fieldset mb-2\""
176+
assert html =~ "<label>"
177+
assert html =~ "<span class=\"label mb-1\">Communication Channel</span>"
178+
assert html =~ "Email"
179+
assert html =~ "Text Message"
180+
# Should have hidden input for form integration
181+
assert html =~ ~r/type="hidden"/
182+
assert html =~ ~r/name="form\[channel\]"/
183+
end
130184
end
131185
end

0 commit comments

Comments
 (0)