diff --git a/app/assets/stylesheets/components/_index.scss b/app/assets/stylesheets/components/_index.scss
index f53837b5671b1e059632a6d53ad94ddd1acbb5c0..1ab85d921679f03d501a759c85a86dcaecf9689a 100644
--- a/app/assets/stylesheets/components/_index.scss
+++ b/app/assets/stylesheets/components/_index.scss
@@ -1,4 +1,5 @@
@import './avatar';
+@import './action_card_component';
@import './collapsible_card';
@import './content_editor';
@import './deployment_instance';
diff --git a/app/assets/stylesheets/components/action_card_component.scss b/app/assets/stylesheets/components/action_card_component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..37297fb6fd110b6d411aa974a07bcebb0782d385
--- /dev/null
+++ b/app/assets/stylesheets/components/action_card_component.scss
@@ -0,0 +1,74 @@
+.action-card {
+ position: relative;
+ display: block;
+ padding: $gl-spacing-scale-5;
+ border-radius: $gl-border-radius-base;
+
+ &-default {
+ border: 1px solid $gray-100;
+ background-color: $white;
+ }
+
+ &-success {
+ border: 1px solid $green-100;
+ background-color: $green-50;
+ }
+
+ &-promo {
+ border: 1px solid $purple-100;
+ background-color: $purple-50;
+ }
+
+ &-title {
+ display: flex;
+ align-items: center;
+ gap: $gl-spacing-scale-2;
+ font-weight: $gl-font-weight-bold;
+ text-wrap: balance;
+
+ &:is(.gl-link):not(:hover) {
+ color: $gl-text-color;
+ }
+
+ &:is(.gl-link) {
+ transition: color .2s cubic-bezier(0.22, 0.61, 0.36, 1);
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+ }
+ }
+
+ &-title:hover &-arrow {
+ opacity: 1;
+ translate: 0;
+ }
+
+ &-arrow {
+ opacity: 0;
+ translate: -$gl-spacing-scale-2;
+ transition: opacity .2s cubic-bezier(0.22, 0.61, 0.36, 1),
+ translate .2s cubic-bezier(0.22, 0.61, 0.36, 1);
+ }
+
+ &-text {
+ margin-bottom: 0;
+ margin-top: $gl-spacing-scale-2;
+ text-wrap: balance;
+ }
+
+ &-controls {
+ margin-top: $gl-spacing-scale-4;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .action-card-title, .action-card-arrow {
+ transition-duration: .01ms !important;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/dashboard_projects.scss b/app/assets/stylesheets/page_bundles/dashboard_projects.scss
index db07e5fe7b698c6b3808835975017e3df72ecac5..ac097d951fedc8cf721aa26cae8df4d143eca44f 100644
--- a/app/assets/stylesheets/page_bundles/dashboard_projects.scss
+++ b/app/assets/stylesheets/page_bundles/dashboard_projects.scss
@@ -1,35 +1,9 @@
@import 'mixins_and_variables_and_functions';
-.blank-state {
- padding: 20px 50px;
- min-height: 240px;
- width: calc(50% - #{$gl-padding-8});
-
- @include media-breakpoint-down(sm) {
- width: 100%;
- flex-direction: column;
- justify-content: center;
- padding: 50px 20px;
- }
-}
-
-.blank-state-link {
- &:hover {
- background-color: var(--gl-background-color-subtle);
- color: var(--gl-text-color, $gl-text-color);
- text-decoration: none;
- }
-}
-
-.blank-state-icon {
- svg {
- display: block;
- }
-}
-
-.blank-state-body {
- @include media-breakpoint-down(sm) {
- text-align: center;
- margin-top: 20px;
- }
+.welcome-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
+ gap: $gl-spacing-scale-5;
+ max-width: 90ch;
+ margin: auto;
}
diff --git a/app/components/onboarding/action_card_component.html.haml b/app/components/onboarding/action_card_component.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..afc7afe3a63434ecd0b63447a56dcaaf268ed802
--- /dev/null
+++ b/app/components/onboarding/action_card_component.html.haml
@@ -0,0 +1,13 @@
+%div{ **html_options }><
+ - if link?
+ %a.gl-link.action-card-title{ href: @href, **@link_options }
+ = sprite_icon(card_icon)
+ = @title
+ = sprite_icon('arrow-right', css_class: 'action-card-arrow')
+ - else
+ .action-card-title
+ = sprite_icon(card_icon)
+ = @title
+ %p.action-card-text= @description
+ - if content.present?
+ .action-card-controls= content
diff --git a/app/components/onboarding/action_card_component.rb b/app/components/onboarding/action_card_component.rb
new file mode 100644
index 0000000000000000000000000000000000000000..69eee164a6828c2462a651fe15eb5add79ce3b0a
--- /dev/null
+++ b/app/components/onboarding/action_card_component.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class ActionCardComponent < Onboarding::Component
+ VARIANT_OPTIONS = [:default, :success, :promo].freeze
+
+ # @param [String] title
+ # @param [String] description
+ # @param [String] icon
+ # @param [String] href
+ # @param [Symbol] variant
+ # @param [Hash] html_options
+ # @param [Hash] link_options
+
+ def initialize(
+ title: nil,
+ description: nil,
+ icon: nil,
+ href: nil,
+ variant: :default,
+ link_options: {},
+ **html_options
+ )
+ @title = title
+ @description = description
+ @icon = icon.to_s
+ @href = href
+ @variant = filter_attribute(variant.to_sym, VARIANT_OPTIONS, default: :default)
+ @html_options = html_options
+ @link_options = link_options
+ end
+
+ private
+
+ delegate :sprite_icon, to: :helpers
+ renders_one :this_is_text
+
+ def card_classes
+ ["action-card", "action-card-#{@variant}"]
+ end
+
+ def card_icon
+ @variant == :success ? 'check' : @icon
+ end
+
+ def link?
+ @href.present?
+ end
+
+ def html_options
+ format_options(options: @html_options, css_classes: card_classes)
+ end
+ end
+end
diff --git a/app/components/onboarding/component.rb b/app/components/onboarding/component.rb
new file mode 100644
index 0000000000000000000000000000000000000000..118bdfd1a559e6e5c0b93ef6a85f3bf44a5b1563
--- /dev/null
+++ b/app/components/onboarding/component.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class Component < ViewComponent::Base
+ private
+
+ # Filter a given value against a list of allowed values
+ # If no value is given or value is not allowed return default one
+ #
+ # @param [Object] value
+ # @param [Enumerable] allowed_values
+ # @param [Object] default
+ def filter_attribute(value, allowed_values, default: nil)
+ return default unless value
+ return value if allowed_values.include?(value)
+
+ default
+ end
+
+ # Add CSS classes and additional options to an existing options hash
+ #
+ # @param [Hash] options
+ # @param [Array] css_classes
+ # @param [Hash] additional_option
+ def format_options(options:, css_classes: [], additional_options: {})
+ options.merge({ class: [*css_classes, options[:class]].flatten.compact }, additional_options)
+ end
+ end
+end
diff --git a/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml b/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml
index c3d8da0f9a0ab35bd5ec0be9e7591f7a78aa1405..830d927ff58b5f74263f26745415bfa9c976727d 100644
--- a/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml
+++ b/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml
@@ -1,44 +1,31 @@
-- link_classes = "blank-state blank-state-link gl-text-body gl-display-flex gl-align-items-center gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base gl-mb-5"
-
-.gl-display-flex.gl-flex-wrap.gl-justify-content-space-between
+.welcome-grid
= render_if_exists "dashboard/projects/blank_state_extra_info"
- if has_start_trial?
= render_if_exists "dashboard/projects/blank_state_ee_trial"
- = link_to new_project_path, class: link_classes, data: { testid: 'new-project-button' } do
- .blank-state-icon
- = custom_icon("add_new_project", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Create a project')
- %p
- = _('Projects are where you store your code, access issues, wiki and other features of GitLab.')
+ = render Onboarding::ActionCardComponent.new(title: _('Create a project'),
+ description: _('Projects are where you store your code, access issues, wiki, and other features of GitLab.'),
+ icon: 'project',
+ href: new_project_path,
+ variant: 'default',
+ link_options: { data: { testid: 'new-project-button' }})
- if current_user.can_create_group?
- = link_to new_group_path, class: link_classes do
- .blank-state-icon
- = custom_icon("add_new_group", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Create a group')
- %p
- = _('Groups are a great way to organize projects and people.')
+ = render Onboarding::ActionCardComponent.new(title: _('Create a group'),
+ description: _('Groups are a great way to organize projects and people.'),
+ icon: 'group',
+ href: new_group_path,
+ variant: 'default')
- = link_to new_admin_user_path, class: link_classes do
- .blank-state-icon
- = custom_icon("add_new_user", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Add people')
- %p
- = _('Add your team members and others to GitLab.')
+ = render Onboarding::ActionCardComponent.new(title: _('Add people'),
+ description: _('Add your team members and others to GitLab.'),
+ icon: 'user',
+ href: new_admin_user_path,
+ variant: 'default')
- = link_to admin_root_path, class: link_classes do
- .blank-state-icon
- = custom_icon("configure_server", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Configure GitLab')
- %p
- = _('Make adjustments to how your GitLab instance is set up.')
+ = render Onboarding::ActionCardComponent.new(title: _('Configure GitLab'),
+ description: _('Make adjustments to how your GitLab instance is set up.'),
+ icon: 'tanuki',
+ href: admin_root_path,
+ variant: 'default')
diff --git a/app/views/dashboard/projects/_blank_state_welcome.html.haml b/app/views/dashboard/projects/_blank_state_welcome.html.haml
index 15048d29146c6c83e87548a9c110acec1bc6a5fe..a2fa64878e52f0a3414c31a7dbeebddb6c5c8b1f 100644
--- a/app/views/dashboard/projects/_blank_state_welcome.html.haml
+++ b/app/views/dashboard/projects/_blank_state_welcome.html.haml
@@ -1,46 +1,29 @@
-- link_classes = "blank-state blank-state-link gl-text-body gl-display-flex gl-align-items-center gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base gl-mb-5"
-
-.gl-display-flex.gl-flex-wrap.gl-justify-content-space-between
+.welcome-grid
= render_if_exists "dashboard/projects/blank_state_extra_info"
- if current_user.can_create_project?
- = link_to new_project_path, class: link_classes, data: { testid: 'new-project-button' } do
- .blank-state-icon
- = custom_icon("add_new_project", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Create a project')
- %p
- = _('Projects are where you store your code, access issues, wiki and other features of GitLab.')
- - else
- = render Pajamas::AlertComponent.new(variant: :info, alert_options: { class: 'gl-mb-5 gl-w-full' }) do |c|
- - c.with_body do
- = _("You see projects here when you're added to a group or project.").html_safe
+ = render Onboarding::ActionCardComponent.new(title: _('Create a project'),
+ description: _('Projects are where you store your code, access issues, wiki, and other features of GitLab.'),
+ icon: 'project',
+ href: new_project_path,
+ variant: 'default',
+ link_options: { data: { testid: 'new-project-button' }})
- - if current_user.can_create_group?
- = link_to new_group_path, class: link_classes do
- .blank-state-icon
- = custom_icon("add_new_group", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Create a group')
- %p
- = _('Groups are the best way to manage projects and members.')
+ = render Onboarding::ActionCardComponent.new(title: _('Explore public projects'),
+ description: _('Public projects are an easy way to allow everyone to have read-only access.'),
+ icon: 'project',
+ href: trending_explore_projects_path,
+ variant: 'default')
- = link_to trending_explore_projects_path, class: link_classes do
- .blank-state-icon
- = custom_icon("globe", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Explore public projects')
- %p
- = _('Public projects are an easy way to allow everyone to have read-only access.')
+ - if current_user.can_create_group?
+ = render Onboarding::ActionCardComponent.new(title: _('Create a group'),
+ description: _('Groups are the best way to manage projects and members.'),
+ icon: 'group',
+ href: new_group_path,
+ variant: 'default')
- = link_to Gitlab::Saas::doc_url, class: link_classes do
- .blank-state-icon
- = custom_icon("lightbulb", size: 50)
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Learn more about GitLab')
- %p
- = _('Take a look at the documentation to discover all of GitLab’s capabilities.')
+ = render Onboarding::ActionCardComponent.new(title: _('Learn more about GitLab'),
+ description: _('Take a look at the documentation to discover all of GitLab’s capabilities.'),
+ icon: 'bulb',
+ href: Gitlab::Saas::doc_url,
+ variant: 'default')
diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
index ba4e8961f28f3a1aa2705030e4df5bb882e5ac29..d48590d06956210c713049cc7c36ccbd295130cf 100644
--- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
@@ -1,11 +1,9 @@
-.container
- = render_if_exists 'dashboard/projects/joining_a_project_alert'
- .gl-text-center.gl-pt-6.gl-pb-7
- %h2.gl-font-size-h1{ data: { testid: 'welcome-title-content' } }
- = _('Welcome to GitLab')
- %p.gl-m-0
- = _('Faster releases. Better code. Less pain.')
- - if current_user.admin?
- = render "blank_state_admin_welcome"
- - else
- = render "blank_state_welcome"
+%h1.page-title.gl-text-size-h-display.gl-text-center.gl-mt-11{ data: { testid: 'welcome-title-content' } }
+ = safe_format(_('Welcome to GitLab, %{name}!'), name: current_user.first_name)
+%p.gl-text-center.gl-mb-7= _('Ready to get started with GitLab? Follow these steps to get familiar with us:')
+
+= render_if_exists 'dashboard/projects/joining_a_project_alert'
+- if current_user.admin?
+ = render "blank_state_admin_welcome"
+- else
+ = render "blank_state_welcome"
diff --git a/app/views/shared/icons/_add_new_group.svg b/app/views/shared/icons/_add_new_group.svg
deleted file mode 100644
index ecd52c5e99fd571670fe3346e7370fd54c85bb3f..0000000000000000000000000000000000000000
--- a/app/views/shared/icons/_add_new_group.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/app/views/shared/icons/_add_new_project.svg b/app/views/shared/icons/_add_new_project.svg
deleted file mode 100644
index 2a29a0ec9c41ae4b7d9a12fef77ba45727fea719..0000000000000000000000000000000000000000
--- a/app/views/shared/icons/_add_new_project.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/app/views/shared/icons/_globe.svg b/app/views/shared/icons/_globe.svg
deleted file mode 100644
index 2322375ee98ab5aef5db963d29d7f9491f38bdbc..0000000000000000000000000000000000000000
--- a/app/views/shared/icons/_globe.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/shared/icons/_lightbulb.svg b/app/views/shared/icons/_lightbulb.svg
deleted file mode 100644
index 6be5005cdc67df38c97a256edf045bb8bc4f7419..0000000000000000000000000000000000000000
--- a/app/views/shared/icons/_lightbulb.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml b/ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml
index bd02844a684ce046469c0c06b19e819c038988c1..90dabcaf03675a20a9790fb43ac9e67eb96e9078 100644
--- a/ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml
+++ b/ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml
@@ -1,11 +1,7 @@
-# EE:Self Managed
-.blank-state.gl-display-flex.gl-align-items-center.gl-border-1.gl-border-solid.gl-border-gray-100.gl-rounded-base.gl-mb-5
- .blank-state-icon
- = image_tag("illustrations/lock_promotion.svg", { lazy: false })
- .blank-state-body.gl-sm-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Unlock more features with GitLab Ultimate')
- %p
- = _('GitLab is free to use. Many features for larger teams are part of our %{link_start}paid products%{link_end}. You can try Ultimate for free without any obligation or payment details.').html_safe % { link_start: ''.html_safe, link_end: ''.html_safe }
- = link_button_to new_trial_url, variant: :confirm do
- = _('Start free trial')
+= render Onboarding::ActionCardComponent.new(title: _('Unlock more features with GitLab Ultimate'),
+ description: _('GitLab is free to use. Many features for larger teams are part of our %{link_start}paid products%{link_end}. You can try Ultimate for free without any obligation or payment details.').html_safe % { link_start: ''.html_safe, link_end: ''.html_safe },
+ icon: 'lock',
+ variant: 'default') do
+ = link_button_to new_trial_url, variant: :confirm do
+ = _('Start free trial')
diff --git a/ee/spec/support/helpers/saas_registration_helpers.rb b/ee/spec/support/helpers/saas_registration_helpers.rb
index 718a1877f806fdefb264f1f80fba84b61d5082b7..2eaedca9173bf0e662a2d0c6690e49cf678e6a60 100644
--- a/ee/spec/support/helpers/saas_registration_helpers.rb
+++ b/ee/spec/support/helpers/saas_registration_helpers.rb
@@ -312,7 +312,7 @@ def expect_to_be_on_projects_dashboard
def expect_to_be_on_projects_dashboard_with_zero_authorized_projects
expect(page).to have_content 'Welcome to GitLab'
- expect(page).to have_content 'Faster releases. Better code. Less pain.'
+ expect(page).to have_content 'Ready to get started with GitLab? Follow these steps to get familiar with us:'
page.within('[data-testid="joining-a-project-alert"') do
expect(page).to have_content 'Looking for your team?'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 01ebf1c9cea9aa253dabd58e530beb74d741e7c7..d7a50901540e28dcd1c7fcd65e56d77cc4aade95 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -22158,9 +22158,6 @@ msgstr ""
msgid "Fast timeout"
msgstr ""
-msgid "Faster releases. Better code. Less pain."
-msgstr ""
-
msgid "Favicon"
msgstr ""
@@ -42206,7 +42203,7 @@ msgstr ""
msgid "Projects are graded based on the highest severity vulnerability present"
msgstr ""
-msgid "Projects are where you store your code, access issues, wiki and other features of GitLab."
+msgid "Projects are where you store your code, access issues, wiki, and other features of GitLab."
msgstr ""
msgid "Projects contributed to"
@@ -43499,6 +43496,9 @@ msgstr ""
msgid "Read their documentation."
msgstr ""
+msgid "Ready to get started with GitLab? Follow these steps to get familiar with us:"
+msgstr ""
+
msgid "Ready to merge by members who can write to the target branch."
msgstr ""
@@ -59578,10 +59578,10 @@ msgstr ""
msgid "Welcome back! Your account had been deactivated due to inactivity but is now reactivated."
msgstr ""
-msgid "Welcome to GitLab"
+msgid "Welcome to GitLab, %{first_name}!"
msgstr ""
-msgid "Welcome to GitLab, %{first_name}!"
+msgid "Welcome to GitLab, %{name}!"
msgstr ""
msgid "Welcome to GitLab,%{br_tag}%{name}!"
@@ -61431,9 +61431,6 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
-msgid "You see projects here when you're added to a group or project."
-msgstr ""
-
msgid "You should run a new pipeline, because the target branch has changed for this merge request."
msgstr ""
diff --git a/spec/components/onboarding/action_card_component_spec.rb b/spec/components/onboarding/action_card_component_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5ce3a7c45c0fe33ee1996e6740f7d4d63771e9b3
--- /dev/null
+++ b/spec/components/onboarding/action_card_component_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Onboarding::ActionCardComponent, type: :component, feature_category: :shared do
+ let(:icon) { 'group' }
+ let(:title) { 'Create a group' }
+ let(:description) { 'Groups are the best way to manage projects and members' }
+ let(:href) { nil }
+ let(:link_options) { {} }
+ let(:html_options) { {} }
+ let(:variant) { :default }
+
+ before do
+ render_inline described_class.new(icon: icon,
+ title: title,
+ description: description,
+ href: href,
+ variant: variant,
+ **link_options,
+ **html_options
+ )
+ end
+
+ describe 'default appearance' do
+ it 'has icon' do
+ expect(page).to have_css "svg[data-testid='group-icon']"
+ end
+
+ it 'has title' do
+ expect(page).to have_css ".action-card-title"
+ end
+
+ it 'has description' do
+ expect(page).to have_css ".action-card-text"
+ end
+ end
+
+ describe 'variants' do
+ context 'when variant is default' do
+ it 'renders the card in correct variant' do
+ expect(page).to have_css ".action-card-default"
+ end
+ end
+
+ context 'when variant is success' do
+ let(:variant) { :success }
+
+ it 'renders the card in correct variant' do
+ expect(page).to have_css ".action-card-success"
+ end
+
+ it 'renders the check-mark icon' do
+ expect(page).to have_css "svg[data-testid='check-icon']"
+ end
+ end
+
+ context 'when variant is promo' do
+ let(:variant) { :promo }
+
+ it 'renders the card in correct variant' do
+ expect(page).to have_css ".action-card-promo"
+ end
+ end
+ end
+
+ context 'when link href is defined' do
+ let(:href) { 'gitlab.com' }
+
+ it 'has link' do
+ expect(page).to have_css ".action-card a"
+ end
+
+ it 'has link arrow' do
+ expect(page).to have_css ".action-card-arrow[data-testid='arrow-right-icon']"
+ end
+ end
+
+ context 'with custom card options' do
+ let(:html_options) { { data: { testid: 'card_test_id' } } }
+
+ it 'sets the testid' do
+ expect(page).to have_selector('.action-card[data-testid="card_test_id"]')
+ end
+ end
+
+ context 'with custom link options' do
+ let(:link_options) { { data: { testid: 'link_test_id' } } }
+
+ it 'sets the testid' do
+ expect(page).to have_selector('[data-testid="link_test_id"]')
+ end
+ end
+end
diff --git a/spec/components/onboarding/component_spec.rb b/spec/components/onboarding/component_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..db4ed0485ef69b758bb03e2372326408a9657fbd
--- /dev/null
+++ b/spec/components/onboarding/component_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Onboarding::Component, type: :component, feature_category: :shared do
+ subject(:component) { described_class.new }
+
+ describe '#filter_attribute' do
+ let(:allowed) { %w[default something] }
+
+ it 'returns default value when no value is given' do
+ value = component.send(:filter_attribute, nil, allowed, default: 'default')
+
+ expect(value).to eq('default')
+ end
+
+ it 'returns default value when invalid value is given' do
+ value = component.send(:filter_attribute, 'invalid', allowed, default: 'default')
+
+ expect(value).to eq('default')
+ end
+
+ it 'returns given value when it is part of allowed list' do
+ value = component.send(:filter_attribute, 'something', allowed, default: 'default')
+
+ expect(value).to eq('something')
+ end
+ end
+
+ describe '#format_options' do
+ it 'merges CSS classes and additional options' do
+ expect(
+ component.send(
+ :format_options,
+ options: { foo: 'bar', class: 'gl-flex gl-py-5' },
+ css_classes: %w[gl-px-5 gl-mt-5],
+ additional_options: { baz: 'bax' }
+ )
+ ).to match({
+ foo: 'bar',
+ baz: 'bax',
+ class: ['gl-px-5', 'gl-mt-5', 'gl-flex gl-py-5']
+ })
+ end
+ end
+end
diff --git a/spec/components/previews/onboarding/action_card_component_preview.rb b/spec/components/previews/onboarding/action_card_component_preview.rb
new file mode 100644
index 0000000000000000000000000000000000000000..798a56caf10db5dbf270e1f2b40e51ca91d78e54
--- /dev/null
+++ b/spec/components/previews/onboarding/action_card_component_preview.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class ActionCardComponentPreview < ViewComponent::Preview
+ # Action card
+ # ---
+ #
+ # @param icon select [~, star-o, issue-closed, group]
+ # @param href url
+ # @param description text
+ # @param title text
+ # @param variant select {{ Onboarding::ActionCardComponent::VARIANT_OPTIONS }}
+ def default(
+ icon: :group,
+ variant: :default,
+ href: "gitlab.com",
+ description: "Groups are the best way to manage projects and members",
+ title: "Create a group")
+ render Onboarding::ActionCardComponent.new(
+ title,
+ description,
+ icon: icon,
+ href: href,
+ variant: variant
+ )
+ end
+ end
+end
diff --git a/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb b/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb
index 6f6596caabba55111856df3539a638d45605493c..3a80f38e78df1a912d280e1ce2a6e2d0a4cfa8fc 100644
--- a/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb
+++ b/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb
@@ -36,12 +36,6 @@
expect(rendered).not_to include(_('Create a project'))
end
-
- it 'shows an alert' do
- render
-
- expect(rendered).to include(_("You see projects here when you're added to a group or project."))
- end
end
end
@@ -57,11 +51,5 @@
expect(rendered).not_to include(_('Create a project'))
end
-
- it 'shows an alert' do
- render
-
- expect(rendered).to include(_("You see projects here when you're added to a group or project."))
- end
end
end