Skip to content
Snippets Groups Projects
Verified Commit cbee01ed authored by Phil Hughes's avatar Phil Hughes
Browse files

Updated approvals widget collapse button

Changed the approvals widget collapse button to match
the same styling as the rest of the widget. Doing this removes
the suggested approvers user avatar list.

#387070
parent 42a8054a
No related branches found
No related tags found
No related merge requests found
Showing
with 175 additions and 128 deletions
......@@ -9,8 +9,7 @@ import { s__, __ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import eventHub from '../../event_hub';
import approvalsMixin from '../../mixins/approvals';
import MrWidgetContainer from '../mr_widget_container.vue';
import MrWidgetIcon from '../mr_widget_icon.vue';
import StateContainer from '../state_container.vue';
import { INVALID_RULES_DOCS_PATH } from '../../constants';
import ApprovalsSummary from './approvals_summary.vue';
import ApprovalsSummaryOptional from './approvals_summary_optional.vue';
......@@ -19,14 +18,17 @@ import { FETCH_LOADING, APPROVE_ERROR, UNAPPROVE_ERROR } from './messages';
export default {
name: 'MRWidgetApprovals',
components: {
MrWidgetContainer,
MrWidgetIcon,
ApprovalsSummary,
ApprovalsSummaryOptional,
StateContainer,
GlButton,
GlSprintf,
},
mixins: [approvalsMixin, glFeatureFlagsMixin()],
provide: {
expandDetailsTooltip: __('Expand eligible approvers'),
collapseDetailsTooltip: __('Collapse eligible approvers'),
},
props: {
mr: {
type: Object,
......@@ -56,6 +58,11 @@ export default {
required: false,
default: false,
},
isCollapsed: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -209,10 +216,17 @@ export default {
};
</script>
<template>
<mr-widget-container>
<div class="js-mr-approvals d-flex align-items-start align-items-md-center">
<mr-widget-icon name="approval" />
<div v-if="$apollo.queries.approvals.loading">{{ $options.FETCH_LOADING }}</div>
<div class="js-mr-approvals mr-section-container mr-widget-workflow">
<state-container
:is-loading="$apollo.queries.approvals.loading"
:mr="mr"
status="approval"
is-collapsible
collapse-on-desktop
:is-collapsed="isCollapsed"
@toggle="() => $emit('toggle')"
>
<template v-if="$apollo.queries.approvals.loading">{{ $options.FETCH_LOADING }}</template>
<template v-else>
<div class="gl-display-flex gl-flex-direction-column">
<div class="gl-display-flex gl-flex-direction-row gl-align-items-center">
......@@ -221,7 +235,7 @@ export default {
:variant="action.variant"
:category="action.category"
:loading="isApproving"
class="gl-mr-5"
class="gl-mr-3"
data-qa-selector="approve_button"
@click="action.action"
>
......@@ -250,9 +264,7 @@ export default {
:has-approval-auth-error="hasApprovalAuthError"
></slot>
</template>
</div>
<template #footer>
<slot name="footer"></slot>
</template>
</mr-widget-container>
</state-container>
<slot name="footer"></slot>
</div>
</template>
......@@ -32,6 +32,7 @@ export default {
<div class="gl-display-flex gl-m-auto">
<gl-icon v-if="isMerged" name="merge" :size="16" class="gl-text-blue-500" />
<gl-icon v-else-if="isClosed" name="merge-request-close" :size="16" class="gl-text-red-500" />
<gl-icon v-else-if="status === 'approval'" name="approval" :size="16" />
<status-icon v-else :is-loading="isLoading" :icon-name="status" :level="1" class="gl-m-0!" />
</div>
</div>
......
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { STATUS_CLOSED, STATUS_MERGED } from '~/issues/constants';
import { __ } from '~/locale';
import StatusIcon from './mr_widget_status_icon.vue';
import Actions from './action_buttons.vue';
......@@ -14,7 +13,30 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
inject: {
expandDetailsTooltip: {
default: '',
},
collapseDetailsTooltip: {
default: '',
},
},
props: {
isCollapsible: {
type: Boolean,
required: false,
default: false,
},
collapseOnDesktop: {
type: Boolean,
required: false,
default: false,
},
isCollapsed: {
type: Boolean,
required: false,
default: false,
},
mr: {
type: Object,
required: false,
......@@ -36,10 +58,6 @@ export default {
default: () => [],
},
},
i18n: {
expandDetailsTooltip: __('Expand merge details'),
collapseDetailsTooltip: __('Collapse merge details'),
},
computed: {
wrapperClasses() {
if (this.status === STATUS_MERGED) return 'gl-bg-blue-50';
......@@ -55,7 +73,7 @@ export default {
<template>
<div
class="mr-widget-body media gl-display-flex gl-align-items-center"
class="mr-widget-body media gl-display-flex gl-align-items-center gl-pl-5 gl-pr-4 gl-py-4"
:class="wrapperClasses"
v-on="$listeners"
>
......@@ -95,21 +113,19 @@ export default {
</div>
</div>
<div
v-if="mr"
class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
v-if="isCollapsible"
:class="{ 'gl-md-display-none': !collapseOnDesktop }"
class="gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6"
>
<gl-button
v-gl-tooltip
:title="
mr.mergeDetailsCollapsed
? $options.i18n.expandDetailsTooltip
: $options.i18n.collapseDetailsTooltip
"
:icon="mr.mergeDetailsCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
:title="isCollapsed ? expandDetailsTooltip : collapseDetailsTooltip"
:icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
category="tertiary"
size="small"
class="gl-vertical-align-top"
@click="() => mr.toggleMergeDetails()"
data-testid="widget-toggle"
@click="() => $emit('toggle')"
/>
</div>
</div>
......
......@@ -43,7 +43,12 @@ export default {
</script>
<template>
<state-container :mr="mr" status="failed">
<state-container
status="failed"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span class="gl-ml-3 gl-w-100 gl-flex-grow-1 gl-md-mr-3 gl-ml-0! gl-text-body!">
<bold-text :message="failedText" />
</span>
......
......@@ -24,7 +24,12 @@ export default {
</script>
<template>
<state-container :mr="mr" status="failed">
<state-container
status="failed"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<bold-text :message="$options.message" />
</state-container>
</template>
......@@ -131,7 +131,14 @@ export default {
};
</script>
<template>
<state-container :mr="mr" status="scheduled" :is-loading="loading" :actions="actions">
<state-container
status="scheduled"
:is-loading="loading"
:actions="actions"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="3" width="24" height="24" rx="4" />
......
......@@ -54,7 +54,13 @@ export default {
};
</script>
<template>
<state-container :mr="mr" status="failed" :actions="actions">
<state-container
status="failed"
:actions="actions"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span class="gl-font-weight-bold">
<template v-if="mergeError">{{ mergeError }}</template>
{{ s__('mrWidget|This merge request failed to be merged automatically') }}
......
......@@ -15,7 +15,12 @@ export default {
};
</script>
<template>
<state-container :mr="mr" status="loading">
<state-container
status="loading"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
{{ s__('mrWidget|Checking if merge request can be merged…') }}
</state-container>
</template>
......@@ -79,7 +79,13 @@ export default {
};
</script>
<template>
<state-container :mr="mr" status="closed" :actions="actions">
<state-container
status="closed"
:actions="actions"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<mr-widget-author-time
:action-text="s__('mrWidget|Closed by')"
:author="mr.metrics.closedBy"
......
......@@ -72,7 +72,13 @@ export default {
};
</script>
<template>
<state-container :mr="mr" status="failed" :is-loading="isLoading">
<state-container
status="failed"
:is-loading="isLoading"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="7" width="150" height="16" rx="4" />
......
......@@ -95,12 +95,25 @@ export default {
};
</script>
<template>
<state-container v-if="isRefreshing" :mr="mr" status="loading">
<state-container
v-if="isRefreshing"
status="loading"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span class="gl-font-weight-bold">
{{ s__('mrWidget|Refreshing now') }}
</span>
</state-container>
<state-container v-else :mr="mr" status="failed" :actions="actions">
<state-container
v-else
status="failed"
:actions="actions"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span
v-if="mr.mergeError"
class="has-error-message gl-font-weight-bold"
......
......@@ -150,7 +150,13 @@ export default {
};
</script>
<template>
<state-container :mr="mr" :actions="actions" status="merged">
<state-container
:actions="actions"
status="merged"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<mr-widget-author-time
:action-text="s__('mrWidget|Merged by')"
:author="mr.metrics.mergedBy"
......
......@@ -195,7 +195,13 @@ export default {
</script>
<template>
<div>
<state-container :mr="mr" :status="status" :is-loading="isLoading">
<state-container
:status="status"
:is-loading="isLoading"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="3" width="24" height="24" rx="4" />
......
......@@ -24,7 +24,12 @@ export default {
</script>
<template>
<state-container :mr="mr" status="failed">
<state-container
status="failed"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span
class="gl-md-mr-3 gl-flex-grow-1 gl-ml-0! gl-text-body!"
data-qa-selector="head_mismatch_content"
......
......@@ -30,7 +30,12 @@ export default {
</script>
<template>
<state-container :mr="mr" status="failed">
<state-container
status="failed"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span class="gl-ml-3 gl-w-100 gl-flex-grow-1 gl-md-mr-3 gl-ml-0! gl-text-body!">
<bold-text :message="$options.message" />
</span>
......
......@@ -137,7 +137,12 @@ export default {
</script>
<template>
<state-container :mr="mr" status="failed">
<state-container
status="failed"
is-collapsible
:is-collapsed="mr.mergeDetailsCollapsed"
@toggle="() => mr.toggleMergeDetails()"
>
<span class="gl-ml-0! gl-text-body! gl-flex-grow-1">
<bold-text :message="$options.i18n.removeDraftStatus" />
</span>
......
......@@ -156,6 +156,10 @@ export default {
},
},
mixins: [mergeRequestQueryVariablesMixin],
provide: {
expandDetailsTooltip: __('Expand merge details'),
collapseDetailsTooltip: __('Collapse merge details'),
},
props: {
mrData: {
type: Object,
......
......@@ -25,6 +25,7 @@ export default {
data() {
return {
modalId: 'approvals-auth',
isCollapsed: true,
};
},
computed: {
......@@ -49,8 +50,10 @@ export default {
invalidRules() {
return this.approvals.approvalState?.invalidApproversRules || [];
},
suggestedApprovers() {
return this.approvals.approvalState?.suggestedApprovers?.nodes || [];
},
methods: {
toggleIsCollapsed() {
this.isCollapsed = !this.isCollapsed;
},
},
};
......@@ -62,6 +65,8 @@ export default {
:is-optional-default="isOptional"
:require-password-to-approve="requirePasswordToApprove"
:modal-id="modalId"
:is-collapsed="isCollapsed"
@toggle="toggleIsCollapsed"
>
<template v-if="!isBasic" #default="{ isApproving, approveWithAuth, hasApprovalAuthError }">
<approvals-auth
......@@ -72,10 +77,9 @@ export default {
@hide="clearError"
/>
</template>
<template v-if="!isBasic" #footer>
<template v-if="!isBasic && !isCollapsed" #footer>
<approvals-footer
v-if="hasFooter"
:suggested-approvers="suggestedApprovers"
:invalid-approvers-rules="invalidRules"
:security-approvals-help-page-path="mr.securityApprovalsHelpPagePath"
:eligible-approvers-docs-path="mr.eligibleApproversDocsPath"
......
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
import ApprovalsList from './approvals_list.vue';
export default {
components: {
GlButton,
UserAvatarList,
ApprovalsList,
},
props: {
suggestedApprovers: {
type: Array,
required: true,
},
securityApprovalsHelpPagePath: {
type: String,
required: false,
......@@ -38,76 +29,20 @@ export default {
required: true,
},
},
data() {
return {
isCollapsed: true,
};
},
computed: {
ariaLabel() {
return this.isCollapsed ? __('Expand approvers') : __('Collapse approvers');
},
angleIcon() {
return this.isCollapsed ? 'chevron-right' : 'chevron-down';
},
suggestedApproversTrimmed() {
return this.suggestedApprovers.slice(0, Math.min(5, this.suggestedApprovers.length));
},
},
methods: {
toggle() {
this.isCollapsed = !this.isCollapsed;
},
},
};
</script>
<template>
<div class="mr-widget-extension">
<div class="d-flex align-items-center pl-3 gl-py-3">
<gl-button
class="gl-mr-3"
size="small"
:aria-label="ariaLabel"
:icon="angleIcon"
category="tertiary"
@click="toggle"
/>
<template v-if="isCollapsed">
<user-avatar-list
:items="suggestedApproversTrimmed"
:img-size="24"
:breakpoint="0"
empty-text=""
/>
<gl-button
data-testid="approvers-expand-button"
category="tertiary"
variant="confirm"
size="small"
@click="toggle"
>{{ __('View eligible approvers') }}</gl-button
>
</template>
<template v-else>
<gl-button
data-testid="approvers-collapse-button"
category="tertiary"
variant="confirm"
size="small"
@click="toggle"
>{{ __('Collapse') }}</gl-button
>
</template>
</div>
<div v-if="!isCollapsed" class="border-top">
<approvals-list
:invalid-approvers-rules="invalidApproversRules"
:security-approvals-help-page-path="securityApprovalsHelpPagePath"
:eligible-approvers-docs-path="eligibleApproversDocsPath"
:project-path="projectPath"
:iid="iid"
/>
</div>
<div
class="mr-widget-extension gl-rounded-bottom-left-base gl-rounded-bottom-right-base"
data-testid="approvals-footer"
>
<approvals-list
:invalid-approvers-rules="invalidApproversRules"
:security-approvals-help-page-path="securityApprovalsHelpPagePath"
:eligible-approvers-docs-path="eligibleApproversDocsPath"
:project-path="projectPath"
:iid="iid"
/>
</div>
</template>
......@@ -9,11 +9,6 @@ query approvedByEE($projectPath: ID!, $iid: String!) {
approvalsLeft
approvalsRequired
approvalState {
suggestedApprovers {
nodes {
...User
}
}
invalidApproversRules {
id
name
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment