Commit 5ca49bfb authored by Eulyeon Ko's avatar Eulyeon Ko Committed by Kushal Pandya

Make issuable_header_warnings shared component

Also added a js class in haml as a render target

Apply suggested patch

Update relevant specs

Add js-lock-issue-data and update mock noteable data
Test issuable_header_warnings for both mr and issue

Move to vue_shared

Update issuable header warning component

Also update the spec

Apply the suggestions from the second review round

Re-organize the spec
Remove not needed mock data
parent cfa204bd
<script>
import { mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
Icon,
},
computed: {
...mapState({
confidential: ({ noteableData }) => noteableData.confidential,
dicussionLocked: ({ noteableData }) => noteableData.discussion_locked,
}),
},
};
</script>
<template>
<div class="gl-display-inline-block">
<div v-if="confidential" class="issuable-warning-icon inline">
<icon class="icon" name="eye-slash" data-testid="confidential" />
</div>
<div v-if="dicussionLocked" class="issuable-warning-icon inline">
<icon class="icon" name="lock" data-testid="locked" />
</div>
</div>
</template>
import Vue from 'vue';
import issuableApp from './components/app.vue';
import IssuableHeaderWarnings from './components/issuable_header_warnings.vue';
import { parseIssuableData } from './utils/parse_data';
import { store } from '~/notes/stores';
export default function initIssueableApp() {
return new Vue({
......@@ -17,13 +15,3 @@ export default function initIssueableApp() {
},
});
}
export function issuableHeaderWarnings() {
return new Vue({
el: document.getElementById('js-issuable-header-warnings'),
store,
render(createElement) {
return createElement(IssuableHeaderWarnings);
},
});
}
......@@ -5,6 +5,7 @@ import store from '~/mr_notes/stores';
import notesApp from '../notes/components/notes_app.vue';
import discussionKeyboardNavigator from '../notes/components/discussion_keyboard_navigator.vue';
import initWidget from '../vue_merge_request_widget';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
// eslint-disable-next-line no-new
......@@ -20,6 +21,7 @@ export default () => {
const noteableData = JSON.parse(notesDataset.noteableData);
noteableData.noteableType = notesDataset.noteableType;
noteableData.targetType = notesDataset.targetType;
noteableData.discussion_locked = parseBoolean(notesDataset.isLocked);
return {
noteableData,
......
// Note: this getter is important because
// `noteableData` is namespaced under `notes` for `~/mr_notes/stores`
// while `noteableData` is directly available as `state.noteableData` for `~/notes/stores`
export const getNoteableData = state => state.notes.noteableData;
export default {
isLoggedIn(state, getters) {
return Boolean(getters.getUserData.id);
......
......@@ -3,16 +3,18 @@ import Issue from '~/issue';
import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import ZenMode from '~/zen_mode';
import '~/notes/index';
import initIssueableApp, { issuableHeaderWarnings } from '~/issue_show';
import { store } from '~/notes/stores';
import initIssueableApp from '~/issue_show';
import initIssuableHeaderWarning from '~/vue_shared/components/issuable/init_issuable_header_warning';
import initSentryErrorStackTraceApp from '~/sentry_error_stack_trace';
import initRelatedMergeRequestsApp from '~/related_merge_requests';
import initVueIssuableSidebarApp from '~/issuable_sidebar/sidebar_bundle';
export default function() {
initIssueableApp();
initIssuableHeaderWarning(store);
initSentryErrorStackTraceApp();
initRelatedMergeRequestsApp();
issuableHeaderWarnings();
import(/* webpackChunkName: 'design_management' */ '~/design_management')
.then(module => module.default())
......
......@@ -2,6 +2,8 @@ import initMrNotes from '~/mr_notes';
import { initReviewBar } from '~/batch_comments';
import initSidebarBundle from '~/sidebar/sidebar_bundle';
import initShow from '../init_merge_request_show';
import initIssuableHeaderWarning from '~/vue_shared/components/issuable/init_issuable_header_warning';
import store from '~/mr_notes/stores';
document.addEventListener('DOMContentLoaded', () => {
initShow();
......@@ -10,4 +12,5 @@ document.addEventListener('DOMContentLoaded', () => {
}
initMrNotes();
initReviewBar();
initIssuableHeaderWarning(store);
});
import Vue from 'vue';
import IssuableHeaderWarnings from './issuable_header_warnings.vue';
export default function issuableHeaderWarnings(store) {
return new Vue({
el: document.getElementById('js-issuable-header-warnings'),
store,
render(createElement) {
return createElement(IssuableHeaderWarnings);
},
});
}
<script>
import { mapGetters } from 'vuex';
import { GlIcon } from '@gitlab/ui';
export default {
components: {
GlIcon,
},
computed: {
...mapGetters(['getNoteableData']),
isLocked() {
return this.getNoteableData.discussion_locked;
},
isConfidential() {
return this.getNoteableData.confidential;
},
warningIconsMeta() {
return [
{
iconName: 'lock',
visible: this.isLocked,
dataTestId: 'locked',
},
{
iconName: 'eye-slash',
visible: this.isConfidential,
dataTestId: 'confidential',
},
];
},
},
};
</script>
<template>
<div class="gl-display-inline-block">
<template v-for="meta in warningIconsMeta">
<div v-if="meta.visible" :key="meta.iconName" class="issuable-warning-icon inline">
<gl-icon :name="meta.iconName" :data-testid="meta.dataTestId" class="icon" />
</div>
</template>
</div>
</template>
......@@ -15,8 +15,7 @@
= state_human_name
.issuable-meta
- if @merge_request.discussion_locked?
.issuable-warning-icon.inline= sprite_icon('lock', size: 16, css_class: 'icon')
#js-issuable-header-warnings
= issuable_meta(@merge_request, @project, "Merge request")
%a.btn.btn-default.float-right.d-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" }
......
......@@ -70,7 +70,8 @@
noteable_type: 'MergeRequest',
target_type: 'merge_request',
help_page_path: suggest_changes_help_path,
current_user_data: @current_user_data} }
current_user_data: @current_user_data,
is_locked: @merge_request.discussion_locked.to_s } }
= render "projects/merge_requests/tabs/pane", name: "commits", id: "commits", class: "commits" do
-# This tab is always loaded via AJAX
......
---
title: Consolidate issuable_header_warning for both MR and issue
merge_request: 37043
author:
type: other
import initSidebarBundle from 'ee/sidebar/sidebar_bundle';
import { initReviewBar } from '~/batch_comments';
import initMrNotes from '~/mr_notes';
import store from '~/mr_notes/stores';
import initIssuableHeaderWarning from '~/vue_shared/components/issuable/init_issuable_header_warning';
import initShow from '~/pages/projects/merge_requests/init_merge_request_show';
import trackShowInviteMemberLink from 'ee/projects/track_invite_members';
......@@ -11,6 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
initMrNotes();
initReviewBar();
initIssuableHeaderWarning(store);
const assigneeDropdown = document.querySelector('.js-sidebar-assignee-dropdown');
......
......@@ -22,6 +22,7 @@ export default function initVueMRPage() {
mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock));
mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock));
mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request');
mrDiscussionsEl.setAttribute('data-is-locked', 'false');
mrTestEl.appendChild(mrDiscussionsEl);
const discussionCounterEl = document.createElement('div');
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import IssuableHeaderWarnings from '~/issue_show/components/issuable_header_warnings.vue';
import createStore from '~/notes/stores';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('IssuableHeaderWarnings', () => {
let wrapper;
let store;
const findConfidential = () => wrapper.find('[data-testid="confidential"]');
const findLocked = () => wrapper.find('[data-testid="locked"]');
const confidentialIconName = () => findConfidential().attributes('name');
const lockedIconName = () => findLocked().attributes('name');
const createComponent = () => {
wrapper = shallowMount(IssuableHeaderWarnings, { store, localVue });
};
beforeEach(() => {
store = createStore();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
store = null;
});
describe('when confidential is on', () => {
beforeEach(() => {
store.state.noteableData.confidential = true;
createComponent();
});
it('renders the confidential icon', () => {
expect(confidentialIconName()).toBe('eye-slash');
});
});
describe('when confidential is off', () => {
beforeEach(() => {
store.state.noteableData.confidential = false;
createComponent();
});
it('does not find the component', () => {
expect(findConfidential().exists()).toBe(false);
});
});
describe('when discussion locked is on', () => {
beforeEach(() => {
store.state.noteableData.discussion_locked = true;
createComponent();
});
it('renders the locked icon', () => {
expect(lockedIconName()).toBe('lock');
});
});
describe('when discussion locked is off', () => {
beforeEach(() => {
store.state.noteableData.discussion_locked = false;
createComponent();
});
it('does not find the component', () => {
expect(findLocked().exists()).toBe(false);
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import IssuableHeaderWarnings from '~/vue_shared/components/issuable/issuable_header_warnings.vue';
import createIssueStore from '~/notes/stores';
import { createStore as createMrStore } from '~/mr_notes/stores';
const ISSUABLE_TYPE_ISSUE = 'issue';
const ISSUABLE_TYPE_MR = 'merge request';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('IssuableHeaderWarnings', () => {
let wrapper;
let store;
const findConfidentialIcon = () => wrapper.find('[data-testid="confidential"]');
const findLockedIcon = () => wrapper.find('[data-testid="locked"]');
const renderTestMessage = renders => (renders ? 'renders' : 'does not render');
const setLock = locked => {
store.getters.getNoteableData.discussion_locked = locked;
};
const setConfidential = confidential => {
store.getters.getNoteableData.confidential = confidential;
};
const createComponent = () => {
wrapper = shallowMount(IssuableHeaderWarnings, { store, localVue });
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
store = null;
});
describe.each`
issuableType
${ISSUABLE_TYPE_ISSUE} | ${ISSUABLE_TYPE_MR}
`(`when issuableType=$issuableType`, ({ issuableType }) => {
beforeEach(() => {
store = issuableType === ISSUABLE_TYPE_ISSUE ? createIssueStore() : createMrStore();
createComponent();
});
describe.each`
lockStatus | confidentialStatus
${true} | ${true}
${true} | ${false}
${false} | ${true}
${false} | ${false}
`(
`when locked=$lockStatus and confidential=$confidentialStatus`,
({ lockStatus, confidentialStatus }) => {
beforeEach(() => {
setLock(lockStatus);
setConfidential(confidentialStatus);
});
it(`${renderTestMessage(lockStatus)} the locked icon`, () => {
expect(findLockedIcon().exists()).toBe(lockStatus);
});
it(`${renderTestMessage(confidentialStatus)} the confidential icon`, () => {
expect(findConfidentialIcon().exists()).toBe(confidentialStatus);
});
},
);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment