Commit b3fa2c43 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch...

Merge branch '65473-when-the-branch-name-is-a-divider-or-separator-the-project-home-page-cannot-display-the-branch-name-correctly-ee' into 'master'

EE Port of CE !32198

See merge request gitlab-org/gitlab-ee!16165
parents 61abf926 a4635a15
...@@ -7,6 +7,7 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus'; ...@@ -7,6 +7,7 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { visitUrl } from './lib/utils/url_utility'; import { visitUrl } from './lib/utils/url_utility';
import { isObject } from './lib/utils/type_utility'; import { isObject } from './lib/utils/type_utility';
import renderItem from './gl_dropdown/render';
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput; var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput;
...@@ -521,8 +522,8 @@ GitLabDropdown = (function() { ...@@ -521,8 +522,8 @@ GitLabDropdown = (function() {
html.push( html.push(
this.renderItem( this.renderItem(
{ {
header: name, content: name,
// Add header for each group type: 'header',
}, },
name, name,
), ),
...@@ -542,16 +543,7 @@ GitLabDropdown = (function() { ...@@ -542,16 +543,7 @@ GitLabDropdown = (function() {
}; };
GitLabDropdown.prototype.renderData = function(data, group) { GitLabDropdown.prototype.renderData = function(data, group) {
if (group == null) { return data.map((obj, index) => this.renderItem(obj, group || false, index));
group = false;
}
return data.map(
(function(_this) {
return function(obj, index) {
return _this.renderItem(obj, group, index);
};
})(this),
);
}; };
GitLabDropdown.prototype.shouldPropagate = function(e) { GitLabDropdown.prototype.shouldPropagate = function(e) {
...@@ -688,104 +680,25 @@ GitLabDropdown = (function() { ...@@ -688,104 +680,25 @@ GitLabDropdown = (function() {
}; };
GitLabDropdown.prototype.renderItem = function(data, group, index) { GitLabDropdown.prototype.renderItem = function(data, group, index) {
var field, html, selected, text, url, value, rowHidden; let parent;
if (!this.options.renderRow) {
value = this.options.id ? this.options.id(data) : data.id;
if (value) {
value = value.toString().replace(/'/g, "\\'");
}
}
// Hide element
if (this.options.hideRow && this.options.hideRow(value)) {
rowHidden = true;
}
if (group == null) {
group = false;
}
if (index == null) {
// Render the row
index = false;
}
html = document.createElement('li');
if (rowHidden) {
html.style.display = 'none';
}
if (data === 'divider' || data === 'separator') { if (this.dropdown && this.dropdown[0]) {
html.className = data; parent = this.dropdown[0].parentNode;
return html;
} }
// Header
if (data.header != null) {
html.className = 'dropdown-header';
html.innerHTML = data.header;
return html;
}
if (this.options.renderRow) {
// Call the render function
html = this.options.renderRow.call(this.options, data, this);
} else {
if (!selected) {
const { fieldName } = this.options;
if (value) { return renderItem({
field = this.dropdown.parent().find(`input[name='${fieldName}'][value='${value}']`); instance: this,
if (field.length) { options: Object.assign({}, this.options, {
selected = true; icon: this.icon,
} highlight: this.highlight,
} else { highlightText: text => this.highlightTextMatches(text, this.filterInput.val()),
field = this.dropdown.parent().find(`input[name='${fieldName}']`); highlightTemplate: this.highlightTemplate.bind(this),
selected = !field.length; parent,
} }),
} data,
// Set URL group,
if (this.options.url != null) { index,
url = this.options.url(data); });
} else {
url = data.url != null ? data.url : '#';
}
// Set Text
if (this.options.text != null) {
text = this.options.text(data);
} else {
text = data.text != null ? data.text : '';
}
if (this.highlight) {
text = data.template
? this.highlightTemplate(text, data.template)
: this.highlightTextMatches(text, this.filterInput.val());
}
// Create the list item & the link
var link = document.createElement('a');
link.href = url;
if (this.icon) {
text = `<span>${text}</span>`;
link.classList.add('d-flex', 'align-items-center');
link.innerHTML = data.icon ? data.icon + text : text;
} else if (this.highlight) {
link.innerHTML = text;
} else {
link.textContent = text;
}
if (selected) {
link.classList.add('is-active');
}
if (group) {
link.dataset.group = group;
link.dataset.index = index;
}
html.appendChild(link);
}
return html;
}; };
GitLabDropdown.prototype.highlightTemplate = function(text, template) { GitLabDropdown.prototype.highlightTemplate = function(text, template) {
...@@ -809,7 +722,6 @@ GitLabDropdown = (function() { ...@@ -809,7 +722,6 @@ GitLabDropdown = (function() {
}; };
GitLabDropdown.prototype.noResults = function() { GitLabDropdown.prototype.noResults = function() {
var html;
return '<li class="dropdown-menu-empty-item"><a>No matching results</a></li>'; return '<li class="dropdown-menu-empty-item"><a>No matching results</a></li>';
}; };
......
const renderersByType = {
divider(element) {
element.classList.add('divider');
return element;
},
separator(element) {
element.classList.add('separator');
return element;
},
header(element, data) {
element.classList.add('dropdown-header');
element.innerHTML = data.content;
return element;
},
};
function getPropertyWithDefault(data, options, property, defaultValue = '') {
let result;
if (options[property] != null) {
result = options[property](data);
} else {
result = data[property] != null ? data[property] : defaultValue;
}
return result;
}
function getHighlightTextBuilder(text, data, options) {
if (options.highlight) {
return data.template
? options.highlightTemplate(text, data.template)
: options.highlightText(text);
}
return text;
}
function getIconTextBuilder(text, data, options) {
if (options.icon) {
const wrappedText = `<span>${text}</span>`;
return data.icon ? `${data.icon}${wrappedText}` : wrappedText;
}
return text;
}
function getLinkText(data, options) {
const text = getPropertyWithDefault(data, options, 'text');
return [getHighlightTextBuilder, getIconTextBuilder].reduce(
(acc, fn) => fn(acc, data, options),
text,
);
}
function escape(text) {
return text ? String(text).replace(/'/g, "\\'") : text;
}
function getOptionValue(data, options) {
if (options.renderRow) {
return undefined;
}
return escape(options.id ? options.id(data) : data.id);
}
function shouldHide(data, { options }) {
const value = getOptionValue(data, options);
return options.hideRow && options.hideRow(value);
}
function hideElement(element) {
element.style.display = 'none';
return element;
}
function checkSelected(data, options) {
const value = getOptionValue(data, options);
if (!options.parent) {
return !data.id;
} else if (value) {
return (
options.parent.querySelector(`input[name='${options.fieldName}'][value='${value}']`) != null
);
}
return options.parent.querySelector(`input[name='${options.fieldName}']`) == null;
}
function createLink(url, selected, options) {
const link = document.createElement('a');
link.href = url;
if (options.icon) {
link.classList.add('d-flex', 'align-items-center');
}
link.classList.toggle('is-active', selected);
return link;
}
function assignTextToLink(el, data, options) {
const text = getLinkText(data, options);
if (options.icon || options.highlight) {
el.innerHTML = text;
} else {
el.textContent = text;
}
return el;
}
function renderLink(row, data, { options, group, index }) {
const selected = checkSelected(data, options);
const url = getPropertyWithDefault(data, options, 'url', '#');
const link = createLink(url, selected, options);
assignTextToLink(link, data, options);
if (group) {
link.dataset.group = group;
link.dataset.index = index;
}
row.appendChild(link);
return row;
}
function getOptionRenderer({ options, instance }) {
return options.renderRow && ((li, data) => options.renderRow(data, instance));
}
function getRenderer(data, params) {
return renderersByType[data.type] || getOptionRenderer(params) || renderLink;
}
export default function item({ data, ...params }) {
const renderer = getRenderer(data, params);
const li = document.createElement('li');
if (shouldHide(data, params)) {
hideElement(li);
}
return renderer(li, data, params);
}
...@@ -14,7 +14,7 @@ export default class TransferDropdown { ...@@ -14,7 +14,7 @@ export default class TransferDropdown {
} }
buildDropdown() { buildDropdown() {
const extraOptions = [{ id: '-1', text: __('No parent group') }, 'divider']; const extraOptions = [{ id: '-1', text: __('No parent group') }, { type: 'divider' }];
this.groupDropdown.glDropdown({ this.groupDropdown.glDropdown({
selectable: true, selectable: true,
......
...@@ -231,7 +231,7 @@ export default class LabelsSelect { ...@@ -231,7 +231,7 @@ export default class LabelsSelect {
}); });
} }
if (extraData.length) { if (extraData.length) {
extraData.push('divider'); extraData.push({ type: 'divider' });
data = extraData.concat(data); data = extraData.concat(data);
} }
} }
...@@ -243,7 +243,7 @@ export default class LabelsSelect { ...@@ -243,7 +243,7 @@ export default class LabelsSelect {
}) })
.catch(() => flash(__('Error fetching labels.'))); .catch(() => flash(__('Error fetching labels.')));
}, },
renderRow: function(label, instance) { renderRow: function(label) {
var linkEl, var linkEl,
listItemEl, listItemEl,
color, color,
......
...@@ -100,7 +100,7 @@ export default class MilestoneSelect { ...@@ -100,7 +100,7 @@ export default class MilestoneSelect {
}); });
} }
if (extraOptions.length) { if (extraOptions.length) {
extraOptions.push('divider'); extraOptions.push({ type: 'divider' });
} }
callback(extraOptions.concat(data)); callback(extraOptions.concat(data));
......
...@@ -34,7 +34,7 @@ export default class NamespaceSelect { ...@@ -34,7 +34,7 @@ export default class NamespaceSelect {
id: null, id: null,
}; };
namespaces.unshift(anyNamespace); namespaces.unshift(anyNamespace);
namespaces.splice(1, 0, 'divider'); namespaces.splice(1, 0, { type: 'divider' });
} }
return dataCallback(namespaces); return dataCallback(namespaces);
}); });
......
...@@ -30,7 +30,7 @@ export default class Search { ...@@ -30,7 +30,7 @@ export default class Search {
data.unshift({ data.unshift({
full_name: __('Any'), full_name: __('Any'),
}); });
data.splice(1, 0, 'divider'); data.splice(1, 0, { type: 'divider' });
return callback(data); return callback(data);
}); });
}, },
...@@ -57,7 +57,7 @@ export default class Search { ...@@ -57,7 +57,7 @@ export default class Search {
data.unshift({ data.unshift({
name_with_namespace: __('Any'), name_with_namespace: __('Any'),
}); });
data.splice(1, 0, 'divider'); data.splice(1, 0, { type: 'divider' });
return data; return data;
}) })
......
...@@ -191,13 +191,14 @@ export class SearchAutocomplete { ...@@ -191,13 +191,14 @@ export class SearchAutocomplete {
// Add group header before list each group // Add group header before list each group
if (lastCategory !== suggestion.category) { if (lastCategory !== suggestion.category) {
if (!firstCategory) { if (!firstCategory) {
data.push('separator'); data.push({ type: 'separator' });
} }
if (firstCategory) { if (firstCategory) {
firstCategory = false; firstCategory = false;
} }
data.push({ data.push({
header: suggestion.category, type: 'header',
content: suggestion.category,
}); });
lastCategory = suggestion.category; lastCategory = suggestion.category;
} }
...@@ -221,7 +222,7 @@ export class SearchAutocomplete { ...@@ -221,7 +222,7 @@ export class SearchAutocomplete {
template = s__('SearchAutocomplete|in this group'); template = s__('SearchAutocomplete|in this group');
} }
data.unshift('separator'); data.unshift({ type: 'separator' });
data.unshift({ data.unshift({
icon, icon,
text: term, text: term,
...@@ -271,7 +272,8 @@ export class SearchAutocomplete { ...@@ -271,7 +272,8 @@ export class SearchAutocomplete {
if (name) { if (name) {
baseItems.push({ baseItems.push({
header: `${name}`, type: 'header',
content: `${name}`,
}); });
} }
......
...@@ -333,7 +333,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -333,7 +333,7 @@ function UsersSelect(currentUser, els, options = {}) {
} }
if (showDivider) { if (showDivider) {
users.splice(showDivider, 0, 'divider'); users.splice(showDivider, 0, { type: 'divider' });
} }
if ($dropdown.hasClass('js-multiselect')) { if ($dropdown.hasClass('js-multiselect')) {
...@@ -343,7 +343,8 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -343,7 +343,8 @@ function UsersSelect(currentUser, els, options = {}) {
if ($dropdown.data('dropdownHeader')) { if ($dropdown.data('dropdownHeader')) {
showDivider += 1; showDivider += 1;
users.splice(showDivider, 0, { users.splice(showDivider, 0, {
header: $dropdown.data('dropdownHeader'), type: 'header',
content: $dropdown.data('dropdownHeader'),
}); });
} }
...@@ -358,7 +359,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -358,7 +359,7 @@ function UsersSelect(currentUser, els, options = {}) {
users.splice(showDivider, 0, selectedUser); users.splice(showDivider, 0, selectedUser);
}); });
users.splice(showDivider + 1, 0, 'divider'); users.splice(showDivider + 1, 0, { type: 'divider' });
} }
} }
} }
......
---
title: Fix ref switcher separators from conflicting with branch names
merge_request: 32198
author:
type: fixed
...@@ -33,7 +33,9 @@ export default class CiTemplate { ...@@ -33,7 +33,9 @@ export default class CiTemplate {
name: __('No required pipeline'), name: __('No required pipeline'),
id: null, id: null,
}, },
'divider', {
type: 'divider',
},
], ],
...this.$dropdown.data('data'), ...this.$dropdown.data('data'),
}; };
......
...@@ -369,24 +369,27 @@ export default class AccessDropdown { ...@@ -369,24 +369,27 @@ export default class AccessDropdown {
}); });
if (roles.length) { if (roles.length) {
consolidatedData = consolidatedData.concat([{ header: s__('AccessDropdown|Roles') }], roles); consolidatedData = consolidatedData.concat(
[{ type: 'header', content: s__('AccessDropdown|Roles') }],
roles,
);
} }
if (groups.length) { if (groups.length) {
if (roles.length) { if (roles.length) {
consolidatedData = consolidatedData.concat(['divider']); consolidatedData = consolidatedData.concat([{ type: 'divider' }]);
} }
consolidatedData = consolidatedData.concat( consolidatedData = consolidatedData.concat(
[{ header: s__('AccessDropdown|Groups') }], [{ type: 'header', content: s__('AccessDropdown|Groups') }],
groups, groups,
); );
} }
if (users.length) { if (users.length) {
consolidatedData = consolidatedData.concat( consolidatedData = consolidatedData.concat(
['divider'], [{ type: 'divider' }],
[{ header: s__('AccessDropdown|Users') }], [{ type: 'header', content: s__('AccessDropdown|Users') }],
users, users,
); );
} }
......
...@@ -38,7 +38,7 @@ describe('CI Template Dropdown (ee/pages/admin/application_settings/ci_cd/ci_tem ...@@ -38,7 +38,7 @@ describe('CI Template Dropdown (ee/pages/admin/application_settings/ci_cd/ci_tem
describe('Format dropdown list', () => { describe('Format dropdown list', () => {
it('Adds a reset option and divider', () => { it('Adds a reset option and divider', () => {
const expected = { const expected = {
Reset: [{ name: 'No required pipeline', id: null }, 'divider'], Reset: [{ name: 'No required pipeline', id: null }, { type: 'divider' }],
...DROPDOWN_DATA, ...DROPDOWN_DATA,
}; };
const actual = CiTemplateInstance.formatDropdownList(); const actual = CiTemplateInstance.formatDropdownList();
......
...@@ -243,14 +243,23 @@ describe('glDropdown', function describeDropdown() { ...@@ -243,14 +243,23 @@ describe('glDropdown', function describeDropdown() {
}); });
describe('renderItem', () => { describe('renderItem', () => {
function dropdownWithOptions(options) {
const $dropdownDiv = $('<div />');
$dropdownDiv.glDropdown(options);
return $dropdownDiv.data('glDropdown');
}
function basicDropdown() {
return dropdownWithOptions({});
}
describe('without selected value', () => { describe('without selected value', () => {
let dropdown; let dropdown;
beforeEach(() => { beforeEach(() => {
const dropdownOptions = {}; dropdown = basicDropdown();
const $dropdownDiv = $('<div />');
$dropdownDiv.glDropdown(dropdownOptions);
dropdown = $dropdownDiv.data('glDropdown');
}); });
it('marks items without ID as active', () => { it('marks items without ID as active', () => {
...@@ -275,6 +284,35 @@ describe('glDropdown', function describeDropdown() { ...@@ -275,6 +284,35 @@ describe('glDropdown', function describeDropdown() {
expect(link).not.toHaveClass('is-active'); expect(link).not.toHaveClass('is-active');
}); });
}); });
it('should return an empty .separator li when when appropriate', () => {
const dropdown = basicDropdown();
const sep = { type: 'separator' };
const li = dropdown.renderItem(sep);
expect(li).toHaveClass('separator');
expect(li.childNodes.length).toEqual(0);
});
it('should return an empty .divider li when when appropriate', () => {
const dropdown = basicDropdown();
const div = { type: 'divider' };
const li = dropdown.renderItem(div);
expect(li).toHaveClass('divider');
expect(li.childNodes.length).toEqual(0);
});
it('should return a .dropdown-header li with the correct content when when appropriate', () => {
const dropdown = basicDropdown();
const text = 'My Header';
const header = { type: 'header', content: text };
const li = dropdown.renderItem(header);
expect(li).toHaveClass('dropdown-header');
expect(li.childNodes.length).toEqual(1);
expect(li.textContent).toEqual(text);
});
}); });
it('should keep selected item after selecting a second time', () => { it('should keep selected item after selecting a second time', () => {
......
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