Commit 1f54c381 authored by Mike Lewis's avatar Mike Lewis

Merge branch 'master' into 'template-improvements-for-documentation'

# Conflicts:
#   .gitlab/issue_templates/Feature proposal.md
parents 75cf4bdf b9494bf3
......@@ -80,3 +80,4 @@ eslint-report.html
package-lock.json
/junit_*.xml
/coverage-frontend/
jsdoc/
......@@ -499,6 +499,22 @@ rspec-mysql:
<<: *rspec-metadata-mysql
parallel: 50
.rspec-quarantine: &rspec-quarantine
script:
- export CACHE_CLASSES=true
- scripts/gitaly-test-spawn
- bin/rspec --color --format documentation --tag quarantine spec/
rspec-pg-quarantine:
<<: *rspec-metadata-pg
<<: *rspec-quarantine
allow_failure: true
rspec-mysql-quarantine:
<<: *rspec-metadata-mysql
<<: *rspec-quarantine
allow_failure: true
static-analysis:
<<: *dedicated-no-docs-no-db-pull-cache-job
dependencies:
......@@ -788,6 +804,7 @@ qa:selectors:
- bundle exec bin/qa Test::Sanity::Selectors
.qa-frontend-node: &qa-frontend-node
<<: *dedicated-no-docs-no-db-pull-cache-job
stage: test
variables:
NODE_OPTIONS: --max_old_space_size=3584
......@@ -802,11 +819,6 @@ qa:selectors:
- yarn install --frozen-lockfile --cache-folder .yarn-cache
- date
- yarn run webpack-prod
<<: *except-docs
qa-frontend-node:6:
<<: *qa-frontend-node
image: node:6-alpine
qa-frontend-node:8:
<<: *qa-frontend-node
......@@ -854,6 +866,21 @@ lint:javascript:report:
paths:
- eslint-report.html
jsdoc:
<<: *dedicated-no-docs-pull-cache-job
stage: post-test
dependencies:
- compile-assets
before_script: []
script:
- date
- yarn run jsdoc || true # ignore exit code
artifacts:
name: jsdoc
expire_in: 31d
paths:
- jsdoc/
pages:
<<: *dedicated-no-docs-no-db-pull-cache-job
before_script: []
......@@ -863,6 +890,7 @@ pages:
- karma
- gitlab:assets:compile
- lint:javascript:report
- jsdoc
script:
- mv public/ .public/
- mkdir public/
......@@ -872,6 +900,7 @@ pages:
- mv webpack-report/ public/webpack-report/ || true
- cp .public/assets/application-*.css public/application.css || true
- cp .public/assets/application-*.css.gz public/application.css.gz || true
- mv jsdoc/ public/jsdoc/ || true
artifacts:
paths:
- public
......
......@@ -4,8 +4,8 @@
### Target audience
<!-- For whom are we doing this? Include either a persona from https://design.gitlab.com/getting-started/personas
or define a specific company role. e.a. "Release Manager" or "Security Analyst" -->
<!-- For whom are we doing this? Include either a persona from https://design.gitlab.com/getting-started/personas or define a specific company role. e.a. "Release Manager" or "Security Analyst".
Use the persona labels as well https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=persona%3A -->
### Further details
......@@ -13,25 +13,24 @@ or define a specific company role. e.a. "Release Manager" or "Security Analyst"
### Proposal
<!-- How are we going to solve the problem? -->
<!-- How are we going to solve the problem? Try to include the user journey! https://about.gitlab.com/handbook/journeys/#user-journey -->
### Documentation
<!--
* What doc pages need to be created or updated across user, admin, and API docs?
* What concepts, procedures, or information is needed in each area? Is there an 'old way' to deprecate in docs?
<!-- What doc pages need to be created or updated across user, admin, and API docs?
What concepts, procedures, or information is needed in each area? Is there an 'old way' to deprecate in docs?
Product managers:
* By the kickoff, finalize the answers to the bullets above, and:
* If applicable, specify new or updated feature name(s), description(s), benefits,
* By the kickoff, finalize the answers to the bullets above, and also:
* When applicable, specify a new or updated feature name, description, benefits,
and use cases, which may all be used in the documentation or features.yml.
* Specify which use cases or scenarios would benefit from a set of instructions
or a guide unique to that use case. -->
* Specify which use cases or scenarios would benefit from a set of instructions or a guide,
whether unique to a single use case or flexible enough to cover multiple use cases. -->
### What does success look like, and how can we measure that?
<!-- If no way to measure success, link to an issue that will implement a way to measure this -->
<!-- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this. -->
### Links / references
/label ~"feature proposal"
/label ~feature
<!--
# Read me first!
Set the title to: `Security Release: 11.4.X, 11.3.X, and 11.2.X`
-->
## Releases tasks
- https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/release-manager.md
- https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md
- https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/security-engineer.md
## Version issues:
* 11.4.X: {release task link}
* 11.3.X: {release task link}
* 11.2.X: {release task link}
## Security Issues:
### CE
* {https://gitlab.com/gitlab-org/gitlab-ce/issues link}
### EE
* {https://gitlab.com/gitlab-org/gitlab-ee/issues link}
## Security Issues in dev.gitlab.org:
### CE
- {https://dev.gitlab.org/gitlab/gitlabhq/issues link}
| Version | MR | Status|
|---------|----|-------|
| 11.4 | {https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/ link} | |
| 11.3 | {https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/ link} | |
| 11.2 | {https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/ link} | |
| master | {https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/ link} | |
### EE
* {https://dev.gitlab.org/gitlab/gitlabhq/issues/ link}
| Version | MR | Status|
|---------|----|-------|
| 11.4| {https://dev.gitlab.org/gitlab/gitlab-ee/merge_requests/ link} | |
| 11.3 | {https://dev.gitlab.org/gitlab/gitlab-ee/merge_requests/ link} | |
| 11.2 | {https://dev.gitlab.org/gitlab/gitlab-ee/merge_requests/ link} | |
| master | {https://dev.gitlab.org/gitlab/gitlab-ee/merge_requests/ link} | |
## QA
{QA issue link}
## Blog post
Dev: {https://dev.gitlab.org/gitlab/www-gitlab-com/merge_requests/ link}<br/>
gitlab.com: {https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ link}
## Email notification
{https://gitlab.com/gitlab-com/marketing/general/issues/ link}
/label ~security
/confidential
......@@ -20,7 +20,7 @@ Set the title to: `[Security] Description of the original issue`
#### Backports
- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases
- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases, plus the current RC if between the 7th and 22nd of the month.
- [ ] At this point, it might be easy to squash the commits from the MR into one
- You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [secpick documentation]
- [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
......
......@@ -686,17 +686,6 @@ Style/TrailingUnderscoreVariable:
- 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
- 'spec/services/quick_actions/interpret_service_spec.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
Style/TrivialAccessors:
Exclude:
- 'app/models/external_issue.rb'
- 'app/serializers/base_serializer.rb'
- 'lib/gitlab/auth/ldap/person.rb'
- 'lib/system_check/base_check.rb'
# Offense count: 4
# Cop supports --auto-correct.
Style/UnlessElse:
......
......@@ -2,6 +2,24 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.6.5 (2019-01-17)
### Fixed (5 changes)
- Add syntax highlighting to suggestion diff. !24156
- Fix broken templated "Too many changes to show" text. !24282
- Fix requests profiler in admin page not rendering HTML properly. !24291
- Fix no avatar not showing in user selection box. !24346
- Fixed diff suggestions removing dashes.
## 11.6.4 (2019-01-15)
### Security (1 change)
- Validate bundle files before unpacking them.
## 11.6.3 (2019-01-04)
### Fixed (1 change)
......
1.12.0
1.13.0
\ No newline at end of file
......@@ -112,7 +112,7 @@ gem 'seed-fu', '~> 2.3.7'
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.0.0'
gem 'deckar01-task_list', '2.0.1'
gem 'gitlab-markup', '~> 1.6.5'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'redcarpet', '~> 3.4'
......@@ -125,9 +125,9 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.8'
gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 3.1'
gem 'truncato', '~> 0.7.9'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 2.7.0'
gem 'nokogiri', '~> 1.8.5'
gem 'nokogiri', '~> 1.10.1'
gem 'escape_utils', '~> 1.1'
# Calendar rendering
......@@ -160,12 +160,12 @@ gem 'acts-as-taggable-on', '~> 5.0'
# Background jobs
gem 'sidekiq', '~> 5.2.1'
gem 'sidekiq-cron', '~> 0.6.0'
gem 'sidekiq-cron', '~> 1.0'
gem 'redis-namespace', '~> 1.6.0'
gem 'gitlab-sidekiq-fetcher', '~> 0.4.0', require: 'sidekiq-reliable-fetch'
# Cron Parser
gem 'rufus-scheduler', '~> 3.4'
gem 'fugit', '~> 1.1'
# HTTP requests
gem 'httparty', '~> 0.13.3'
......@@ -304,6 +304,12 @@ group :metrics do
gem 'raindrops', '~> 0.18'
end
group :tracing do
# OpenTracing
gem 'opentracing', '~> 0.4.3'
gem 'jaeger-client', '~> 0.10.0'
end
group :development do
gem 'foreman', '~> 0.84.0'
gem 'brakeman', '~> 4.2', require: false
......
......@@ -143,7 +143,7 @@ GEM
database_cleaner (1.7.0)
debug_inspector (0.0.3)
debugger-ruby_core_source (1.3.8)
deckar01-task_list (2.0.0)
deckar01-task_list (2.0.1)
html-pipeline
declarative (0.0.10)
declarative-option (0.1.0)
......@@ -185,7 +185,7 @@ GEM
erubi (1.7.1)
erubis (2.7.0)
escape_utils (1.2.1)
et-orbi (1.0.3)
et-orbi (1.1.7)
tzinfo
eventmachine (1.2.7)
excon (0.62.0)
......@@ -258,6 +258,9 @@ GEM
foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
fugit (1.1.7)
et-orbi (~> 1.1, >= 1.1.7)
raabro (~> 1.1)
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
......@@ -282,7 +285,7 @@ GEM
gitlab-markup (1.6.5)
gitlab-sidekiq-fetcher (0.4.0)
sidekiq (~> 5)
gitlab-styles (2.4.1)
gitlab-styles (2.5.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
......@@ -389,6 +392,9 @@ GEM
cause
json
ipaddress (0.8.3)
jaeger-client (0.10.0)
opentracing (~> 0.3)
thrift
jira-ruby (1.4.1)
activesupport
multipart-post
......@@ -462,7 +468,7 @@ GEM
mimemagic (0.3.2)
mini_magick (4.8.0)
mini_mime (1.0.1)
mini_portile2 (2.3.0)
mini_portile2 (2.4.0)
minitest (5.11.3)
msgpack (1.2.4)
multi_json (1.13.1)
......@@ -477,8 +483,8 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
nokogiri (1.10.1)
mini_portile2 (~> 2.4.0)
nokogumbo (1.5.0)
nokogiri
numerizer (0.1.1)
......@@ -544,6 +550,7 @@ GEM
activesupport
nokogiri (>= 1.4.4)
omniauth (~> 1.0)
opentracing (0.4.3)
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
......@@ -606,6 +613,7 @@ GEM
get_process_mem (~> 0.2)
puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6)
rack (2.0.6)
rack-accept (0.4.5)
rack (>= 0.4)
......@@ -775,8 +783,6 @@ GEM
rubyntlm (0.6.2)
rubypants (0.2.0)
rubyzip (1.2.2)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.27.5)
safe_yaml (1.0.4)
sanitize (4.6.6)
......@@ -820,8 +826,8 @@ GEM
connection_pool (~> 2.2, >= 2.2.2)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
sidekiq-cron (0.6.0)
rufus-scheduler (>= 3.3.0)
sidekiq-cron (1.0.4)
fugit (~> 1.1)
sidekiq (>= 4.2.1)
signet (0.11.0)
addressable (~> 2.3)
......@@ -868,6 +874,7 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
thrift (0.11.0.0)
tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
......@@ -876,9 +883,9 @@ GEM
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
trollop (2.1.3)
truncato (0.7.10)
truncato (0.7.11)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
nokogiri (>= 1.7.0, <= 2.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
u2f (0.2.1)
......@@ -974,7 +981,7 @@ DEPENDENCIES
connection_pool (~> 2.0)
creole (~> 0.5.0)
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.0.0)
deckar01-task_list (= 2.0.1)
device_detector
devise (~> 4.4)
devise-two-factor (~> 3.0.0)
......@@ -1003,6 +1010,7 @@ DEPENDENCIES
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.84.0)
fugit (~> 1.1)
fuubar (~> 2.2.0)
gemojione (~> 3.3)
gettext (~> 3.2.2)
......@@ -1037,6 +1045,7 @@ DEPENDENCIES
httparty (~> 0.13.3)
icalendar
influxdb (~> 0.2)
jaeger-client (~> 0.10.0)
jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2)
js_regex (~> 2.2.1)
......@@ -1059,7 +1068,7 @@ DEPENDENCIES
nakayoshi_fork (~> 0.0.4)
net-ldap
net-ssh (~> 5.0)
nokogiri (~> 1.8.5)
nokogiri (~> 1.10.1)
oauth2 (~> 1.4)
octokit (~> 4.9)
omniauth (~> 1.8)
......@@ -1077,6 +1086,7 @@ DEPENDENCIES
omniauth-shibboleth (~> 1.3.0)
omniauth-twitter (~> 1.4)
omniauth_crowd (~> 2.2.0)
opentracing (~> 0.4.3)
org-ruby (~> 0.9.12)
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
......@@ -1127,7 +1137,6 @@ DEPENDENCIES
ruby-prof (~> 0.17.0)
ruby-progressbar
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
sanitize (~> 4.6)
sass (~> 3.5)
......@@ -1141,7 +1150,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
sidekiq (~> 5.2.1)
sidekiq-cron (~> 0.6.0)
sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2)
simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1)
......@@ -1156,7 +1165,7 @@ DEPENDENCIES
thin (~> 1.7.0)
timecop (~> 0.8.0)
toml-rb (~> 1.0.0)
truncato (~> 0.7.9)
truncato (~> 0.7.11)
u2f (~> 0.2.1)
uglifier (~> 2.7.2)
unf (~> 0.1.4)
......
......@@ -56,7 +56,7 @@ Below we describe the contributing process to GitLab for two reasons:
Several people from the [GitLab team][team] are helping community members to get
their contributions accepted by meeting our [Definition of done][done].
What you can expect from them is described at https://about.gitlab.com/roles/merge-request-coach/.
What you can expect from them is described at https://about.gitlab.com/job-families/expert/merge-request-coach/.
### Milestones on community contribution issues
......
......@@ -55,7 +55,7 @@ export default {
:disabled="badge.isDeleting"
class="btn btn-default append-right-8"
type="button"
@click="editBadge(badge);"
@click="editBadge(badge)"
>
<icon :size="16" :aria-label="__('Edit')" name="pencil" />
</button>
......@@ -65,7 +65,7 @@ export default {
type="button"
data-toggle="modal"
data-target="#delete-badge-modal"
@click="updateBadgeInModal(badge);"
@click="updateBadgeInModal(badge)"
>
<icon :size="16" :aria-label="__('Delete')" name="remove" />
</button>
......
......@@ -86,7 +86,7 @@ export default {
class="board-card"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event);"
@mouseup="showIssue($event)"
>
<issue-card-inner
:list="list"
......
......@@ -221,7 +221,7 @@ export default {
</script>
<template>
<div class="board-list-component">
<div class="board-list-component d-flex flex-column">
<div v-if="loading" class="board-list-loading text-center" aria-label="Loading issues">
<gl-loading-icon />
</div>
......
......@@ -96,7 +96,7 @@ export default {
<template>
<div class="board-new-issue-form">
<div class="board-card">
<form @submit="submit($event);">
<form @submit="submit($event)">
<div v-if="error" class="flash-container">
<div class="flash-alert">An error occurred. Please try again.</div>
</div>
......
......@@ -184,7 +184,7 @@ export default {
:title="label.description"
class="badge color-label append-right-4 prepend-top-4"
type="button"
@click="filterByLabel(label);"
@click="filterByLabel(label)"
>
{{ label.title }}
</button>
......
......@@ -58,7 +58,7 @@ export default {
v-if="activeTab === 'selected'"
class="btn btn-default"
type="button"
@click="changeTab('all');"
@click="changeTab('all')"
>
Open issues
</button>
......
......@@ -71,7 +71,7 @@ export default {
<span class="inline add-issues-footer-to-list"> to list </span>
<lists-dropdown />
</div>
<button class="btn btn-default float-right" type="button" @click="toggleModal(false);">
<button class="btn btn-default float-right" type="button" @click="toggleModal(false)">
Cancel
</button>
</footer>
......
......@@ -58,7 +58,7 @@ export default {
class="close"
data-dismiss="modal"
aria-label="Close"
@click="toggleModal(false);"
@click="toggleModal(false)"
>
<span aria-hidden="true">×</span>
</button>
......
......@@ -130,7 +130,7 @@ export default {
<div
:class="{ 'is-active': issue.selected }"
class="board-card"
@click="toggleIssue($event, issue);"
@click="toggleIssue($event, issue)"
>
<issue-card-inner :issue="issue" :issue-link-base="issueLinkBase" :root-path="rootPath" />
<icon
......
......@@ -38,7 +38,7 @@ export default {
:class="{ 'is-active': list.id == selected.id }"
href="#"
role="button"
@click.prevent="modal.selectedList = list;"
@click.prevent="modal.selectedList = list"
>
<span :style="{ backgroundColor: list.label.color }" class="dropdown-label-box"> </span>
{{ list.title }}
......
......@@ -21,12 +21,12 @@ export default {
<div class="top-area prepend-top-10 append-bottom-10">
<ul class="nav-links issues-state-filters">
<li :class="{ active: activeTab == 'all' }">
<a href="#" role="button" @click.prevent="changeTab('all');">
<a href="#" role="button" @click.prevent="changeTab('all')">
Open issues <span class="badge badge-pill"> {{ issuesCount }} </span>
</a>
</li>
<li :class="{ active: activeTab == 'selected' }">
<a href="#" role="button" @click.prevent="changeTab('selected');">
<a href="#" role="button" @click.prevent="changeTab('selected')">
Selected issues <span class="badge badge-pill"> {{ selectedCount }} </span>
</a>
</li>
......
......@@ -82,7 +82,7 @@ export default {
<template>
<div>
<label class="label-bold prepend-top-10"> Project </label>
<div ref="projectsDropdown" class="dropdown">
<div ref="projectsDropdown" class="dropdown dropdown-projects">
<button
class="dropdown-menu-toggle wide"
type="button"
......
......@@ -13,6 +13,9 @@ export default class ContextualSidebar {
initDomElements() {
this.$page = $('.layout-page');
this.$sidebar = $('.nav-sidebar');
if (!this.$sidebar.length) return;
this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar);
this.$overlay = $('.mobile-overlay');
this.$openSidebar = $('.toggle-mobile-nav');
......@@ -21,12 +24,14 @@ export default class ContextualSidebar {
}
bindEvents() {
if (!this.$sidebar.length) return;
document.addEventListener('click', e => {
if (
!e.target.closest('.nav-sidebar') &&
(bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md')
) {
this.toggleCollapsedSidebar(true);
this.toggleCollapsedSidebar(true, true);
}
});
this.$openSidebar.on('click', () => this.toggleSidebarNav(true));
......@@ -34,7 +39,7 @@ export default class ContextualSidebar {
this.$overlay.on('click', () => this.toggleSidebarNav(false));
this.$sidebarToggle.on('click', () => {
const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop');
this.toggleCollapsedSidebar(value);
this.toggleCollapsedSidebar(value, true);
});
$(window).on('resize', () => _.debounce(this.render(), 100));
......@@ -53,16 +58,19 @@ export default class ContextualSidebar {
this.$sidebar.removeClass('sidebar-collapsed-desktop');
}
toggleCollapsedSidebar(collapsed) {
toggleCollapsedSidebar(collapsed, saveCookie) {
const breakpoint = bp.getBreakpointSize();
if (this.$sidebar.length) {
this.$sidebar.toggleClass('sidebar-collapsed-desktop', collapsed);
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
}
ContextualSidebar.setCollapsedCookie(collapsed);
this.toggleSidebarOverflow();
if (saveCookie) {
ContextualSidebar.setCollapsedCookie(collapsed);
}
requestIdleCallback(() => this.toggleSidebarOverflow());
}
toggleSidebarOverflow() {
......@@ -74,13 +82,15 @@ export default class ContextualSidebar {
}
render() {
if (!this.$sidebar.length) return;
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'sm' || breakpoint === 'md') {
this.toggleCollapsedSidebar(true);
this.toggleCollapsedSidebar(true, false);
} else if (breakpoint === 'lg') {
const collapse = parseBoolean(Cookies.get('sidebar_collapsed'));
this.toggleCollapsedSidebar(collapse);
this.toggleCollapsedSidebar(collapse, false);
}
}
}
......@@ -113,7 +113,7 @@ export default {
<div class="gl-responsive-table-row deploy-key">
<div class="table-section section-40">
<div role="rowheader" class="table-mobile-header">{{ s__('DeployKeys|Deploy key') }}</div>
<div class="table-mobile-content">
<div class="table-mobile-content qa-key">
<strong class="title qa-key-title"> {{ deployKey.title }} </strong>
<div class="fingerprint qa-key-fingerprint">{{ deployKey.fingerprint }}</div>
</div>
......
......@@ -54,6 +54,9 @@ export default {
showDropdowns() {
return !this.commit && this.mergeRequestDiffs.length;
},
baseVersionPath() {
return this.mergeRequestDiff.base_version_path;
},
},
methods: {
...mapActions('diffs', [
......@@ -95,6 +98,7 @@ export default {
and
<compare-versions-dropdown
:other-versions="comparableDiffs"
:base-version-path="baseVersionPath"
:start-version="startVersion"
:target-branch="targetBranch"
class="mr-version-compare-dropdown"
......
......@@ -34,14 +34,13 @@ export default {
required: false,
default: false,
},
baseVersionPath: {
type: String,
required: false,
default: null,
},
},
computed: {
baseVersion() {
return {
name: 'hii',
versionIndex: -1,
};
},
targetVersions() {
if (this.mergeRequestVersion) {
return this.otherVersions;
......@@ -62,6 +61,9 @@ export default {
);
},
href(version) {
if (this.isBase(version)) {
return this.baseVersionPath;
}
if (this.showCommitCount) {
return version.version_path;
}
......
......@@ -127,7 +127,7 @@ export default {
:save-button-title="__('Comment')"
class="diff-comment-form new-note discussion-form discussion-form-container"
@handleFormUpdate="handleSaveNote"
@cancelForm="closeDiffFileCommentForm(diffFile.file_hash);"
@cancelForm="closeDiffFileCommentForm(diffFile.file_hash)"
/>
</div>
</diff-viewer>
......
......@@ -68,7 +68,7 @@ export default {
}"
type="button"
class="js-diff-notes-toggle"
@click="toggleDiscussion({ discussionId: discussion.id });"
@click="toggleDiscussion({ discussionId: discussion.id })"
>
<icon v-if="discussion.expanded" name="collapse" class="collapse-icon" />
<template v-else>
......
......@@ -145,7 +145,7 @@ export default {
<div
ref="header"
class="js-file-title file-title file-title-flex-parent"
@click="handleToggleFile($event, true);"
@click="handleToggleFile($event, true)"
>
<div class="file-header-content">
<icon
......
......@@ -179,7 +179,7 @@ export default {
v-if="lineNumber"
:data-linenumber="lineNumber"
:href="lineHref"
@click="setHighlightedRow(lineCode);"
@click="setHighlightedRow(lineCode)"
>
</a>
<diff-gutter-avatars v-if="shouldShowAvatarsOnGutter" :discussions="line.discussions" />
......
......@@ -28,6 +28,11 @@ export default {
type: Object,
required: true,
},
helpPagePath: {
type: String,
required: false,
default: '',
},
},
computed: {
...mapState({
......@@ -95,6 +100,7 @@ export default {
:is-editing="true"
:line-code="line.line_code"
:line="line"
:help-page-path="helpPagePath"
save-button-title="Comment"
class="diff-comment-form"
@cancelForm="handleCancelCommentForm"
......
......@@ -97,7 +97,7 @@ export default {
v-if="canComment"
type="button"
class="btn-transparent position-absolute image-diff-overlay-add-comment w-100 h-100 js-add-image-diff-note-button"
@click="clickedImage($event.offsetX, $event.offsetY);"
@click="clickedImage($event.offsetX, $event.offsetY)"
>
<span class="sr-only"> {{ __('Add image comment') }} </span>
</button>
......@@ -109,7 +109,7 @@ export default {
:disabled="!shouldToggleDiscussion"
class="js-image-badge"
type="button"
@click="toggleDiscussion({ discussionId: discussion.id });"
@click="toggleDiscussion({ discussionId: discussion.id })"
>
<icon v-if="showCommentIcon" name="image-comment-dark" />
<template v-else>
......
......@@ -54,6 +54,7 @@ export default {
:diff-file-hash="diffFileHash"
:line="line"
:note-target-line="line"
:help-page-path="helpPagePath"
/>
</div>
</td>
......
......@@ -101,6 +101,7 @@ export default {
:diff-file-hash="diffFileHash"
:line="line.left"
:note-target-line="line.left"
:help-page-path="helpPagePath"
line-position="left"
/>
</td>
......
......@@ -81,7 +81,7 @@ export default {
:placeholder="s__('MergeRequest|Filter files')"
type="search"
class="form-control"
@focus="toggleFocusSearch(true);"
@focus="toggleFocusSearch(true)"
@blur="blurSearch"
/>
<button
......@@ -104,7 +104,7 @@ export default {
}"
class="btn btn-default pt-0 pb-0 d-flex align-items-center"
type="button"
@click="toggleRenderTreeList(false);"
@click="toggleRenderTreeList(false)"
>
<icon name="hamburger" />
</button>
......@@ -117,7 +117,7 @@ export default {
}"
class="btn btn-default pt-0 pb-0 d-flex align-items-center"
type="button"
@click="toggleRenderTreeList(true);"
@click="toggleRenderTreeList(true)"
>
<icon name="file-tree" />
</button>
......
......@@ -92,7 +92,7 @@ export default {
:disabled="isActionDisabled(action)"
type="button"
class="js-manual-action-link no-btn btn d-flex align-items-center"
@click="onClickAction(action);"
@click="onClickAction(action)"
>
<span class="flex-fill"> {{ action.name }} </span>
<span v-if="action.scheduledAt" class="text-secondary">
......
......@@ -96,9 +96,9 @@ export default {
<tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" />
<div v-if="canCreateEnvironment && !isLoading" class="nav-controls">
<a :href="newEnvironmentPath" class="btn btn-success">
{{ s__('Environments|New environment') }}
</a>
<a :href="newEnvironmentPath" class="btn btn-success">{{
s__('Environments|New environment')
}}</a>
</div>
</div>
......
......@@ -85,9 +85,9 @@ export default {
<div :key="`sub-div-${i}`">
<div class="text-center prepend-top-10">
<a :href="folderUrl(model)" class="btn btn-default">
{{ s__('Environments|Show all') }}
</a>
<a :href="folderUrl(model)" class="btn btn-default">{{
s__('Environments|Show all')
}}</a>
</div>
</div>
</template>
......
......@@ -15,11 +15,11 @@ export default () =>
const environmentsData = document.querySelector(this.$options.el).dataset;
return {
endpoint: environmentsData.endpoint,
folderName: environmentsData.folderName,
endpoint: environmentsData.environmentsDataEndpoint,
folderName: environmentsData.environmentsDataFolderName,
cssContainerClass: environmentsData.cssClass,
canCreateDeployment: parseBoolean(environmentsData.canCreateDeployment),
canReadEnvironment: parseBoolean(environmentsData.canReadEnvironment),
canCreateDeployment: parseBoolean(environmentsData.environmentsDataCanCreateDeployment),
canReadEnvironment: parseBoolean(environmentsData.environmentsDataCanReadEnvironment),
};
},
render(createElement) {
......
<script>
import { mapActions, mapState } from 'vuex';
import { GlEmptyState, GlButton, GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale';
export default {
fields: [
{ key: 'error', label: __('Open errors') },
{ key: 'events', label: __('Events') },
{ key: 'users', label: __('Users') },
{ key: 'lastSeen', label: __('Last seen') },
],
components: {
GlEmptyState,
GlButton,
GlLink,
GlLoadingIcon,
GlTable,
Icon,
TimeAgo,
},
props: {
indexPath: {
type: String,
required: true,
},
enableErrorTrackingLink: {
type: String,
required: true,
},
errorTrackingEnabled: {
type: Boolean,
required: true,
},
illustrationPath: {
type: String,
required: true,
},
},
computed: {
...mapState(['errors', 'externalUrl', 'loading']),
},
created() {
if (this.errorTrackingEnabled) {
this.startPolling(this.indexPath);
}
},
methods: {
...mapActions(['startPolling']),
},
};
</script>
<template>
<div>
<div v-if="errorTrackingEnabled">
<div v-if="loading" class="py-3"><gl-loading-icon :size="3" /></div>
<div v-else>
<div class="d-flex justify-content-end">
<gl-button class="my-3 ml-auto" variant="primary" :href="externalUrl" target="_blank"
>View in Sentry <icon name="external-link" />
</gl-button>
</div>
<gl-table
:items="errors"
:fields="$options.fields"
:show-empty="true"
:empty-text="__('No errors to display')"
>
<template slot="HEAD_events" slot-scope="data">
<div class="text-right">{{ data.label }}</div>
</template>
<template slot="HEAD_users" slot-scope="data">
<div class="text-right">{{ data.label }}</div>
</template>
<template slot="error" slot-scope="errors">
<div class="d-flex flex-column">
<div class="d-flex">
<gl-link :href="errors.item.externalUrl" class="d-flex text-dark" target="_blank">
<strong>{{ errors.item.title.trim() }}</strong>
<icon name="external-link" class="ml-1" />
</gl-link>
<span class="text-secondary ml-2">{{ errors.item.culprit }}</span>
</div>
{{ errors.item.message || __('No details available') }}
</div>
</template>
<template slot="events" slot-scope="errors">
<div class="text-right">{{ errors.item.count }}</div>
</template>
<template slot="users" slot-scope="errors">
<div class="text-right">{{ errors.item.userCount }}</div>
</template>
<template slot="lastSeen" slot-scope="errors">
<div class="d-flex align-items-center">
<icon name="calendar" css-classes="text-secondary mr-1" />
<time-ago :time="errors.item.lastSeen" class="text-secondary" />
</div>
</template>
</gl-table>
</div>
</div>
<div v-else>
<gl-empty-state
:title="__('Get started with error tracking')"
:description="__('Monitor your errors by integrating with Sentry')"
:primary-button-text="__('Enable error tracking')"
:primary-button-link="enableErrorTrackingLink"
:svg-path="illustrationPath"
/>
</div>
</div>
</template>
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import store from './store';
import ErrorTrackingList from './components/error_tracking_list.vue';
export default () => {
if (!gon.features.errorTracking) {
return;
}
// eslint-disable-next-line no-new
new Vue({
el: '#js-error_tracking',
components: {
ErrorTrackingList,
},
store,
render(createElement) {
const domEl = document.querySelector(this.$options.el);
const { indexPath, enableErrorTrackingLink, illustrationPath } = domEl.dataset;
let { errorTrackingEnabled } = domEl.dataset;
errorTrackingEnabled = parseBoolean(errorTrackingEnabled);
return createElement('error-tracking-list', {
props: {
indexPath,
enableErrorTrackingLink,
errorTrackingEnabled,
illustrationPath,
},
});
},
});
};
import axios from '~/lib/utils/axios_utils';
export default {
getErrorList({ endpoint }) {
return axios.get(endpoint);
},
};
import Service from '../services';
import * as types from './mutation_types';
import createFlash from '~/flash';
import Poll from '~/lib/utils/poll';
import { __ } from '~/locale';
let eTagPoll;
export function startPolling({ commit }, endpoint) {
eTagPoll = new Poll({
resource: Service,
method: 'getErrorList',
data: { endpoint },
successCallback: ({ data }) => {
if (!data) {
return;
}
commit(types.SET_ERRORS, data.errors);
commit(types.SET_EXTERNAL_URL, data.external_url);
commit(types.SET_LOADING, false);
},
errorCallback: () => {
commit(types.SET_LOADING, false);
createFlash(__('Failed to load errors from Sentry'));
},
});
eTagPoll.makeRequest();
}
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
state: {
errors: [],
externalUrl: '',
loading: true,
},
actions,
mutations,
});
export default createStore();
export const SET_ERRORS = 'SET_ERRORS';
export const SET_EXTERNAL_URL = 'SET_EXTERNAL_URL';
export const SET_LOADING = 'SET_LOADING';
import * as types from './mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default {
[types.SET_ERRORS](state, data) {
state.errors = convertObjectPropsToCamelCase(data, { deep: true });
},
[types.SET_EXTERNAL_URL](state, url) {
state.externalUrl = url;
},
[types.SET_LOADING](state, loading) {
state.loading = loading;
},
};
......@@ -66,7 +66,7 @@ export default {
<button
type="button"
class="filtered-search-history-dropdown-item"
@click="onItemActivated(item.text);"
@click="onItemActivated(item.text)"
>
<span>
<span
......@@ -88,7 +88,7 @@ export default {
<button
type="button"
class="filtered-search-history-clear-button"
@click="onRequestClearRecentSearches($event);"
@click="onRequestClearRecentSearches($event)"
>
Clear recent searches
</button>
......
......@@ -24,6 +24,9 @@ export const slope = (a, b) => (b.y - a.y) / (b.x - a.x);
let headerHeight = 50;
export const getHeaderHeight = () => headerHeight;
const setHeaderHeight = () => {
headerHeight = sidebar.offsetTop;
};
export const isSidebarCollapsed = () =>
sidebar && sidebar.classList.contains('sidebar-collapsed-desktop');
......@@ -186,7 +189,7 @@ export default () => {
});
}
headerHeight = document.querySelector('.nav-sidebar').offsetTop;
requestIdleCallback(setHeaderHeight);
items.forEach(el => {
const subItems = el.querySelector('.sidebar-sub-level-items');
......
......@@ -45,7 +45,7 @@ export default {
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-edit-mode"
@click.prevent="changedActivityView($event, $options.activityBarViews.edit);"
@click.prevent="changedActivityView($event, $options.activityBarViews.edit)"
>
<icon name="code" />
</button>
......@@ -62,7 +62,7 @@ export default {
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-review-mode"
@click.prevent="changedActivityView($event, $options.activityBarViews.review);"
@click.prevent="changedActivityView($event, $options.activityBarViews.review)"
>
<icon name="file-modified" />
</button>
......@@ -79,7 +79,7 @@ export default {
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-commit-mode"
@click.prevent="changedActivityView($event, $options.activityBarViews.commit);"
@click.prevent="changedActivityView($event, $options.activityBarViews.commit)"
>
<icon name="commit" />
</button>
......
......@@ -111,8 +111,8 @@ export default {
name="commit-message"
@scroll="handleScroll"
@input="onInput"
@focus="updateIsFocused(true);"
@blur="updateIsFocused(false);"
@focus="updateIsFocused(true)"
@blur="updateIsFocused(false)"
@keydown.ctrl.enter="onCtrlEnter"
@keydown.meta.enter="onCtrlEnter"
>
......
......@@ -65,7 +65,7 @@ export default {
:disabled="disabled"
type="radio"
name="commit-action"
@change="updateCommitAction($event.target.value);"
@change="updateCommitAction($event.target.value)"
/>
<span class="prepend-left-10">
<span v-if="label" class="ide-radio-label"> {{ label }} </span> <slot v-else></slot>
......@@ -76,7 +76,7 @@ export default {
:placeholder="newBranchName"
type="text"
class="form-control monospace"
@input="updateBranchName($event.target.value);"
@input="updateBranchName($event.target.value)"
/>
</div>
</fieldset>
......
......@@ -48,7 +48,7 @@ export default {
data-container="body"
data-boundary="viewport"
data-placement="bottom"
@click.stop.prevent="stageChange(path);"
@click.stop.prevent="stageChange(path)"
>
<icon :size="16" name="mobile-issue-close" class="ml-auto mr-auto" />
</button>
......@@ -70,7 +70,7 @@ export default {
:header-title-text="modalTitle"
:footer-primary-button-text="__('Discard changes')"
footer-primary-button-variant="danger"
@submit="discardFileChanges(path);"
@submit="discardFileChanges(path)"
>
{{ __("You will loose all changes you've made to this file. This action cannot be undone.") }}
</gl-modal>
......
......@@ -33,7 +33,7 @@ export default {
data-container="body"
data-boundary="viewport"
data-placement="bottom"
@click.stop.prevent="unstageChange(path);"
@click.stop.prevent="unstageChange(path)"
>
<icon :size="16" name="redo" class="ml-auto mr-auto" />
</button>
......
......@@ -40,7 +40,7 @@ export default {
'is-active': viewer === $options.viewerTypes.mr,
}"
href="#"
@click.prevent="changeMode($options.viewerTypes.mr);"
@click.prevent="changeMode($options.viewerTypes.mr)"
>
<strong class="dropdown-menu-inner-title"> {{ mergeReviewLine }} </strong>
<span class="dropdown-menu-inner-content">
......@@ -54,7 +54,7 @@ export default {
'is-active': viewer === $options.viewerTypes.diff,
}"
href="#"
@click.prevent="changeMode($options.viewerTypes.diff);"
@click.prevent="changeMode($options.viewerTypes.diff)"
>
<strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong>
<span class="dropdown-menu-inner-content">
......
......@@ -164,7 +164,7 @@ export default {
</script>
<template>
<div class="ide-file-finder-overlay" @mousedown.self="toggleFileFinder(false);">
<div class="ide-file-finder-overlay" @mousedown.self="toggleFileFinder(false)">
<div class="dropdown-menu diff-file-changes ide-file-finder show">
<div class="dropdown-input">
<input
......@@ -174,8 +174,8 @@ export default {
type="search"
class="dropdown-input-field"
autocomplete="off"
@keydown="onKeydown($event);"
@keyup="onKeyup($event);"
@keydown="onKeydown($event)"
@keyup="onKeyup($event)"
/>
<i
:class="{
......
......@@ -91,7 +91,7 @@ export default {
<gl-loading-icon v-if="showLoading" :size="2" />
<ul v-else>
<li v-for="(item, index) in outputData" :key="index">
<button type="button" @click="clickItem(item);">{{ item.name }}</button>
<button type="button" @click="clickItem(item)">{{ item.name }}</button>
</li>
</ul>
</div>
......
......@@ -84,7 +84,7 @@ export default {
<button
type="button"
class="p-0 border-0 h-50"
@click="openRightPane($options.rightSidebarViews.pipelines);"
@click="openRightPane($options.rightSidebarViews.pipelines)"
>
<ci-icon
v-tooltip
......
......@@ -43,7 +43,7 @@ export default {
:show-label="false"
class="d-flex border-0 p-0 mr-3 qa-new-file"
icon="doc-new"
@click="openNewEntryModal({ type: 'blob' });"
@click="openNewEntryModal({ type: 'blob' })"
/>
<upload
:show-label="false"
......@@ -56,7 +56,7 @@ export default {
:show-label="false"
class="d-flex border-0 p-0"
icon="folder-new"
@click="openNewEntryModal({ type: 'tree' });"
@click="openNewEntryModal({ type: 'tree' })"
/>
</div>
</template>
......
......@@ -75,7 +75,7 @@ export default {
<template>
<div class="ide-pipeline build-page d-flex flex-column flex-fill">
<header class="ide-job-header d-flex align-items-center">
<button class="btn btn-default btn-sm d-flex" @click="setDetailJob(null);">
<button class="btn btn-default btn-sm d-flex" @click="setDetailJob(null)">
<icon name="chevron-left" /> {{ __('View jobs') }}
</button>
</header>
......
......@@ -84,7 +84,7 @@ export default {
:placeholder="__('Search merge requests')"
@focus="onSearchFocus"
@input="searchMergeRequests"
@removeToken="setSearchType(null);"
@removeToken="setSearchType(null)"
/>
<icon :size="18" name="search" class="input-icon" />
</div>
......@@ -102,7 +102,7 @@ export default {
<button
type="button"
class="btn-link d-flex align-items-center"
@click.stop="setSearchType(searchType);"
@click.stop="setSearchType(searchType)"
>
<span class="d-flex append-right-default ide-search-list-current-icon">
<icon :size="18" name="search" />
......
......@@ -73,7 +73,7 @@ export default {
:aria-label="__('Create new file or directory')"
type="button"
class="rounded border-0 d-flex ide-entry-dropdown-toggle"
@click.stop="openDropdown();"
@click.stop="openDropdown()"
>
<icon name="ellipsis_v" /> <icon name="arrow-down" />
</button>
......@@ -85,7 +85,7 @@ export default {
class="d-flex"
icon="doc-new"
icon-classes="mr-2"
@click="createNewItem('blob');"
@click="createNewItem('blob')"
/>
</li>
<li><upload :path="path" @create="createTempEntry" /></li>
......@@ -95,7 +95,7 @@ export default {
class="d-flex"
icon="folder-new"
icon-classes="mr-2"
@click="createNewItem($options.modalTypes.tree);"
@click="createNewItem($options.modalTypes.tree)"
/>
</li>
<li class="divider"></li>
......@@ -106,7 +106,7 @@ export default {
class="d-flex"
icon="pencil"
icon-classes="mr-2"
@click="createNewItem($options.modalTypes.rename);"
@click="createNewItem($options.modalTypes.rename)"
/>
</li>
<li>
......@@ -115,7 +115,7 @@ export default {
class="d-flex"
icon="remove"
icon-classes="mr-2"
@click="deleteEntry(path);"
@click="deleteEntry(path)"
/>
</li>
</ul>
......
......@@ -114,7 +114,7 @@ export default {
<button
type="button"
class="btn btn-missing p-1 pr-2 pl-2"
@click="createFromTemplate(template);"
@click="createFromTemplate(template)"
>
{{ template.name }}
</button>
......
......@@ -122,7 +122,7 @@ export default {
data-placement="left"
class="ide-sidebar-link is-right"
type="button"
@click="clickTab($event, tab);"
@click="clickTab($event, tab)"
>
<icon :size="16" :name="tab.icon" />
</button>
......
......@@ -219,7 +219,7 @@ export default {
<a
href="javascript:void(0);"
role="button"
@click.prevent="setFileViewMode({ file, viewMode: 'editor' });"
@click.prevent="setFileViewMode({ file, viewMode: 'editor' })"
>
<template v-if="viewer === $options.viewerTypes.edit">
{{ __('Edit') }}
......@@ -233,7 +233,7 @@ export default {
<a
href="javascript:void(0);"
role="button"
@click.prevent="setFileViewMode({ file, viewMode: 'preview' });"
@click.prevent="setFileViewMode({ file, viewMode: 'preview' })"
>
{{ file.previewMode.previewTitle }}
</a>
......
......@@ -74,7 +74,7 @@ export default {
active: tab.active,
disabled: tab.pending,
}"
@click="clickFile(tab);"
@click="clickFile(tab)"
@mouseover="mouseOverTab"
@mouseout="mouseOutTab"
>
......@@ -88,7 +88,7 @@ export default {
:disabled="tab.pending"
type="button"
class="multi-file-tab-close"
@click.stop.prevent="closeFile(tab);"
@click.stop.prevent="closeFile(tab)"
>
<icon v-if="!showChangedIcon" :size="12" name="close" />
<changed-file-icon v-else :file="tab" />
......
......@@ -78,8 +78,8 @@ export default {
:min-size="minSize"
:max-size="$options.maxSize"
:side="side === 'right' ? 'left' : 'right'"
@resize-start="setResizingStatus(true);"
@resize-end="setResizingStatus(false);"
@resize-start="setResizingStatus(true)"
@resize-end="setResizingStatus(false)"
/>
</div>
</template>
......@@ -76,8 +76,8 @@ export default {
<button
class="selectable btn-blank"
type="button"
@click.stop="removeToken(token);"
@keyup.delete="removeToken(token);"
@click.stop="removeToken(token)"
@keyup.delete="removeToken(token)"
>
<div class="value-container rounded">
<div class="value">{{ token.label }}</div>
......
......@@ -9,6 +9,13 @@ import { parseBoolean } from '../lib/utils/common_utils';
Vue.use(Translate);
/**
* Function that receives the default store and returns an extended one.
* @callback extendStoreCallback
* @param {Vuex.Store} store
* @param {Element} el
*/
/**
* Initialize the IDE on the given element.
*
......@@ -16,7 +23,7 @@ Vue.use(Translate);
* @param {Object} options - Extra options for the IDE (Used by EE).
* @param {Component} options.rootComponent -
* Component that overrides the root component.
* @param {(store:Vuex.Store, el:Element) => Vuex.Store} options.extendStore -
* @param {extendStoreCallback} options.extendStore -
* Function that receives the default store and returns an extended one.
*/
export function initIde(el, options = {}) {
......
......@@ -80,7 +80,6 @@ export default {
'hasError',
]),
...mapGetters([
'headerActions',
'headerTime',
'shouldRenderCalloutMessage',
'shouldRenderTriggeredLabel',
......@@ -202,7 +201,6 @@ export default {
:item-id="job.id"
:time="headerTime"
:user="job.user"
:actions="headerActions"
:has-sidebar-button="true"
:should-render-triggered-label="shouldRenderTriggeredLabel"
:item-name="__('Job')"
......
......@@ -48,8 +48,7 @@ export default {
return `${this.job.runner.description} (#${this.job.runner.id})`;
},
retryButtonClass() {
let className =
'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
let className = 'js-retry-button btn btn-retry';
className +=
this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
return className;
......@@ -110,24 +109,27 @@ export default {
<aside class="right-sidebar build-sidebar" data-offset-top="101" data-spy="affix">
<div class="sidebar-container">
<div class="blocks-container">
<div class="block d-flex align-items-center">
<h4 class="flex-grow-1 prepend-top-8 m-0">{{ job.name }}</h4>
<gl-link
v-if="job.retry_path"
:class="retryButtonClass"
:href="job.retry_path"
data-method="post"
rel="nofollow"
>{{ __('Retry') }}</gl-link
>
<gl-link
v-if="job.terminal_path"
:href="job.terminal_path"
class="js-terminal-link pull-right btn btn-primary btn-inverted visible-md-block visible-lg-block"
target="_blank"
>
{{ __('Debug') }} <icon name="external-link" />
</gl-link>
<div class="block d-flex flex-nowrap align-items-center">
<h4 class="my-0 mr-2">{{ job.name }}</h4>
<div class="flex-grow-1 flex-shrink-0 text-right">
<gl-link
v-if="job.retry_path"
:class="retryButtonClass"
:href="job.retry_path"
data-method="post"
rel="nofollow"
>{{ __('Retry') }}</gl-link
>
<gl-link
v-if="job.cancel_path"
:href="job.cancel_path"
class="js-cancel-job btn btn-default"
data-method="post"
rel="nofollow"
>{{ __('Cancel') }}</gl-link
>
</div>
<gl-button
:aria-label="__('Toggle Sidebar')"
type="button"
......@@ -137,22 +139,24 @@ export default {
<i aria-hidden="true" data-hidden="true" class="fa fa-angle-double-right"></i>
</gl-button>
</div>
<div v-if="job.retry_path || job.new_issue_path" class="block retry-link">
<div v-if="job.terminal_path || job.new_issue_path" class="block retry-link">
<gl-link
v-if="job.new_issue_path"
:href="job.new_issue_path"
class="js-new-issue btn btn-success btn-inverted"
class="js-new-issue btn btn-success btn-inverted float-left mr-2"
>{{ __('New issue') }}</gl-link
>
<gl-link
v-if="job.retry_path"
:href="job.retry_path"
class="js-retry-job btn btn-inverted-secondary"
data-method="post"
rel="nofollow"
>{{ __('Retry') }}</gl-link
v-if="job.terminal_path"
:href="job.terminal_path"
class="js-terminal-link btn btn-primary btn-inverted visible-md-block visible-lg-block float-left"
target="_blank"
>
{{ __('Debug') }} <icon name="external-link" :size="14" />
</gl-link>
</div>
<div :class="{ block: renderBlock }">
<detail-row
v-if="job.duration"
......@@ -193,16 +197,6 @@ export default {
tag
}}</span>
</p>
<div v-if="job.cancel_path" class="btn-group prepend-top-5" role="group">
<gl-link
:href="job.cancel_path"
class="js-cancel-job btn btn-sm btn-default"
data-method="post"
rel="nofollow"
>{{ __('Cancel') }}</gl-link
>
</div>
</div>
<artifacts-block v-if="hasArtifact" :artifact="job.artifact" />
......
......@@ -55,7 +55,7 @@ export default {
<ul class="dropdown-menu">
<li v-for="stage in stages" :key="stage.name">
<button type="button" class="js-stage-item stage-item" @click="onStageClick(stage);">
<button type="button" class="js-stage-item stage-item" @click="onStageClick(stage)">
{{ stage.name }}
</button>
</li>
......
import _ from 'underscore';
import { __ } from '~/locale';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
export const headerActions = state => {
if (state.job.new_issue_path) {
return [
{
label: __('New issue'),
path: state.job.new_issue_path,
cssClass:
'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
type: 'link',
},
];
}
return [];
};
export const headerTime = state => (state.job.started ? state.job.started : state.job.created_at);
export const shouldRenderCalloutMessage = state =>
......
......@@ -11,48 +11,53 @@ function hideEndFade($scrollingTabs) {
});
}
function initDeferred() {
$(document).trigger('init.scrolling-tabs');
}
export default function initLayoutNav() {
const contextualSidebar = new ContextualSidebar();
contextualSidebar.bindEvents();
initFlyOutNav();
$(document)
.on('init.scrolling-tabs', () => {
const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized');
$scrollingTabs.addClass('is-initialized');
// We need to init it on DomContentLoaded as others could also call it
$(document).on('init.scrolling-tabs', () => {
const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized');
$scrollingTabs.addClass('is-initialized');
$(window)
.on('resize.nav', () => {
hideEndFade($scrollingTabs);
})
.trigger('resize.nav');
$(window)
.on('resize.nav', () => {
hideEndFade($scrollingTabs);
})
.trigger('resize.nav');
$scrollingTabs.on('scroll', function tabsScrollEvent() {
const $this = $(this);
const currentPosition = $this.scrollLeft();
const maxPosition = $this.prop('scrollWidth') - $this.outerWidth();
$scrollingTabs.on('scroll', function tabsScrollEvent() {
const $this = $(this);
const currentPosition = $this.scrollLeft();
const maxPosition = $this.prop('scrollWidth') - $this.outerWidth();
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
$this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
});
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
$this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
});
$scrollingTabs.each(function scrollTabsEachLoop() {
const $this = $(this);
const scrollingTabWidth = $this.width();
const $active = $this.find('.active');
const activeWidth = $active.width();
$scrollingTabs.each(function scrollTabsEachLoop() {
const $this = $(this);
const scrollingTabWidth = $this.width();
const $active = $this.find('.active');
const activeWidth = $active.width();
if ($active.length) {
const offset = $active.offset().left + activeWidth;
if ($active.length) {
const offset = $active.offset().left + activeWidth;
if (offset > scrollingTabWidth - 30) {
const scrollLeft = offset - scrollingTabWidth / 2 - activeWidth / 2;
if (offset > scrollingTabWidth - 30) {
const scrollLeft = offset - scrollingTabWidth / 2 - activeWidth / 2;
$this.scrollLeft(scrollLeft);
}
$this.scrollLeft(scrollLeft);
}
});
})
.trigger('init.scrolling-tabs');
}
});
});
requestIdleCallback(initDeferred);
}
/**
* @module common-utils
*/
import $ from 'jquery';
import axios from './axios_utils';
import { getLocationHash } from './url_utility';
......@@ -426,13 +430,14 @@ export const historyPushState = newUrl => {
};
/**
* Returns true for a String "true" and false otherwise.
* This is the opposite of Boolean(...).toString()
* Returns true for a String value of "true" and false otherwise.
* This is the opposite of Boolean(...).toString().
* `parseBoolean` is idempotent.
*
* @param {String} value
* @returns {Boolean}
*/
export const parseBoolean = value => value === 'true';
export const parseBoolean = value => (value && value.toString()) === 'true';
/**
* Converts permission provided as strings to booleans.
......@@ -449,11 +454,17 @@ export const convertPermissionToBoolean = permission => {
return parseBoolean(permission);
};
/**
* @callback backOffCallback
* @param {Function} next
* @param {Function} stop
*/
/**
* Back Off exponential algorithm
* backOff :: (Function<next, stop>, Number) -> Promise<Any, Error>
*
* @param {Function<next, stop>} fn function to be called
* @param {backOffCallback} fn function to be called
* @param {Number} timeout
* @return {Promise<Any, Error>}
* @example
......
......@@ -4,8 +4,8 @@ import _ from 'underscore';
Very limited implementation of sprintf supporting only named parameters.
@param input (translated) text with parameters (e.g. '%{num_users} users use us')
@param parameters object mapping parameter names to values (e.g. { num_users: 5 })
@param escapeParameters whether parameter values should be escaped (see http://underscorejs.org/#escape)
@param {Object} parameters object mapping parameter names to values (e.g. { num_users: 5 })
@param {Boolean} escapeParameters whether parameter values should be escaped (see http://underscorejs.org/#escape)
@returns {String} the text with parameters replaces (e.g. '5 users use us')
@see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf
......
......@@ -257,8 +257,8 @@ export default {
<template>
<div
class="prometheus-graph"
@mouseover="showFlagContent = true;"
@mouseleave="showFlagContent = false;"
@mouseover="showFlagContent = true"
@mouseleave="showFlagContent = false"
>
<div class="prometheus-graph-header">
<h5 class="prometheus-graph-title">{{ graphData.title }}</h5>
......@@ -300,7 +300,7 @@ export default {
:height="graphHeight - 100"
class="prometheus-graph-overlay"
transform="translate(-5, 20)"
@mousemove="handleMouseOverGraph($event);"
@mousemove="handleMouseOverGraph($event)"
/>
</svg>
<svg v-else :viewBox="innerViewBox" class="js-no-data-to-display">
......
<script>
import CodeCell from './code/index.vue';
import CodeOutput from './code/index.vue';
import OutputCell from './output/index.vue';
export default {
name: 'CodeCell',
components: {
'code-cell': CodeCell,
'output-cell': OutputCell,
CodeOutput,
OutputCell,
},
props: {
cell: {
......@@ -29,8 +30,8 @@ export default {
hasOutput() {
return this.cell.outputs.length;
},
output() {
return this.cell.outputs[0];
outputs() {
return this.cell.outputs;
},
},
};
......@@ -38,7 +39,7 @@ export default {
<template>
<div class="cell">
<code-cell
<code-output
:raw-code="rawInputCode"
:count="cell.execution_count"
:code-css-class="codeCssClass"
......@@ -47,7 +48,7 @@ export default {
<output-cell
v-if="hasOutput"
:count="cell.execution_count"
:output="output"
:outputs="outputs"
:code-css-class="codeCssClass"
/>
</div>
......
......@@ -3,8 +3,9 @@ import Prism from '../../lib/highlight';
import Prompt from '../prompt.vue';
export default {
name: 'CodeOutput',
components: {
prompt: Prompt,
Prompt,
},
props: {
count: {
......
......@@ -4,13 +4,21 @@ import Prompt from '../prompt.vue';
export default {
components: {
prompt: Prompt,
Prompt,
},
props: {
count: {
type: Number,
required: true,
},
rawCode: {
type: String,
required: true,
},
index: {
type: Number,
required: true,
},
},
computed: {
sanitizedOutput() {
......@@ -21,13 +29,16 @@ export default {
},
});
},
showOutput() {
return this.index === 0;
},
},
};
</script>
<template>
<div class="output">
<prompt />
<prompt type="Out" :count="count" :show-output="showOutput" />
<div v-html="sanitizedOutput"></div>
</div>
</template>
......@@ -6,6 +6,10 @@ export default {
prompt: Prompt,
},
props: {
count: {
type: Number,
required: true,
},
outputType: {
type: String,
required: true,
......@@ -14,10 +18,24 @@ export default {
type: String,
required: true,
},
index: {
type: Number,
required: true,
},
},
computed: {
imgSrc() {
return `data:${this.outputType};base64,${this.rawCode}`;
},
showOutput() {
return this.index === 0;
},
},
};
</script>
<template>
<div class="output"><prompt /> <img :src="'data:' + outputType + ';base64,' + rawCode" /></div>
<div class="output">
<prompt type="out" :count="count" :show-output="showOutput" /> <img :src="imgSrc" />
</div>
</template>
<script>
import CodeCell from '../code/index.vue';
import Html from './html.vue';
import Image from './image.vue';
import CodeOutput from '../code/index.vue';
import HtmlOutput from './html.vue';
import ImageOutput from './image.vue';
export default {
components: {
'code-cell': CodeCell,
'html-output': Html,
'image-output': Image,
},
props: {
codeCssClass: {
type: String,
......@@ -20,68 +15,69 @@ export default {
required: false,
default: 0,
},
output: {
type: Object,
outputs: {
type: Array,
required: true,
default: () => ({}),
},
},
computed: {
componentName() {
if (this.output.text) {
return 'code-cell';
} else if (this.output.data['image/png']) {
return 'image-output';
} else if (this.output.data['text/html']) {
return 'html-output';
} else if (this.output.data['image/svg+xml']) {
return 'html-output';
}
data() {
return {
outputType: '',
};
},
methods: {
dataForType(output, type) {
let data = output.data[type];
return 'code-cell';
},
rawCode() {
if (this.output.text) {
return this.output.text.join('');
if (typeof data === 'object') {
data = data.join('');
}
return this.dataForType(this.outputType);
return data;
},
outputType() {
if (this.output.text) {
return '';
} else if (this.output.data['image/png']) {
return 'image/png';
} else if (this.output.data['text/html']) {
return 'text/html';
} else if (this.output.data['image/svg+xml']) {
return 'image/svg+xml';
getComponent(output) {
if (output.text) {
return CodeOutput;
} else if (output.data['image/png']) {
this.outputType = 'image/png';
return ImageOutput;
} else if (output.data['text/html']) {
this.outputType = 'text/html';
return HtmlOutput;
} else if (output.data['image/svg+xml']) {
this.outputType = 'image/svg+xml';
return HtmlOutput;
}
return 'text/plain';
this.outputType = 'text/plain';
return CodeOutput;
},
},
methods: {
dataForType(type) {
let data = this.output.data[type];
if (typeof data === 'object') {
data = data.join('');
rawCode(output) {
if (output.text) {
return output.text.join('');
}
return data;
return this.dataForType(output, this.outputType);
},
},
};
</script>
<template>
<component
:is="componentName"
:output-type="outputType"
:count="count"
:raw-code="rawCode"
:code-css-class="codeCssClass"
type="output"
/>
<div>
<component
:is="getComponent(output)"
v-for="(output, index) in outputs"
:key="index"
type="output"
:output-type="outputType"
:count="count"
:index="index"
:raw-code="rawCode(output)"
:code-css-class="codeCssClass"
/>
</div>
</template>
......@@ -11,18 +11,26 @@ export default {
required: false,
default: 0,
},
showOutput: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
hasKeys() {
return this.type !== '' && this.count;
},
showTypeText() {
return this.type && this.count && this.showOutput;
},
},
};
</script>
<template>
<div class="prompt">
<span v-if="hasKeys"> {{ type }} [{{ count }}]: </span>
<span v-if="showTypeText"> {{ type }} [{{ count }}]: </span>
</div>
</template>
......
......@@ -3,8 +3,8 @@ import { MarkdownCell, CodeCell } from './cells';
export default {
components: {
'code-cell': CodeCell,
'markdown-cell': MarkdownCell,
CodeCell,
MarkdownCell,
},
props: {
notebook: {
......
......@@ -357,9 +357,9 @@ js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
data-supports-quick-actions="true"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@keydown.up="editCurrentUserLastNote();"
@keydown.meta.enter="handleSave();"
@keydown.ctrl.enter="handleSave();"
@keydown.up="editCurrentUserLastNote()"
@keydown.meta.enter="handleSave()"
@keydown.ctrl.enter="handleSave()"
>
</textarea>
</markdown-field>
......@@ -373,7 +373,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
class="btn btn-success js-comment-button js-comment-submit-button
qa-comment-button"
type="submit"
@click.prevent="handleSave();"
@click.prevent="handleSave()"
>
{{ __(commentButtonTitle) }}
</button>
......@@ -394,7 +394,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<button
type="button"
class="btn btn-transparent"
@click.prevent="setNoteType('comment');"
@click.prevent="setNoteType('comment')"
>
<i aria-hidden="true" class="fa fa-check icon"> </i>
<div class="description">
......@@ -408,7 +408,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<button
type="button"
class="btn btn-transparent qa-discussion-option"
@click.prevent="setNoteType('discussion');"
@click.prevent="setNoteType('discussion')"
>
<i aria-hidden="true" class="fa fa-check icon"> </i>
<div class="description">
......@@ -429,7 +429,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
]"
:disabled="isToggleStateButtonLoading || isSubmitting"
:label="issueActionButtonTitle"
@click="handleSave(true);"
@click="handleSave(true)"
/>
</div>
</form>
......
......@@ -112,7 +112,7 @@ export default {
:class="{ 'is-active': filter.value === currentValue }"
class="qa-filter-options"
type="button"
@click="selectFilter(filter.value);"
@click="selectFilter(filter.value)"
>
{{ filter.title }}
</button>
......
......@@ -174,7 +174,7 @@ export default {
data-placement="bottom"
class="btn award-control"
type="button"
@click="handleAward(awardName);"
@click="handleAward(awardName)"
>
<span v-html="getAwardHTML(awardName)"></span>
<span class="award-control-text js-counter">{{ awardList.length }}</span>
......
......@@ -219,10 +219,10 @@ export default {
class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleKeySubmit();"
@keydown.ctrl.enter="handleKeySubmit();"
@keydown.up="editMyLastNote();"
@keydown.esc="cancelHandler(true);"
@keydown.meta.enter="handleKeySubmit()"
@keydown.ctrl.enter="handleKeySubmit()"
@keydown.up="editMyLastNote()"
@keydown.esc="cancelHandler(true)"
></textarea>
</markdown-field>
<div class="note-form-actions clearfix">
......@@ -230,21 +230,21 @@ export default {
:disabled="isDisabled"
type="button"
class="js-vue-issue-save btn btn-success js-comment-button qa-reply-comment-button"
@click="handleUpdate();"
@click="handleUpdate()"
>
{{ saveButtonTitle }}
</button>
<button
v-if="discussion.resolvable"
class="btn btn-nr btn-default append-right-10 js-comment-resolve-button"
@click.prevent="handleUpdate(true);"
@click.prevent="handleUpdate(true)"
>
{{ resolveButtonTitle }}
</button>
<button
class="btn btn-cancel note-edit-cancel js-close-discussion-note-form"
type="button"
@click="cancelHandler();"
@click="cancelHandler()"
>
Cancel
</button>
......
......@@ -440,7 +440,7 @@ Please check your network connection and try again.`;
<button
type="button"
class="btn btn-default ml-sm-2"
@click="resolveHandler();"
@click="resolveHandler()"
>
<i v-if="isResolving" aria-hidden="true" class="fa fa-spinner fa-spin"></i>
{{ resolveButtonTitle }}
......
import ErrorTracking from '~/error_tracking';
document.addEventListener('DOMContentLoaded', () => {
ErrorTracking();
});
......@@ -70,7 +70,7 @@ export default {
:checked="isEditable"
class="label-bold"
type="radio"
@click="toggleCustomInput(true);"
@click="toggleCustomInput(true)"
/>
<label for="custom"> {{ s__('PipelineSheduleIntervalPattern|Custom') }} </label>
......@@ -88,7 +88,7 @@ export default {
:value="cronIntervalPresets.everyDay"
class="label-bold"
type="radio"
@click="toggleCustomInput(false);"
@click="toggleCustomInput(false)"
/>
<label class="label-bold" for="every-day"> {{ __('Every day (at 4:00am)') }} </label>
......@@ -102,7 +102,7 @@ export default {
:value="cronIntervalPresets.everyWeek"
class="label-bold"
type="radio"
@click="toggleCustomInput(false);"
@click="toggleCustomInput(false)"
/>
<label class="label-bold" for="every-week">
......@@ -118,7 +118,7 @@ export default {
:value="cronIntervalPresets.everyMonth"
class="label-bold"
type="radio"
@click="toggleCustomInput(false);"
@click="toggleCustomInput(false)"
/>
<label class="label-bold" for="every-month">
......
<script>
import _ from 'underscore';
import { GlLoadingIcon } from '@gitlab/ui';
import StageColumnComponent from './stage_column_component.vue';
import GraphMixin from '../../mixins/graph_component_mixin';
export default {
components: {
StageColumnComponent,
GlLoadingIcon,
},
props: {
isLoading: {
type: Boolean,
required: true,
},
pipeline: {
type: Object,
required: true,
},
},
computed: {
graph() {
return this.pipeline.details && this.pipeline.details.stages;
},
},
methods: {
capitalizeStageName(name) {
const escapedName = _.escape(name);
return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
},
isFirstColumn(index) {
return index === 0;
},
stageConnectorClass(index, stage) {
let className;
// If it's the first stage column and only has one job
if (index === 0 && stage.groups.length === 1) {
className = 'no-margin';
} else if (index > 0) {
// If it is not the first column
className = 'left-margin';
}
return className;
},
refreshPipelineGraph() {
this.$emit('refreshPipelineGraph');
},
},
mixins: [GraphMixin],
};
</script>
<template>
<div class="build-content middle-block js-pipeline-graph">
<div class="pipeline-visualization pipeline-graph pipeline-tab-content">
<div class="text-center"><gl-loading-icon v-if="isLoading" :size="3" /></div>
<div v-if="isLoading" class="m-auto"><gl-loading-icon :size="3" /></div>
<ul v-if="!isLoading" class="stage-column-list">
<stage-column-component
......
......@@ -77,7 +77,7 @@ export default {
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
class="js-pipeline-action-link no-btn btn"
@click="onClickAction(action);"
@click="onClickAction(action)"
>
{{ action.name }}
<span v-if="action.scheduled_at" class="pull-right">
......
......@@ -172,8 +172,6 @@ export default {
<span :aria-label="stage.title" aria-hidden="true" class="no-pointer-events">
<icon :name="borderlessIcon" />
</span>
<i class="fa fa-caret-down" aria-hidden="true"> </i>
</button>
<div
......
import _ from 'underscore';
export default {
props: {
isLoading: {
type: Boolean,
required: true,
},
pipeline: {
type: Object,
required: true,
},
},
computed: {
graph() {
return this.pipeline.details && this.pipeline.details.stages;
},
},
methods: {
capitalizeStageName(name) {
const escapedName = _.escape(name);
return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
},
isFirstColumn(index) {
return index === 0;
},
stageConnectorClass(index, stage) {
let className;
// If it's the first stage column and only has one job
if (index === 0 && stage.groups.length === 1) {
className = 'no-margin';
} else if (index > 0) {
// If it is not the first column
className = 'left-margin';
}
return className;
},
refreshPipelineGraph() {
this.$emit('refreshPipelineGraph');
},
},
};
......@@ -108,9 +108,7 @@ export default {
</span>
</li>
<li v-for="result in results" :key="result.id">
<button type="button" @click.prevent="setItem(result.name);">
{{ result.name }}
</button>
<button type="button" @click.prevent="setItem(result.name)">{{ result.name }}</button>
</li>
</ul>
</div>
......
......@@ -169,7 +169,7 @@ export default {
</span>
</li>
<li v-for="result in results" :key="result.project_number">
<button type="button" @click.prevent="setItem(result);">{{ result.name }}</button>
<button type="button" @click.prevent="setItem(result)">{{ result.name }}</button>
</li>
</ul>
</div>
......
......@@ -82,9 +82,7 @@ export default {
</span>
</li>
<li v-for="result in results" :key="result.id">
<button type="button" @click.prevent="setItem(result.name);">
{{ result.name }}
</button>
<button type="button" @click.prevent="setItem(result.name)">{{ result.name }}</button>
</li>
</ul>
</div>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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