summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWinnie Hellmann <winnie@gitlab.com>2018-02-16 11:30:55 +0100
committerWinnie Hellmann <winnie@gitlab.com>2018-02-19 21:04:22 +0100
commit2ec44372ebaaa2a5544f28ecfbe41f62093becf7 (patch)
treeafc31e092976232f138f3872a9c4b69f677950b9
parent0b032daa11d5ea140f2eea99fa10b21da4b50f0b (diff)
downloadgitlab-ce-winh-more-modal-components.tar.gz
Add secondary button to gl-modal componentwinh-more-modal-components
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue56
-rw-r--r--app/assets/javascripts/vue_shared/components/modal_button.vue32
-rw-r--r--changelogs/unreleased/winh-more-modal-components.yml5
-rw-r--r--doc/development/fe_guide/components.md21
-rw-r--r--doc/development/fe_guide/img/gl-modal-secondary.pngbin0 -> 30994 bytes
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_spec.js54
6 files changed, 148 insertions, 20 deletions
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index 67c9181c7b1..3520f984083 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -1,14 +1,13 @@
<script>
- const buttonVariants = [
- 'danger',
- 'primary',
- 'success',
- 'warning',
- ];
+ import ModalButton from './modal_button.vue';
export default {
name: 'GlModal',
+ components: {
+ ModalButton,
+ },
+
props: {
id: {
type: String,
@@ -24,19 +23,37 @@
type: String,
required: false,
default: 'primary',
- validator: value => buttonVariants.indexOf(value) !== -1,
},
footerPrimaryButtonText: {
type: String,
required: false,
default: '',
},
+ footerSecondaryButtonVariant: {
+ type: String,
+ required: false,
+ default: 'default',
+ },
+ footerSecondaryButtonText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+
+ computed: {
+ hasSecondaryButton() {
+ return this.footerSecondaryButtonText && this.footerSecondaryButtonText !== '';
+ },
},
methods: {
emitCancel(event) {
this.$emit('cancel', event);
},
+ emitSecondaryAction(event) {
+ this.$emit('secondaryAction', event);
+ },
emitSubmit(event) {
this.$emit('submit', event);
},
@@ -81,23 +98,22 @@
<div class="modal-footer">
<slot name="footer">
- <button
- type="button"
- class="btn"
- data-dismiss="modal"
- @click="emitCancel($event)"
- >
+ <modal-button @click="emitCancel($event)">
{{ s__('Modal|Cancel') }}
- </button>
- <button
- type="button"
- class="btn"
- :class="`btn-${footerPrimaryButtonVariant}`"
- data-dismiss="modal"
+ </modal-button>
+ <modal-button
+ v-if="hasSecondaryButton"
+ :variant="footerSecondaryButtonVariant"
+ @click="emitSecondaryAction($event)"
+ >
+ {{ footerSecondaryButtonText }}
+ </modal-button>
+ <modal-button
+ :variant="footerPrimaryButtonVariant"
@click="emitSubmit($event)"
>
{{ footerPrimaryButtonText }}
- </button>
+ </modal-button>
</slot>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/modal_button.vue b/app/assets/javascripts/vue_shared/components/modal_button.vue
new file mode 100644
index 00000000000..f22e740d568
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/modal_button.vue
@@ -0,0 +1,32 @@
+<script>
+ const buttonVariants = [
+ 'danger',
+ 'default',
+ 'primary',
+ 'success',
+ 'warning',
+ ];
+
+ export default {
+ name: 'ModalButton',
+
+ props: {
+ variant: {
+ type: String,
+ required: false,
+ default: 'default',
+ validator: value => buttonVariants.indexOf(value) !== -1,
+ },
+ },
+ };
+</script>
+
+<template>
+ <button
+ type="button"
+ class="btn"
+ :class="`btn-${variant}`"
+ data-dismiss="modal">
+ <slot></slot>
+ </button>
+</template>
diff --git a/changelogs/unreleased/winh-more-modal-components.yml b/changelogs/unreleased/winh-more-modal-components.yml
new file mode 100644
index 00000000000..8c1b6a8da62
--- /dev/null
+++ b/changelogs/unreleased/winh-more-modal-components.yml
@@ -0,0 +1,5 @@
+---
+title: Add secondary button to gl-modal component
+merge_request: 17172
+author:
+type: changed
diff --git a/doc/development/fe_guide/components.md b/doc/development/fe_guide/components.md
index 66a8abe42f7..255f92e8fbb 100644
--- a/doc/development/fe_guide/components.md
+++ b/doc/development/fe_guide/components.md
@@ -59,3 +59,24 @@ Here is an example of how to use it:
```
![example modal](img/gl-modal.png)
+
+### Modals with secondary action
+
+You can also add a secondary button to modals:
+
+```html
+ <gl-modal
+ id="dogs-out-modal"
+ :header-title-text="s__('ModalExample|Let the dogs out?')"
+ footer-primary-button-variant="danger"
+ :footer-primary-button-text="s__('ModalExample|Let them out')"
+ footer-secondary-button-variant="success"
+ :footer-secondary-button-text="s__('ModalExample|Call Ghostbusters')"
+ @submit="letOut(theDogs)"
+ @secondaryAction="Ghostbusters.call()"
+ >
+ {{ s__('ModalExample|You’re about to let the dogs out.') }}
+ </gl-modal>
+```
+
+![example modal with secondary action](img/gl-modal-secondary.png)
diff --git a/doc/development/fe_guide/img/gl-modal-secondary.png b/doc/development/fe_guide/img/gl-modal-secondary.png
new file mode 100644
index 00000000000..e34309935ff
--- /dev/null
+++ b/doc/development/fe_guide/img/gl-modal-secondary.png
Binary files differ
diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
index d6148cb785b..2b2115041c0 100644
--- a/spec/javascripts/vue_shared/components/gl_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -80,6 +80,52 @@ describe('GlModal', () => {
expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText);
});
});
+
+ describe('with footerSecondaryButtonVariant', () => {
+ const props = {
+ footerSecondaryButtonText: 'something to make the button visible',
+ footerSecondaryButtonVariant: 'danger',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('sets the secondary button class', () => {
+ const secondaryButton = vm.$el.querySelector('.modal-footer button:nth-of-type(2)');
+ expect(secondaryButton.classList).toContain(`btn-${props.footerSecondaryButtonVariant}`);
+ });
+ });
+
+ describe('with footerSecondaryButtonText', () => {
+ const props = {
+ footerSecondaryButtonText: 'my second button text',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('sets the secondary button text', () => {
+ const secondaryButton = vm.$el.querySelector('.modal-footer button:nth-of-type(2)');
+ expect(secondaryButton.innerHTML.trim()).toBe(props.footerSecondaryButtonText);
+ });
+ });
+
+ describe('without footerSecondaryButtonText', () => {
+ const props = {
+ footerSecondaryButtonText: null,
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('does not render a secondary button', () => {
+ const buttons = vm.$el.querySelectorAll('.modal-footer button');
+ expect(buttons.length).toBe(2);
+ });
+ });
});
it('works with data-toggle="modal"', (done) => {
@@ -114,6 +160,14 @@ describe('GlModal', () => {
});
});
+ describe('emitSecondaryAction', () => {
+ it('emits a secondaryAction event', () => {
+ vm.emitSecondaryAction(dummyEvent);
+
+ expect(vm.$emit).toHaveBeenCalledWith('secondaryAction', dummyEvent);
+ });
+ });
+
describe('emitSubmit', () => {
it('emits a submit event', () => {
vm.emitSubmit(dummyEvent);