BigW Consortium Gitlab

Commit ea9721f1 by Phil Hughes Committed by Mark Fletcher

Merge branch 'winh-new-branch-dropdown-style' into 'master'

Cleanup new branch/merge request form in issues Closes #41938 See merge request gitlab-org/gitlab-ce!16854
parent 3ca88730
...@@ -180,6 +180,7 @@ export default class CreateMergeRequestDropdown { ...@@ -180,6 +180,7 @@ export default class CreateMergeRequestDropdown {
valueAttribute: 'data-text', valueAttribute: 'data-text',
}, },
], ],
hideOnClick: false,
}; };
} }
......
...@@ -3,7 +3,6 @@ const DATA_DROPDOWN = 'data-dropdown'; ...@@ -3,7 +3,6 @@ const DATA_DROPDOWN = 'data-dropdown';
const SELECTED_CLASS = 'droplab-item-selected'; const SELECTED_CLASS = 'droplab-item-selected';
const ACTIVE_CLASS = 'droplab-item-active'; const ACTIVE_CLASS = 'droplab-item-active';
const IGNORE_CLASS = 'droplab-item-ignore'; const IGNORE_CLASS = 'droplab-item-ignore';
const IGNORE_HIDING_CLASS = 'droplab-item-ignore-hiding';
// Matches `{{anything}}` and `{{ everything }}`. // Matches `{{anything}}` and `{{ everything }}`.
const TEMPLATE_REGEX = /\{\{(.+?)\}\}/g; const TEMPLATE_REGEX = /\{\{(.+?)\}\}/g;
...@@ -14,5 +13,4 @@ export { ...@@ -14,5 +13,4 @@ export {
ACTIVE_CLASS, ACTIVE_CLASS,
TEMPLATE_REGEX, TEMPLATE_REGEX,
IGNORE_CLASS, IGNORE_CLASS,
IGNORE_HIDING_CLASS,
}; };
import utils from './utils'; import utils from './utils';
import { SELECTED_CLASS, IGNORE_CLASS, IGNORE_HIDING_CLASS } from './constants'; import { SELECTED_CLASS, IGNORE_CLASS } from './constants';
class DropDown { class DropDown {
constructor(list, config = {}) { constructor(list, config = { }) {
this.currentIndex = 0; this.currentIndex = 0;
this.hidden = true; this.hidden = true;
this.list = typeof list === 'string' ? document.querySelector(list) : list; this.list = typeof list === 'string' ? document.querySelector(list) : list;
this.items = []; this.items = [];
this.eventWrapper = {}; this.eventWrapper = {};
this.hideOnClick = config.hideOnClick !== false;
if (config.addActiveClassToDropdownButton) { if (config.addActiveClassToDropdownButton) {
this.dropdownToggle = this.list.parentNode.querySelector('.js-dropdown-toggle'); this.dropdownToggle = this.list.parentNode.querySelector('.js-dropdown-toggle');
...@@ -37,15 +38,17 @@ class DropDown { ...@@ -37,15 +38,17 @@ class DropDown {
clickEvent(e) { clickEvent(e) {
if (e.target.tagName === 'UL') return; if (e.target.tagName === 'UL') return;
if (e.target.classList.contains(IGNORE_CLASS)) return; if (e.target.closest(`.${IGNORE_CLASS}`)) return;
const selected = utils.closest(e.target, 'LI'); const selected = e.target.closest('li');
if (!selected) return; if (!selected) return;
this.addSelectedClass(selected); this.addSelectedClass(selected);
e.preventDefault(); e.preventDefault();
if (!e.target.classList.contains(IGNORE_HIDING_CLASS)) this.hide(); if (this.hideOnClick) {
this.hide();
}
const listEvent = new CustomEvent('click.dl', { const listEvent = new CustomEvent('click.dl', {
detail: { detail: {
......
...@@ -449,9 +449,11 @@ img.emoji { ...@@ -449,9 +449,11 @@ img.emoji {
.prepend-top-10 { margin-top: 10px; } .prepend-top-10 { margin-top: 10px; }
.prepend-top-15 { margin-top: 15px; } .prepend-top-15 { margin-top: 15px; }
.prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-16 { margin-top: 16px; }
.prepend-top-20 { margin-top: 20px; } .prepend-top-20 { margin-top: 20px; }
.prepend-left-4 { margin-left: 4px; } .prepend-left-4 { margin-left: 4px; }
.prepend-left-5 { margin-left: 5px; } .prepend-left-5 { margin-left: 5px; }
.prepend-left-8 { margin-left: 8px; }
.prepend-left-10 { margin-left: 10px; } .prepend-left-10 { margin-left: 10px; }
.prepend-left-default { margin-left: $gl-padding; } .prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; } .prepend-left-20 { margin-left: 20px; }
......
...@@ -736,10 +736,6 @@ ...@@ -736,10 +736,6 @@
} }
} }
.droplab-item-ignore {
pointer-events: none;
}
.pika-single.animate-picker.is-bound, .pika-single.animate-picker.is-bound,
.pika-single.animate-picker.is-bound.is-hidden { .pika-single.animate-picker.is-bound.is-hidden {
/* /*
......
...@@ -182,6 +182,7 @@ label { ...@@ -182,6 +182,7 @@ label {
.help-block { .help-block {
margin-bottom: 0; margin-bottom: 0;
margin-top: #{$grid-size / 2};
} }
.gl-field-error { .gl-field-error {
......
...@@ -181,11 +181,6 @@ ul.related-merge-requests > li { ...@@ -181,11 +181,6 @@ ul.related-merge-requests > li {
} }
.create-mr-dropdown-wrap { .create-mr-dropdown-wrap {
.branch-message,
.ref-message {
display: none;
}
.ref::selection { .ref::selection {
color: $placeholder-text-color; color: $placeholder-text-color;
} }
...@@ -216,6 +211,17 @@ ul.related-merge-requests > li { ...@@ -216,6 +211,17 @@ ul.related-merge-requests > li {
transform: translateY(0); transform: translateY(0);
display: none; display: none;
margin-top: 4px; margin-top: 4px;
// override dropdown item styles
.btn.btn-success {
@include btn-default;
@include btn-green;
border-style: solid;
border-width: 1px;
line-height: $line-height-base;
width: auto;
}
} }
.create-merge-request-dropdown-toggle { .create-merge-request-dropdown-toggle {
...@@ -225,66 +231,6 @@ ul.related-merge-requests > li { ...@@ -225,66 +231,6 @@ ul.related-merge-requests > li {
margin-left: 0; margin-left: 0;
} }
} }
.droplab-item-ignore {
pointer-events: auto;
}
.create-item {
cursor: pointer;
margin: 0 1px;
&:hover,
&:focus {
background-color: $dropdown-item-hover-bg;
color: $gl-text-color;
}
}
li.divider {
margin: 8px 10px;
}
li:not(.divider) {
padding: 8px 9px;
&:last-child {
padding-bottom: 8px;
}
&.droplab-item-selected {
.icon-container {
i {
visibility: visible;
}
}
.description {
display: block;
}
}
&.droplab-item-ignore {
padding-top: 8px;
}
.icon-container {
float: left;
i {
visibility: hidden;
}
}
.description {
padding-left: 22px;
}
input,
span {
margin: 4px 0 0;
}
}
} }
.discussion-reply-holder .note-edit-form { .discussion-reply-holder .note-edit-form {
......
...@@ -21,30 +21,33 @@ ...@@ -21,30 +21,33 @@
%button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' } } } %button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' } } }
= icon('caret-down') = icon('caret-down')
%ul#create-merge-request-dropdown.create-merge-request-dropdown-menu.dropdown-menu.dropdown-menu-align-right.gl-show-field-errors{ data: { dropdown: true } } .droplab-dropdown
- if can_create_merge_request %ul#create-merge-request-dropdown.create-merge-request-dropdown-menu.dropdown-menu.dropdown-menu-align-right.gl-show-field-errors{ data: { dropdown: true } }
%li.create-item.droplab-item-selected.droplab-item-ignore-hiding{ role: 'button', data: { value: 'create-mr', text: 'Create merge request' } } - if can_create_merge_request
.menu-item.droplab-item-ignore-hiding %li.droplab-item-selected{ role: 'button', data: { value: 'create-mr', text: _('Create merge request') } }
.icon-container.droplab-item-ignore-hiding= icon('check') .menu-item
.description.droplab-item-ignore-hiding Create merge request and branch = icon('check', class: 'icon')
= _('Create merge request and branch')
%li.create-item.droplab-item-ignore-hiding{ class: [!can_create_merge_request && 'droplab-item-selected'], role: 'button', data: { value: 'create-branch', text: 'Create branch' } }
.menu-item.droplab-item-ignore-hiding %li{ class: [!can_create_merge_request && 'droplab-item-selected'], role: 'button', data: { value: 'create-branch', text: _('Create branch') } }
.icon-container.droplab-item-ignore-hiding= icon('check') .menu-item
.description.droplab-item-ignore-hiding Create branch = icon('check', class: 'icon')
%li.divider = _('Create branch')
%li.divider.droplab-item-ignore
%li.droplab-item-ignore
Branch name %li.droplab-item-ignore.prepend-left-8.append-right-8.prepend-top-16
%input.js-branch-name.form-control.droplab-item-ignore{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" } .form-group
%span.js-branch-message.branch-message.droplab-item-ignore %label{ for: 'new-branch-name' }
= _('Branch name')
%li.droplab-item-ignore %input#new-branch-name.js-branch-name.form-control{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" }
Source (branch or tag) %span.js-branch-message.help-block
%input.js-ref.ref.form-control.droplab-item-ignore{ type: 'text', placeholder: "#{@project.default_branch}", value: "#{@project.default_branch}", data: { value: "#{@project.default_branch}" } }
%span.js-ref-message.ref-message.droplab-item-ignore .form-group
%label{ for: 'source-name' }
%li.droplab-item-ignore = _('Source (branch or tag)')
%button.btn.btn-success.js-create-target.droplab-item-ignore{ type: 'button', data: { action: 'create-mr' } } %input#source-name.js-ref.ref.form-control{ type: 'text', placeholder: "#{@project.default_branch}", value: "#{@project.default_branch}", data: { value: "#{@project.default_branch}" } }
Create merge request %span.js-ref-message.help-block
.form-group
%button.btn.btn-success.js-create-target{ type: 'button', data: { action: 'create-mr' } }
= _('Create merge request')
---
title: Cleanup new branch/merge request form in issues
merge_request: 16854
author:
type: fixed
import DropDown from '~/droplab/drop_down'; import DropDown from '~/droplab/drop_down';
import utils from '~/droplab/utils'; import utils from '~/droplab/utils';
import { SELECTED_CLASS, IGNORE_CLASS } from '~/droplab/constants'; import { SELECTED_CLASS } from '~/droplab/constants';
describe('DropDown', function () { describe('DropLab DropDown', function () {
describe('class constructor', function () { describe('class constructor', function () {
beforeEach(function () { beforeEach(function () {
spyOn(DropDown.prototype, 'getItems'); spyOn(DropDown.prototype, 'getItems');
...@@ -128,93 +128,131 @@ describe('DropDown', function () { ...@@ -128,93 +128,131 @@ describe('DropDown', function () {
beforeEach(function () { beforeEach(function () {
this.classList = jasmine.createSpyObj('classList', ['contains']); this.classList = jasmine.createSpyObj('classList', ['contains']);
this.list = { dispatchEvent: () => {} }; this.list = { dispatchEvent: () => {} };
this.dropdown = { hide: () => {}, list: this.list, addSelectedClass: () => {} }; this.dropdown = {
this.event = { preventDefault: () => {}, target: { classList: this.classList } }; hideOnClick: true,
hide: () => {},
list: this.list,
addSelectedClass: () => {},
};
this.event = {
preventDefault: () => {},
target: {
classList: this.classList,
closest: () => null,
},
};
this.customEvent = {}; this.customEvent = {};
this.closestElement = {}; this.dummyListItem = document.createElement('li');
spyOn(this.event.target, 'closest').and.callFake((selector) => {
if (selector === 'li') {
return this.dummyListItem;
}
return null;
});
spyOn(this.dropdown, 'hide'); spyOn(this.dropdown, 'hide');
spyOn(this.dropdown, 'addSelectedClass'); spyOn(this.dropdown, 'addSelectedClass');
spyOn(this.list, 'dispatchEvent'); spyOn(this.list, 'dispatchEvent');
spyOn(this.event, 'preventDefault'); spyOn(this.event, 'preventDefault');
spyOn(window, 'CustomEvent').and.returnValue(this.customEvent); spyOn(window, 'CustomEvent').and.returnValue(this.customEvent);
spyOn(utils, 'closest').and.returnValues(this.closestElement, undefined);
this.classList.contains.and.returnValue(false); this.classList.contains.and.returnValue(false);
});
it('should call event.target.closest', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event); DropDown.prototype.clickEvent.call(this.dropdown, this.event);
});
it('should call utils.closest', function () { expect(this.event.target.closest).toHaveBeenCalledWith('.droplab-item-ignore');
expect(utils.closest).toHaveBeenCalledWith(this.event.target, 'LI'); expect(this.event.target.closest).toHaveBeenCalledWith('li');
}); });
it('should call addSelectedClass', function () { it('should call addSelectedClass', function () {
expect(this.dropdown.addSelectedClass).toHaveBeenCalledWith(this.closestElement); DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.dropdown.addSelectedClass).toHaveBeenCalledWith(this.dummyListItem);
}); });
it('should call .preventDefault', function () { it('should call .preventDefault', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.preventDefault).toHaveBeenCalled(); expect(this.event.preventDefault).toHaveBeenCalled();
}); });
it('should call .hide', function () { it('should call .hide', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.dropdown.hide).toHaveBeenCalled(); expect(this.dropdown.hide).toHaveBeenCalled();
}); });
it('should construct CustomEvent', function () { it('should construct CustomEvent', function () {
expect(window.CustomEvent).toHaveBeenCalledWith('click.dl', jasmine.any(Object)); DropDown.prototype.clickEvent.call(this.dropdown, this.event);
});
it('should call .classList.contains checking for IGNORE_CLASS', function () { expect(window.CustomEvent).toHaveBeenCalledWith('click.dl', jasmine.any(Object));
expect(this.classList.contains).toHaveBeenCalledWith(IGNORE_CLASS);
}); });
it('should call .dispatchEvent with the customEvent', function () { it('should call .dispatchEvent with the customEvent', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent); expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent);
}); });
describe('if the target is a UL element', function () { describe('if the target is a UL element', function () {
beforeEach(function () { beforeEach(function () {
this.event = { preventDefault: () => {}, target: { tagName: 'UL', classList: this.classList } }; this.event.target = document.createElement('ul');
spyOn(this.event, 'preventDefault');
utils.closest.calls.reset();
DropDown.prototype.clickEvent.call(this.dropdown, this.event); spyOn(this.event.target, 'closest');
}); });
it('should return immediately', function () { it('should return immediately', function () {
expect(utils.closest).not.toHaveBeenCalled(); DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.target.closest).not.toHaveBeenCalled();
expect(this.dropdown.addSelectedClass).not.toHaveBeenCalled();
}); });
}); });
describe('if the target has the IGNORE_CLASS class', function () { describe('if the target has the droplab-item-ignore class', function () {
beforeEach(function () { beforeEach(function () {
this.event = { preventDefault: () => {}, target: { tagName: 'LI', classList: this.classList } }; this.ignoredButton = document.createElement('button');
this.ignoredButton.classList.add('droplab-item-ignore');
this.event.target = this.ignoredButton;
spyOn(this.event, 'preventDefault'); spyOn(this.ignoredButton, 'closest').and.callThrough();
this.classList.contains.and.returnValue(true); });
utils.closest.calls.reset();
it('does not select element', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event); DropDown.prototype.clickEvent.call(this.dropdown, this.event);
});
it('should return immediately', function () { expect(this.ignoredButton.closest.calls.count()).toBe(1);
expect(utils.closest).not.toHaveBeenCalled(); expect(this.ignoredButton.closest).toHaveBeenCalledWith('.droplab-item-ignore');
expect(this.dropdown.addSelectedClass).not.toHaveBeenCalled();
}); });
}); });
describe('if no selected element exists', function () { describe('if no selected element exists', function () {
beforeEach(function () { beforeEach(function () {
this.event.preventDefault.calls.reset(); this.event.preventDefault.calls.reset();
this.clickEvent = DropDown.prototype.clickEvent.call(this.dropdown, this.event); this.dummyListItem = null;
});
it('should return undefined', function () {
expect(this.clickEvent).toBe(undefined);
}); });
it('should return before .preventDefault is called', function () { it('should return before .preventDefault is called', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.preventDefault).not.toHaveBeenCalled(); expect(this.event.preventDefault).not.toHaveBeenCalled();
expect(this.dropdown.addSelectedClass).not.toHaveBeenCalled();
});
});
describe('if hideOnClick is false', () => {
beforeEach(function () {
this.dropdown.hideOnClick = false;
this.dropdown.hide.calls.reset();
});
it('should not call .hide', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.dropdown.hide).not.toHaveBeenCalled();
}); });
}); });
}); });
...@@ -278,20 +316,23 @@ describe('DropDown', function () { ...@@ -278,20 +316,23 @@ describe('DropDown', function () {
describe('addEvents', function () { describe('addEvents', function () {
beforeEach(function () { beforeEach(function () {
this.list = { addEventListener: () => {} }; this.list = {
addEventListener: () => {},
querySelectorAll: () => [],
};
this.dropdown = { this.dropdown = {
list: this.list, list: this.list,
clickEvent: () => {}, clickEvent: () => {},
closeDropdown: () => {}, closeDropdown: () => {},
eventWrapper: {}, eventWrapper: {},
}; };
});
it('should call .addEventListener', function () {
spyOn(this.list, 'addEventListener'); spyOn(this.list, 'addEventListener');
DropDown.prototype.addEvents.call(this.dropdown); DropDown.prototype.addEvents.call(this.dropdown);
});
it('should call .addEventListener', function () {
expect(this.list.addEventListener).toHaveBeenCalledWith('click', jasmine.any(Function)); expect(this.list.addEventListener).toHaveBeenCalledWith('click', jasmine.any(Function));
expect(this.list.addEventListener).toHaveBeenCalledWith('keyup', jasmine.any(Function)); expect(this.list.addEventListener).toHaveBeenCalledWith('keyup', jasmine.any(Function));
}); });
......
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