diff --git a/.changeset/early-news-help.md b/.changeset/early-news-help.md new file mode 100644 index 0000000000..a7af27729e --- /dev/null +++ b/.changeset/early-news-help.md @@ -0,0 +1,5 @@ +--- +'@openproject/primer-view-components': minor +--- + +Create OpenProject::InputGroupComponent diff --git a/app/components/primer/open_project/input_group.html.erb b/app/components/primer/open_project/input_group.html.erb new file mode 100644 index 0000000000..b587f9cdf1 --- /dev/null +++ b/app/components/primer/open_project/input_group.html.erb @@ -0,0 +1,4 @@ +<%= render(Primer::BaseComponent.new(**@system_arguments)) do %> + <%= text_input %> + <%= trailing_action %> +<% end %> diff --git a/app/components/primer/open_project/input_group.rb b/app/components/primer/open_project/input_group.rb new file mode 100644 index 0000000000..1bd6d2cad6 --- /dev/null +++ b/app/components/primer/open_project/input_group.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Primer + module OpenProject + # Add a general description of component here + # Add additional usage considerations or best practices that may aid the user to use the component correctly. + # @accessibility Add any accessibility considerations + class InputGroup < Primer::Component + status :open_project + + # A component that will render to the right of the label. + # + # To render a clipboardCopyButton, call the `with_trailing_action_clipboard_copy_button` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::ClipboardCopyButton) %>. + # + # To render an iconButton, call the `with_trailing_action_icon` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::Octicon) %>. + renders_one :trailing_action, types: { + clipboard_copy_button: lambda { |**system_arguments, &block| + system_arguments[:classes] = class_names( + system_arguments[:classes], + "rounded-left-0 border-left-0" + ) + Primer::Beta::ClipboardCopyButton.new(**system_arguments, &block) + }, + icon: lambda { |icon: nil, **system_arguments, &block| + system_arguments[:classes] = class_names( + system_arguments[:classes], + "rounded-left-0 border-left-0" + ) + Primer::Beta::IconButton.new(icon: icon, **system_arguments, &block) + } + } + + # Input that is the central part of the input group + # + # Since the central use case of this component is a copyClipboard Button next to the input, it is readonly by default + # + # @param readonly [Boolean] Shall the text field be editable. + # @param system_arguments [Hash] <%= link_to_system_arguments_docs %> + renders_one :text_input, lambda { |readonly: true, **system_arguments| + system_arguments[:classes] = class_names( + system_arguments[:classes], + "rounded-right-0" + ) + + if readonly + system_arguments[:readonly] = readonly + system_arguments[:aria] = merge_aria( + system_arguments, + { aria: { readonly: "true" } } + ) + end + + Primer::Alpha::TextField.new(**system_arguments) + } + + # @param system_arguments [Hash] <%= link_to_system_arguments_docs %> + def initialize(**system_arguments) + @system_arguments = system_arguments + @system_arguments[:tag] = :div + @system_arguments[:display] = :flex + @system_arguments[:align_items] = :flex_end + + system_arguments[:classes] = class_names( + system_arguments[:classes], + "InputGroup" + ) + end + + def render? + text_input? && trailing_action? + end + end + end +end diff --git a/previews/primer/open_project/input_group_preview.rb b/previews/primer/open_project/input_group_preview.rb new file mode 100644 index 0000000000..0f739cb6bf --- /dev/null +++ b/previews/primer/open_project/input_group_preview.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# Setup Playground to use all available component props +# Setup Features to use individual component props and combinations + +module Primer + module OpenProject + # @label InputGroup + class InputGroupPreview < ViewComponent::Preview + # @label Default + # @snapshot + def default + render(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: 'a name', label: 'My input group', value: "Copyable value") + menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) + end + end + + # @label Playground + # @param trailing_action [Symbol] select [clipboardCopy, icon] + # @param value [String] + # @param visually_hide_label toggle + # @param readonly toggle + def playground( + trailing_action: :clipboardCopy, + value: 'Copyable value', + visually_hide_label: false, + readonly: true + ) + render(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: 'Test', label: 'My input group', visually_hide_label: visually_hide_label, value: value, readonly: readonly) + + case trailing_action + when :icon + menu.with_trailing_action_icon(icon: :check, aria: { label: "Successful" }) + when :clipboardCopy + menu.with_trailing_action_clipboard_copy_button(id: "button-2", value: value, aria: { label: "Copy some text" }) + else + menu.with_trailing_action_clipboard_copy_button(id: "button-3", value: value, aria: { label: "Copy some text" }) + end + end + end + + # @label with iconButton + def icon_button + render(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: 'a name', label: 'My input group', value: "Some value") + menu.with_trailing_action_icon(icon: :check, aria: { label: "Successful" }) + end + end + end + end +end diff --git a/test/components/component_test.rb b/test/components/component_test.rb index 489762134c..c72183459c 100644 --- a/test/components/component_test.rb +++ b/test/components/component_test.rb @@ -8,6 +8,10 @@ class PrimerComponentTest < Minitest::Test # Components with any arguments necessary to make them render COMPONENTS_WITH_ARGS = [ + [Primer::OpenProject::InputGroup, {}, proc { |component| + menu.with_text_input(name: "a name", label: "My input group", value: "Copyable value") + menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) + }], [Primer::OpenProject::GridLayout, { css_class: "grid-layout", tag: :div }, proc { |component| component.with_area(:area) do "Foo" diff --git a/test/components/primer/open_project/input_group_test.rb b/test/components/primer/open_project/input_group_test.rb new file mode 100644 index 0000000000..506cbe60c0 --- /dev/null +++ b/test/components/primer/open_project/input_group_test.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "components/test_helper" + +class PrimerOpenProjectInputGroupTest < Minitest::Test + include Primer::ComponentTestHelpers + + def test_renders + render_inline(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: "a name", label: "My input group", value: "Copyable value") + menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) + end + + assert_selector(".InputGroup") + assert_selector(".FormControl-input.rounded-right-0") + assert_selector("clipboard-copy.rounded-left-0.border-left-0") + end + + def test_renders_icon + render_inline(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: "a name", label: "My input group", value: "Copyable value") + menu.with_trailing_action_icon(icon: :gear, aria: { label: "Copy some text" }) + end + + assert_selector(".InputGroup") + assert_selector(".FormControl-input.rounded-right-0") + assert_selector(".Button--iconOnly.rounded-left-0.border-left-0") + end + + def test_does_not_render_without_trailing_action + render_inline(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_text_input(name: "a name", label: "My input group", value: "Copyable value") + end + + refute_selector(".InputGroup") + end + + def test_does_not_render_without_input + render_inline(Primer::OpenProject::InputGroup.new) do |menu| + menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) + end + + refute_selector(".InputGroup") + end +end diff --git a/test/system/open_project/input_group_test.rb b/test/system/open_project/input_group_test.rb new file mode 100644 index 0000000000..2370870fd6 --- /dev/null +++ b/test/system/open_project/input_group_test.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "system/test_case" + +class IntegrationOpenProjectInputGroupTest < System::TestCase + def test_renders_component + visit_preview(:default) + + assert_selector(".InputGroup") + end +end