diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index 4ede8fda01d42d0edbdf90cb597e7b9f21764b01..6e3ad6b614a535beb0f280ad328928e219f491cb 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -1,4 +1,5 @@ <script> +import emptySearchSvgUrl from '@gitlab/svgs/dist/illustrations/empty-state/empty-search-md.svg?url'; import { GlLoadingIcon, GlModal, GlEmptyState } from '@gitlab/ui'; import { createAlert } from '~/alert'; import { HTTP_STATUS_FORBIDDEN } from '~/lib/utils/http_status'; @@ -22,7 +23,6 @@ export default { GlLoadingIcon, GlEmptyState, }, - inject: ['emptySearchIllustration'], props: { action: { type: String, @@ -231,6 +231,7 @@ export default { } }, }, + emptySearchSvgUrl, }; </script> @@ -246,7 +247,7 @@ export default { <groups-component v-if="hasGroups" :groups="groups" :page-info="pageInfo" :action="action" /> <gl-empty-state v-else-if="fromSearch" - :svg-path="emptySearchIllustration" + :svg-path="$options.emptySearchSvgUrl" :title="$options.i18n.searchEmptyState.title" :description="$options.i18n.searchEmptyState.description" data-testid="search-empty-state" diff --git a/app/assets/javascripts/groups/components/empty_states/groups_dashboard_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/groups_dashboard_empty_state.vue index e74d827af9ba0b9a38fc17733c961f538eeac0a2..812e78742f3b014f77f352d5e6071a2fdddbd431 100644 --- a/app/assets/javascripts/groups/components/empty_states/groups_dashboard_empty_state.vue +++ b/app/assets/javascripts/groups/components/empty_states/groups_dashboard_empty_state.vue @@ -5,12 +5,14 @@ import { s__ } from '~/locale'; export default { components: { GlEmptyState }, - inject: ['groupsEmptyStateIllustration'], + inject: ['groupsEmptyStateIllustration', 'newGroupPath', 'exploreGroupsPath'], i18n: { title: s__('GroupsEmptyState|A group is a collection of several projects'), description: s__( - "GroupsEmptyState|If you organize your projects under a group, it works like a folder. You can manage your group member's permissions and access to each project in the group.", + 'GroupsEmptyState|If you organize your projects under a group, it works like a folder. You can manage the permissions and access of your group members for each project in the group.', ), + secondary_button_text: s__('GroupsEmptyState|Explore groups'), + primary_button_text: s__('GroupsEmptyState|New group'), }, }; </script> @@ -20,6 +22,10 @@ export default { :title="$options.i18n.title" :description="$options.i18n.description" :svg-path="groupsEmptyStateIllustration" - :svg-height="null" + :primary-button-text="$options.i18n.primary_button_text" + :primary-button-link="newGroupPath" + :secondary-button-text="$options.i18n.secondary_button_text" + :secondary-button-link="exploreGroupsPath" + data-testid="groups-empty-state" /> </template> diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index a1147ec312146dd7de6e5b2cb32cef20c60b441d..7c6c34d664044488e49abf859c95dadce4e27c6d 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -37,9 +37,19 @@ export default (EmptyStateComponent) => { GroupsApp, }, provide() { - const { groupsEmptyStateIllustration, emptySearchIllustration } = dataset; + const { + groupsEmptyStateIllustration, + emptySearchIllustration, + newGroupPath, + exploreGroupsPath, + } = dataset; - return { groupsEmptyStateIllustration, emptySearchIllustration }; + return { + groupsEmptyStateIllustration, + emptySearchIllustration, + newGroupPath, + exploreGroupsPath, + }; }, data() { const showSchemaMarkup = parseBoolean(dataset.showSchemaMarkup); diff --git a/app/assets/javascripts/groups/init_overview_tabs.js b/app/assets/javascripts/groups/init_overview_tabs.js index 2f03705b453d4e9556f6af67cf67a821f2b3b4b1..80dd1d3673497135e385203b391b7c548313355e 100644 --- a/app/assets/javascripts/groups/init_overview_tabs.js +++ b/app/assets/javascripts/groups/init_overview_tabs.js @@ -47,7 +47,6 @@ export const initGroupOverviewTabs = () => { newProjectIllustration, emptyProjectsIllustration, emptySubgroupIllustration, - emptySearchIllustration, canCreateSubgroups, canCreateProjects, currentGroupVisibility, @@ -68,7 +67,6 @@ export const initGroupOverviewTabs = () => { newProjectIllustration, emptyProjectsIllustration, emptySubgroupIllustration, - emptySearchIllustration, canCreateSubgroups: parseBoolean(canCreateSubgroups), canCreateProjects: parseBoolean(canCreateProjects), currentGroupVisibility, diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 9e3b856e9565ee95cc31162d68e7fc68c6b07158..6c00a92b149b3c8178225ca6bb1a617acec65a5e 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -155,7 +155,6 @@ def group_overview_tabs_app_data(group) new_project_illustration: image_path('illustrations/project-create-new-sm.svg'), empty_projects_illustration: image_path('illustrations/empty-state/empty-projects-md.svg'), empty_subgroup_illustration: image_path('illustrations/empty-state/empty-subgroup-md.svg'), - empty_search_illustration: image_path('illustrations/empty-state/empty-search-md.svg'), render_empty_state: 'true', can_create_subgroups: can?(current_user, :create_subgroup, group).to_s, can_create_projects: can?(current_user, :create_projects, group).to_s diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml index 52e832f2482b409c02a6b1ff4328efd2e5d4ef4b..762a1fe8ebe341ad139afbc6a07cb098c8fc5d97 100644 --- a/app/views/dashboard/_groups_head.html.haml +++ b/app/views/dashboard/_groups_head.html.haml @@ -1,13 +1,14 @@ -.page-title-holder.d-flex.gl-align-items-center - %h1.page-title.gl-font-size-h-display= _('Groups') +- if current_user.groups.length > 0 + .page-title-holder.d-flex.gl-align-items-center + %h1.page-title.gl-font-size-h-display= _('Groups') - .page-title-controls.gl-display-flex.gl-align-items-center.gl-gap-5 - = link_to _("Explore groups"), explore_groups_path - - if current_user.can_create_group? - = render Pajamas::ButtonComponent.new(href: new_group_path, variant: :confirm, button_options: { data: { testid: "new-group-button" } }) do - = _("New group") + .page-title-controls.gl-display-flex.gl-align-items-center.gl-gap-5 + = link_to _("Explore groups"), explore_groups_path + - if current_user.can_create_group? + = render Pajamas::ButtonComponent.new(href: new_group_path, variant: :confirm, button_options: { data: { testid: "new-group-button" } }) do + = _("New group") -.top-area.gl-py-3.gl-justify-content-end.gl-border-bottom-0 - .nav-controls - = render 'shared/groups/search_form' - = render 'shared/groups/dropdown' + .top-area.gl-py-3.gl-justify-content-end.gl-border-bottom-0 + .nav-controls + = render 'shared/groups/search_form' + = render 'shared/groups/dropdown' diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml index fbe443a2e369c0b5b174cb2b9d09df8746f0a36d..bc73a2fc7cd83ee4f246321e4e80148e852459c0 100644 --- a/app/views/dashboard/groups/_groups.html.haml +++ b/app/views/dashboard/groups/_groups.html.haml @@ -1,2 +1,2 @@ .js-groups-list-holder - #js-groups-tree{ data: { endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap', groups_empty_state_illustration: image_path('illustrations/empty-state/empty-groups-md.svg'), empty_search_illustration: image_path('illustrations/empty-state/empty-search-md.svg') } } + #js-groups-tree{ data: { endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, new_group_path: new_group_path, explore_groups_path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap', groups_empty_state_illustration: image_path('illustrations/empty-state/empty-groups-md.svg'), empty_search_illustration: image_path('illustrations/empty-state/empty-search-md.svg') } } diff --git a/app/views/explore/groups/_nav.html.haml b/app/views/explore/groups/_nav.html.haml index 176bfd307b26a96a07773fe95ea2955b6f026f8a..09fb30af72b78566c61cb34510d5a163135d613f 100644 --- a/app/views/explore/groups/_nav.html.haml +++ b/app/views/explore/groups/_nav.html.haml @@ -1,4 +1,4 @@ -.top-area.gl-p-3.gl-justify-content-end +.top-area.gl-py-3.gl-border-0.gl-justify-content-end .nav-controls = render 'shared/groups/search_form' = render 'shared/groups/dropdown' diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index ddb82411add0abf31df704d9fc40ed8121c7bfff..d12fb84f4f9e7f975bdb95653cf7550f817c6c89 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -11,15 +11,7 @@ = render Pajamas::ButtonComponent.new(href: new_group_path, variant: :confirm) do = _("New group") -= render 'nav' - -- if cookies[:explore_groups_landing_dismissed] != 'true' - .explore-groups.landing.content-block.js-explore-groups-landing.hide - %button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= sprite_icon('close') - .svg-container - = custom_icon('icon_explore_groups_splash') - .inner-content - %p= _("Below you will find all the groups that are public.") - %p= _("You can easily contribute to them by requesting to join these groups.") +%p= _("Below you will find all the groups that are public. You can easily contribute to them by requesting to join these groups.") += render 'nav' = render 'groups' diff --git a/locale/gitlab.pot b/locale/gitlab.pot index eb1f9c3c53dec7dc5528729c66de426f7790f4df..7e0d1fdaea449800868b3ffc007f1a5ca0353ddd 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7688,7 +7688,7 @@ msgstr "" msgid "Below are the settings for %{link_to_gitlab_pages}." msgstr "" -msgid "Below you will find all the groups that are public." +msgid "Below you will find all the groups that are public. You can easily contribute to them by requesting to join these groups." msgstr "" msgid "Beta" @@ -24137,10 +24137,16 @@ msgstr "" msgid "GroupsEmptyState|Create new subgroup" msgstr "" +msgid "GroupsEmptyState|Explore groups" +msgstr "" + msgid "GroupsEmptyState|Groups are the best way to manage multiple projects and members." msgstr "" -msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder. You can manage your group member's permissions and access to each project in the group." +msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder. You can manage the permissions and access of your group members for each project in the group." +msgstr "" + +msgid "GroupsEmptyState|New group" msgstr "" msgid "GroupsEmptyState|No archived projects." @@ -57090,9 +57096,6 @@ msgstr "" msgid "You can create new ones at your Personal Access Tokens settings %{pat_link}" msgstr "" -msgid "You can easily contribute to them by requesting to join these groups." -msgstr "" - msgid "You can enable Registration Features because Service Ping is enabled. To continue using Registration Features in the future, you will also need to register with GitLab via a new cloud licensing service." msgstr "" diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb index 7510a92e19b4fca12f88d37e410a3c2f9b945c44..aac2336f5ecf24cb5d030a9ba9457ea6c7c081c1 100644 --- a/spec/features/dashboard/group_spec.rb +++ b/spec/features/dashboard/group_spec.rb @@ -7,15 +7,15 @@ sign_in(create(:user)) end - it 'defaults sort dropdown to last created' do + it 'shows empty state', :js do visit dashboard_groups_path - expect(page).to have_button('Last created') + expect(page).to have_selector('[data-testid="groups-empty-state"]') end it 'creates new group', :js do visit dashboard_groups_path - find_by_testid('new-group-button').click + click_link 'New group' click_link 'Create group' new_name = 'Samurai' @@ -26,4 +26,14 @@ expect(page).to have_current_path group_path(Group.find_by(name: new_name)), ignore_query: true expect(page).to have_content(new_name) end + + it 'defaults sort dropdown to last created' do + user = create(:user) + group = create(:group) + group.add_owner(user) + sign_in(user) + visit dashboard_groups_path + + expect(page).to have_selector('[data-testid="group_sort_by_dropdown"]') + end end diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index e1614aec323bf80a993a452cc7aa436adafb19c0..85f1eda4dacde6bd522e1cf8f3a84007640be0bb 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -32,7 +32,7 @@ find('body').send_keys([:shift, 'G']) - check_page_title('Groups') + expect(page).to have_selector('.js-groups-list-holder') find('body').send_keys([:shift, 'P']) diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb index 91ee6d48a4858fa5cb3f970597cc41c18635d3f1..6048220f526d497b5c3472469ef362549f067214 100644 --- a/spec/features/explore/groups_list_spec.rb +++ b/spec/features/explore/groups_list_spec.rb @@ -70,26 +70,6 @@ # Check project count expect(find('.js-groups-list-holder .groups-list li:first-child .stats .number-projects')).to have_text("1") end - - describe 'landing component' do - it 'shows a landing component' do - expect(page).to have_content('Below you will find all the groups that are public.') - end - - it 'is dismissable' do - find('.dismiss-button').click - - expect(page).not_to have_content('Below you will find all the groups that are public.') - end - - it 'does not show persistently once dismissed' do - find('.dismiss-button').click - - visit explore_groups_path - - expect(page).not_to have_content('Below you will find all the groups that are public.') - end - end end context 'when there are no groups to show' do diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js index 027c1709e0b47f2576288dd83607a2c78e3eb006..8ac410c87b180b93310e3068d534490966fc2dad 100644 --- a/spec/frontend/groups/components/app_spec.js +++ b/spec/frontend/groups/components/app_spec.js @@ -58,9 +58,6 @@ describe('AppComponent', () => { mocks: { $toast, }, - provide: { - emptySearchIllustration: '/assets/illustrations/empty-state/empty-search-md.svg', - }, }); vm = wrapper.vm; }; diff --git a/spec/frontend/groups/components/empty_states/groups_dashboard_empty_state_spec.js b/spec/frontend/groups/components/empty_states/groups_dashboard_empty_state_spec.js index d2afbad802cb2624c5506b33bf9d046ffbed6bc2..67e3d5f5e86cc02a3c9cbeb08337d3ca193a16d8 100644 --- a/spec/frontend/groups/components/empty_states/groups_dashboard_empty_state_spec.js +++ b/spec/frontend/groups/components/empty_states/groups_dashboard_empty_state_spec.js @@ -7,6 +7,8 @@ let wrapper; const defaultProvide = { groupsEmptyStateIllustration: '/assets/illustrations/empty-state/empty-groups-md.svg', + newGroupPath: '/groups/new', + exploreGroupsPath: '/explore/groups', }; const createComponent = () => { @@ -22,8 +24,12 @@ describe('GroupsDashboardEmptyState', () => { expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({ title: 'A group is a collection of several projects', description: - "If you organize your projects under a group, it works like a folder. You can manage your group member's permissions and access to each project in the group.", + 'If you organize your projects under a group, it works like a folder. You can manage the permissions and access of your group members for each project in the group.', svgPath: defaultProvide.groupsEmptyStateIllustration, + primaryButtonText: 'New group', + primaryButtonLink: defaultProvide.newGroupPath, + secondaryButtonText: 'Explore groups', + secondaryButtonLink: defaultProvide.exploreGroupsPath, }); }); }); diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js index 8b80330c9105a3546bf4afdd33427aadfc669fc6..6c9681180b05ecf5c3eec5f7b1378918a8593034 100644 --- a/spec/frontend/groups/components/overview_tabs_spec.js +++ b/spec/frontend/groups/components/overview_tabs_spec.js @@ -45,7 +45,6 @@ describe('OverviewTabs', () => { newProjectIllustration: '', emptyProjectsIllustration: '', emptySubgroupIllustration: '', - emptySearchIllustration: '', canCreateSubgroups: false, canCreateProjects: false, initialSort: 'name_asc', diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index d73fb0f2d4c6d8f3b10df9ee9a9e3aec7def5461..87add426c1bda4ad49f0f42a2c6f82975285184f 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -490,7 +490,6 @@ new_project_illustration: including('illustrations/project-create-new-sm'), empty_projects_illustration: including('illustrations/empty-state/empty-projects-md'), empty_subgroup_illustration: including('illustrations/empty-state/empty-subgroup-md'), - empty_search_illustration: including('illustrations/empty-state/empty-search-md'), render_empty_state: 'true', can_create_subgroups: 'true', can_create_projects: 'true'