Current section

48 Versions

Jump to

Compare versions

5 files changed
+150 additions
-182 deletions
  @@ -1,5 +1,9 @@
1 1 # Changelog
2 2
3 + ## 0.6.1
4 +
5 + Fixes reading the required state of input fields.
6 +
3 7 ## 0.6.0
4 8
5 9 Added support for forms created with [Ash Framework](https://www.ash-hq.org/). See [test/frameworks/ash/form_test.exs](https://github.com/ArthurClemens/primer_live/tree/development/test/frameworks/ash/form_test.exs) for an example.
  @@ -1,7 +1,7 @@
1 1 {<<"links">>,
2 2 [{<<"GitHub">>,<<"https://github.com/ArthurClemens/primer_live">>}]}.
3 3 {<<"name">>,<<"primer_live">>}.
4 - {<<"version">>,<<"0.6.0">>}.
4 + {<<"version">>,<<"0.6.1">>}.
5 5 {<<"description">>,
6 6 <<"An implementation of GitHub's Primer Design System for Phoenix LiveView.">>}.
7 7 {<<"app">>,<<"primer_live">>}.
unknownlib/component.ex
File is too large to be displayed (100 KB limit).
  @@ -53,74 +53,57 @@ defmodule PrimerLive.Helpers.FormHelpers do
53 53 %PrimerLive.FieldState{valid?: false, changeset: nil, message: nil, field_errors: [], caption: nil}
54 54
55 55 # If validation_message_fn returns a string it will be added to FieldState, regardless of the changeset action value:
56 - iex> PrimerLive.Helpers.FormHelpers.field_state(
56 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
57 + ...> update_changeset = %Ecto.Changeset{changeset |
58 + ...> action: :update,
59 + ...> changes: %{first_name: "annette"}
60 + ...> }
61 + ...> PrimerLive.Helpers.FormHelpers.field_state(
57 62 ...> %Phoenix.HTML.Form{
58 - ...> source: %Ecto.Changeset{
59 - ...> action: :update,
60 - ...> changes: %{first_name: "annette"},
61 - ...> errors: [],
62 - ...> data: nil,
63 - ...> valid?: true
64 - ...> },
63 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
64 + ...> source: update_changeset
65 65 ...> },
66 - ...> :first_name, fn _field_state -> "always" end, nil)
67 - %PrimerLive.FieldState{valid?: true, changeset: %Ecto.Changeset{action: :update, changes: %{first_name: "annette"}, data: nil, errors: [], valid?: true}, message: "always", field_errors: []}
66 + ...> :first_name,
67 + ...> fn _field_state -> "always" end,
68 + ...> nil
69 + ...> )
70 + %PrimerLive.FieldState{valid?: false, required?: true, changeset: update_changeset, message: "always", field_errors: ["can't be blank"]}
68 71
69 72 # If changeset action is :validate and no validation_message_fn is provided, the default field error is added to FieldState:
70 - iex> PrimerLive.Helpers.FormHelpers.field_state(
73 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
74 + ...> validate_changeset = %Ecto.Changeset{changeset |
75 + ...> action: :validate,
76 + ...> changes: %{first_name: "annette"}
77 + ...> }
78 + ...> PrimerLive.Helpers.FormHelpers.field_state(
71 79 ...> %Phoenix.HTML.Form{
72 - ...> source: %Ecto.Changeset{
73 - ...> action: :validate,
74 - ...> changes: %{},
75 - ...> errors: [first_name: {"can't be blank", [validation: :required]}],
76 - ...> data: nil,
77 - ...> valid?: true
78 - ...> },
80 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
81 + ...> source: validate_changeset
79 82 ...> },
80 - ...> :first_name, nil, nil)
81 - %PrimerLive.FieldState{required?: true, valid?: false, changeset: %Ecto.Changeset{action: :validate, changes: %{}, data: nil, errors: [first_name: {"can't be blank", [validation: :required]}], valid?: true}, message: "can't be blank", field_errors: ["can't be blank"]}
82 -
83 - # If changeset action is :update and no validation_message_fn is provided, no message is added to FieldState:
84 - iex> PrimerLive.Helpers.FormHelpers.field_state(
85 - ...> %Phoenix.HTML.Form{
86 - ...> source: %Ecto.Changeset{
87 - ...> action: :update,
88 - ...> changes: %{},
89 - ...> errors: [first_name: {"can't be blank", [validation: :required]}],
90 - ...> data: nil,
91 - ...> valid?: true,
92 - ...> },
93 - ...> },
94 - ...> :first_name, nil, nil)
95 - %PrimerLive.FieldState{required?: true, valid?: false, changeset: %Ecto.Changeset{action: :update, changes: %{}, data: nil, errors: [first_name: {"can't be blank", [validation: :required]}], valid?: true}, message: "can't be blank", field_errors: ["can't be blank"]}
96 -
97 - # Custom error message: if changeset action is :update and a validation_message_fn is provided, the resulting message is added to FieldState:
98 - iex> PrimerLive.Helpers.FormHelpers.field_state(
99 - ...> %Phoenix.HTML.Form{
100 - ...> source: %Ecto.Changeset{
101 - ...> action: :update,
102 - ...> changes: %{},
103 - ...> errors: [first_name: {"can't be blank", [validation: :required]}],
104 - ...> data: nil,
105 - ...> valid?: true,
106 - ...> },
107 - ...> },
108 - ...> :first_name, fn field_state -> if !field_state.valid?, do: "Please select your availability" end, nil)
109 - %PrimerLive.FieldState{required?: true, valid?: false, changeset: %Ecto.Changeset{action: :update, changes: %{}, data: nil, errors: [first_name: {"can't be blank", [validation: :required]}], valid?: true}, message: "Please select your availability", field_errors: ["can't be blank"]}
83 + ...> :first_name,
84 + ...> nil,
85 + ...> nil
86 + ...> )
87 + %PrimerLive.FieldState{valid?: false, required?: true, changeset: validate_changeset, message: "can't be blank", field_errors: ["can't be blank"]}
110 88
111 89 # Custom success message: if changeset action is :update and a validation_message_fn is provided, the resulting message is added to FieldState:
112 - iex> PrimerLive.Helpers.FormHelpers.field_state(
90 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
91 + ...> update_changeset = %Ecto.Changeset{changeset |
92 + ...> action: :update,
93 + ...> changes: %{first_name: "annette"},
94 + ...> errors: [],
95 + ...> valid?: true,
96 + ...> }
97 + ...> PrimerLive.Helpers.FormHelpers.field_state(
113 98 ...> %Phoenix.HTML.Form{
114 - ...> source: %Ecto.Changeset{
115 - ...> action: :update,
116 - ...> changes: %{first_name: "annette"},
117 - ...> errors: [],
118 - ...> data: nil,
119 - ...> valid?: true,
120 - ...> },
99 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
100 + ...> source: update_changeset,
121 101 ...> },
122 - ...> :first_name, fn field_state -> if field_state.valid?, do: "Great!" end, nil)
123 - %PrimerLive.FieldState{required?: false, valid?: true, changeset: %Ecto.Changeset{action: :update, changes: %{first_name: "annette"}, data: nil, errors: [], valid?: true}, message: "Great!", field_errors: []}
102 + ...> :first_name,
103 + ...> fn field_state -> if field_state.valid?, do: "Great!" end,
104 + ...> nil
105 + ...> )
106 + %PrimerLive.FieldState{valid?: true, required?: true, changeset: update_changeset, message: "Great!", field_errors: []}
124 107
125 108 # Caption: return a caption if changeset is nil
126 109 iex> PrimerLive.Helpers.FormHelpers.field_state(nil, nil, nil, fn _field_state -> "Caption" end)
  @@ -135,24 +118,28 @@ defmodule PrimerLive.Helpers.FormHelpers do
135 118 %PrimerLive.FieldState{required?: false, valid?: false, changeset: nil, message: nil, field_errors: [], caption: "Caption"}
136 119
137 120 # Caption: return a caption dependent on the state
138 - iex> PrimerLive.Helpers.FormHelpers.field_state(
121 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
122 + ...> validate_changeset = %Ecto.Changeset{changeset |
123 + ...> action: :validate,
124 + ...> changes: %{first_name: "annette"}
125 + ...> }
126 + ...> PrimerLive.Helpers.FormHelpers.field_state(
139 127 ...> %Phoenix.HTML.Form{
140 - ...> source: %Ecto.Changeset{
141 - ...> action: :validate,
142 - ...> changes: %{},
143 - ...> errors: [first_name: {"can't be blank", [validation: :required]}],
144 - ...> data: nil,
145 - ...> valid?: true,
146 - ...> },
128 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
129 + ...> source: validate_changeset
147 130 ...> },
148 - ...> :first_name, nil, fn field_state -> if !field_state.valid?, do: "Please select your availability" end)
149 - %PrimerLive.FieldState{caption: "Please select your availability", changeset: %Ecto.Changeset{action: :validate, changes: %{}, errors: [first_name: {"can't be blank", [validation: :required]}], data: nil, valid?: true}, field_errors: ["can't be blank"], ignore_errors?: false, message: "can't be blank", message_id: nil, required?: true, valid?: false}
131 + ...> :first_name,
132 + ...> nil,
133 + ...> fn field_state -> if !field_state.valid?, do: "Please select your availability" end
134 + ...> )
135 + %PrimerLive.FieldState{caption: "Please select your availability", changeset: validate_changeset, field_errors: ["can't be blank"], ignore_errors?: false, message: "can't be blank", message_id: nil, required?: true, valid?: false}
150 136 """
151 137 def field_state(maybe_form, field, validation_message_fn, caption_fn) do
152 138 form = get_form(maybe_form)
153 139 changeset = get_form_changeset(form)
154 140
155 141 get_field_state_for_changeset(
142 + form,
156 143 changeset,
157 144 %PrimerLive.FieldState{},
158 145 field,
  @@ -162,6 +149,7 @@ defmodule PrimerLive.Helpers.FormHelpers do
162 149 end
163 150
164 151 defp get_field_state_for_changeset(
152 + _form,
165 153 changeset,
166 154 field_state,
167 155 _field,
  @@ -174,6 +162,7 @@ defmodule PrimerLive.Helpers.FormHelpers do
174 162 end
175 163
176 164 defp get_field_state_for_changeset(
165 + form,
177 166 changeset,
178 167 field_state,
179 168 field,
  @@ -181,7 +170,7 @@ defmodule PrimerLive.Helpers.FormHelpers do
181 170 caption_fn
182 171 ) do
183 172 with field_errors <- get_field_errors(changeset, field),
184 - required? <- get_field_required(changeset, field),
173 + required? <- get_field_required(form, changeset, field),
185 174 valid? <- Enum.count(field_errors) == 0,
186 175 field_state <- %{
187 176 field_state
  @@ -237,36 +226,36 @@ defmodule PrimerLive.Helpers.FormHelpers do
237 226 @doc """
238 227 Extracts the form struct from either a form struct or a nested struct that contains a form.
239 228
240 - iex> PrimerLive.Helpers.FormHelpers.get_form(%Phoenix.HTML.Form{
241 - ...> source: %Ecto.Changeset{
242 - ...> action: :update,
243 - ...> changes: %{first_name: "annette"},
244 - ...> errors: [],
245 - ...> data: nil,
246 - ...> valid?: true
247 - ...> }
229 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
230 + ...> PrimerLive.Helpers.FormHelpers.get_form(%Phoenix.HTML.Form{
231 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
232 + ...> source: changeset
248 233 ...> })
249 - %Phoenix.HTML.Form{source: %Ecto.Changeset{action: :update, changes: %{first_name: "annette"}, errors: [], data: nil, valid?: true}, impl: nil, id: nil, name: nil, data: nil, hidden: [], params: %{}, errors: [], options: [], index: nil, action: nil}
234 + %Phoenix.HTML.Form{source: changeset, impl: Phoenix.HTML.FormData.Ecto.Changeset}
250 235
251 - iex> PrimerLive.Helpers.FormHelpers.get_form(%Phoenix.HTML.Form{
236 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
237 + ...> PrimerLive.Helpers.FormHelpers.get_form(%Phoenix.HTML.Form{
238 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
252 239 ...> source: %Phoenix.HTML.Form{
253 - ...> source: %Ecto.Changeset{
240 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
241 + ...> source: %Ecto.Changeset{changeset |
254 242 ...> action: :update,
255 - ...> changes: %{first_name: "annette"},
256 - ...> errors: [],
257 - ...> data: nil,
258 - ...> valid?: true
243 + ...> changes: %{first_name: "annette"}
259 244 ...> }
260 245 ...> }
261 246 ...> })
262 - %Phoenix.HTML.Form{source: %Ecto.Changeset{action: :update, changes: %{first_name: "annette"}, errors: [], data: nil, valid?: true}, impl: nil, id: nil, name: nil, data: nil, hidden: [], params: %{}, errors: [], options: [], index: nil, action: nil}
247 + %Phoenix.HTML.Form{source: %Ecto.Changeset{changeset | action: :update, changes: %{first_name: "annette"}}, impl: Phoenix.HTML.FormData.Ecto.Changeset}
263 248
264 249 iex> PrimerLive.Helpers.FormHelpers.get_form(%PrimerLive.Helpers.TestForm{
250 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
265 251 ...> source: %PrimerLive.Helpers.TestForm{
266 - ...> source: %Phoenix.HTML.Form{}
252 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
253 + ...> source: %Phoenix.HTML.Form{
254 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset
255 + ...> }
267 256 ...> }
268 257 ...> })
269 - %Phoenix.HTML.Form{source: nil, impl: nil, id: nil, name: nil, data: nil, hidden: [], params: %{}, errors: [], options: [], index: nil, action: nil}
258 + %Phoenix.HTML.Form{source: nil, impl: Phoenix.HTML.FormData.Ecto.Changeset, id: nil, name: nil, data: nil, hidden: [], params: %{}, errors: [], options: [], index: nil, action: nil}
270 259
271 260 iex> PrimerLive.Helpers.FormHelpers.get_form(:form)
272 261 nil
  @@ -292,19 +281,16 @@ defmodule PrimerLive.Helpers.FormHelpers do
292 281 @doc """
293 282 Gets the form name - either an atom or a string.
294 283
295 - iex> PrimerLive.Helpers.FormHelpers.get_form_name(%Phoenix.HTML.Form{
284 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
285 + ...> PrimerLive.Helpers.FormHelpers.get_form_name(%Phoenix.HTML.Form{
286 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
296 287 ...> name: :profile,
297 - ...> source: %Ecto.Changeset{
298 - ...> action: :update,
299 - ...> changes: %{first_name: "annette"},
300 - ...> errors: [],
301 - ...> data: nil,
302 - ...> valid?: true
303 - ...> }
288 + ...> source: changeset
304 289 ...> })
305 290 :profile
306 291
307 292 iex> PrimerLive.Helpers.FormHelpers.get_form_name(%PrimerLive.Helpers.TestForm{
293 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
308 294 ...> source: %PrimerLive.Helpers.TestForm{
309 295 ...> name: "test",
310 296 ...> source: %Phoenix.HTML.Form{
  @@ -389,54 +375,53 @@ defmodule PrimerLive.Helpers.FormHelpers do
389 375 @doc """
390 376 Returns all errors for a given field from a changeset.
391 377
392 - iex> PrimerLive.Helpers.FormHelpers.get_field_errors(
378 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
379 + ...> PrimerLive.Helpers.FormHelpers.get_field_errors(
393 380 ...> %Ecto.Changeset{
394 - ...> action: :update,
395 - ...> changes: %{},
396 - ...> errors: [],
397 - ...> data: nil,
398 - ...> valid?: true
399 - ...> }, :first_name)
381 + ...> changeset | action: :update, errors: []
382 + ...> },
383 + ...> :first_name
384 + ...> )
400 385 []
401 386
402 - iex> PrimerLive.Helpers.FormHelpers.get_field_errors(
387 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
388 + ...> PrimerLive.Helpers.FormHelpers.get_field_errors(
403 389 ...> %Ecto.Changeset{
404 - ...> action: :update,
405 - ...> changes: %{},
406 - ...> errors: [
407 - ...> first_name: {"can't be blank", [validation: :required]},
408 - ...> work_experience: {"invalid value", [validation: :required]}
409 - ...> ],
410 - ...> data: nil,
411 - ...> valid?: true
412 - ...> }, :first_name)
390 + ...> changeset | action: :update,
391 + ...> errors: [
392 + ...> first_name: {"can't be blank", [validation: :required]},
393 + ...> work_experience: {"invalid value", [validation: :required]}
394 + ...> ],
395 + ...> },
396 + ...> :first_name
397 + ...> )
413 398 ["can't be blank"]
414 399
415 - iex> PrimerLive.Helpers.FormHelpers.get_field_errors(
400 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
401 + ...> PrimerLive.Helpers.FormHelpers.get_field_errors(
416 402 ...> %Ecto.Changeset{
417 - ...> action: :update,
418 - ...> changes: %{},
419 - ...> errors: [
420 - ...> first_name: {"can't be blank", [validation: :required]},
421 - ...> work_experience: {"invalid value", [validation: :required]}
422 - ...> ],
423 - ...> data: nil,
424 - ...> valid?: true
425 - ...> }, :work_experience)
426 - ["invalid value"]
427 -
428 - iex> PrimerLive.Helpers.FormHelpers.get_field_errors(
429 - ...> %Ecto.Changeset{
430 - ...> action: :update,
431 - ...> changes: %{},
432 - ...> errors: [
433 - ...> first_name: {"should be at most %{count} character(s)",
403 + ...> changeset | action: :update,
404 + ...> errors: [
405 + ...> first_name: {"should be at most %{count} character(s)",
434 406 ...> [count: 255, validation: :length, kind: :max, type: :string]}
435 - ...> ],
436 - ...> data: nil,
437 - ...> valid?: true
438 - ...> }, :first_name)
407 + ...> ],
408 + ...> },
409 + ...> :first_name
410 + ...> )
439 411 ["should be at most 255 character(s)"]
412 +
413 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
414 + ...> PrimerLive.Helpers.FormHelpers.get_field_errors(
415 + ...> %Ecto.Changeset{
416 + ...> changeset | action: :update,
417 + ...> errors: [
418 + ...> first_name: {"can't be blank", [validation: :required]},
419 + ...> work_experience: {"invalid value", [validation: :required]}
420 + ...> ],
421 + ...> },
422 + ...> :work_experience
423 + ...> )
424 + ["invalid value"]
440 425 """
441 426 def get_field_errors(%Ecto.Changeset{} = changeset, field) do
442 427 changeset.errors
  @@ -488,75 +473,54 @@ defmodule PrimerLive.Helpers.FormHelpers do
488 473 @doc """
489 474 Returns the required state for a given field from a changeset.
490 475
491 - iex> PrimerLive.Helpers.FormHelpers.get_field_required(
492 - ...> %Ecto.Changeset{
493 - ...> action: :update,
494 - ...> changes: %{},
495 - ...> errors: [],
476 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
477 + ...> PrimerLive.Helpers.FormHelpers.get_field_required(
478 + ...> %Phoenix.HTML.Form{
479 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
496 480 ...> data: nil,
497 - ...> valid?: true
498 - ...> }, :first_name)
499 - false
500 -
501 - iex> PrimerLive.Helpers.FormHelpers.get_field_required(
502 - ...> %Ecto.Changeset{
503 - ...> action: :update,
504 - ...> changes: %{},
505 - ...> errors: [
506 - ...> first_name: {"can't be blank", [validation: :required]},
507 - ...> work_experience: {"invalid value", [validation: :required]}
508 - ...> ],
509 - ...> data: nil,
510 - ...> valid?: true
511 - ...> }, :first_name)
481 + ...> source: changeset
482 + ...> },
483 + ...> changeset,
484 + ...> :first_name
485 + ...> )
512 486 true
513 487
514 - iex> PrimerLive.Helpers.FormHelpers.get_field_required(
515 - ...> %Ecto.Changeset{
516 - ...> action: :update,
517 - ...> changes: %{},
518 - ...> errors: [
519 - ...> first_name: {"should be at most %{count} character(s)",
520 - ...> [count: 255, validation: :length, kind: :max, type: :string]}
521 - ...> ],
488 + iex> changeset = PrimerLive.TestHelpers.Repo.Users.init()
489 + ...> PrimerLive.Helpers.FormHelpers.get_field_required(
490 + ...> %Phoenix.HTML.Form{
491 + ...> impl: Phoenix.HTML.FormData.Ecto.Changeset,
522 492 ...> data: nil,
523 - ...> valid?: true
524 - ...> }, :first_name)
493 + ...> source: changeset
494 + ...> },
495 + ...> changeset,
496 + ...> :work_experience
497 + ...> )
525 498 false
526 499 """
527 - def get_field_required(%Ecto.Changeset{} = changeset, field) do
528 - changeset.errors
529 - |> Enum.filter(fn {error_field, _content} -> error_field == field end)
530 - |> Enum.map(fn {_error_field, {_content, details}} -> details end)
531 - |> Enum.map(fn
532 - [validation: :required] -> true
533 - _ -> false
534 - end)
535 - |> Enum.any?(fn value -> !!value end)
500 + def get_field_required(%Phoenix.HTML.Form{} = form, %Ecto.Changeset{} = _changeset, field) do
501 + input_validations(form, field)[:required] == true
536 502 end
537 503
538 504 # Handle other types of changesets
539 - def get_field_required(%_{} = maybe_changeset, field)
505 + def get_field_required(form, %_{} = maybe_changeset, field)
540 506 when not is_nil(maybe_changeset.__struct__) do
541 507 changeset_type = "#{maybe_changeset.__struct__}"
542 508
543 509 cond do
544 510 changeset_type |> String.contains?("Ash.Changeset") ->
545 511 ash_changeset = maybe_changeset
546 - _get_ash_framework_field_required(ash_changeset, field)
512 + get_ash_framework_field_required(form, ash_changeset, field)
547 513
548 514 true ->
549 515 false
550 516 end
551 517 end
552 518
553 - def get_field_required(_, _), do: []
519 + def get_field_required(_, _), do: false
554 520
555 521 # Handle other types of changesets: Ash Framework
556 - def _get_ash_framework_field_required(ash_changeset, field) do
557 - ash_changeset.errors
558 - |> Enum.filter(fn ash_error -> ash_error.field == field end)
559 - |> Enum.count() > 0
522 + defp get_ash_framework_field_required(form, ash_changeset, field) do
523 + Phoenix.HTML.FormData.input_validations(form, ash_changeset, field)[:required] == true
560 524 end
561 525
562 526 @doc """
  @@ -4,7 +4,7 @@ defmodule PrimerLive.MixProject do
4 4 def project do
5 5 [
6 6 app: :primer_live,
7 - version: "0.6.0",
7 + version: "0.6.1",
8 8 homepage_url: "https://github.com/ArthurClemens/primer_live",
9 9 description: description(),
10 10 package: package(),