Commit b1d7b012 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent aabf412b
import _ from 'underscore';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import updateDescription from '../utils/update_description';
export default class Store {
constructor(initialState) {
......@@ -19,8 +21,15 @@ export default class Store {
}
Object.assign(this.state, convertObjectPropsToCamelCase(data));
// find if there is an open details node inside of the issue description.
const descriptionSection = document.body.querySelector(
'.detail-page-description.content-block',
);
const details =
!_.isNull(descriptionSection) && descriptionSection.getElementsByTagName('details');
this.state.descriptionHtml = updateDescription(data.description, details);
this.state.titleHtml = data.title;
this.state.descriptionHtml = data.description;
this.state.lock_version = data.lock_version;
}
......
import _ from 'underscore';
/**
* Function that replaces the open attribute for the <details> element.
*
* @param {String} descriptionHtml - The html string passed back from the server as a result of polling
* @param {Array} details - All detail nodes inside of the issue description.
*/
const updateDescription = (descriptionHtml = '', details) => {
let detailNodes = details;
if (_.isEmpty(details)) {
detailNodes = [];
}
const placeholder = document.createElement('div');
placeholder.innerHTML = descriptionHtml;
const newDetails = placeholder.getElementsByTagName('details');
if (newDetails.length !== detailNodes.length) {
return descriptionHtml;
}
Array.from(newDetails).forEach((el, i) => {
/*
* <details> has an open attribute that can have a value, "", "true", "false"
* and will show the dropdown, which is why we are setting the attribute
* explicitly to true.
*/
if (detailNodes[i].open) el.setAttribute('open', true);
});
return placeholder.innerHTML;
};
export default updateDescription;
......@@ -122,7 +122,7 @@ export default {
},
},
series: this.scatterSeries,
dataZoom: this.dataZoomConfig,
dataZoom: [this.dataZoomConfig],
};
},
dataZoomConfig() {
......
......@@ -104,7 +104,7 @@ export default {
v-gl-tooltip
:title="
__(
'Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more on the documentation for Pipelines for Merged Results.',
'Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more in the documentation for Pipelines for Merged Results.',
)
"
class="js-pipeline-url-detached badge badge-info"
......
......@@ -64,12 +64,16 @@ module Boards
%i[label_id]
end
def list_update_attrs
%i[collapsed position]
end
def create_list_params
params.require(:list).permit(list_creation_attrs)
end
def update_list_params
params.require(:list).permit(:collapsed, :position)
params.require(:list).permit(list_update_attrs)
end
def serialize_as_json(resource)
......
# frozen_string_literal: true
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpClientController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
......
......@@ -43,7 +43,11 @@ module Boards
end
def create_list(board, type, target, position)
board.lists.create(type => target, list_type: type, position: position)
board.lists.create(create_list_attributes(type, target, position))
end
def create_list_attributes(type, target, position)
{ type => target, list_type: type, position: position }
end
end
end
......
......@@ -4,16 +4,22 @@ module Boards
module Lists
class UpdateService < Boards::BaseService
def execute(list)
update_preferences_result = update_preferences(list) if can_read?(list)
update_position_result = update_position(list) if can_admin?(list)
if update_preferences_result || update_position_result
if execute_by_params(list)
success(list: list)
else
error(list.errors.messages, 422)
end
end
private
def execute_by_params(list)
update_preferences_result = update_preferences(list) if can_read?(list)
update_position_result = update_position(list) if can_admin?(list)
update_preferences_result || update_position_result
end
def update_preferences(list)
return unless preferences?
......@@ -50,3 +56,5 @@ module Boards
end
end
end
Boards::Lists::UpdateService.prepend_if_ee('EE::Boards::Lists::UpdateService')
......@@ -43,7 +43,7 @@
} }
Auto DevOps
- if @pipeline.detached_merge_request_pipeline?
%span.js-pipeline-url-mergerequest.badge.badge-info.has-tooltip{ title: _('Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more on the documentation for Pipelines for Merged Results.') }
%span.js-pipeline-url-mergerequest.badge.badge-info.has-tooltip{ title: _('Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more in the documentation for Pipelines for Merged Results.') }
detached
- if @pipeline.stuck?
%span.js-pipeline-url-stuck.badge.badge-warning
......
---
title: When user toggles task list item, keep details open until user closes the details
manually
merge_request: 16153
author:
type: fixed
---
title: Handle wiki and graphql attachments in gitlab-workhorse
merge_request: 17690
author:
type: performance
---
title: Better job naming for Docker.gitlab-ci.yml
merge_request: 17218
author: luca.orlandi@gmail.com
type: other
# frozen_string_literal: true
class AddMaxIssueCountToList < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
add_column_with_default :lists, :max_issue_count, :integer, default: 0
end
def down
remove_column :lists, :max_issue_count
end
end
......@@ -2014,6 +2014,7 @@ ActiveRecord::Schema.define(version: 2019_09_19_162036) do
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "milestone_id"
t.integer "max_issue_count", default: 0, null: false
t.index ["board_id", "label_id"], name: "index_lists_on_board_id_and_label_id", unique: true
t.index ["label_id"], name: "index_lists_on_label_id"
t.index ["list_type"], name: "index_lists_on_list_type"
......
......@@ -48,7 +48,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
},
{
"id" : 2,
......@@ -57,7 +58,8 @@ Example response:
"color" : "#FF0000",
"description" : null
},
"position" : 2
"position" : 2,
"max_issue_count": 0
},
{
"id" : 3,
......@@ -66,7 +68,8 @@ Example response:
"color" : "#FF5F00",
"description" : null
},
"position" : 3
"position" : 3,
"max_issue_count": 0
}
]
}
......@@ -117,7 +120,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
},
{
"id" : 2,
......@@ -126,7 +130,8 @@ Example response:
"color" : "#FF0000",
"description" : null
},
"position" : 2
"position" : 2,
"max_issue_count": 0
},
{
"id" : 3,
......@@ -135,7 +140,8 @@ Example response:
"color" : "#FF5F00",
"description" : null
},
"position" : 3
"position" : 3,
"max_issue_count": 0
}
]
}
......@@ -185,7 +191,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
},
{
"id" : 2,
......@@ -194,7 +201,8 @@ Example response:
"color" : "#FF0000",
"description" : null
},
"position" : 2
"position" : 2,
"max_issue_count": 0
},
{
"id" : 3,
......@@ -203,7 +211,8 @@ Example response:
"color" : "#FF5F00",
"description" : null
},
"position" : 3
"position" : 3,
"max_issue_count": 0
}
]
}
......@@ -336,7 +345,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
},
{
"id" : 2,
......@@ -345,7 +355,8 @@ Example response:
"color" : "#FF0000",
"description" : null
},
"position" : 2
"position" : 2,
"max_issue_count": 0
},
{
"id" : 3,
......@@ -354,7 +365,8 @@ Example response:
"color" : "#FF5F00",
"description" : null
},
"position" : 3
"position" : 3,
"max_issue_count": 0
}
]
```
......@@ -387,7 +399,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
}
```
......@@ -427,7 +440,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
}
```
......@@ -460,7 +474,8 @@ Example response:
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
"position" : 1,
"max_issue_count": 0
}
```
......
build-master:
docker-build-master:
# Official docker image.
image: docker:latest
stage: build
......@@ -12,7 +12,7 @@ build-master:
only:
- master
build:
docker-build:
# Official docker image.
image: docker:latest
stage: build
......
......@@ -11176,7 +11176,7 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
msgid "Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more on the documentation for Pipelines for Merged Results."
msgid "Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more in the documentation for Pipelines for Merged Results."
msgstr ""
msgid "Pipelines settings for '%{project_name}' were successfully updated."
......
......@@ -35,7 +35,8 @@
}
},
"title": { "type": "string" },
"position": { "type": ["integer", "null"] }
"position": { "type": ["integer", "null"] },
"max_issue_count": { "type": "integer" }
},
"additionalProperties": true
}
......@@ -76,7 +76,8 @@
"name": { "type": "string" }
}
},
"position": { "type": ["integer", "null"] }
"position": { "type": ["integer", "null"] },
"max_issue_count": { "type": "integer" }
},
"additionalProperties": false
}
......
import Store from '~/issue_show/stores';
import updateDescription from '~/issue_show/utils/update_description';
jest.mock('~/issue_show/utils/update_description');
describe('Store', () => {
let store;
beforeEach(() => {
store = new Store({
descriptionHtml: '<p>This is a description</p>',
});
});
describe('updateState', () => {
beforeEach(() => {
document.body.innerHTML = `
<div class="detail-page-description content-block">
<details open>
<summary>One</summary>
</details>
<details>
<summary>Two</summary>
</details>
</div>
`;
});
afterEach(() => {
document.getElementsByTagName('html')[0].innerHTML = '';
});
it('calls updateDetailsState', () => {
store.updateState({ description: '' });
expect(updateDescription).toHaveBeenCalledTimes(1);
});
});
});
import updateDescription from '~/issue_show/utils/update_description';
describe('updateDescription', () => {
it('returns the correct value to be set as descriptionHtml', () => {
const actual = updateDescription(
'<details><summary>One</summary></details><details><summary>Two</summary></details>',
[{ open: true }, { open: false }], // mocking NodeList from the dom.
);
expect(actual).toEqual(
'<details open="true"><summary>One</summary></details><details><summary>Two</summary></details>',
);
});
describe('when description details returned from api is different then whats currently on the dom', () => {
it('returns the description from the api', () => {
const dataDescription = '<details><summary>One</summary></details>';
const actual = updateDescription(dataDescription, []);
expect(actual).toEqual(dataDescription);
});
});
});
......@@ -23,6 +23,14 @@ describe('Issuable output', () => {
beforeEach(done => {
setFixtures(`
<div>
<div class="detail-page-description content-block">
<details open>
<summary>One</summary>
</details>
<details>
<summary>Two</summary>
</details>
</div>
<div class="flash-container"></div>
<span id="task_status"></span>
</div>
......
......@@ -140,6 +140,16 @@ describe('Time series component', () => {
expect(timeSeriesChart.vm.svgs[mockSvgName]).toBe(`path://${mockSvgPathContent}`);
});
});
it('contains an svg object within an array to properly render icon', () => {
timeSeriesChart.vm.$nextTick(() => {
expect(timeSeriesChart.vm.chartOptions.dataZoom).toEqual([
{
handleIcon: `path://${mockSvgPathContent}`,
},
]);
});
});
});
describe('onResize', () => {
......
......@@ -717,6 +717,7 @@ List:
- updated_at
- milestone_id
- user_id
- max_issue_count
ExternalPullRequest:
- id
- created_at
......
# frozen_string_literal: true
Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f }
RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com/' }
......
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