Repository: primer/view_components (Ruby)

Issue:
AvatarStack doesn't have `disable_expand`  functionality
AvatarStack doesn't have the `disable_expand`  functionality that the matching React component [does](https://github.com/primer/react/blob/main/packages/react/src/AvatarStack/AvatarStack.tsx#L24). It would nice to have parity here for a uniform experience when using both components throughout an application.


Discussion:
Hello @sarahkemi ! Unfortunately we won't be able to find engineering capacity to make these changes, but our PVC release conductor for this week @jonrohan is happy to help with any release stuff relating to your linked PR



Patch:
diff --git a/.changeset/rotten-banks-speak.md b/.changeset/rotten-banks-speak.md
new file mode 100644
index 0000000000..4e3ab4edc6
--- /dev/null
+++ b/.changeset/rotten-banks-speak.md
@@ -0,0 +1,5 @@
+---
+'@primer/view-components': minor
+---
+
+Add `disable_expand` param to AvatarStack for React component parity
diff --git a/app/components/primer/beta/avatar_stack.pcss b/app/components/primer/beta/avatar_stack.pcss
index 90f580681d..df79708e1c 100644
--- a/app/components/primer/beta/avatar_stack.pcss
+++ b/app/components/primer/beta/avatar_stack.pcss
@@ -1,3 +1,8 @@
+/* stylelint-disable selector-max-specificity */
+/* The selector-max-specificity rule is disabled here because the nested selectors
+ in AvatarStack require high specificity to properly override default styles and 
+ achieve the intended visual stacking. */
+
 /* AvatarStack */
 
 /* Stacked avatars can be used to show who is participating in thread when
@@ -60,7 +65,7 @@
     }
   }
 
-  &:hover {
+  &:hover:not([data-disable-expand]) {
     & .avatar {
       margin-right: var(--base-size-4);
     }
@@ -74,6 +79,10 @@
       display: none !important;
     }
   }
+
+  &[data-disable-expand] {
+    position: relative;
+  }
 }
 
 .avatar.avatar-more {
@@ -110,7 +119,7 @@
     right: 0;
     flex-direction: row-reverse;
 
-    &:hover .avatar {
+    &:hover:not([data-disable-expand]) .avatar {
       margin-right: 0;
       margin-left: var(--base-size-4);
     }
diff --git a/app/components/primer/beta/avatar_stack.rb b/app/components/primer/beta/avatar_stack.rb
index c28903e5c6..f119f629d8 100644
--- a/app/components/primer/beta/avatar_stack.rb
+++ b/app/components/primer/beta/avatar_stack.rb
@@ -22,14 +22,16 @@ class AvatarStack < Primer::Component
       # @param tag [Symbol] <%= one_of(Primer::Beta::AvatarStack::TAG_OPTIONS) %>
       # @param align [Symbol] <%= one_of(Primer::Beta::AvatarStack::ALIGN_OPTIONS) %>
       # @param tooltipped [Boolean] Whether to add a tooltip to the stack or not.
+      # @param disable_expand [Boolean] Whether to disable the expand behavior on hover. If true, avatars will not expand.
       # @param body_arguments [Hash] Parameters to add to the Body. If `tooltipped` is set, has the same arguments as <%= link_to_component(Primer::Tooltip) %>.
       #   The default tag is <%= pretty_value(Primer::Beta::AvatarStack::DEFAULT_BODY_TAG) %> but can be changed using `tag:`
       #   to <%= one_of(Primer::Beta::AvatarStack::BODY_TAG_OPTIONS, lower: true) %>
       # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
-      def initialize(tag: DEFAULT_TAG, align: ALIGN_DEFAULT, tooltipped: false, body_arguments: {}, **system_arguments)
+      def initialize(tag: DEFAULT_TAG, align: ALIGN_DEFAULT, tooltipped: false, disable_expand: false, body_arguments: {}, **system_arguments)
         @align = fetch_or_fallback(ALIGN_OPTIONS, align, ALIGN_DEFAULT)
         @system_arguments = system_arguments
         @tooltipped = tooltipped
+        @disable_expand = disable_expand
         @body_arguments = body_arguments
         @direction = @body_arguments[:direction]
 
@@ -39,6 +41,7 @@ def initialize(tag: DEFAULT_TAG, align: ALIGN_DEFAULT, tooltipped: false, body_a
           "AvatarStack-body",
           @body_arguments[:classes]
         )
+        @body_arguments[:"data-disable-expand"] = true if @disable_expand
 
         @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
         @system_arguments[:classes] = class_names(
diff --git a/previews/primer/beta/avatar_stack_preview.rb b/previews/primer/beta/avatar_stack_preview.rb
index 4cff00d46e..405aca8dd4 100644
--- a/previews/primer/beta/avatar_stack_preview.rb
+++ b/previews/primer/beta/avatar_stack_preview.rb
@@ -11,8 +11,9 @@ class AvatarStackPreview < ViewComponent::Preview
       # @param align select [["Left", left], ["Right", right]]
       # @param tooltipped toggle
       # @param tooltip_label text
-      def playground(number_of_avatars: 1, tag: :div, align: :left, tooltipped: false, tooltip_label: "This is a tooltip!")
-        render(Primer::Beta::AvatarStack.new(tag: tag, align: align, tooltipped: tooltipped, body_arguments: { label: tooltip_label })) do |component|
+      # @param disable_expand toggle
+      def playground(number_of_avatars: 1, tag: :div, align: :left, tooltipped: false, tooltip_label: "This is a tooltip!", disable_expand: false)
+        render(Primer::Beta::AvatarStack.new(tag: tag, align: align, tooltipped: tooltipped, disable_expand: disable_expand, body_arguments: { label: tooltip_label })) do |component|
           Array.new(number_of_avatars&.to_i || 1) do
             component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser")
           end
@@ -97,6 +98,17 @@ def with_tooltip
           component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser", href: "primer.style")
         end
       end
+
+      # @label With disabled expand
+      def with_disabled_expand
+        render(Primer::Beta::AvatarStack.new(disable_expand: true)) do |component|
+          component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser")
+          component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser")
+          component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser")
+          component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser")
+          component.with_avatar(src: Primer::ExampleImage::BASE64_SRC, alt: "@kittenuser")
+        end
+      end
       #
       # @!endgroup
     end


Test patch:
diff --git a/test/components/beta/avatar_stack_test.rb b/test/components/beta/avatar_stack_test.rb
index 164dccb78d..39f51b3dc6 100644
--- a/test/components/beta/avatar_stack_test.rb
+++ b/test/components/beta/avatar_stack_test.rb
@@ -109,4 +109,18 @@ def test_renders_three_plus_avatar_stack
       end
     end
   end
+
+  def test_renders_with_disable_expand
+    render_inline(Primer::Beta::AvatarStack.new(disable_expand: true)) do |component|
+      component.with_avatar(src: "Foo", alt: "Bar")
+      component.with_avatar(src: "Foo", alt: "Bar")
+      component.with_avatar(src: "Foo", alt: "Bar")
+    end
+
+    assert_selector(".AvatarStack") do
+      assert_selector(".AvatarStack-body[data-disable-expand]") do
+        assert_selector("img.avatar", count: 3)
+      end
+    end
+  end
 end