summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-08-16 10:45:15 +0000
committerPhil Hughes <me@iamphill.com>2018-08-16 10:45:15 +0000
commitbd9e5c9bcce90bd2289ee818bfac58ea3a090aaf (patch)
tree67965359679011e71c10a87451429a5557b8e61e
parenta7045bd3dc3380f9f4d0a6230aa98ddd48152503 (diff)
parent8369ea8972f1c244bf1fe37f1128f824be25f69b (diff)
downloadgitlab-ce-bd9e5c9bcce90bd2289ee818bfac58ea3a090aaf.tar.gz
Merge branch '50101-empty-state-component' into 'master'
Creates empty state component for job log view See merge request gitlab-org/gitlab-ce!21195
-rw-r--r--app/assets/javascripts/jobs/components/empty_state.vue76
-rw-r--r--changelogs/unreleased/50101-empty-state-component.yml5
-rw-r--r--spec/javascripts/jobs/empty_state_spec.js90
3 files changed, 171 insertions, 0 deletions
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue
new file mode 100644
index 00000000000..4faf08387fb
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/empty_state.vue
@@ -0,0 +1,76 @@
+<script>
+ export default {
+ props: {
+ illustrationPath: {
+ type: String,
+ required: true,
+ },
+ illustrationSizeClass: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ content: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ action: {
+ type: Object,
+ required: false,
+ default: null,
+ validator(value) {
+ return (
+ value === null ||
+ (Object.prototype.hasOwnProperty.call(value, 'link') &&
+ Object.prototype.hasOwnProperty.call(value, 'method') &&
+ Object.prototype.hasOwnProperty.call(value, 'title'))
+ );
+ },
+ },
+ },
+ };
+</script>
+<template>
+ <div class="row empty-state">
+ <div class="col-12">
+ <div
+ :class="illustrationSizeClass"
+ class="svg-content"
+ >
+ <img :src="illustrationPath" />
+ </div>
+ </div>
+
+ <div class="col-12">
+ <div class="text-content">
+ <h4 class="js-job-empty-state-title text-center">
+ {{ title }}
+ </h4>
+
+ <p
+ v-if="content"
+ class="js-job-empty-state-content"
+ >
+ {{ content }}
+ </p>
+
+ <div
+ v-if="action"
+ class="text-center"
+ >
+ <a
+ :href="action.link"
+ :data-method="action.method"
+ class="js-job-empty-state-action btn btn-primary"
+ >
+ {{ action.title }}
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/changelogs/unreleased/50101-empty-state-component.yml b/changelogs/unreleased/50101-empty-state-component.yml
new file mode 100644
index 00000000000..ee99b65d964
--- /dev/null
+++ b/changelogs/unreleased/50101-empty-state-component.yml
@@ -0,0 +1,5 @@
+---
+title: Creates empty state vue component for job view
+merge_request:
+author:
+type: other
diff --git a/spec/javascripts/jobs/empty_state_spec.js b/spec/javascripts/jobs/empty_state_spec.js
new file mode 100644
index 00000000000..f8feb069fe0
--- /dev/null
+++ b/spec/javascripts/jobs/empty_state_spec.js
@@ -0,0 +1,90 @@
+import Vue from 'vue';
+import component from '~/jobs/components/empty_state.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Empty State', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ illustrationPath: 'illustrations/pending_job_empty.svg',
+ illustrationSizeClass: 'svg-430',
+ title: 'This job has not started yet',
+ };
+
+ const content = 'This job is in pending state and is waiting to be picked by a runner';
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('renders image and title', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+ });
+
+ it('renders img with provided path and size', () => {
+ expect(vm.$el.querySelector('img').getAttribute('src')).toEqual(props.illustrationPath);
+ expect(vm.$el.querySelector('.svg-content').classList).toContain(props.illustrationSizeClass);
+ });
+
+ it('renders provided title', () => {
+ expect(vm.$el.querySelector('.js-job-empty-state-title').textContent.trim()).toEqual(
+ props.title,
+ );
+ });
+ });
+
+ describe('with content', () => {
+ it('renders content', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-content').textContent.trim()).toEqual(
+ content,
+ );
+ });
+ });
+
+ describe('without content', () => {
+ it('does not render content', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ });
+ expect(vm.$el.querySelector('.js-job-empty-state-content')).toBeNull();
+ });
+ });
+
+ describe('with action', () => {
+ it('renders action', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ action: {
+ link: 'runner',
+ title: 'Check runner',
+ method: 'post',
+ },
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-action').getAttribute('href')).toEqual(
+ 'runner',
+ );
+ });
+ });
+
+ describe('without action', () => {
+ it('does not render action', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+ expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull();
+ });
+ });
+});