diff --git a/.changeset/many-sheep-yell.md b/.changeset/many-sheep-yell.md new file mode 100644 index 0000000000..68e97e0366 --- /dev/null +++ b/.changeset/many-sheep-yell.md @@ -0,0 +1,5 @@ +--- +'@openproject/primer-view-components': patch +--- + +Align Primer::OpenProject::InputGroup with error messages diff --git a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/default.png b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/default.png index cb467834a5..f7a7a1cec4 100644 Binary files a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/default.png and b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/default.png differ diff --git a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/focused.png b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/focused.png index 81b7626fac..d02befb54a 100644 Binary files a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/focused.png and b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/alpha/segmented_control/trailing_label_width_medium/focused.png differ diff --git a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/filterable_tree_view/custom_segmented_control/dark_high_contrast.png b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/filterable_tree_view/custom_segmented_control/dark_high_contrast.png index 6cd8569021..3ad3aa47b8 100644 Binary files a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/filterable_tree_view/custom_segmented_control/dark_high_contrast.png and b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/filterable_tree_view/custom_segmented_control/dark_high_contrast.png differ diff --git a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/aria-snapshot.yml b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/aria-snapshot.yml new file mode 100644 index 0000000000..9801015830 --- /dev/null +++ b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/aria-snapshot.yml @@ -0,0 +1,4 @@ +- text: My input group +- textbox "My input group": Copyable value +- button "Copy some text" +- text: Custom validation message \ No newline at end of file diff --git a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/default.png b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/default.png new file mode 100644 index 0000000000..c0a2642043 Binary files /dev/null and b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/default.png differ diff --git a/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/focused.png b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/focused.png new file mode 100644 index 0000000000..74f094b4d9 Binary files /dev/null and b/.playwright/screenshots/snapshots.test.ts-snapshots/primer/open_project/input_group/with_error_message/focused.png differ diff --git a/app/components/primer/open_project/input_group.html.erb b/app/components/primer/open_project/input_group.html.erb index 0f27558763..ef18afe2ac 100644 --- a/app/components/primer/open_project/input_group.html.erb +++ b/app/components/primer/open_project/input_group.html.erb @@ -4,6 +4,15 @@ <%= trailing_action %> <% end %> + <% if @text_input_validation_message %> +
+ + <%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %> + + <%= @text_input_validation_message %> +
+ <% end %> + <% if caption %> <%= caption %> <% end %> diff --git a/app/components/primer/open_project/input_group.rb b/app/components/primer/open_project/input_group.rb index da3e0cf458..6a824189b6 100644 --- a/app/components/primer/open_project/input_group.rb +++ b/app/components/primer/open_project/input_group.rb @@ -49,6 +49,19 @@ class InputGroup < Primer::Component renders_one :text_input, lambda { |readonly: true, **system_arguments| deny_single_argument(:input_width, "Set the `input_width` on the `InputGroup`", **system_arguments) deny_single_argument(:caption, "Set the `caption` on the `InputGroup`", **system_arguments) + + # Extract validation_message so it can be rendered outside the flex row. + # This prevents the trailing action from misaligning when validation is present. + @text_input_validation_message = system_arguments.delete(:validation_message) + if @text_input_validation_message + @text_input_validation_message_id = self.class.generate_id(base_name: "input-group-validation") + system_arguments[:invalid] = true + system_arguments[:aria] = merge_aria( + system_arguments, + { aria: { describedby: @text_input_validation_message_id } } + ) + end + system_arguments[:input_width] = @system_arguments[:input_width] system_arguments[:classes] = class_names( diff --git a/previews/primer/open_project/input_group_preview.rb b/previews/primer/open_project/input_group_preview.rb index 46ffc8e826..d0be6263a5 100644 --- a/previews/primer/open_project/input_group_preview.rb +++ b/previews/primer/open_project/input_group_preview.rb @@ -71,6 +71,15 @@ def with_caption menu.with_caption { "Some caption" } end end + + # @label With error message + # @snapshot + def with_error_message + render(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: "a name", label: "My input group", validation_message: "Custom validation message", value: "Copyable value") + menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) + end + end end end end diff --git a/test/components/primer/open_project/input_group_test.rb b/test/components/primer/open_project/input_group_test.rb index b5bc6ec66c..70ff48e053 100644 --- a/test/components/primer/open_project/input_group_test.rb +++ b/test/components/primer/open_project/input_group_test.rb @@ -53,6 +53,17 @@ def test_renders_caption assert_selector(".FormControl-caption") end + def test_renders_validation_message_outside_flex_row + render_inline(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: "a name", label: "My input group", value: "Copyable value", validation_message: "Custom validation message") + menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) + end + + validation_id = page.find(".InputGroup > .FormControl-inlineValidation", text: "Custom validation message")[:id] + assert_selector(%(.FormControl-input[aria-describedby~="#{validation_id}"])) + assert_selector(".FormControl-input[aria-invalid]") + end + def test_does_not_render_caption_from_system_argument render_inline(Primer::OpenProject::InputGroup.new(caption: "Some caption")) do |menu| menu.with_text_input(name: "a name", label: "My input group", value: "Copyable value")