PhoenixTestDatastar.Signals
(PhoenixTestDatastar v0.0.2)
Copy Markdown
Handles Datastar signal extraction from HTML and signal state management.
Datastar signals are declared in HTML via attributes:
data-signals:count="0"→ individual signal named 'count' with value 0data-signals:name="'hello'"→ string value (JS-style single quotes)data-signals="{foo: 1, bar: 2}"→ object of signalsdata-signals:_csrfToken="'abc123'"→ underscore prefix = client-only signal
Summary
Functions
Merges new signals into existing state.
Extracts all Datastar signals from HTML.
Converts a kebab-case string to camelCase.
Converts a JavaScript-like expression to valid JSON.
Parses a JavaScript-like value string into an Elixir term.
Filters out client-only signals (prefixed with underscore).
Encodes signals as datastar query parameter, excluding client-only signals.
Encodes signals as JSON for POST body, excluding client-only signals.
Functions
Merges new signals into existing state.
Options
:only_if_missing- When true, only add signals not already present (default: false)
When a value is nil, that signal is removed from the state.
Examples
iex> PhoenixTestDatastar.Signals.apply_patch(%{"count" => 1}, %{"name" => "test"})
%{"count" => 1, "name" => "test"}
iex> PhoenixTestDatastar.Signals.apply_patch(%{"count" => 1}, %{"count" => 2}, only_if_missing: true)
%{"count" => 1}
iex> PhoenixTestDatastar.Signals.apply_patch(%{"count" => 1}, %{"count" => nil})
%{}
Extracts all Datastar signals from HTML.
Parses the HTML, finds all data-signals and data-signals:* attributes,
and returns a merged map of all signals.
Examples
iex> html = ~s(<div data-signals:count="0" data-signals:name="'hello'"></div>)
iex> PhoenixTestDatastar.Signals.extract_from_html(html)
%{"count" => 0, "name" => "hello"}
iex> html = ~s(<div data-signals="{foo: 1, bar: 2}"></div>)
iex> PhoenixTestDatastar.Signals.extract_from_html(html)
%{"foo" => 1, "bar" => 2}
Converts a kebab-case string to camelCase.
HTML attributes are case-insensitive, so Datastar uses kebab-case in
attribute suffixes (e.g., data-signals:_csrf-token) and converts to
camelCase signal names (_csrfToken) on the client.
Only converts dashes followed by a letter (actual kebab-case). Dashes between hex digits (e.g., UUIDs) are preserved.
Handles leading underscores (preserved) and already-camelCase input.
Examples
iex> PhoenixTestDatastar.Signals.kebab_to_camel("_csrf-token")
"_csrfToken"
iex> PhoenixTestDatastar.Signals.kebab_to_camel("my-signal-name")
"mySignalName"
iex> PhoenixTestDatastar.Signals.kebab_to_camel("count")
"count"
iex> PhoenixTestDatastar.Signals.kebab_to_camel("_dstar_module")
"_dstar_module"
iex> PhoenixTestDatastar.Signals.kebab_to_camel("item_da576dd7-7f1f-4917-8dfa-05fe84a95779")
"item_da576dd7-7f1f-4917-8dfa-05fe84a95779"
Converts a JavaScript-like expression to valid JSON.
- Converts single quotes to double quotes
- Adds quotes around unquoted object keys
- Handles numbers, booleans, null, arrays, and objects
Examples
iex> PhoenixTestDatastar.Signals.normalize_to_json("'hello'")
~s("hello")
iex> PhoenixTestDatastar.Signals.normalize_to_json("{foo: 1, bar: 2}")
~s({"foo": 1,"bar": 2})
Parses a JavaScript-like value string into an Elixir term.
Handles JS-style syntax including single quotes, unquoted object keys, and standard JSON primitives.
Examples
iex> PhoenixTestDatastar.Signals.parse_js_value("0")
0
iex> PhoenixTestDatastar.Signals.parse_js_value("'hello'")
"hello"
iex> PhoenixTestDatastar.Signals.parse_js_value("true")
true
iex> PhoenixTestDatastar.Signals.parse_js_value("{foo: 1}")
%{"foo" => 1}
Filters out client-only signals (prefixed with underscore).
Examples
iex> PhoenixTestDatastar.Signals.public_signals(%{"count" => 1, "_csrfToken" => "abc"})
%{"count" => 1}
Encodes signals as datastar query parameter, excluding client-only signals.
Returns a query string in the format: datastar=<json>
Examples
iex> PhoenixTestDatastar.Signals.to_get_query(%{"count" => 1, "name" => "test"})
"datastar=%7B%22count%22%3A1%2C%22name%22%3A%22test%22%7D"
Encodes signals as JSON for POST body, excluding client-only signals.
Client-only signals (prefixed with underscore) are excluded.
Examples
iex> PhoenixTestDatastar.Signals.to_post_body(%{"count" => 1, "_csrfToken" => "abc"})
~s({"count":1})