Commit 1ef003e3 authored by Andrew Fontaine's avatar Andrew Fontaine Committed by Phil Hughes

Extract Components to Reuse in Other Dashboards

Pulls out the components for alerts, project search, project pipelines,
and a new one for time stamps.
parent 3fb07dcd
<script>
import { mapState, mapActions } from 'vuex';
import { GlLoadingIcon, GlDashboardSkeleton } from '@gitlab/ui';
import ProjectSearch from 'ee/vue_shared/dashboards/components/project_search.vue';
import DashboardProject from './project.vue';
import ProjectSearch from './project_search.vue';
export default {
components: {
......
......@@ -7,10 +7,11 @@ import Icon from '~/vue_shared/components/icon.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import Commit from '~/vue_shared/components/commit.vue';
import Alerts from 'ee/vue_shared/dashboards/components/alerts.vue';
import TimeAgo from 'ee/vue_shared/dashboards/components/time_ago.vue';
import ProjectPipeline from 'ee/vue_shared/dashboards/components/project_pipeline.vue';
import { STATUS_FAILED, STATUS_RUNNING } from 'ee/vue_shared/dashboards/constants';
import ProjectHeader from './project_header.vue';
import Alerts from './alerts.vue';
import ProjectPipeline from './project_pipeline.vue';
import { STATUS_FAILED, STATUS_RUNNING } from '../../constants';
export default {
components: {
......@@ -19,6 +20,7 @@ export default {
Commit,
Alerts,
ProjectPipeline,
TimeAgo,
GlTooltip,
Icon,
},
......@@ -128,20 +130,11 @@ export default {
</div>
<div class="col-sm-5 pl-0 text-right align-self-center d-none d-sm-block">
<div v-if="shouldShowTimeAgo" class="text-secondary">
<icon
name="clock"
class="dashboard-card-time-ago-icon align-text-bottom js-dashboard-project-clock-icon"
/>
<time ref="timeAgo" class="js-dashboard-project-time-ago">
{{ timeFormated(finishedTime) }}
</time>
<gl-tooltip :target="() => $refs.timeAgo">
<div class="bold">{{ $options.tooltips.timeAgo }}</div>
<div>{{ finishedTimeTitle }}</div>
</gl-tooltip>
</div>
<time-ago
v-if="shouldShowTimeAgo"
:time="finishedTime"
:tooltip-text="$options.tooltips.timeAgo"
/>
<alerts :count="project.alert_count" />
</div>
......
<script>
import { __, n__, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
Icon,
},
props: {
count: {
type: Number,
required: false,
default: 0,
},
},
computed: {
alertClasses() {
return {
'text-tertiary': this.count <= 0,
'text-warning': this.count > 0,
};
},
alertCount() {
return sprintf(__('%{count} %{alerts}'), {
count: this.count,
alerts: this.pluralizedAlerts,
});
},
pluralizedAlerts() {
return n__('Alert', 'Alerts', this.count);
},
},
};
</script>
<template>
<div class="dashboard-card-alert row">
<div class="col-12">
<icon
:class="alertClasses"
class="align-text-bottom js-dashboard-alerts-icon"
name="warning"
/>
<span class="js-alert-count text-secondary prepend-left-4"> {{ alertCount }} </span>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import inputFocus from '../../mixins';
import inputFocus from '../mixins';
export default {
components: {
......
......@@ -4,7 +4,7 @@ import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import { GlLink, GlTooltip } from '@gitlab/ui';
import { STATUS_FAILED } from '../../constants';
import { STATUS_FAILED } from '../constants';
export default {
components: {
......
......@@ -5,8 +5,8 @@ import { __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import TokenizedInput from '../tokenized_input/input.vue';
import inputFocus from '../../mixins';
import TokenizedInput from './input.vue';
import inputFocus from '../mixins';
const inputSearchDelay = 300;
......
<script>
import { GlTooltip } from '@gitlab/ui';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
GlTooltip,
Icon,
},
mixins: [timeagoMixin],
props: {
time: {
type: String,
required: true,
},
tooltipText: {
type: String,
required: true,
},
},
computed: {
timeTitle() {
return this.tooltipTitle(this.time);
},
formattedTime() {
return this.timeFormated(this.time);
},
},
};
</script>
<template>
<div class="text-secondary">
<icon
name="clock"
class="dashboard-card-time-ago-icon align-text-bottom js-dashboard-project-clock-icon"
/>
<time ref="timeAgo" class="js-dashboard-project-time-ago">
{{ formattedTime }}
</time>
<gl-tooltip :target="() => $refs.timeAgo">
<div class="bold">{{ tooltipText }}</div>
<div>{{ timeTitle }}</div>
</gl-tooltip>
</div>
</template>
export const STATUS_FAILED = 'failed';
export const STATUS_RUNNING = 'running';
export default {
data() {
return {
isInputFocused: false,
};
},
methods: {
onFocus() {
this.isInputFocused = true;
this.$emit('focus');
},
onBlur() {
this.isInputFocused = false;
this.$emit('blur');
},
},
};
import Vue from 'vue';
import store from 'ee/operations/store/index';
import Dashboard from 'ee/operations/components/dashboard/dashboard.vue';
import ProjectSearch from 'ee/operations/components/dashboard/project_search.vue';
import ProjectSearch from 'ee/vue_shared/dashboards/components/project_search.vue';
import DashboardProject from 'ee/operations/components/dashboard/project.vue';
import { getChildInstances, clearState } from '../../helpers';
import { mockProjectData, mockText } from '../../mock_data';
......
import Vue from 'vue';
import store from 'ee/operations/store/index';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import ProjectSearch from 'ee/operations/components/dashboard/project_search.vue';
import TokenizedInput from 'ee/operations/components/tokenized_input/input.vue';
import ProjectSearch from 'ee/vue_shared/dashboards/components/project_search.vue';
import TokenizedInput from 'ee/vue_shared/dashboards/components/input.vue';
import { mockText, mockProjectData } from '../../mock_data';
import { getChildInstances, mouseEvent, clearState } from '../../helpers';
......
......@@ -3,7 +3,7 @@ import Vuex from 'vuex';
import Commit from '~/vue_shared/components/commit.vue';
import Project from 'ee/operations/components/dashboard/project.vue';
import ProjectHeader from 'ee/operations/components/dashboard/project_header.vue';
import Alerts from 'ee/operations/components/dashboard/alerts.vue';
import Alerts from 'ee/vue_shared/dashboards/components/alerts.vue';
import store from 'ee/operations/store';
import { mockOneProject } from '../../mock_data';
......@@ -79,18 +79,5 @@ describe('project component', () => {
expect(commit.props('tag')).toBe(wrapper.props().project.last_pipeline.ref.tag);
});
});
describe('deploy finished at', () => {
it('renders clock icon', () => {
expect(wrapper.contains('.js-dashboard-project-clock-icon')).toBe(true);
});
it('renders time ago of finished time', () => {
const timeago = '1 day ago';
const container = wrapper.element.querySelector('.js-dashboard-project-time-ago');
expect(container.innerText.trim()).toBe(timeago);
});
});
});
});
import Vue from 'vue';
import store from 'ee/operations/store/index';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import TokenizedInput from 'ee/operations/components/tokenized_input/input.vue';
import TokenizedInput from 'ee/vue_shared/dashboards/components/input.vue';
import { clearState } from '../../helpers';
import { mockProjectData } from '../../mock_data';
......
import { TEST_HOST } from 'spec/test_constants';
const AVATAR_URL = `${TEST_HOST}/dummy.jpg`;
import mockPipelineData from 'ee_spec/vue_shared/dashboards/mock_data';
export const mockText = {
ADD_PROJECTS: 'Add projects',
......@@ -18,71 +16,6 @@ export const mockText = {
SEARCH_DESCRIPTION_SUFFIX: 'in projects',
};
export function mockPipelineData(
status = 'success',
id = 1,
finishedTimeStamp = new Date(Date.now() - 86400000).toISOString(),
isTag = false,
) {
return {
id,
user: {
id: 1,
name: 'Test',
username: 'test',
state: 'active',
avatar_url: AVATAR_URL,
web_url: '/test',
status_tooltip_html: null,
path: '/test',
},
active: false,
path: '/test/test-project/pipelines/1',
details: {
status: {
icon: `status_${status}`,
text: status,
label: status,
group: status,
tooltip: status,
has_details: true,
details_path: '/test/test-project/pipelines/1',
illustration: null,
},
finished_at: finishedTimeStamp,
},
ref: {
name: 'master',
path: 'test/test-project/commits/master',
tag: isTag,
branch: true,
merge_request: false,
},
commit: {
id: 'e778416d94deaf75bdabcc8fdd6b7d21f482bcca',
short_id: 'e778416d',
title: "Add new file to the branch I'm working on",
message: "Add new file to the branch I'm working on",
author: {
id: 1,
name: 'Test',
username: 'test',
state: 'active',
avatar_url: AVATAR_URL,
status_tooltip_html: null,
path: '/test',
},
commit_url: '/test/test-project/commit/e778416d94deaf75bdabcc8fdd6b7d21f482bcca',
commit_path: '/test/test-project/commit/e778416d94deaf75bdabcc8fdd6b7d21f482bcca',
},
project: {
full_name: 'Test / test-project',
full_path: '/test/test-project',
name: 'test-project',
},
};
}
export function mockProjectData(
projectCount = 1,
currentPipelineStatus = 'success',
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Alerts from 'ee/operations/components/dashboard/alerts.vue';
import Alerts from 'ee/vue_shared/dashboards/components/alerts.vue';
const localVue = createLocalVue();
......
import { mount, createLocalVue } from '@vue/test-utils';
import ProjectPipeline from 'ee/operations/components/dashboard/project_pipeline.vue';
import { mockPipelineData } from '../../mock_data';
import ProjectPipeline from 'ee/vue_shared/dashboards/components/project_pipeline.vue';
import mockPipelineData from '../mock_data';
const localVue = createLocalVue();
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import TimeAgo from 'ee/vue_shared/dashboards/components/time_ago.vue';
const localVue = createLocalVue();
describe('time ago component', () => {
const TimeAgoComponent = localVue.extend(TimeAgo);
let wrapper;
beforeEach(() => {
wrapper = shallowMount(TimeAgoComponent, {
sync: false,
localVue,
propsData: {
time: new Date(Date.now() - 86400000).toISOString(),
tooltipText: 'Finished',
},
});
});
describe('render', () => {
it('renders clock icon', () => {
expect(wrapper.contains('.js-dashboard-project-clock-icon')).toBe(true);
});
it('renders time ago of finished time', () => {
const timeago = '1 day ago';
const container = wrapper.element.querySelector('.js-dashboard-project-time-ago');
expect(container.innerText.trim()).toBe(timeago);
});
});
});
import { TEST_HOST } from 'spec/test_constants';
const AVATAR_URL = `${TEST_HOST}/dummy.jpg`;
export default function mockPipelineData(
status = 'success',
id = 1,
finishedTimeStamp = new Date(Date.now() - 86400000).toISOString(),
isTag = false,
) {
return {
id,
user: {
id: 1,
name: 'Test',
username: 'test',
state: 'active',
avatar_url: AVATAR_URL,
web_url: '/test',
status_tooltip_html: null,
path: '/test',
},
active: false,
path: '/test/test-project/pipelines/1',
details: {
status: {
icon: `status_${status}`,
text: status,
label: status,
group: status,
tooltip: status,
has_details: true,
details_path: '/test/test-project/pipelines/1',
illustration: null,
},
finished_at: finishedTimeStamp,
},
ref: {
name: 'master',
path: 'test/test-project/commits/master',
tag: isTag,
branch: true,
merge_request: false,
},
commit: {
id: 'e778416d94deaf75bdabcc8fdd6b7d21f482bcca',
short_id: 'e778416d',
title: "Add new file to the branch I'm working on",
message: "Add new file to the branch I'm working on",
author: {
id: 1,
name: 'Test',
username: 'test',
state: 'active',
avatar_url: AVATAR_URL,
status_tooltip_html: null,
path: '/test',
},
commit_url: '/test/test-project/commit/e778416d94deaf75bdabcc8fdd6b7d21f482bcca',
commit_path: '/test/test-project/commit/e778416d94deaf75bdabcc8fdd6b7d21f482bcca',
},
project: {
full_name: 'Test / test-project',
full_path: '/test/test-project',
name: 'test-project',
},
};
}
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