Skip to content
代码片段 群组 项目
未验证 提交 b5bb479e 编辑于 作者: Peter Hegman's avatar Peter Hegman 提交者: GitLab
浏览文件

Setup new group from organization route, controller, and Vue app

Allows user to create a group in an organization from the UI
上级 5ae5634e
No related branches found
No related tags found
无相关合并请求
显示
319 个添加43 个删除
<script>
import { GlSprintf, GlLink } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
export default {
name: 'OrganizationGroupsNewApp',
i18n: {
pageTitle: __('New group'),
description1: s__(
'GroupsNew|%{linkStart}Groups%{linkEnd} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects.',
),
description2: s__(
'GroupsNew|Groups can also be nested by creating %{linkStart}subgroups%{linkEnd}.',
),
},
groupsHelpPagePath: helpPagePath('user/group/index'),
subgroupsHelpPagePath: helpPagePath('user/group/subgroups/index'),
components: {
GlLink,
GlSprintf,
},
inject: [
'organizationId',
'basePath',
'groupsOrganizationPath',
'mattermostEnabled',
'availableVisibilityLevels',
'restrictedVisibilityLevels',
],
};
</script>
<template>
<div class="gl-py-6">
<h1 class="gl-mt-0 gl-font-size-h-display">{{ $options.i18n.pageTitle }}</h1>
<p>
<gl-sprintf :message="$options.i18n.description1">
<template #link="{ content }">
<gl-link :href="$options.groupsHelpPagePath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
<p>
<gl-sprintf :message="$options.i18n.description2">
<template #link="{ content }">
<gl-link :href="$options.subgroupsHelpPagePath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</div>
</template>
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import createDefaultClient from '~/lib/graphql';
import App from './components/app.vue';
export const initOrganizationsGroupsNew = () => {
const el = document.getElementById('js-organizations-groups-new');
if (!el) return false;
const {
dataset: { appData },
} = el;
const {
organizationId,
basePath,
groupsOrganizationPath,
mattermostEnabled,
availableVisibilityLevels,
restrictedVisibilityLevels,
} = convertObjectPropsToCamelCase(JSON.parse(appData));
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
return new Vue({
el,
name: 'OrganizationGroupsNewRoot',
apolloProvider,
provide: {
organizationId,
basePath,
groupsOrganizationPath,
mattermostEnabled,
availableVisibilityLevels,
restrictedVisibilityLevels,
},
render(createElement) {
return createElement(App);
},
});
};
import { initOrganizationsGroupsNew } from '~/organizations/groups/new';
initOrganizationsGroupsNew();
......@@ -35,5 +35,9 @@ def authorize_read_organization_user!
def authorize_admin_organization!
access_denied! unless can?(current_user, :admin_organization, organization)
end
def authorize_create_group!
access_denied! unless can?(current_user, :create_group, organization)
end
end
end
# frozen_string_literal: true
module Organizations
class GroupsController < ApplicationController
feature_category :cell
def new
authorize_create_group!
end
end
end
......@@ -2,6 +2,12 @@
module Organizations
module OrganizationHelper
def organization_layout_nav
return 'organization' unless current_controller?('organizations')
current_action?(:index, :new) ? "your_work" : "organization"
end
def organization_show_app_data(organization)
{
organization: organization.slice(:id, :name, :description_html)
......@@ -52,6 +58,17 @@ def home_organization_setting_app_data
}.to_json
end
def organization_groups_new_app_data(organization)
{
organization_id: organization.id,
base_path: root_url,
groups_organization_path: groups_and_projects_organization_path(organization, { display: 'groups' }),
mattermost_enabled: Gitlab.config.mattermost.enabled,
available_visibility_levels: available_visibility_levels(Group),
restricted_visibility_levels: restricted_visibility_levels
}.to_json
end
private
def shared_groups_and_projects_app_data(organization)
......
- page_title @organization.name if @organization
- header_title @organization.name, organization_path(@organization) if @organization
- nav(%w[index new].include?(params[:action]) ? "your_work" : "organization")
- nav(organization_layout_nav)
= render template: "layouts/application"
- page_title _('New group')
- add_to_breadcrumbs _('Groups and projects'), groups_and_projects_organization_path(@organization)
#js-organizations-groups-new{ data: { app_data: organization_groups_new_app_data(@organization) } }
......@@ -17,5 +17,7 @@
resource :settings, only: [], as: :settings_organization do
get :general
end
resource :groups, only: [:new], as: :groups_organization
end
end
......@@ -33,7 +33,12 @@ def groups_and_projects_menu_item
title: _('Groups and projects'),
link: groups_and_projects_organization_path(context.container),
super_sidebar_parent: ::Sidebars::Organizations::Menus::ManageMenu,
active_routes: { path: 'organizations/organizations#groups_and_projects' },
active_routes: {
path: %w[
organizations/organizations#groups_and_projects
organizations/groups#new
]
},
item_id: :organization_groups_and_projects
)
)
......
import { GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import App from '~/organizations/groups/new/components/app.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
describe('OrganizationGroupsNewApp', () => {
let wrapper;
const defaultProvide = {
organizationId: 1,
basePath: 'https://gitlab.com',
groupsOrganizationPath: '/-/organizations/carrot/groups_and_projects?display=groups',
mattermostEnabled: false,
availableVisibilityLevels: [0, 10, 20],
restrictedVisibilityLevels: [],
};
const createComponent = () => {
wrapper = shallowMountExtended(App, {
provide: defaultProvide,
stubs: {
GlSprintf,
GlLink,
},
});
};
const findAllParagraphs = () => wrapper.findAll('p');
const findAllLinks = () => wrapper.findAllComponents(GlLink);
it('renders page title and description', () => {
createComponent();
expect(wrapper.findByRole('heading', { name: 'New group' }).exists()).toBe(true);
expect(findAllParagraphs().at(0).text()).toMatchInterpolatedText(
'Groups allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects.',
);
expect(findAllLinks().at(0).attributes('href')).toBe(helpPagePath('user/group/index'));
expect(findAllParagraphs().at(1).text()).toContain(
'Groups can also be nested by creating subgroups.',
);
expect(findAllLinks().at(1).attributes('href')).toBe(
helpPagePath('user/group/subgroups/index'),
);
});
});
......@@ -14,6 +14,7 @@
let_it_be(:groups_empty_state_svg_path) { 'illustrations/empty-state/empty-groups-md.svg' }
let_it_be(:projects_empty_state_svg_path) { 'illustrations/empty-state/empty-projects-md.svg' }
let_it_be(:preview_markdown_organizations_path) { '/-/organizations/preview_markdown' }
let_it_be(:groups_and_projects_organization_path) { '/-/organizations/default/groups_and_projects' }
before do
allow(organization).to receive(:to_global_id).and_return(organization_gid)
......@@ -28,11 +29,43 @@
allow(helper).to receive(:preview_markdown_organizations_path).and_return(preview_markdown_organizations_path)
end
describe '#organization_layout_nav' do
context 'when current controller is not organizations' do
it 'returns organization' do
allow(helper).to receive(:current_controller?).with('organizations').and_return(false)
expect(helper.organization_layout_nav).to eq('organization')
end
end
context 'when current controller is organizations' do
before do
allow(helper).to receive(:current_controller?).with('organizations').and_return(true)
end
context 'when current action is index or new' do
it 'returns your_work' do
allow(helper).to receive(:current_action?).with(:index, :new).and_return(true)
expect(helper.organization_layout_nav).to eq('your_work')
end
end
context 'when current action is not index or new' do
it 'returns organization' do
allow(helper).to receive(:current_action?).with(:index, :new).and_return(false)
expect(helper.organization_layout_nav).to eq('organization')
end
end
end
end
describe '#organization_show_app_data' do
before do
allow(helper).to receive(:groups_and_projects_organization_path)
.with(organization)
.and_return('/-/organizations/default/groups_and_projects')
.and_return(groups_and_projects_organization_path)
end
it 'returns expected json' do
......@@ -50,7 +83,7 @@
'description_html' => organization.description_html,
'avatar_url' => 'avatar.jpg'
},
'groups_and_projects_organization_path' => '/-/organizations/default/groups_and_projects',
'groups_and_projects_organization_path' => groups_and_projects_organization_path,
'new_group_path' => new_group_path,
'new_project_path' => new_project_path,
'groups_empty_state_svg_path' => groups_empty_state_svg_path,
......@@ -151,4 +184,30 @@
)
end
end
describe '#organization_groups_new_app_data' do
before do
allow(helper).to receive(:groups_and_projects_organization_path)
.with(organization, { display: 'groups' })
.and_return(groups_and_projects_organization_path)
allow(helper).to receive(:restricted_visibility_levels).and_return([])
end
it 'returns expected json' do
expect(Gitlab::Json.parse(helper.organization_groups_new_app_data(organization))).to eq(
{
'organization_id' => organization.id,
'base_path' => root_url,
'groups_organization_path' => groups_and_projects_organization_path,
'mattermost_enabled' => false,
'available_visibility_levels' => [
Gitlab::VisibilityLevel::PRIVATE,
Gitlab::VisibilityLevel::INTERNAL,
Gitlab::VisibilityLevel::PUBLIC
],
'restricted_visibility_levels' => []
}
)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Organizations::GroupsController, feature_category: :cell do
let_it_be(:organization) { create(:organization) }
describe 'GET #new' do
subject(:gitlab_request) { get new_groups_organization_path(organization) }
context 'when the user is not signed in' do
it_behaves_like 'organization - redirects to sign in page'
context 'when `ui_for_organizations` feature flag is disabled' do
before do
stub_feature_flags(ui_for_organizations: false)
end
it_behaves_like 'organization - redirects to sign in page'
end
end
context 'when the user is signed in' do
let_it_be(:user) { create(:user) }
before do
sign_in(user)
end
context 'with no association to an organization' do
it_behaves_like 'organization - not found response'
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
end
context 'as as admin', :enable_admin_mode do
let_it_be(:user) { create(:admin) }
it_behaves_like 'organization - successful response'
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
end
context 'as an organization user' do
let_it_be(:organization_user) { create(:organization_user, organization: organization, user: user) }
it_behaves_like 'organization - successful response'
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Organizations::GroupsController, :routing, feature_category: :cell do
let_it_be(:organization) { build(:organization) }
it 'routes to groups#new' do
expect(get("/-/organizations/#{organization.path}/groups/new"))
.to route_to('organizations/groups#new', organization_path: organization.path)
end
end
......@@ -12,48 +12,13 @@
allow(view).to receive(:users_path).and_return('/root')
end
subject do
render
rendered
end
describe 'navigation' do
context 'when action is #index' do
before do
allow(view).to receive(:params).and_return({ action: 'index' })
end
it 'renders your_work navigation' do
subject
expect(view.instance_variable_get(:@nav)).to eq('your_work')
end
end
context 'when action is #new' do
before do
allow(view).to receive(:params).and_return({ action: 'new' })
end
it 'renders your_work navigation' do
subject
expect(view.instance_variable_get(:@nav)).to eq('your_work')
end
end
context 'when action is #show' do
before do
allow(view).to receive(:params).and_return({ action: 'show' })
view.instance_variable_set(:@organization, organization)
end
it 'calls organization_layout_nav and sets @nav instance variable' do
expect(view).to receive(:organization_layout_nav).and_return('your_work')
it 'renders organization navigation' do
subject
render
expect(view.instance_variable_get(:@nav)).to eq('organization')
end
expect(view.instance_variable_get(:@nav)).to eq('your_work')
end
end
end
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册