From 0386a386de483c905404554183c1f73a7755bc6f Mon Sep 17 00:00:00 2001 From: karimsemmoud Date: Wed, 10 Jun 2026 18:58:18 +0700 Subject: [PATCH] fix(file-upload): hidden-input a11y matching zagjs attributes --- e2e/test/e2e_web/doc_routes_a11y_test.exs | 6 ++--- e2e/test/support/model.ex | 33 +++++++++++++++++++++++ lib/components/file_upload/anatomy.ex | 2 ++ lib/components/file_upload/connect.ex | 5 ++++ test/components/file_upload_test.exs | 3 +++ 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/e2e/test/e2e_web/doc_routes_a11y_test.exs b/e2e/test/e2e_web/doc_routes_a11y_test.exs index a03989e4b..b225b45ab 100644 --- a/e2e/test/e2e_web/doc_routes_a11y_test.exs +++ b/e2e/test/e2e_web/doc_routes_a11y_test.exs @@ -6,7 +6,6 @@ defmodule E2eWeb.DocRoutesA11yTest do import Wallaby.Query - alias E2eWeb.AccordionModel, as: Accordion alias E2eWeb.SiteModel for {path, ready} <- E2eWeb.DocA11yRoutes.all() do @@ -20,12 +19,13 @@ defmodule E2eWeb.DocRoutesA11yTest do session |> SiteModel.visit_ready(@path, ready_query) |> then(fn sess -> - if String.contains?(@path, "/accordion/") do - Accordion.wait_root_no_loading(sess, @ready) + if SiteModel.doc_live_page?(@path) do + SiteModel.prepare_live_form(sess) else sess end end) + |> SiteModel.wait_doc_page_interactive(@ready) SiteModel.check_accessibility(session, filter: E2eWeb.A11yDocPageFilter) end diff --git a/e2e/test/support/model.ex b/e2e/test/support/model.ex index 25402c13e..2710786ea 100644 --- a/e2e/test/support/model.ex +++ b/e2e/test/support/model.ex @@ -268,6 +268,39 @@ defmodule E2eWeb.Model do ) end + def wait_doc_page_interactive(session, id_selector, opts \\ []) + when is_binary(id_selector) do + unless String.match?(id_selector, ~r/^#[a-zA-Z0-9_-]+$/) do + raise ArgumentError, "expected #id selector, got: #{id_selector}" + end + + timeout = Keyword.get(opts, :timeout) + + for q <- [ + css(~s|#{id_selector}[data-loading]|, count: 0, visible: :any), + css(~s|#{id_selector} [phx-hook][data-loading]|, count: 0, visible: :any) + ] do + case timeout do + nil -> + assert_has(session, q) + + max_ms when is_integer(max_ms) and max_ms > 0 -> + wait_for_has(session, q, timeout: max_ms) + end + end + + session + end + + def doc_live_page?(path) when is_binary(path) do + String.contains?(path, "/events") or + String.contains?(path, "/api") or + String.contains?(path, "/patterns") or + String.contains?(path, "/playground") or + String.contains?(path, "live-form") or + String.contains?(path, "-live/") + end + def prepare_live_form(session) do E2eWeb.Model.with_layout_toast_ready( session, diff --git a/lib/components/file_upload/anatomy.ex b/lib/components/file_upload/anatomy.ex index 30837b8ab..23b3ac677 100644 --- a/lib/components/file_upload/anatomy.ex +++ b/lib/components/file_upload/anatomy.ex @@ -136,6 +136,8 @@ defmodule Corex.FileUpload.Anatomy do "capture", "webkitdirectory", "aria-hidden", + "ariaHidden", + "tabindex", "tabIndex", "style", "data-disabled", diff --git a/lib/components/file_upload/connect.ex b/lib/components/file_upload/connect.ex index ab8bbf5b0..fbb2d1015 100644 --- a/lib/components/file_upload/connect.ex +++ b/lib/components/file_upload/connect.ex @@ -19,6 +19,8 @@ defmodule Corex.FileUpload.Connect do alias Phoenix.LiveView.JS + @visually_hidden_style "border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;word-wrap:normal;" + defp zid(id), do: "file:#{id}" defp maybe_put_int(map, _key, nil), do: map @@ -136,6 +138,9 @@ defmodule Corex.FileUpload.Connect do "data-part" => "hidden-input", "type" => "file", "id" => "#{zid(assigns.id)}:input", + "tabindex" => -1, + "aria-hidden" => true, + "style" => @visually_hidden_style, "disabled" => get_boolean(assigns.disabled), "name" => assigns.name, "form" => assigns.form diff --git a/test/components/file_upload_test.exs b/test/components/file_upload_test.exs index 0e52db31a..9a4ad442f 100644 --- a/test/components/file_upload_test.exs +++ b/test/components/file_upload_test.exs @@ -134,6 +134,9 @@ defmodule Corex.FileUploadTest do result = Connect.hidden_input(assigns) assert result["id"] == "file:test-up:input" assert result["type"] == "file" + assert result["tabindex"] == -1 + assert result["aria-hidden"] == true + assert result["style"] =~ "position:absolute" assert result["name"] == "user[avatar]" assert result["form"] == "f1" end