From 5a81e1a202a08d1579b1a9114554075d7d3d5945 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Wed, 18 Dec 2024 16:00:39 +0100 Subject: [PATCH] change the text input field component to have the possibility of trailing visuals including icon, text, label and counter --- app/components/primer/alpha/text_field.pcss | 58 +++++++------------- app/lib/primer/forms/dsl/text_field_input.rb | 29 +++++----- app/lib/primer/forms/text_field.html.erb | 11 +--- app/lib/primer/forms/text_field.rb | 31 +++++++++-- previews/primer/alpha/text_field_preview.rb | 18 +++++- 5 files changed, 75 insertions(+), 72 deletions(-) diff --git a/app/components/primer/alpha/text_field.pcss b/app/components/primer/alpha/text_field.pcss index fe1239e1fd..490578458c 100644 --- a/app/components/primer/alpha/text_field.pcss +++ b/app/components/primer/alpha/text_field.pcss @@ -259,39 +259,24 @@ position: absolute; top: var(--base-size-8); right: var(--base-size-8); - display: block; - width: var(--base-size-16); - height: var(--base-size-16); - color: var(--fgColor-muted); - pointer-events: none; - - /* octicon */ - & .FormControl-input-trailingVisual { - display: block; - user-select: none; - } - } - & .FormControl-input-trailingTextWrap { - position: absolute; - top: var(--base-size-8); - right: var(--base-size-8); - width: auto; - max-width: 25%; - padding-left: var(--base-size-8); display: flex; - align-items: center; height: var(--base-size-16); + align-items: center; + gap: var(--base-size-4); color: var(--fgColor-muted); pointer-events: none; - - /* text */ - & .FormControl-input-trailingText { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + &:has( .FormControl-input-trailingText) { + width: auto; + max-width: 25%; + padding-left: var(--base-size-8); + + & .FormControl-input-trailingText { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } } - /* TODO: replace with new Button component */ & .FormControl-input-trailingAction { position: absolute; @@ -384,14 +369,9 @@ &.FormControl-input-wrap--trailingVisual { & .FormControl-input { - padding-inline-end: calc( - var(--control-medium-paddingInline-condensed) + var(--base-size-16) + var(--control-medium-gap) - ); /* 32px */ + padding-inline-end: calc(var(--control-medium-paddingInline-condensed) + var(--base-size-16) + var(--control-medium-gap)); } - } - - &.FormControl-input-wrap--trailingText { - & .FormControl-input { + &:has(.FormControl-input-trailingText) .FormControl-input { padding-inline-end: 25% } } @@ -514,11 +494,13 @@ ); /* 36px */ } } + &.FormControl-input-wrap--trailingVisual { - & .FormControl-input.FormControl-large { - padding-inline-end: calc( - var(--control-large-paddingInline-normal) + var(--base-size-16) + var(--control-large-gap) - ); /* 36px */ + & .FormControl-input { + padding-inline-end: calc(var(--control-large-paddingInline-normal) + var(--base-size-16) + var(--control-large-gap)); + } + &:has(.FormControl-input-trailingText) .FormControl-input { + padding-inline-end: 25% } } diff --git a/app/lib/primer/forms/dsl/text_field_input.rb b/app/lib/primer/forms/dsl/text_field_input.rb index daacdb3e66..9aa4dd23f2 100644 --- a/app/lib/primer/forms/dsl/text_field_input.rb +++ b/app/lib/primer/forms/dsl/text_field_input.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - module Primer module Forms module Dsl @@ -8,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 trailing_text + visually_hide_label inset monospace field_wrap_classes auto_check_src ] ) @@ -20,8 +18,7 @@ def initialize(name:, label:, **system_arguments) @show_clear_button = system_arguments.delete(:show_clear_button) @leading_visual = system_arguments.delete(:leading_visual) - @trailing_visual = system_arguments.delete(:trailing_visual) - @trailing_text = system_arguments.delete(:trailing_text) + @trailing_visual = build_trailing_visual(system_arguments.delete(:trailing_visual)) @leading_spinner = !!system_arguments.delete(:leading_spinner) @clear_button_id = system_arguments.delete(:clear_button_id) @inset = system_arguments.delete(:inset) @@ -35,13 +32,6 @@ def initialize(name:, label:, **system_arguments) ) end - if @trailing_visual - @trailing_visual[:classes] = class_names( - "FormControl-input-trailingVisual", - @trailing_visual[:classes] - ) - end - if @leading_spinner && !@leading_visual raise ArgumentError, "text fields that request a leading spinner must also specify a leading visual" end @@ -65,6 +55,17 @@ def leading_visual? !!@leading_visual end + def build_trailing_visual(trailing_visual) + return nil unless trailing_visual + + icon = trailing_visual[:icon] + text = trailing_visual[:text] + counter = trailing_visual[:counter] + label = trailing_visual[:label] + + { icon: icon, text: text, counter: counter, label: label }.compact + end + def to_component TextField.new(input: self) end @@ -108,10 +109,6 @@ def validation_message_arguments super end end - - def trailing_text? - !!@trailing_text - end end end end diff --git a/app/lib/primer/forms/text_field.html.erb b/app/lib/primer/forms/text_field.html.erb index 5983d2baef..207ad1973e 100644 --- a/app/lib/primer/forms/text_field.html.erb +++ b/app/lib/primer/forms/text_field.html.erb @@ -17,15 +17,8 @@ <%= render(Primer::Beta::Octicon.new(icon: :"x-circle-fill")) %> <% end %> - <% if @input.trailing_visual%> - - <%= render(Primer::Beta::Octicon.new(**@input.trailing_visual, data: { target: "primer-text-field.trailingVisual" })) %> - - <% end %> - <% if @input.trailing_text %> - - <%= @input.trailing_text %> - + <% if @input.trailing_visual %> + <%= trailing_visual_render %> <% end %> <% end %> <% end %> diff --git a/app/lib/primer/forms/text_field.rb b/app/lib/primer/forms/text_field.rb index a56b7b0e2f..9b11670d51 100644 --- a/app/lib/primer/forms/text_field.rb +++ b/app/lib/primer/forms/text_field.rb @@ -1,8 +1,5 @@ -# frozen_string_literal: true - module Primer module Forms - # :nodoc: class TextField < BaseComponent delegate :builder, :form, to: :@input @@ -25,10 +22,8 @@ def initialize(input:) INPUT_WRAP_SIZE[input.size], "FormControl-input-wrap--trailingAction": @input.show_clear_button?, "FormControl-input-wrap--trailingVisual": @input.trailing_visual?, - "FormControl-input-wrap--leadingVisual": @input.leading_visual?, - "FormControl-input-wrap--trailingText": @input.trailing_text? + "FormControl-input-wrap--leadingVisual": @input.leading_visual? ), - hidden: @input.hidden? } end @@ -43,6 +38,30 @@ def auto_check_authenticity_token ) end end + + def trailing_visual_render + visual = @input.trailing_visual + return unless visual + + content = ActiveSupport::SafeBuffer.new # Use SafeBuffer for safe HTML concatenation + + # Render icon if specified + content << render(Primer::Beta::Octicon.new(icon: visual[:icon])) if visual[:icon] + + # Render label if specified + content << render(Primer::Beta::Label.new()) { visual[:label] } if visual[:label] + + # Render counter if specified + content << render(Primer::Beta::Counter.new(count: visual[:counter])) if visual[:counter] + + # Render text if specified + content << content_tag(:span, visual[:text], class: "FormControl-input-trailingText") if visual[:text] + + # Wrap in the trailing visual container + content_tag(:span, content, class: "FormControl-input-trailingVisualWrap") + end + + end end end diff --git a/previews/primer/alpha/text_field_preview.rb b/previews/primer/alpha/text_field_preview.rb index b48b78dfeb..e280a619aa 100644 --- a/previews/primer/alpha/text_field_preview.rb +++ b/previews/primer/alpha/text_field_preview.rb @@ -172,16 +172,28 @@ def monospace render(Primer::Alpha::TextField.new(monospace: true, name: "my-text-field", label: "My text field")) end - # @label With trailing visual + # @label With trailing icon # @snapshot - def with_trailing_visual + def with_trailing_icon render(Primer::Alpha::TextField.new(trailing_visual: { icon: :search }, name: "my-text-field", label: "My text field")) end # @label With trailing text # @snapshot def with_trailing_text - render(Primer::Alpha::TextField.new(trailing_text: "minutes", name: "my-text-field", label: "My text field")) + render(Primer::Alpha::TextField.new( trailing_visual: { text: "Foobarbaz" }, name: "my-text-field", label: "My text field")) + end + + # @label With trailing counter + # @snapshot + def with_trailing_counter + render(Primer::Alpha::TextField.new( trailing_visual: { counter: 5 }, name: "my-text-field", label: "My text field")) + end + + # @label With trailing label + # @snapshot + def with_trailing_label + render(Primer::Alpha::TextField.new( trailing_visual: { label: "Hello" }, name: "my-text-field", label: "My text field")) end # @label With leading visual