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