From 11f4d0a5b0c4b5d7db47bab26b3bd4c2bd89c87e Mon Sep 17 00:00:00 2001
From: Sascha Eggenberger <seggenberger@gitlab.com>
Date: Wed, 7 Aug 2024 11:25:49 +0000
Subject: [PATCH] Migrates token card components to CRUD

---
 .../components/access_token_table_app.vue     |  4 +-
 app/components/layouts/crud_component.haml    |  2 +-
 app/components/layouts/crud_component.rb      | 14 ++--
 .../impersonation_tokens/index.html.haml      | 48 +++++++-------
 app/views/admin/labels/index.html.haml        |  2 +-
 .../settings/access_tokens/index.html.haml    | 64 +++++++++----------
 app/views/projects/pages/_list.html.haml      |  2 +-
 .../settings/access_tokens/index.html.haml    | 42 ++++++------
 .../personal_access_tokens/index.html.haml    | 39 ++++++-----
 .../components/layouts/crud_component_spec.rb |  2 +-
 10 files changed, 99 insertions(+), 120 deletions(-)

diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
index 54b06ac2cbd52..188b5c176527f 100644
--- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
+++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
@@ -108,9 +108,7 @@ export default {
 <template>
   <dom-element-listener :selector="$options.FORM_SELECTOR" @[$options.EVENT_SUCCESS]="onSuccess">
     <div>
-      <div
-        class="gl-new-card-body gl-px-0 gl-overflow-x-auto gl-bg-gray-10 gl-border-l gl-border-r gl-border-b gl-rounded-bottom-base gl-mb-5 gl-md-mb-0"
-      >
+      <div>
         <gl-table
           data-testid="active-tokens"
           :empty-text="noActiveTokensMessage"
diff --git a/app/components/layouts/crud_component.haml b/app/components/layouts/crud_component.haml
index 56c6f7857597d..2ad5a7cb2f2e6 100644
--- a/app/components/layouts/crud_component.haml
+++ b/app/components/layouts/crud_component.haml
@@ -8,7 +8,7 @@
             %span.gl-inline-flex.gl-items-center.gl-gap-2.gl-text-sm.gl-text-subtle{ data: { testid: 'crud-count' } }
               - if @icon
                 = sprite_icon(@icon, css_class: @icon_class)
-              %span{ class: @count_class }
+              %span{ @count_options }
                 = @count
         - if description? || @description
           .gl-text-sm.gl-text-subtle.gl-mt-1.gl-mb-0{ data: { testid: 'crud-description' } }
diff --git a/app/components/layouts/crud_component.rb b/app/components/layouts/crud_component.rb
index 219fa507c8eaf..7635e295dd6ff 100644
--- a/app/components/layouts/crud_component.rb
+++ b/app/components/layouts/crud_component.rb
@@ -6,25 +6,27 @@ class CrudComponent < ViewComponent::Base
     # @param [String] description
     # @param [Number] count
     # @param [String] icon
+    # @param [String] icon_class
     # @param [String] toggle_text
     # @param [Hash] options
+    # @param [Hash] count_options
     # @param [Hash] body_options
     # @param [Hash] form_options
     # @param [Hash] toggle_options
     # @param [Hash] footer_options
     def initialize(
-      title, description: nil, count: nil, count_class: nil, icon: nil,
-      icon_class: nil, toggle_text: nil, options: {}, body_options: {},
+      title, description: nil, count: nil, icon: nil, icon_class: nil,
+      toggle_text: nil, options: {}, count_options: {}, body_options: {},
       form_options: {}, toggle_options: {}, footer_options: {}
     )
       @title = title
       @description = description
       @count = count
-      @count_class = count_class
       @icon = icon
       @icon_class = icon_class
       @toggle_text = toggle_text
       @options = options
+      @count_options = count_options
       @body_options = body_options
       @form_options = form_options
       @toggle_options = toggle_options
@@ -63,11 +65,7 @@ def form_options_attrs
 
     def footer_options_attrs
       default_testid = 'crud-footer'
-      default_classes = [
-        ('js-toggle-content' if @toggle_text),
-        ('gl-hidden' if @toggle_text && !@form_options[:form_errors])
-      ]
-      @form_options.merge(default_attrs(@form_options, default_testid, default_classes))
+      @footer_options.merge(default_attrs(@footer_options, default_testid))
     end
 
     delegate :sprite_icon, to: :helpers
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index 40140e2c4ba4a..3171f019abbee 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -8,29 +8,27 @@
 
 #js-new-access-token-app{ data: { access_token_type: type } }
 
-= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card gl-border-b-0 gl-rounded-bottom-left-none gl-rounded-bottom-right-none js-toggle-container js-token-card' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body' }) do |c|
-  - c.with_header do
-    .gl-new-card-title-wrapper.gl-flex-direction-column
-      %h3.gl-new-card-title
-        = _('Impersonation tokens')
-        .gl-new-card-count
-          = sprite_icon('token', css_class: 'gl-mr-2')
-          %span.js-token-count= @active_impersonation_tokens.size
-      .gl-new-card-description
-        = _("To see all the user's personal access tokens you must impersonate them first.")
-    .gl-new-card-actions
-      = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
-        = _('Add new token')
-  - c.with_body do
-    .gl-new-card-add-form.gl-mt-3.gl-hidden.js-toggle-content.js-add-new-token-form
-      = render 'shared/access_tokens/form',
-        ajax: true,
-        type: type,
-        title: _('Add an impersonation token'),
-        path: admin_user_impersonation_tokens_path,
-        impersonation: true,
-        token: @impersonation_token,
-        scopes: @scopes,
-        help_path: help_page_path('api/rest/index', anchor: 'impersonation-tokens')
+= render ::Layouts::CrudComponent.new(_('Impersonation tokens'),
+  icon: 'token',
+  count: @active_impersonation_tokens.size,
+  count_options: { class: 'js-token-count' },
+  toggle_text: _('Add new token'),
+  toggle_options: { data: { testid: 'add-new-token-button' } },
+  form_options: { class: 'js-add-new-token-form' },
+  options: { class: 'gl-mt-5 js-token-card' }) do |c|
+  - c.with_description do
+    = _("To see all the user's personal access tokens you must impersonate them first.")
+
+  - c.with_form do
+    = render 'shared/access_tokens/form',
+      ajax: true,
+      type: type,
+      title: _('Add an impersonation token'),
+      path: admin_user_impersonation_tokens_path,
+      impersonation: true,
+      token: @impersonation_token,
+      scopes: @scopes,
+      help_path: help_page_path('api/rest/index', anchor: 'impersonation-tokens')
 
-#js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_impersonation_tokens.to_json } }
+  - c.with_body do
+    #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_impersonation_tokens.to_json } }
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index 86bf67ffe92e4..5989f628096b6 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -5,7 +5,7 @@
 %div{ data: { event_tracking_load: 'true', event_tracking: 'view_admin_labels_pageload' } }
 
 - if @labels.present?
-  = render ::Layouts::CrudComponent.new(_('Labels'), description: page_description, count: number_with_delimiter(@labels.total_count), count_class: 'js-admin-labels-count', icon: 'label', options: { class: 'labels other-labels gl-mt-5 js-admin-labels-container' }) do |c|
+  = render ::Layouts::CrudComponent.new(_('Labels'), description: page_description, count: number_with_delimiter(@labels.total_count), count_options: { class: 'js-admin-labels-count' }, icon: 'label', options: { class: 'labels other-labels gl-mt-5 js-admin-labels-container' }) do |c|
     - c.with_actions do
       = render Pajamas::ButtonComponent.new(variant: :default,
         size: :small,
diff --git a/app/views/groups/settings/access_tokens/index.html.haml b/app/views/groups/settings/access_tokens/index.html.haml
index 3695c5a1e2148..b012ba6283f2f 100644
--- a/app/views/groups/settings/access_tokens/index.html.haml
+++ b/app/views/groups/settings/access_tokens/index.html.haml
@@ -3,7 +3,6 @@
 - type = _('group access token')
 - type_plural = _('group access tokens')
 - @force_desktop_expanded_sidebar = true
-- shared_card_component_classes = "gl-new-card gl-border-b-0 gl-rounded-bottom-left-none gl-rounded-bottom-right-none"
 
 .settings-section.js-search-settings-section
   .settings-sticky-header
@@ -26,44 +25,39 @@
 
   #js-new-access-token-app{ data: { access_token_type: type } }
 
-  = render Pajamas::CardComponent.new(card_options: { class: "#{shared_card_component_classes} js-toggle-container js-token-card" }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body' }) do |c|
-    - c.with_header do
-      .gl-new-card-title-wrapper
-        %h3.gl-new-card-title
-          = _('Active group access tokens')
-        .gl-new-card-count
-          = sprite_icon('token', css_class: 'gl-mr-2')
-          %span.js-token-count= @active_access_tokens.size
+  = render ::Layouts::CrudComponent.new(_('Active group access tokens'),
+    icon: 'token',
+    count: @active_access_tokens.size,
+    count_options: { class: 'js-token-count' },
+    form_options: { class: 'gl-hidden js-toggle-content js-add-new-token-form' },
+    options: { class: 'gl-mt-5 js-toggle-container js-token-card' }) do |c|
+    - c.with_actions do
       - if current_user.can?(:create_resource_access_tokens, @group)
-        .gl-new-card-actions
-          = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
-            = _('Add new token')
-    - c.with_body do
+        = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
+          = _('Add new token')
+
+    - c.with_form do
       - if current_user.can?(:create_resource_access_tokens, @group)
-        .gl-new-card-add-form.gl-mt-3.gl-hidden.js-toggle-content.js-add-new-token-form
-          = render 'shared/access_tokens/form',
-            ajax: true,
-            type: type,
-            path: group_settings_access_tokens_path(@group),
-            resource: @group,
-            token: @resource_access_token,
-            scopes: @scopes,
-            access_levels: access_level_roles_user_can_assign(@group, GroupMember.access_level_roles),
-            default_access_level: Gitlab::Access::GUEST,
-            prefix: :resource_access_token,
-            description_prefix: :group_access_token,
-            help_path: help_page_path('user/group/settings/group_access_tokens', anchor: 'scopes-for-a-group-access-token')
+        = render 'shared/access_tokens/form',
+          ajax: true,
+          type: type,
+          path: group_settings_access_tokens_path(@group),
+          resource: @group,
+          token: @resource_access_token,
+          scopes: @scopes,
+          access_levels: access_level_roles_user_can_assign(@group, GroupMember.access_level_roles),
+          default_access_level: Gitlab::Access::GUEST,
+          prefix: :resource_access_token,
+          description_prefix: :group_access_token,
+          help_path: help_page_path('user/group/settings/group_access_tokens', anchor: 'scopes-for-a-group-access-token')
 
-  #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This group has no active access tokens.'), show_role: true } }
+    - c.with_body do
+      #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This group has no active access tokens.'), show_role: true } }
 
   - if Feature.enabled?(:retain_resource_access_token_user_after_revoke, @group.root_ancestor)
-    = render Pajamas::CardComponent.new(card_options: { class: shared_card_component_classes }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0 gl-bg-gray-10 gl-border-b gl-rounded-bottom-base' }) do |c|
-      - c.with_header do
-        .gl-new-card-title-wrapper
-          %h3.gl-new-card-title
-            = _('Inactive group access tokens')
-          .gl-new-card-count
-            = sprite_icon('token', css_class: 'gl-mr-2')
-            %span.js-token-count= @inactive_access_tokens.size
+    = render ::Layouts::CrudComponent.new(_('Inactive group access tokens'),
+      icon: 'token',
+      count: @inactive_access_tokens.size,
+      count_options: { class: 'js-token-count' }) do |c|
       - c.with_body do
         #js-inactive-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_inactive_access_tokens: @inactive_access_tokens.to_json, no_inactive_tokens_message: _('This group has no inactive access tokens.')} }
diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml
index b58cb7f4961a1..f29332a6a7ff6 100644
--- a/app/views/projects/pages/_list.html.haml
+++ b/app/views/projects/pages/_list.html.haml
@@ -5,7 +5,7 @@
   = render ::Layouts::CrudComponent.new(s_('GitLabPages|Domains'),
     icon: 'earth',
     count: @domains.size,
-    count_class: 'js-admin-labels-count',
+    count_options: { class: 'js-admin-labels-count' },
     options: { class: 'gl-mt-5' }) do |c|
     - c.with_actions do
       - if can_add_new_domain
diff --git a/app/views/projects/settings/access_tokens/index.html.haml b/app/views/projects/settings/access_tokens/index.html.haml
index 8c33b418b6521..4249fec371788 100644
--- a/app/views/projects/settings/access_tokens/index.html.haml
+++ b/app/views/projects/settings/access_tokens/index.html.haml
@@ -3,7 +3,6 @@
 - type = _('project access token')
 - type_plural = _('project access tokens')
 - @force_desktop_expanded_sidebar = true
-- shared_card_component_classes = "gl-new-card gl-border-b-0 gl-rounded-bottom-left-none gl-rounded-bottom-right-none"
 
 .settings-section.js-search-settings-section
   .settings-sticky-header
@@ -25,33 +24,28 @@
 
   #js-new-access-token-app{ data: { access_token_type: type } }
 
-  = render Pajamas::CardComponent.new(card_options: { class: "#{shared_card_component_classes} js-toggle-container js-token-card" }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body' }) do |c|
-    - c.with_header do
-      .gl-new-card-title-wrapper
-        %h3.gl-new-card-title
-          = _('Active project access tokens')
-        .gl-new-card-count
-          = sprite_icon('token', css_class: 'gl-mr-2')
-          %span.js-token-count= @active_access_tokens.size
+  = render ::Layouts::CrudComponent.new(_('Active project access tokens'),
+    icon: 'token',
+    count: @active_access_tokens.size,
+    count_options: { class: 'js-token-count' },
+    form_options: { class: 'gl-hidden js-toggle-content js-add-new-token-form' },
+    options: { class: 'gl-mt-5 js-toggle-container js-token-card' }) do |c|
+    - c.with_actions do
       - if current_user.can?(:create_resource_access_tokens, @project)
-        .gl-new-card-actions
-          = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
-            = _('Add new token')
-    - c.with_body do
+        = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
+          = _('Add new token')
+
+    - c.with_form do
       - if current_user.can?(:create_resource_access_tokens, @project)
-        .gl-new-card-add-form.gl-mt-3.gl-hidden.js-toggle-content.js-add-new-token-form
-          = render_if_exists 'projects/settings/access_tokens/form', type: type
+        = render_if_exists 'projects/settings/access_tokens/form', type: type
 
-  #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This project has no active access tokens.'), show_role: true } }
+    - c.with_body do
+      #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This project has no active access tokens.'), show_role: true } }
 
   - if Feature.enabled?(:retain_resource_access_token_user_after_revoke, @project.root_ancestor)
-    = render Pajamas::CardComponent.new(card_options: { class: shared_card_component_classes }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0 gl-bg-gray-10 gl-border-b gl-rounded-bottom-base' }) do |c|
-      - c.with_header do
-        .gl-new-card-title-wrapper
-          %h3.gl-new-card-title
-            = _('Inactive project access tokens')
-          .gl-new-card-count
-            = sprite_icon('token', css_class: 'gl-mr-2')
-            %span.js-token-count= @inactive_access_tokens.size
+    = render ::Layouts::CrudComponent.new(_('Inactive project access tokens'),
+      icon: 'token',
+      count: @inactive_access_tokens.size,
+      count_options: { class: 'js-token-count' }) do |c|
       - c.with_body do
         #js-inactive-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_inactive_access_tokens: @inactive_access_tokens.to_json, no_inactive_tokens_message: _('This project has no inactive access tokens.')} }
diff --git a/app/views/user_settings/personal_access_tokens/index.html.haml b/app/views/user_settings/personal_access_tokens/index.html.haml
index efb5852093a3a..b7a93df82a699 100644
--- a/app/views/user_settings/personal_access_tokens/index.html.haml
+++ b/app/views/user_settings/personal_access_tokens/index.html.haml
@@ -16,27 +16,24 @@
 
   #js-new-access-token-app{ data: { access_token_type: type } }
 
-  = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card gl-border-b-0 gl-rounded-bottom-left-none gl-rounded-bottom-right-none js-toggle-container js-token-card' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body' }) do |c|
-    - c.with_header do
-      .gl-new-card-title-wrapper
-        %h3.gl-new-card-title
-          = _('Active personal access tokens')
-        .gl-new-card-count
-          = sprite_icon('token', css_class: 'gl-mr-2')
-          %span.js-token-count= @active_access_tokens.size
-      .gl-new-card-actions
-        = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
-          = _('Add new token')
-    - c.with_body do
-      .gl-new-card-add-form.gl-mt-3.gl-hidden.js-toggle-content.js-add-new-token-form
-        = render 'shared/access_tokens/form',
-            ajax: true,
-            type: type,
-            path: user_settings_personal_access_tokens_path,
-            token: @personal_access_token,
-            scopes: @scopes,
-            help_path: help_page_path('user/profile/personal_access_tokens', anchor: 'personal-access-token-scopes')
+  = render ::Layouts::CrudComponent.new(_('Active personal access tokens'),
+    icon: 'token',
+    count: @active_access_tokens.size,
+    count_options: { class: 'js-token-count' },
+    toggle_text: _('Add new token'),
+    toggle_options: { data: { testid: 'add-new-token-button' } },
+    form_options: { class: 'js-add-new-token-form' },
+    options: { class: 'gl-mt-5 js-token-card' }) do |c|
+    - c.with_form do
+      = render 'shared/access_tokens/form',
+        ajax: true,
+        type: type,
+        path: user_settings_personal_access_tokens_path,
+        token: @personal_access_token,
+        scopes: @scopes,
+        help_path: help_page_path('user/profile/personal_access_tokens', anchor: 'personal-access-token-scopes')
 
-  #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json } }
+    - c.with_body do
+      #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json } }
 
 #js-tokens-app{ data: { tokens_data: tokens_app_data } }
diff --git a/spec/components/layouts/crud_component_spec.rb b/spec/components/layouts/crud_component_spec.rb
index abaa1b8bf2b55..761b22b712e45 100644
--- a/spec/components/layouts/crud_component_spec.rb
+++ b/spec/components/layouts/crud_component_spec.rb
@@ -139,7 +139,7 @@
 
     it 'renders footer custom attributes' do
       render_inline described_class.new(title,
-        form_options: { class: 'footer-class', data: { testid: 'crud-custom-footer-id' } }) do |c|
+        footer_options: { class: 'footer-class', data: { testid: 'crud-custom-footer-id' } }) do |c|
         c.with_footer { footer }
       end
 
-- 
GitLab