diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue new file mode 100644 index 00000000000..6670b554faf --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/loading_button.vue @@ -0,0 +1,71 @@ + + + diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index aa61ddc6a2c..a0bb9e61436 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -5,6 +5,7 @@ @import "framework/layout"; @import "framework/animations"; +@import "framework/vue_transitions"; @import "framework/avatar"; @import "framework/asciidoctor"; @import "framework/banner"; diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index b131e2d57ee..00a0e9cef67 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -292,6 +292,11 @@ } } +.btn-align-content { + display: flex; + align-items: center; +} + .btn-group { &.btn-grouped { @include btn-with-margin; diff --git a/app/assets/stylesheets/framework/vue_transitions.scss b/app/assets/stylesheets/framework/vue_transitions.scss new file mode 100644 index 00000000000..e07a177e153 --- /dev/null +++ b/app/assets/stylesheets/framework/vue_transitions.scss @@ -0,0 +1,9 @@ +.fade-enter-active, +.fade-leave-active { + transition: opacity $sidebar-transition-duration $general-hover-transition-curve; +} + +.fade-enter, +.fade-leave-to { + opacity: 0; +} diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index ea37ccf5e3d..97ca01f0f54 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -1,8 +1,3 @@ -.fade-enter-active, -.fade-leave-active { - transition: opacity $sidebar-transition-duration; -} - .monaco-loader { position: absolute; top: 0; diff --git a/changelogs/unreleased/add-shared-vue-loading-button.yml b/changelogs/unreleased/add-shared-vue-loading-button.yml new file mode 100644 index 00000000000..a8904acc4e7 --- /dev/null +++ b/changelogs/unreleased/add-shared-vue-loading-button.yml @@ -0,0 +1,5 @@ +--- +title: Add loading button for new UX paradigm +merge_request: 14883 +author: +type: added diff --git a/spec/javascripts/vue_shared/components/loading_button_spec.js b/spec/javascripts/vue_shared/components/loading_button_spec.js new file mode 100644 index 00000000000..97c8a08fcdd --- /dev/null +++ b/spec/javascripts/vue_shared/components/loading_button_spec.js @@ -0,0 +1,93 @@ +import Vue from 'vue'; +import loadingButton from '~/vue_shared/components/loading_button.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +const LABEL = 'Hello'; + +describe('LoadingButton', function () { + let vm; + let LoadingButton; + + beforeEach(() => { + LoadingButton = Vue.extend(loadingButton); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('loading spinner', () => { + it('shown when loading', () => { + vm = mountComponent(LoadingButton, { + loading: true, + }); + + expect(vm.$el.querySelector('.js-loading-button-icon')).toBeDefined(); + }); + }); + + describe('disabled state', () => { + it('disabled when loading', () => { + vm = mountComponent(LoadingButton, { + loading: true, + }); + + expect(vm.$el.disabled).toEqual(true); + }); + + it('not disabled when normal', () => { + vm = mountComponent(LoadingButton, { + loading: false, + }); + + expect(vm.$el.disabled).toEqual(false); + }); + }); + + describe('label', () => { + it('shown when normal', () => { + vm = mountComponent(LoadingButton, { + loading: false, + label: LABEL, + }); + const label = vm.$el.querySelector('.js-loading-button-label'); + + expect(label.textContent.trim()).toEqual(LABEL); + }); + + it('shown when loading', () => { + vm = mountComponent(LoadingButton, { + loading: true, + label: LABEL, + }); + const label = vm.$el.querySelector('.js-loading-button-label'); + + expect(label.textContent.trim()).toEqual(LABEL); + }); + }); + + describe('click callback prop', () => { + it('calls given callback when normal', () => { + vm = mountComponent(LoadingButton, { + loading: false, + }); + spyOn(vm, '$emit'); + + vm.$el.click(); + + expect(vm.$emit).toHaveBeenCalledWith('click', jasmine.any(Object)); + }); + + it('does not call given callback when disabled because of loading', () => { + vm = mountComponent(LoadingButton, { + loading: true, + indeterminate: true, + }); + spyOn(vm, '$emit'); + + vm.$el.click(); + + expect(vm.$emit).not.toHaveBeenCalled(); + }); + }); +});