Commit b66baea9 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 82cef8dd
import $ from 'jquery';
import { slugify } from './lib/utils/text_utility';
import fetchGroupPathAvailability from '~/pages/groups/new/fetch_group_path_availability';
import flash from '~/flash';
import { __ } from '~/locale';
export default class Group {
constructor() {
this.groupPath = $('#group_path');
this.groupName = $('#group_name');
this.parentId = $('#group_parent_id');
this.updateHandler = this.update.bind(this);
this.resetHandler = this.reset.bind(this);
this.updateGroupPathSlugHandler = this.updateGroupPathSlug.bind(this);
if (this.groupName.val() === '') {
this.groupName.on('keyup', this.updateHandler);
this.groupPath.on('keydown', this.resetHandler);
if (!this.parentId.val()) {
this.groupName.on('blur', this.updateGroupPathSlugHandler);
}
}
}
......@@ -21,5 +29,21 @@ export default class Group {
reset() {
this.groupName.off('keyup', this.updateHandler);
this.groupPath.off('keydown', this.resetHandler);
this.groupName.off('blur', this.checkPathHandler);
}
updateGroupPathSlug() {
const slug = this.groupPath.val() || slugify(this.groupName.val());
if (!slug) return;
fetchGroupPathAvailability(slug)
.then(({ data }) => data)
.then(data => {
if (data.exists && data.suggests.length > 0) {
const suggestedSlug = data.suggests[0];
this.groupPath.val(suggestedSlug);
}
})
.catch(() => flash(__('An error occurred while checking group path')));
}
}
......@@ -138,6 +138,14 @@ export const stripHtml = (string, replace = '') => {
*/
export const convertToCamelCase = string => string.replace(/(_\w)/g, s => s[1].toUpperCase());
/**
* Converts camelCase string to snake_case
*
* @param {*} string
*/
export const convertToSnakeCase = string =>
slugifyWithUnderscore(string.match(/([a-zA-Z][^A-Z]*)/g).join(' '));
/**
* Converts a sentence to lower case from the second word onwards
* e.g. Hello World => Hello world
......
import axios from '~/lib/utils/axios_utils';
const rootUrl = gon.relative_url_root;
export default function fetchGroupPathAvailability(groupPath) {
return axios.get(`${rootUrl}/users/${groupPath}/suggests`);
}
import InputValidator from '~/validators/input_validator';
import _ from 'underscore';
import fetchGroupPathAvailability from './fetch_group_path_availability';
import flash from '~/flash';
import { __ } from '~/locale';
const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline';
const successInputClass = 'gl-field-success-outline';
const successMessageSelector = '.validation-success';
const pendingMessageSelector = '.validation-pending';
const unavailableMessageSelector = '.validation-error';
const suggestionsMessageSelector = '.gl-path-suggestions';
export default class GroupPathValidator extends InputValidator {
constructor(opts = {}) {
super();
const container = opts.container || '';
const validateElements = document.querySelectorAll(`${container} .js-validate-group-path`);
this.debounceValidateInput = _.debounce(inputDomElement => {
GroupPathValidator.validateGroupPathInput(inputDomElement);
}, debounceTimeoutDuration);
validateElements.forEach(element =>
element.addEventListener('input', this.eventHandler.bind(this)),
);
}
eventHandler(event) {
const inputDomElement = event.target;
GroupPathValidator.resetInputState(inputDomElement);
this.debounceValidateInput(inputDomElement);
}
static validateGroupPathInput(inputDomElement) {
const groupPath = inputDomElement.value;
if (inputDomElement.checkValidity() && groupPath.length > 0) {
GroupPathValidator.setMessageVisibility(inputDomElement, pendingMessageSelector);
fetchGroupPathAvailability(groupPath)
.then(({ data }) => data)
.then(data => {
GroupPathValidator.setInputState(inputDomElement, !data.exists);
GroupPathValidator.setMessageVisibility(inputDomElement, pendingMessageSelector, false);
GroupPathValidator.setMessageVisibility(
inputDomElement,
data.exists ? unavailableMessageSelector : successMessageSelector,
);
if (data.exists) {
GroupPathValidator.showSuggestions(inputDomElement, data.suggests);
}
})
.catch(() => flash(__('An error occurred while validating group path')));
}
}
static showSuggestions(inputDomElement, suggestions) {
const messageElement = inputDomElement.parentElement.parentElement.querySelector(
suggestionsMessageSelector,
);
const textSuggestions = suggestions && suggestions.length > 0 ? suggestions.join(', ') : 'none';
messageElement.textContent = textSuggestions;
}
static setMessageVisibility(inputDomElement, messageSelector, isVisible = true) {
const messageElement = inputDomElement.parentElement.parentElement.querySelector(
messageSelector,
);
messageElement.classList.toggle('hide', !isVisible);
}
static setInputState(inputDomElement, success = true) {
inputDomElement.classList.toggle(successInputClass, success);
inputDomElement.classList.toggle(invalidInputClass, !success);
}
static resetInputState(inputDomElement) {
GroupPathValidator.setMessageVisibility(inputDomElement, successMessageSelector, false);
GroupPathValidator.setMessageVisibility(inputDomElement, unavailableMessageSelector, false);
if (inputDomElement.checkValidity()) {
inputDomElement.classList.remove(successInputClass, invalidInputClass);
}
}
}
import $ from 'jquery';
import BindInOut from '~/behaviors/bind_in_out';
import Group from '~/group';
import initAvatarPicker from '~/avatar_picker';
import GroupPathValidator from './group_path_validator';
document.addEventListener('DOMContentLoaded', () => {
const parentId = $('#group_parent_id');
if (!parentId.val()) {
new GroupPathValidator(); // eslint-disable-line no-new
}
BindInOut.initAll();
new Group(); // eslint-disable-line no-new
initAvatarPicker();
......
/* eslint-disable func-names, no-var */
import $ from 'jquery';
import BranchGraph from '../../../network/branch_graph';
export default (function() {
function Network(opts) {
var vph;
$('#filter_ref').click(function() {
return $(this)
.closest('form')
.submit();
});
this.branch_graph = new BranchGraph($('.network-graph'), opts);
vph = $(window).height() - 250;
$('.network-graph').css({
height: `${vph}px`,
});
const vph = $(window).height() - 250;
export default class Network {
constructor(opts) {
this.opts = opts;
this.filter_ref = $('#filter_ref');
this.network_graph = $('.network-graph');
this.filter_ref.click(() => this.submit());
this.branch_graph = new BranchGraph(this.network_graph, this.opts);
this.network_graph.css({ height: `${vph}px` });
}
return Network;
})();
submit() {
return this.filter_ref.closest('form').submit();
}
}
......@@ -30,67 +30,65 @@ const projectSelect = () => {
$(select).select2({
placeholder,
minimumInputLength: 0,
query: (function(_this) {
return function(query) {
var finalCallback, projectsCallback;
finalCallback = function(projects) {
var data;
data = {
results: projects,
};
return query.callback(data);
query: query => {
var finalCallback, projectsCallback;
finalCallback = function(projects) {
var data;
data = {
results: projects,
};
if (_this.includeGroups) {
projectsCallback = function(projects) {
var groupsCallback;
groupsCallback = function(groups) {
var data;
data = groups.concat(projects);
return finalCallback(data);
};
return Api.groups(query.term, {}, groupsCallback);
};
} else {
projectsCallback = finalCallback;
}
if (_this.groupId) {
return Api.groupProjects(
_this.groupId,
query.term,
{
with_issues_enabled: _this.withIssuesEnabled,
with_merge_requests_enabled: _this.withMergeRequestsEnabled,
with_shared: _this.withShared,
include_subgroups: _this.includeProjectsInSubgroups,
},
projectsCallback,
);
} else if (_this.userId) {
return Api.userProjects(
_this.userId,
query.term,
{
with_issues_enabled: _this.withIssuesEnabled,
with_merge_requests_enabled: _this.withMergeRequestsEnabled,
with_shared: _this.withShared,
include_subgroups: _this.includeProjectsInSubgroups,
},
projectsCallback,
);
} else {
return Api.projects(
query.term,
{
order_by: _this.orderBy,
with_issues_enabled: _this.withIssuesEnabled,
with_merge_requests_enabled: _this.withMergeRequestsEnabled,
membership: !_this.allProjects,
},
projectsCallback,
);
}
return query.callback(data);
};
})(this),
if (this.includeGroups) {
projectsCallback = function(projects) {
var groupsCallback;
groupsCallback = function(groups) {
var data;
data = groups.concat(projects);
return finalCallback(data);
};
return Api.groups(query.term, {}, groupsCallback);
};
} else {
projectsCallback = finalCallback;
}
if (this.groupId) {
return Api.groupProjects(
this.groupId,
query.term,
{
with_issues_enabled: this.withIssuesEnabled,
with_merge_requests_enabled: this.withMergeRequestsEnabled,
with_shared: this.withShared,
include_subgroups: this.includeProjectsInSubgroups,
},
projectsCallback,
);
} else if (this.userId) {
return Api.userProjects(
this.userId,
query.term,
{
with_issues_enabled: this.withIssuesEnabled,
with_merge_requests_enabled: this.withMergeRequestsEnabled,
with_shared: this.withShared,
include_subgroups: this.includeProjectsInSubgroups,
},
projectsCallback,
);
} else {
return Api.projects(
query.term,
{
order_by: this.orderBy,
with_issues_enabled: this.withIssuesEnabled,
with_merge_requests_enabled: this.withMergeRequestsEnabled,
membership: !this.allProjects,
},
projectsCallback,
);
}
},
id(project) {
if (simpleFilter) return project.id;
return JSON.stringify({
......
......@@ -563,3 +563,10 @@ img.emoji {
.gl-font-size-small { font-size: $gl-font-size-small; }
.gl-line-height-24 { line-height: $gl-line-height-24; }
.gl-font-size-12 { font-size: $gl-font-size-12; }
.gl-font-size-14 { font-size: $gl-font-size-14; }
.gl-font-size-16 { font-size: $gl-font-size-16; }
.gl-font-size-20 { font-size: $gl-font-size-20; }
.gl-font-size-28 { font-size: $gl-font-size-28; }
.gl-font-size-42 { font-size: $gl-font-size-42; }
......@@ -325,6 +325,12 @@ $gl-grayish-blue: #7f8fa4;
$gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54;
$gl-font-size-12: 12px;
$gl-font-size-14: 14px;
$gl-font-size-16: 16px;
$gl-font-size-20: 20px;
$gl-font-size-28: 28px;
$gl-font-size-42: 42px;
$type-scale: (
1: 12px,
......
......@@ -22,11 +22,16 @@
- if parent
%strong= parent.full_path + '/'
= f.hidden_field :parent_id
= f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control',
= f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control js-validate-group-path',
autofocus: local_assigns[:autofocus] || false, required: true,
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
title: _('Please choose a group URL with no special characters.'),
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
%p.validation-error.gl-field-error.field-validation.hide
= _('Group path is already taken. Suggestions: ')
%span.gl-path-suggestions
%p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group path availability...')
- if @group.persisted?
.alert.alert-warning.prepend-top-10
......
---
title: New group path uniqueness check
merge_request: 17394
author:
type: added
---
title: Remove IIFEs from project_select.js
merge_request: 19288
author: minghuan lei
type: other
---
title: Removed IIFEs from network.js file
merge_request: 19254
author: nuwe1
type: other
......@@ -1517,6 +1517,9 @@ msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
msgid "An error occurred while checking group path"
msgstr ""
msgid "An error occurred while deleting the approvers group"
msgstr ""
......@@ -1679,6 +1682,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
msgid "An error occurred while validating group path"
msgstr ""
msgid "An error occurred while validating username"
msgstr ""
......@@ -3059,6 +3065,9 @@ msgstr ""
msgid "Checking branch availability..."
msgstr ""
msgid "Checking group path availability..."
msgstr ""
msgid "Checking username availability..."
msgstr ""
......@@ -8359,6 +8368,12 @@ msgstr ""
msgid "Group overview content"
msgstr ""
msgid "Group path is already taken. Suggestions: "
msgstr ""
msgid "Group path is available."
msgstr ""
msgid "Group pipeline minutes were successfully reset."
msgstr ""
......@@ -16967,6 +16982,12 @@ msgstr ""
msgid "There was an error fetching configuration for charts"
msgstr ""
msgid "There was an error fetching cycle analytics stages."
msgstr ""
msgid "There was an error fetching data for the selected stage"
msgstr ""
msgid "There was an error fetching label data for the selected group"
msgstr ""
......@@ -17009,6 +17030,9 @@ msgstr ""
msgid "There was an error while fetching cycle analytics data."
msgstr ""
msgid "There was an error while fetching cycle analytics summary data."
msgstr ""
msgid "There was an error with the reCAPTCHA. Please solve the reCAPTCHA again."
msgstr ""
......
......@@ -90,6 +90,19 @@ describe('text_utility', () => {
});
});
describe('convertToSnakeCase', () => {
it.each`
txt | result
${'snakeCase'} | ${'snake_case'}
${'snake Case'} | ${'snake_case'}
${'snake case'} | ${'snake_case'}
${'snake_case'} | ${'snake_case'}
${'snakeCasesnake Case'} | ${'snake_casesnake_case'}
`('converts string $txt to $result string', ({ txt, result }) => {
expect(textUtils.convertToSnakeCase(txt)).toEqual(result);
});
});
describe('convertToSentenceCase', () => {
it('converts Sentence Case to Sentence case', () => {
expect(textUtils.convertToSentenceCase('Hello World')).toBe('Hello world');
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
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