diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 11:59:07 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 11:59:07 +0000 |
commit | 8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch) | |
tree | 544930fb309b30317ae9797a9683768705d664c4 /app/assets/javascripts/groups | |
parent | 4b1de649d0168371549608993deac953eb692019 (diff) | |
download | gitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz |
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'app/assets/javascripts/groups')
10 files changed, 159 insertions, 16 deletions
diff --git a/app/assets/javascripts/groups/components/group_folder.vue b/app/assets/javascripts/groups/components/group_folder.vue index d2a613bed4f..5f169832ee4 100644 --- a/app/assets/javascripts/groups/components/group_folder.vue +++ b/app/assets/javascripts/groups/components/group_folder.vue @@ -49,7 +49,7 @@ export default { /> <li v-if="hasMoreChildren" class="group-row"> <a :href="parentGroup.relativePath" class="group-row-contents has-more-items py-2"> - <gl-icon name="external-link" aria-hidden="true" /> {{ moreChildrenStats }} + <gl-icon name="external-link" /> {{ moreChildrenStats }} </a> </li> </ul> diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index 6e99b6ad4fa..ef58b93c049 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -74,6 +74,9 @@ export default { visibilityTooltip() { return GROUP_VISIBILITY_TYPE[this.group.visibility]; }, + microdata() { + return this.group.microdata || {}; + }, }, mounted() { if (this.group.name === 'Learn GitLab') { @@ -99,7 +102,15 @@ export default { </script> <template> - <li :id="groupDomId" :class="rowClass" class="group-row" @click.stop="onClickRowGroup"> + <li + :id="groupDomId" + :class="rowClass" + class="group-row" + :itemprop="microdata.itemprop" + :itemtype="microdata.itemtype" + :itemscope="microdata.itemscope" + @click.stop="onClickRowGroup" + > <div :class="{ 'project-row-contents': !isGroup }" class="group-row-contents d-flex align-items-center py-2 pr-3" @@ -118,7 +129,13 @@ export default { class="avatar-container rect-avatar s32 d-none flex-grow-0 flex-shrink-0 " > <a :href="group.relativePath" class="no-expand"> - <img v-if="hasAvatar" :src="group.avatarUrl" class="avatar s40" /> + <img + v-if="hasAvatar" + :src="group.avatarUrl" + data-testid="group-avatar" + class="avatar s40" + :itemprop="microdata.imageItemprop" + /> <identicon v-else :entity-id="group.id" :entity-name="group.name" size-class="s40" /> </a> </div> @@ -127,9 +144,11 @@ export default { <div class="d-flex align-items-center flex-wrap title namespace-title gl-mr-3"> <a v-gl-tooltip.bottom + data-testid="group-name" :href="group.relativePath" :title="group.fullName" class="no-expand gl-mt-3 gl-mr-3 gl-text-gray-900!" + :itemprop="microdata.nameItemprop" >{{ // ending bracket must be by closing tag to prevent // link hover text-decoration from over-extending @@ -146,7 +165,12 @@ export default { </span> </div> <div v-if="group.description" class="description"> - <span v-html="group.description"> </span> + <span + :itemprop="microdata.descriptionItemprop" + data-testid="group-description" + v-html="group.description" + > + </span> </div> </div> <div v-if="isGroupPendingRemoval"> diff --git a/app/assets/javascripts/groups/components/visibility_level_dropdown.vue b/app/assets/javascripts/groups/components/visibility_level_dropdown.vue new file mode 100644 index 00000000000..ff0f8c3ff46 --- /dev/null +++ b/app/assets/javascripts/groups/components/visibility_level_dropdown.vue @@ -0,0 +1,48 @@ +<script> +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; + +export default { + components: { + GlDropdown, + GlDropdownItem, + }, + props: { + visibilityLevelOptions: { + type: Array, + required: true, + }, + defaultLevel: { + type: Number, + required: true, + }, + }, + data() { + return { + selectedOption: this.getDefaultOption(), + }; + }, + methods: { + getDefaultOption() { + return this.visibilityLevelOptions.find(option => option.level === this.defaultLevel); + }, + onClick(option) { + this.selectedOption = option; + }, + }, +}; +</script> +<template> + <div> + <input type="hidden" name="group[visibility_level]" :value="selectedOption.level" /> + <gl-dropdown :text="selectedOption.label" class="gl-w-full" menu-class="gl-w-full! gl-mb-0"> + <gl-dropdown-item + v-for="option in visibilityLevelOptions" + :key="option.level" + :secondary-text="option.description" + @click="onClick(option)" + > + <div class="gl-font-weight-bold gl-mb-1">{{ option.label }}</div> + </gl-dropdown-item> + </gl-dropdown> + </div> +</template> diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index 522f1d16df2..e11c3aaf984 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -47,8 +47,9 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { data() { const { dataset } = dataEl || this.$options.el; const hideProjects = parseBoolean(dataset.hideProjects); + const showSchemaMarkup = parseBoolean(dataset.showSchemaMarkup); const service = new GroupsService(endpoint || dataset.endpoint); - const store = new GroupsStore(hideProjects); + const store = new GroupsStore({ hideProjects, showSchemaMarkup }); return { action, diff --git a/app/assets/javascripts/groups/members/components/app.vue b/app/assets/javascripts/groups/members/components/app.vue index 2e6dd4a0bad..f6f3a955813 100644 --- a/app/assets/javascripts/groups/members/components/app.vue +++ b/app/assets/javascripts/groups/members/components/app.vue @@ -1,13 +1,16 @@ <script> import { mapState, mapMutations } from 'vuex'; import { GlAlert } from '@gitlab/ui'; -import MembersTable from '~/vue_shared/components/members/table/members_table.vue'; +import MembersTable from '~/members/components/table/members_table.vue'; +import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue'; import { scrollToElement } from '~/lib/utils/common_utils'; -import { HIDE_ERROR } from '~/vuex_shared/modules/members/mutation_types'; +import { HIDE_ERROR } from '~/members/store/mutation_types'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; export default { name: 'GroupMembersApp', - components: { MembersTable, GlAlert }, + components: { MembersTable, FilterSortContainer, GlAlert }, + mixins: [glFeatureFlagsMixin()], computed: { ...mapState(['showError', 'errorMessage']), }, @@ -33,6 +36,7 @@ export default { <gl-alert v-if="showError" ref="errorAlert" variant="danger" @dismiss="hideError">{{ errorMessage }}</gl-alert> + <filter-sort-container v-if="glFeatures.groupMembersFilteredSearch" /> <members-table /> </div> </template> diff --git a/app/assets/javascripts/groups/members/index.js b/app/assets/javascripts/groups/members/index.js index cb28fb057c9..9ce0e3c1179 100644 --- a/app/assets/javascripts/groups/members/index.js +++ b/app/assets/javascripts/groups/members/index.js @@ -3,9 +3,18 @@ import Vuex from 'vuex'; import { GlToast } from '@gitlab/ui'; import { parseDataAttributes } from 'ee_else_ce/groups/members/utils'; import App from './components/app.vue'; -import membersModule from '~/vuex_shared/modules/members'; +import membersStore from '~/members/store'; -export const initGroupMembersApp = (el, tableFields, tableAttrs, requestFormatter) => { +export const initGroupMembersApp = ( + el, + { + tableFields = [], + tableAttrs = {}, + tableSortableFields = [], + requestFormatter = () => {}, + filteredSearchBar = { show: false }, + }, +) => { if (!el) { return () => {}; } @@ -13,15 +22,17 @@ export const initGroupMembersApp = (el, tableFields, tableAttrs, requestFormatte Vue.use(Vuex); Vue.use(GlToast); - const store = new Vuex.Store({ - ...membersModule({ + const store = new Vuex.Store( + membersStore({ ...parseDataAttributes(el), currentUserId: gon.current_user_id || null, tableFields, tableAttrs, + tableSortableFields, requestFormatter, + filteredSearchBar, }), - }); + ); return new Vue({ el, diff --git a/app/assets/javascripts/groups/members/utils.js b/app/assets/javascripts/groups/members/utils.js index 662eecc4e38..2d584556bbc 100644 --- a/app/assets/javascripts/groups/members/utils.js +++ b/app/assets/javascripts/groups/members/utils.js @@ -1,5 +1,5 @@ import { isUndefined } from 'lodash'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils'; import { GROUP_MEMBER_BASE_PROPERTY_NAME, GROUP_MEMBER_ACCESS_LEVEL_PROPERTY_NAME, @@ -8,12 +8,13 @@ import { } from './constants'; export const parseDataAttributes = el => { - const { members, groupId, memberPath } = el.dataset; + const { members, groupId, memberPath, canManageMembers } = el.dataset; return { members: convertObjectPropsToCamelCase(JSON.parse(members), { deep: true }), sourceId: parseInt(groupId, 10), memberPath, + canManageMembers: parseBoolean(canManageMembers), }; }; diff --git a/app/assets/javascripts/groups/store/groups_store.js b/app/assets/javascripts/groups/store/groups_store.js index 6a1197fa163..b6cea38e87f 100644 --- a/app/assets/javascripts/groups/store/groups_store.js +++ b/app/assets/javascripts/groups/store/groups_store.js @@ -1,11 +1,13 @@ import { normalizeHeaders, parseIntPagination } from '../../lib/utils/common_utils'; +import { getGroupItemMicrodata } from './utils'; export default class GroupsStore { - constructor(hideProjects) { + constructor({ hideProjects = false, showSchemaMarkup = false } = {}) { this.state = {}; this.state.groups = []; this.state.pageInfo = {}; this.hideProjects = hideProjects; + this.showSchemaMarkup = showSchemaMarkup; } setGroups(rawGroups) { @@ -94,6 +96,7 @@ export default class GroupsStore { starCount: rawGroupItem.star_count, updatedAt: rawGroupItem.updated_at, pendingRemoval: rawGroupItem.marked_for_deletion, + microdata: this.showSchemaMarkup ? getGroupItemMicrodata(rawGroupItem) : {}, }; } diff --git a/app/assets/javascripts/groups/store/utils.js b/app/assets/javascripts/groups/store/utils.js new file mode 100644 index 00000000000..371b3aa9d52 --- /dev/null +++ b/app/assets/javascripts/groups/store/utils.js @@ -0,0 +1,27 @@ +export const getGroupItemMicrodata = ({ type }) => { + const defaultMicrodata = { + itemscope: true, + itemtype: 'https://schema.org/Thing', + itemprop: 'owns', + imageItemprop: 'image', + nameItemprop: 'name', + descriptionItemprop: 'description', + }; + + switch (type) { + case 'group': + return { + ...defaultMicrodata, + itemtype: 'https://schema.org/Organization', + itemprop: 'subOrganization', + imageItemprop: 'logo', + }; + case 'project': + return { + ...defaultMicrodata, + itemtype: 'https://schema.org/SoftwareSourceCode', + }; + default: + return defaultMicrodata; + } +}; diff --git a/app/assets/javascripts/groups/visibility_level.js b/app/assets/javascripts/groups/visibility_level.js new file mode 100644 index 00000000000..d570b5e65ac --- /dev/null +++ b/app/assets/javascripts/groups/visibility_level.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import VisibilityLevelDropdown from './components/visibility_level_dropdown.vue'; + +export default () => { + const el = document.querySelector('.js-visibility-level-dropdown'); + + if (!el) { + return null; + } + + const { visibilityLevelOptions, defaultLevel } = el.dataset; + + return new Vue({ + el, + render(createElement) { + return createElement(VisibilityLevelDropdown, { + props: { + visibilityLevelOptions: JSON.parse(visibilityLevelOptions), + defaultLevel: Number(defaultLevel), + }, + }); + }, + }); +}; |