Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fifty-cheetahs-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/view-components": minor
---

Allow localization of TextField clear button aria-label
3 changes: 2 additions & 1 deletion app/lib/primer/forms/dsl/text_field_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class TextFieldInput < Input
attr_reader(
*%i[
name label show_clear_button leading_visual leading_spinner trailing_visual clear_button_id
visually_hide_label inset monospace field_wrap_classes auto_check_src character_limit
clear_button_label visually_hide_label inset monospace field_wrap_classes auto_check_src character_limit
]
)

Expand All @@ -21,6 +21,7 @@ def initialize(name:, label:, **system_arguments)
@trailing_visual = system_arguments.delete(:trailing_visual)
@leading_spinner = !!system_arguments.delete(:leading_spinner)
@clear_button_id = system_arguments.delete(:clear_button_id) || SecureRandom.uuid
@clear_button_label = system_arguments.delete(:clear_button_label)
Comment on lines 23 to +24
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clear_button_label is accepted without any normalization/validation. If callers pass an empty string (common with missing i18n keys), the template will render an empty aria-label. Consider normalizing blank strings to nil (or raising an ArgumentError when show_clear_button is true and the label is blank) to prevent generating an unlabeled button.

Copilot uses AI. Check for mistakes.
@inset = system_arguments.delete(:inset)
@monospace = system_arguments.delete(:monospace)
@auto_check_src = system_arguments.delete(:auto_check_src)
Expand Down
2 changes: 1 addition & 1 deletion app/lib/primer/forms/text_field.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<%= builder.text_field(@input.name, **@input.input_arguments) %>
<% end %>
<% if @input.show_clear_button? %>
<button type="button" id="<%= @input.clear_button_id %>" class="FormControl-input-trailingAction" aria-label="Clear" data-action="click:primer-text-field#clearContents">
<button type="button" id="<%= @input.clear_button_id %>" class="FormControl-input-trailingAction" aria-label="<%= @input.clear_button_label || 'Clear' %>" data-action="click:primer-text-field#clearContents">
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clear_button_label will override the default even when it is an empty string, resulting in aria-label="" (no accessible name) and an accessibility regression if a translation returns blank. Consider falling back to the default when the label is blank (e.g., treat blank as nil) or validate/normalize clear_button_label upstream so the rendered aria-label is always non-empty when the clear button is shown.

Suggested change
<button type="button" id="<%= @input.clear_button_id %>" class="FormControl-input-trailingAction" aria-label="<%= @input.clear_button_label || 'Clear' %>" data-action="click:primer-text-field#clearContents">
<button type="button" id="<%= @input.clear_button_id %>" class="FormControl-input-trailingAction" aria-label="<%= @input.clear_button_label.presence || 'Clear' %>" data-action="click:primer-text-field#clearContents">

Copilot uses AI. Check for mistakes.
<%= render(Primer::Beta::Octicon.new(icon: :"x-circle-fill")) %>
</button>
<% end %>
Expand Down
9 changes: 9 additions & 0 deletions previews/primer/alpha/text_field_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TextFieldPreview < ViewComponent::Preview
# @param size [Symbol] select [small, medium, large]
# @param show_clear_button toggle
# @param clear_button_id text
# @param clear_button_label text
# @param full_width toggle
# @param disabled toggle
# @param invalid toggle
Expand All @@ -35,6 +36,7 @@ def playground(
size: Primer::Forms::Dsl::Input::DEFAULT_SIZE.to_s,
show_clear_button: false,
clear_button_id: "my-text-field-clear-button",
clear_button_label: nil,
full_width: true,
disabled: false,
invalid: false,
Expand All @@ -56,6 +58,7 @@ def playground(
size: size,
show_clear_button: show_clear_button,
clear_button_id: clear_button_id,
clear_button_label: clear_button_label,
full_width: full_width,
disabled: disabled,
invalid: invalid,
Expand Down Expand Up @@ -133,6 +136,12 @@ def show_clear_button
render(Primer::Alpha::TextField.new(show_clear_button: true, name: "my-text-field-3", label: "My text field"))
end

# @label Show clear button with custom label
# @snapshot
def show_clear_button_with_custom_label
render(Primer::Alpha::TextField.new(show_clear_button: true, clear_button_label: "Effacer", name: "my-text-field-3-custom", label: "Mon champ de texte"))
end

# @label Full width
# @snapshot
def full_width
Expand Down
13 changes: 13 additions & 0 deletions static/info_arch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9157,6 +9157,19 @@
]
}
},
{
"preview_path": "primer/alpha/text_field/show_clear_button_with_custom_label",
"name": "show_clear_button_with_custom_label",
"snapshot": "true",
"skip_rules": {
"wont_fix": [
"region"
],
"will_fix": [
"color-contrast"
]
}
},
{
"preview_path": "primer/alpha/text_field/full_width",
"name": "full_width",
Expand Down
13 changes: 13 additions & 0 deletions static/previews.json
Original file line number Diff line number Diff line change
Expand Up @@ -7141,6 +7141,19 @@
]
}
},
{
"preview_path": "primer/alpha/text_field/show_clear_button_with_custom_label",
"name": "show_clear_button_with_custom_label",
"snapshot": "true",
"skip_rules": {
"wont_fix": [
"region"
],
"will_fix": [
"color-contrast"
]
}
},
{
"preview_path": "primer/alpha/text_field/full_width",
"name": "full_width",
Expand Down
23 changes: 23 additions & 0 deletions test/components/alpha/text_field_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ def test_renders_a_clear_button
assert_selector "button.FormControl-input-trailingAction#clear-button-id"
end

def test_renders_clear_button_with_default_aria_label
render_inline(
Primer::Alpha::TextField.new(
**@default_params,
show_clear_button: true
)
)

assert_selector "button[aria-label='Clear']"
end

def test_renders_clear_button_with_custom_aria_label
render_inline(
Primer::Alpha::TextField.new(
**@default_params,
show_clear_button: true,
clear_button_label: "Effacer"
)
)

assert_selector "button[aria-label='Effacer']"
end

def test_renders_the_component_full_width
render_inline(Primer::Alpha::TextField.new(**@default_params, full_width: true))

Expand Down
Loading