diff --git a/.changeset/blue-insects-buy.md b/.changeset/blue-insects-buy.md
new file mode 100644
index 0000000000..8931121e1d
--- /dev/null
+++ b/.changeset/blue-insects-buy.md
@@ -0,0 +1,5 @@
+---
+'@primer/view-components': minor
+---
+
+Adds default sr-only text to spinner component
diff --git a/app/components/primer/beta/spinner.html.erb b/app/components/primer/beta/spinner.html.erb
index adabca4226..7b40309a4c 100644
--- a/app/components/primer/beta/spinner.html.erb
+++ b/app/components/primer/beta/spinner.html.erb
@@ -2,3 +2,6 @@
<% end %>
+<% if no_aria_label? %>
+ <%= @sr_text %>
+<% end %>
diff --git a/app/components/primer/beta/spinner.rb b/app/components/primer/beta/spinner.rb
index f47ab1d1c1..e4bc99f05c 100644
--- a/app/components/primer/beta/spinner.rb
+++ b/app/components/primer/beta/spinner.rb
@@ -12,6 +12,7 @@ class Spinner < Primer::Component
DEFAULT_SIZE => 32,
:large => 64
}.freeze
+ DEFAULT_SR_TEXT = "Loading"
SIZE_OPTIONS = SIZE_MAPPINGS.keys
@@ -22,7 +23,7 @@ class Spinner < Primer::Component
# @param size [Symbol] <%= one_of(Primer::Beta::Spinner::SIZE_MAPPINGS) %>
# @param style [String] Custom element styles.
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
- def initialize(size: DEFAULT_SIZE, style: DEFAULT_STYLE, **system_arguments)
+ def initialize(size: DEFAULT_SIZE, style: DEFAULT_STYLE, sr_text: DEFAULT_SR_TEXT, **system_arguments)
@system_arguments = deny_tag_argument(**system_arguments)
@system_arguments[:tag] = :svg
@system_arguments[:style] ||= style
@@ -31,6 +32,19 @@ def initialize(size: DEFAULT_SIZE, style: DEFAULT_STYLE, **system_arguments)
@system_arguments[:height] = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)]
@system_arguments[:viewBox] = "0 0 16 16"
@system_arguments[:fill] = :none
+ @system_arguments[:aria] ||= {}
+ @sr_text = sr_text
+
+ if no_aria_label?
+ @system_arguments[:aria][:hidden] = true
+ else
+ @system_arguments[:role] = "img"
+ end
+ end
+
+ def no_aria_label?
+ !@system_arguments[:aria][:label] && !@system_arguments[:"aria-label"] &&
+ !@system_arguments[:aria][:labelledby] && !@system_arguments[:"aria-labelledby"]
end
end
end
diff --git a/previews/primer/beta/spinner_preview.rb b/previews/primer/beta/spinner_preview.rb
index 937a8a61c2..c2792be7bf 100644
--- a/previews/primer/beta/spinner_preview.rb
+++ b/previews/primer/beta/spinner_preview.rb
@@ -7,8 +7,8 @@ class SpinnerPreview < ViewComponent::Preview
# @label Playground
#
# @param size [Symbol] select [small, medium, large]
- def playground(size: :medium)
- render(Primer::Beta::Spinner.new(size: size))
+ def playground(size: :medium, sr_text: "Loading content...")
+ render(Primer::Beta::Spinner.new(size: size, sr_text: sr_text))
end
# @label Default Options
diff --git a/static/arguments.json b/static/arguments.json
index f9ae2006ac..e32923a040 100644
--- a/static/arguments.json
+++ b/static/arguments.json
@@ -4288,6 +4288,12 @@
"default": "`box-sizing: content-box; color: var(--color-icon-primary);`",
"description": "Custom element styles."
},
+ {
+ "name": "sr_text",
+ "type": "String",
+ "default": "Loading",
+ "description": "Screen reader only text, added unless aria-label is set."
+ },
{
"name": "system_arguments",
"type": "Hash",
diff --git a/test/components/beta/spinner_test.rb b/test/components/beta/spinner_test.rb
index 67231d0688..faab977d31 100644
--- a/test/components/beta/spinner_test.rb
+++ b/test/components/beta/spinner_test.rb
@@ -44,4 +44,52 @@ def test_no_box_sizing_style
def test_status
assert_component_state(Primer::Beta::Spinner, :beta)
end
+
+ def test_adds_default_sr_span
+ render_inline(Primer::Beta::Spinner.new)
+
+ assert_selector("span.sr-only", text: "Loading")
+ end
+
+ def test_adds_custom_sr_span
+ render_inline(Primer::Beta::Spinner.new(sr_text: "Custom Loading"))
+
+ assert_selector("span.sr-only", text: "Custom Loading")
+ end
+
+ def test_no_custom_sr_span_with_aria_label
+ render_inline(Primer::Beta::Spinner.new("aria-label": "Aria label"))
+
+ assert_no_selector("span.sr-only")
+ end
+
+ def test_no_custom_sr_span_with_aria_label_hash
+ render_inline(Primer::Beta::Spinner.new(aria: { label: "Aria label"}))
+
+ assert_no_selector("span.sr-only")
+ end
+
+ def test_no_custom_sr_span_with_aria_labelledby
+ render_inline(Primer::Beta::Spinner.new("aria-labelledby": "my_id"))
+
+ assert_no_selector("span.sr-only")
+ end
+
+ def test_no_custom_sr_span_with_aria_labelledby_hash
+ render_inline(Primer::Beta::Spinner.new(aria: { labelledby: "my_id"}))
+
+ assert_no_selector("span.sr-only")
+ end
+
+ def test_adds_img_role_with_aria_label
+ render_inline(Primer::Beta::Spinner.new("aria-label": "Aria label"))
+
+ assert_selector("svg[role=img]")
+ end
+
+ def test_adds_aria_hidden_with_no_aria_label
+ render_inline(Primer::Beta::Spinner.new)
+
+ assert_selector("svg[aria-hidden=true]")
+ end
end