Support nil fields in forms (#2931)

This commit is contained in:
José Valim 2025-02-12 13:12:03 +01:00 committed by GitHub
parent 00f2fdf1d1
commit e1277fa62a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 14 additions and 9 deletions

View file

@ -57,7 +57,7 @@ defmodule Livebook.Notebook.Cell do
end end
def find_inputs_in_output({_idx, %{type: :control, attrs: %{type: :form, fields: fields}}}) do def find_inputs_in_output({_idx, %{type: :control, attrs: %{type: :form, fields: fields}}}) do
Keyword.values(fields) for {_field, input} <- fields, input != nil, do: input
end end
def find_inputs_in_output({_idx, output}) when output.type in [:frame, :tabs, :grid] do def find_inputs_in_output({_idx, output}) when output.type in [:frame, :tabs, :grid] do

View file

@ -437,7 +437,7 @@ defprotocol Livebook.Runtime do
} }
| %{ | %{
type: :form, type: :form,
fields: list({field :: atom(), input_output()}), fields: list({field :: atom(), input_output() | nil}),
submit: String.t() | nil, submit: String.t() | nil,
# Currently we always use true, but we can support # Currently we always use true, but we can support
# other tracking modes in the future # other tracking modes in the future

View file

@ -3311,8 +3311,9 @@ defmodule Livebook.Session do
:form -> :form ->
Map.update!(attrs, :fields, fn fields -> Map.update!(attrs, :fields, fn fields ->
Enum.map(fields, fn {field, attrs} -> Enum.map(fields, fn
{field, normalize_runtime_output({:input, attrs})} {field, nil} -> {field, nil}
{field, attrs} -> {field, normalize_runtime_output({:input, attrs})}
end) end)
end) end)

View file

@ -13,8 +13,9 @@ defmodule LivebookWeb.Output.ControlFormComponent do
socket = assign(socket, assigns) socket = assign(socket, assigns)
data = data =
Map.new(assigns.control.attrs.fields, fn {field, input} -> Map.new(assigns.control.attrs.fields, fn
{field, assigns.input_views[input.id].value} {field, nil} -> {field, nil}
{field, input} -> {field, assigns.input_views[input.id].value}
end) end)
if prev_data != nil and data != prev_data do if prev_data != nil and data != prev_data do
@ -38,6 +39,7 @@ defmodule LivebookWeb.Output.ControlFormComponent do
<div class="flex flex-col space-y-3"> <div class="flex flex-col space-y-3">
<.live_component <.live_component
:for={{_field, input} <- @control.attrs.fields} :for={{_field, input} <- @control.attrs.fields}
:if={input}
module={LivebookWeb.Output.InputComponent} module={LivebookWeb.Output.InputComponent}
id={"#{@id}-#{input.id}"} id={"#{@id}-#{input.id}"}
input={input} input={input}
@ -75,7 +77,7 @@ defmodule LivebookWeb.Output.ControlFormComponent do
defp reset_inputs(socket) do defp reset_inputs(socket) do
values = values =
for {field, input} <- socket.assigns.control.attrs.fields, for {field, input} <- socket.assigns.control.attrs.fields,
field in socket.assigns.control.attrs.reset_on_submit, input != nil and field in socket.assigns.control.attrs.reset_on_submit,
do: {input.id, input.attrs.default} do: {input.id, input.attrs.default}
send(self(), {:set_input_values, values, true}) send(self(), {:set_input_values, values, true})

View file

@ -548,7 +548,8 @@ defmodule LivebookWeb.SessionLiveTest do
id: "input1", id: "input1",
destination: test, destination: test,
attrs: %{type: :text, default: "initial", label: "Name", debounce: :blur} attrs: %{type: :text, default: "initial", label: "Name", debounce: :blur}
} },
value: nil
], ],
submit: "Send", submit: "Send",
report_changes: %{}, report_changes: %{},
@ -575,7 +576,8 @@ defmodule LivebookWeb.SessionLiveTest do
|> element(~s/[data-el-outputs-container] button/, "Send") |> element(~s/[data-el-outputs-container] button/, "Send")
|> render_click() |> render_click()
assert_receive {:event, "control_ref1", %{data: %{name: "sherlock"}, type: :submit}} assert_receive {:event, "control_ref1",
%{data: %{name: "sherlock", value: nil}, type: :submit}}
end end
test "file input", %{conn: conn, session: session, test: test} do test "file input", %{conn: conn, session: session, test: test} do