From daa6195f84f53cc484ad0730f2c5a88c6e654b15 Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Fri, 22 Dec 2017 00:01:51 +0100 Subject: Add option to run a single Karma spec file --- config/karma.config.js | 8 ++++++++ spec/javascripts/test_bundle.js | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/config/karma.config.js b/config/karma.config.js index 9f018d14b8f..609c4780ccc 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -12,8 +12,16 @@ if (webpackConfig.plugins) { plugin instanceof webpack.DefinePlugin ); }); +} else { + webpackConfig.plugins = []; } +webpackConfig.plugins.push( + new webpack.DefinePlugin({ + TEST_FILE: JSON.stringify(process.env.TEST_FILE), + }) +); + webpackConfig.devtool = 'cheap-inline-source-map'; // Karma configuration diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 6897c991066..8145f905181 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -6,6 +6,7 @@ import '~/commons'; import Vue from 'vue'; import VueResource from 'vue-resource'; +import Translate from '~/vue_shared/translate'; const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent); Vue.config.devtools = !isHeadlessChrome; @@ -24,6 +25,7 @@ Vue.config.errorHandler = function (err) { }; Vue.use(VueResource); +Vue.use(Translate); // enable test fixtures jasmine.getFixtures().fixturesPath = '/base/spec/javascripts/fixtures'; @@ -61,11 +63,20 @@ beforeEach(() => { Vue.http.interceptors = builtinVueHttpInterceptors.slice(); }); -// render all of our tests +// eslint-disable-next-line no-undef +let testFile = TEST_FILE; +if (testFile) { + console.log(`Running only ${testFile}`); + testFile = testFile.replace(/^spec\/javascripts\//, ''); + testFile = testFile.replace(/\.js$/, ''); +} + const testsContext = require.context('.', true, /_spec$/); testsContext.keys().forEach(function (path) { try { - testsContext(path); + if (!testFile || path.indexOf(testFile) > -1) { + testsContext(path); + } } catch (err) { console.error('[ERROR] Unable to load spec: ', path); describe('Test bundle', function () { -- cgit v1.2.1 From 0541f237441a8a046273ac0c15cc509064affa41 Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Mon, 15 Jan 2018 07:46:19 +0100 Subject: Add Inherit quick action Closes #38450 --- app/services/quick_actions/interpret_service.rb | 18 ++++++ changelogs/unreleased/add-inherit-command.yml | 5 ++ doc/user/project/quick_actions.md | 3 +- .../quick_actions/interpret_service_spec.rb | 65 ++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/add-inherit-command.yml diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 669c1ba0a22..b8818399f78 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -256,6 +256,24 @@ module QuickActions end end + desc 'Inherit (copy) labels and milestone from other issue' + explanation do |issue_id| + "Inherit (copy) labels and milestone from issue \"#{issue_id}\"." + end + params '#issue' + condition do + issuable.persisted? && + current_user.can?(:"update_#{issuable.to_ability_name}", issuable) + end + command :inherit do |issue_id| + issue = extract_references(issue_id, :issue).first + if issue.present? && issue.project_id == issuable.project_id + @updates[:add_label_ids] = issue.labels.map(&:id) + + @updates[:milestone_id] = issue.milestone.id if issue.milestone + end + end + desc 'Add a todo' explanation 'Adds a todo.' condition do diff --git a/changelogs/unreleased/add-inherit-command.yml b/changelogs/unreleased/add-inherit-command.yml new file mode 100644 index 00000000000..84f5bc97fbe --- /dev/null +++ b/changelogs/unreleased/add-inherit-command.yml @@ -0,0 +1,5 @@ +--- +title: Add Inherit quick action +merge_request: +author: Mateusz Bajorski +type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 442fc978284..3b6bdc8ca3d 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -40,4 +40,5 @@ do. | `/duplicate #issue` | Closes this issue and marks it as a duplicate of another issue | | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | -| `/shrug` | Append the comment with `¯\_(ツ)_/¯` | \ No newline at end of file +| `/shrug` | Append the comment with `¯\_(ツ)_/¯` | +| `/inherit #issue` | Inherit (copy) labels and milestone from other issue | diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index ae160d104f1..31f9296e3bb 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -306,6 +306,23 @@ describe QuickActions::InterpretService do end end + shared_examples 'inherit command' do + it 'fetches issue and copies labels and milestone if content contains /inherit issue_reference' do + issue_father # populate the issue + todo_label # populate this label + inreview_label # populate this label + _, updates = service.execute(content, issuable) + + expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id]) + + if issue_father.milestone + expect(updates[:milestone_id]).to eq(issue_father.milestone.id) + else + expect(updates).not_to have_key(:milestone_id) + end + end + end + shared_examples 'shrug command' do it 'appends ¯\_(ツ)_/¯ to the comment' do new_content, _ = service.execute(content, issuable) @@ -741,6 +758,54 @@ describe QuickActions::InterpretService do let(:issuable) { issue } end + context '/inherit command' do + let!(:todo_label) { create(:label, project: project, title: 'To Do') } + let!(:inreview_label) { create(:label, project: project, title: 'In Review') } + + it_behaves_like 'inherit command' do + # Without milestone assignment + let(:issue_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } + + let(:content) { "/inherit #{issue_father.to_reference}" } + let(:issuable) { issue } + end + + it_behaves_like 'inherit command' do + # With milestone assignment + let(:issue_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + + let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { '/inherit' } + let(:issuable) { issue } + end + + context 'cross project references' do + it_behaves_like 'empty command' do + let(:other_project) { create(:project, :public) } + let(:issue_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } + let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { "/inherit imaginary#1234" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:other_project) { create(:project, :private) } + let(:issue_father) { create(:issue, project: other_project) } + + let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issuable) { issue } + end + end + end + context '/duplicate command' do it_behaves_like 'duplicate command' do let(:issue_duplicate) { create(:issue, project: project) } -- cgit v1.2.1 From d691df02560f06dc87aa5812ebea31de62e0e6e6 Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Sun, 21 Jan 2018 18:16:56 +0100 Subject: Changed command name to copy_metadata and added MR support --- app/services/quick_actions/interpret_service.rb | 16 ++++---- changelogs/unreleased/add-inherit-command.yml | 2 +- doc/user/project/quick_actions.md | 2 +- .../quick_actions/interpret_service_spec.rb | 46 +++++++++++----------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index b8818399f78..d9f33ebb108 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -256,18 +256,20 @@ module QuickActions end end - desc 'Inherit (copy) labels and milestone from other issue' - explanation do |issue_id| - "Inherit (copy) labels and milestone from issue \"#{issue_id}\"." + desc 'Copy labels and milestone from other issue or merge request' + explanation do |issueable_id| + "Copy labels and milestone from issue or merge_request \"#{issueable_id}\"." end - params '#issue' + params '< #issue | !merge_request >' condition do issuable.persisted? && current_user.can?(:"update_#{issuable.to_ability_name}", issuable) end - command :inherit do |issue_id| - issue = extract_references(issue_id, :issue).first - if issue.present? && issue.project_id == issuable.project_id + command :copy_metadata do |issueable_id| + reference_type = issueable_id.include?("#") ? :issue : :merge_request + issue = extract_references(issueable_id, reference_type).first + + if issue.present? && issue.project.id == issuable.project.id @updates[:add_label_ids] = issue.labels.map(&:id) @updates[:milestone_id] = issue.milestone.id if issue.milestone diff --git a/changelogs/unreleased/add-inherit-command.yml b/changelogs/unreleased/add-inherit-command.yml index 84f5bc97fbe..d47aa8c7b36 100644 --- a/changelogs/unreleased/add-inherit-command.yml +++ b/changelogs/unreleased/add-inherit-command.yml @@ -1,5 +1,5 @@ --- title: Add Inherit quick action -merge_request: +merge_request: 16473 author: Mateusz Bajorski type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 3b6bdc8ca3d..e2e2b859d35 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -41,4 +41,4 @@ do. | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | | `/shrug` | Append the comment with `¯\_(ツ)_/¯` | -| `/inherit #issue` | Inherit (copy) labels and milestone from other issue | +| /copy_metadata < #issue | !merge_request > | copy_metadata labels and milestone from other issue or merge request | diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 31f9296e3bb..54be5020a54 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -306,17 +306,17 @@ describe QuickActions::InterpretService do end end - shared_examples 'inherit command' do - it 'fetches issue and copies labels and milestone if content contains /inherit issue_reference' do - issue_father # populate the issue + shared_examples 'copy_metadata command' do + it 'fetches issue or merge request and copies labels and milestone if content contains /copy_metadata reference' do + issueable_father # populate the issue todo_label # populate this label inreview_label # populate this label _, updates = service.execute(content, issuable) expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id]) - if issue_father.milestone - expect(updates[:milestone_id]).to eq(issue_father.milestone.id) + if issueable_father.milestone + expect(updates[:milestone_id]).to eq(issueable_father.milestone.id) else expect(updates).not_to have_key(:milestone_id) end @@ -758,49 +758,49 @@ describe QuickActions::InterpretService do let(:issuable) { issue } end - context '/inherit command' do + context '/copy_metadata command' do let!(:todo_label) { create(:label, project: project, title: 'To Do') } let!(:inreview_label) { create(:label, project: project, title: 'In Review') } - it_behaves_like 'inherit command' do - # Without milestone assignment - let(:issue_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } - - let(:content) { "/inherit #{issue_father.to_reference}" } + it_behaves_like 'empty command' do + let(:content) { '/copy_metadata' } let(:issuable) { issue } end - it_behaves_like 'inherit command' do - # With milestone assignment - let(:issue_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + it_behaves_like 'copy_metadata command' do + let(:issueable_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } - let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{issueable_father.to_reference}" } let(:issuable) { issue } end - it_behaves_like 'empty command' do - let(:content) { '/inherit' } - let(:issuable) { issue } + context 'when the parent issueable has a milestone' do + it_behaves_like 'copy_metadata command' do + let(:issueable_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + + let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:issuable) { issue } + end end context 'cross project references' do it_behaves_like 'empty command' do let(:other_project) { create(:project, :public) } - let(:issue_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } - let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issueable_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } + let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } let(:issuable) { issue } end it_behaves_like 'empty command' do - let(:content) { "/inherit imaginary#1234" } + let(:content) { "/copy_metadata imaginary#1234" } let(:issuable) { issue } end it_behaves_like 'empty command' do let(:other_project) { create(:project, :private) } - let(:issue_father) { create(:issue, project: other_project) } + let(:issueable_father) { create(:issue, project: other_project) } - let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } let(:issuable) { issue } end end -- cgit v1.2.1 From 427bfae287cb893bb02f6ae54b9e7a08ebe2a267 Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Wed, 31 Jan 2018 18:41:06 +0100 Subject: Fixed typos and improved reference checking --- app/services/quick_actions/interpret_service.rb | 18 ++++++++-------- .../unreleased/add-copy-metadata-command.yml | 5 +++++ changelogs/unreleased/add-inherit-command.yml | 5 ----- doc/user/project/quick_actions.md | 2 +- .../quick_actions/interpret_service_spec.rb | 24 +++++++++++----------- 5 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 changelogs/unreleased/add-copy-metadata-command.yml delete mode 100644 changelogs/unreleased/add-inherit-command.yml diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index d9f33ebb108..6b0d8802a4e 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -257,22 +257,22 @@ module QuickActions end desc 'Copy labels and milestone from other issue or merge request' - explanation do |issueable_id| - "Copy labels and milestone from issue or merge_request \"#{issueable_id}\"." + explanation do |issuable_id| + "Copy labels and milestone from issue or merge_request \"#{issuable_id}\"." end - params '< #issue | !merge_request >' + params '#issue | !merge_request' condition do issuable.persisted? && current_user.can?(:"update_#{issuable.to_ability_name}", issuable) end - command :copy_metadata do |issueable_id| - reference_type = issueable_id.include?("#") ? :issue : :merge_request - issue = extract_references(issueable_id, reference_type).first + command :copy_metadata do |issuable_id| + source_issuable = extract_references(issuable_id, :issue).first + source_issuable = extract_references(issuable_id, :merge_request).first if !source_issuable.present? - if issue.present? && issue.project.id == issuable.project.id - @updates[:add_label_ids] = issue.labels.map(&:id) + if source_issuable.present? && source_issuable.project.id == issuable.project.id + @updates[:add_label_ids] = source_issuable.labels.map(&:id) - @updates[:milestone_id] = issue.milestone.id if issue.milestone + @updates[:milestone_id] = source_issuable.milestone.id if source_issuable.milestone end end diff --git a/changelogs/unreleased/add-copy-metadata-command.yml b/changelogs/unreleased/add-copy-metadata-command.yml new file mode 100644 index 00000000000..3bf25ae6ce0 --- /dev/null +++ b/changelogs/unreleased/add-copy-metadata-command.yml @@ -0,0 +1,5 @@ +--- +title: Add Copy metadata quick action +merge_request: 16473 +author: Mateusz Bajorski +type: added diff --git a/changelogs/unreleased/add-inherit-command.yml b/changelogs/unreleased/add-inherit-command.yml deleted file mode 100644 index d47aa8c7b36..00000000000 --- a/changelogs/unreleased/add-inherit-command.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Inherit quick action -merge_request: 16473 -author: Mateusz Bajorski -type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index e2e2b859d35..75799caacde 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -41,4 +41,4 @@ do. | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | | `/shrug` | Append the comment with `¯\_(ツ)_/¯` | -| /copy_metadata < #issue | !merge_request > | copy_metadata labels and milestone from other issue or merge request | +| /copy_metadata < #issue | !merge_request > | Copy labels and milestone from other issue or merge request | diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 54be5020a54..5b70e434e23 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -308,15 +308,15 @@ describe QuickActions::InterpretService do shared_examples 'copy_metadata command' do it 'fetches issue or merge request and copies labels and milestone if content contains /copy_metadata reference' do - issueable_father # populate the issue + source_issuable # populate the issue todo_label # populate this label inreview_label # populate this label _, updates = service.execute(content, issuable) expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id]) - if issueable_father.milestone - expect(updates[:milestone_id]).to eq(issueable_father.milestone.id) + if source_issuable.milestone + expect(updates[:milestone_id]).to eq(source_issuable.milestone.id) else expect(updates).not_to have_key(:milestone_id) end @@ -768,17 +768,17 @@ describe QuickActions::InterpretService do end it_behaves_like 'copy_metadata command' do - let(:issueable_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } + let(:source_issuable) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } - let(:content) { "/copy_metadata #{issueable_father.to_reference}" } + let(:content) { "/copy_metadata #{source_issuable.to_reference}" } let(:issuable) { issue } end - context 'when the parent issueable has a milestone' do + context 'when the parent issuable has a milestone' do it_behaves_like 'copy_metadata command' do - let(:issueable_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + let(:source_issuable) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } - let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end end @@ -786,8 +786,8 @@ describe QuickActions::InterpretService do context 'cross project references' do it_behaves_like 'empty command' do let(:other_project) { create(:project, :public) } - let(:issueable_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } - let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:source_issuable) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } + let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end @@ -798,9 +798,9 @@ describe QuickActions::InterpretService do it_behaves_like 'empty command' do let(:other_project) { create(:project, :private) } - let(:issueable_father) { create(:issue, project: other_project) } + let(:source_issuable) { create(:issue, project: other_project) } - let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end end -- cgit v1.2.1 From da15007f2314e367ccd7e2bed13629864a5e4307 Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Thu, 1 Feb 2018 19:38:10 +0100 Subject: Fixed inconsistent descriptions and refactored reference checking --- app/services/quick_actions/interpret_service.rb | 4 ++-- doc/user/project/quick_actions.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 6b0d8802a4e..88ca26be1d5 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -258,7 +258,7 @@ module QuickActions desc 'Copy labels and milestone from other issue or merge request' explanation do |issuable_id| - "Copy labels and milestone from issue or merge_request \"#{issuable_id}\"." + "Copy labels and milestone from issue or merge_request #{issuable_id}." end params '#issue | !merge_request' condition do @@ -267,7 +267,7 @@ module QuickActions end command :copy_metadata do |issuable_id| source_issuable = extract_references(issuable_id, :issue).first - source_issuable = extract_references(issuable_id, :merge_request).first if !source_issuable.present? + source_issuable ||= extract_references(issuable_id, :merge_request).first if source_issuable.present? && source_issuable.project.id == issuable.project.id @updates[:add_label_ids] = source_issuable.labels.map(&:id) diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 75799caacde..3e3b699edc4 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -41,4 +41,4 @@ do. | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | | `/shrug` | Append the comment with `¯\_(ツ)_/¯` | -| /copy_metadata < #issue | !merge_request > | Copy labels and milestone from other issue or merge request | +| /copy_metadata #issue | !merge_request | Copy labels and milestone from other issue or merge request | -- cgit v1.2.1 From 27e8d38cea92d165a2e8400b25df23f408b4dca0 Mon Sep 17 00:00:00 2001 From: haseeb Date: Tue, 6 Feb 2018 19:03:18 +0530 Subject: embedded snippets support --- app/assets/images/ext_snippet_icons/doc_code.png | Bin 0 -> 1058 bytes app/assets/images/ext_snippet_icons/doc_text.png | Bin 0 -> 996 bytes app/assets/images/ext_snippet_icons/download.png | Bin 0 -> 1098 bytes app/assets/images/ext_snippet_icons/logo.png | Bin 0 -> 736 bytes app/assets/javascripts/snippet/snippet_embed.js | 18 ++ app/assets/stylesheets/application.scss | 6 +- app/assets/stylesheets/highlight/embedded.scss | 3 + app/assets/stylesheets/highlight/white.scss | 291 +-------------------- app/assets/stylesheets/highlight/white_base.scss | 290 ++++++++++++++++++++ app/assets/stylesheets/snippets.scss | 132 ++++++++++ app/controllers/concerns/snippets_actions.rb | 4 + app/controllers/projects/snippets_controller.rb | 3 + app/controllers/snippets_controller.rb | 4 + app/helpers/icons_helper.rb | 4 + app/helpers/snippets_helper.rb | 35 +++ app/views/projects/blob/_viewer.html.haml | 3 + .../blob/viewers/_highlight_embed.html.haml | 7 + app/views/shared/snippets/_embed.html.haml | 23 ++ app/views/shared/snippets/_header.html.haml | 21 ++ app/views/shared/snippets/show.js.haml | 2 + config/application.rb | 1 + config/webpack.config.js | 6 + spec/helpers/icons_helper_spec.rb | 7 + spec/helpers/snippets_helper_spec.rb | 33 +++ 24 files changed, 602 insertions(+), 291 deletions(-) create mode 100644 app/assets/images/ext_snippet_icons/doc_code.png create mode 100644 app/assets/images/ext_snippet_icons/doc_text.png create mode 100644 app/assets/images/ext_snippet_icons/download.png create mode 100644 app/assets/images/ext_snippet_icons/logo.png create mode 100644 app/assets/javascripts/snippet/snippet_embed.js create mode 100644 app/assets/stylesheets/highlight/embedded.scss create mode 100644 app/assets/stylesheets/highlight/white_base.scss create mode 100644 app/assets/stylesheets/snippets.scss create mode 100644 app/views/projects/blob/viewers/_highlight_embed.html.haml create mode 100644 app/views/shared/snippets/_embed.html.haml create mode 100644 app/views/shared/snippets/show.js.haml create mode 100644 spec/helpers/snippets_helper_spec.rb diff --git a/app/assets/images/ext_snippet_icons/doc_code.png b/app/assets/images/ext_snippet_icons/doc_code.png new file mode 100644 index 00000000000..b1a589a848f Binary files /dev/null and b/app/assets/images/ext_snippet_icons/doc_code.png differ diff --git a/app/assets/images/ext_snippet_icons/doc_text.png b/app/assets/images/ext_snippet_icons/doc_text.png new file mode 100644 index 00000000000..aee32cc3620 Binary files /dev/null and b/app/assets/images/ext_snippet_icons/doc_text.png differ diff --git a/app/assets/images/ext_snippet_icons/download.png b/app/assets/images/ext_snippet_icons/download.png new file mode 100644 index 00000000000..045fb0f2e95 Binary files /dev/null and b/app/assets/images/ext_snippet_icons/download.png differ diff --git a/app/assets/images/ext_snippet_icons/logo.png b/app/assets/images/ext_snippet_icons/logo.png new file mode 100644 index 00000000000..12f8315cad8 Binary files /dev/null and b/app/assets/images/ext_snippet_icons/logo.png differ diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js new file mode 100644 index 00000000000..c032414552e --- /dev/null +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -0,0 +1,18 @@ +(() => { + $(() => { + const { protocol, host, pathname } = location; + + $('#share-btn').click((event) => { + event.preventDefault(); + $('#snippet-url-area').val(`${protocol}//${host + pathname}`); + $('#embed-action').html('Share'); + }); + + $('#embed-btn').click((event) => { + event.preventDefault(); + const scriptTag = ``; + $('#snippet-url-area').val(scriptTag); + $('#embed-action').html('Embed'); + }); + }); +}).call(window); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 0665622fe4a..f2950308019 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -37,7 +37,11 @@ /* * Code highlight */ -@import "highlight/**/*"; +@import "highlight/dark"; +@import "highlight/monokai"; +@import "highlight/solarized_dark"; +@import "highlight/solarized_light"; +@import "highlight/white"; /* * Styles for JS behaviors. diff --git a/app/assets/stylesheets/highlight/embedded.scss b/app/assets/stylesheets/highlight/embedded.scss new file mode 100644 index 00000000000..44c8a1d39ec --- /dev/null +++ b/app/assets/stylesheets/highlight/embedded.scss @@ -0,0 +1,3 @@ +.code { + @import "white_base"; +} diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index c3d8f0c61a2..355c8d223f7 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -1,292 +1,3 @@ -/* https://github.com/aahan/pygments-github-style */ - -/* -* White Syntax Colors -*/ -$white-code-color: $gl-text-color; -$white-highlight: #fafe3d; -$white-pre-hll-bg: #f8eec7; -$white-hll-bg: #f8f8f8; -$white-over-bg: #ded7fc; -$white-expanded-border: #e0e0e0; -$white-expanded-bg: #f7f7f7; -$white-c: #998; -$white-err: #a61717; -$white-err-bg: #e3d2d2; -$white-cm: #998; -$white-cp: #999; -$white-c1: #998; -$white-cs: #999; -$white-gd: $black; -$white-gd-bg: #fdd; -$white-gd-x: $black; -$white-gd-x-bg: #faa; -$white-gr: #a00; -$white-gh: #999; -$white-gi: $black; -$white-gi-bg: #dfd; -$white-gi-x: $black; -$white-gi-x-bg: #afa; -$white-go: #888; -$white-gp: #555; -$white-gu: #800080; -$white-gt: #a00; -$white-kt: #458; -$white-m: #099; -$white-s: #d14; -$white-n: #333; -$white-na: teal; -$white-nb: #0086b3; -$white-nc: #458; -$white-no: teal; -$white-ni: purple; -$white-ne: #900; -$white-nf: #900; -$white-nn: #555; -$white-nt: navy; -$white-nv: teal; -$white-w: #bbb; -$white-mf: #099; -$white-mh: #099; -$white-mi: #099; -$white-mo: #099; -$white-sb: #d14; -$white-sc: #d14; -$white-sd: #d14; -$white-s2: #d14; -$white-se: #d14; -$white-sh: #d14; -$white-si: #d14; -$white-sx: #d14; -$white-sr: #009926; -$white-s1: #d14; -$white-ss: #990073; -$white-bp: #999; -$white-vc: teal; -$white-vg: teal; -$white-vi: teal; -$white-il: #099; -$white-gc-color: #999; -$white-gc-bg: #eaf2f5; - - -@mixin matchLine { - color: $black-transparent; - background-color: $gray-light; -} - .code.white { - // Line numbers - .line-numbers, - .diff-line-num { - background-color: $gray-light; - } - - .diff-line-num, - .diff-line-num a { - color: $black-transparent; - } - - // Code itself - pre.code, - .diff-line-num { - border-color: $white-normal; - } - - &, - pre.code, - .line_holder .line_content { - background-color: $white-light; - color: $white-code-color; - } - - // Diff line - .line_holder { - - &.match .line_content { - @include matchLine; - } - - .diff-line-num { - &.old { - background-color: $line-number-old; - border-color: $line-removed-dark; - - a { - color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); - } - } - - &.new { - background-color: $line-number-new; - border-color: $line-added-dark; - - a { - color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); - } - } - - &.is-over, - &.hll:not(.empty-cell).is-over { - background-color: $white-over-bg; - border-color: darken($white-over-bg, 5%); - - a { - color: darken($white-over-bg, 15%); - } - } - - &.hll:not(.empty-cell) { - background-color: $line-number-select; - border-color: $line-select-yellow-dark; - } - } - - &:not(.diff-expanded) + .diff-expanded, - &.diff-expanded + .line_holder:not(.diff-expanded) { - > .diff-line-num, - > .line_content { - border-top: 1px solid $white-expanded-border; - } - } - - &.diff-expanded { - > .diff-line-num, - > .line_content { - background: $white-expanded-bg; - border-color: $white-expanded-bg; - } - } - - .line_content { - &.old { - background-color: $line-removed; - - &::before { - color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); - } - - span.idiff { - background-color: $line-removed-dark; - } - } - - &.new { - background-color: $line-added; - - &::before { - color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); - } - - span.idiff { - background-color: $line-added-dark; - } - } - - &.match { - @include matchLine; - } - - &.hll:not(.empty-cell) { - background-color: $line-select-yellow; - } - } - } - - // highlight line via anchor - pre .hll { - background-color: $white-pre-hll-bg !important; - } - - // Search result highlight - span.highlight_word { - background-color: $white-highlight !important; - } - - // Links to URLs, emails, or dependencies - .line a { - color: $white-nb; - } - - .hll { background-color: $white-hll-bg; } - .c { color: $white-c; font-style: italic; } - .err { color: $white-err; background-color: $white-err-bg; } - .k { font-weight: $gl-font-weight-bold; } - .o { font-weight: $gl-font-weight-bold; } - .cm { color: $white-cm; font-style: italic; } - .cp { color: $white-cp; font-weight: $gl-font-weight-bold; } - .c1 { color: $white-c1; font-style: italic; } - .cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; } - - .gd { - color: $white-gd; - background-color: $white-gd-bg; - - .x { - color: $white-gd-x; - background-color: $white-gd-x-bg; - } - } - - .ge { font-style: italic; } - .gr { color: $white-gr; } - .gh { color: $white-gh; } - - .gi { - color: $white-gi; - background-color: $white-gi-bg; - - .x { - color: $white-gi-x; - background-color: $white-gi-x-bg; - } - } - - .go { color: $white-go; } - .gp { color: $white-gp; } - .gs { font-weight: $gl-font-weight-bold; } - .gu { color: $white-gu; font-weight: $gl-font-weight-bold; } - .gt { color: $white-gt; } - .kc { font-weight: $gl-font-weight-bold; } - .kd { font-weight: $gl-font-weight-bold; } - .kn { font-weight: $gl-font-weight-bold; } - .kp { font-weight: $gl-font-weight-bold; } - .kr { font-weight: $gl-font-weight-bold; } - .kt { color: $white-kt; font-weight: $gl-font-weight-bold; } - .m { color: $white-m; } - .s { color: $white-s; } - .n { color: $white-n; } - .na { color: $white-na; } - .nb { color: $white-nb; } - .nc { color: $white-nc; font-weight: $gl-font-weight-bold; } - .no { color: $white-no; } - .ni { color: $white-ni; } - .ne { color: $white-ne; font-weight: $gl-font-weight-bold; } - .nf { color: $white-nf; font-weight: $gl-font-weight-bold; } - .nn { color: $white-nn; } - .nt { color: $white-nt; } - .nv { color: $white-nv; } - .ow { font-weight: $gl-font-weight-bold; } - .w { color: $white-w; } - .mf { color: $white-mf; } - .mh { color: $white-mh; } - .mi { color: $white-mi; } - .mo { color: $white-mo; } - .sb { color: $white-sb; } - .sc { color: $white-sc; } - .sd { color: $white-sd; } - .s2 { color: $white-s2; } - .se { color: $white-se; } - .sh { color: $white-sh; } - .si { color: $white-si; } - .sx { color: $white-sx; } - .sr { color: $white-sr; } - .s1 { color: $white-s1; } - .ss { color: $white-ss; } - .bp { color: $white-bp; } - .vc { color: $white-vc; } - .vg { color: $white-vg; } - .vi { color: $white-vi; } - .il { color: $white-il; } - .gc { color: $white-gc-color; background-color: $white-gc-bg; } + @import "white_base"; } diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss new file mode 100644 index 00000000000..8cc5252648d --- /dev/null +++ b/app/assets/stylesheets/highlight/white_base.scss @@ -0,0 +1,290 @@ +/* https://github.com/aahan/pygments-github-style */ + +/* +* White Syntax Colors +*/ +$white-code-color: $gl-text-color; +$white-highlight: #fafe3d; +$white-pre-hll-bg: #f8eec7; +$white-hll-bg: #f8f8f8; +$white-over-bg: #ded7fc; +$white-expanded-border: #e0e0e0; +$white-expanded-bg: #f7f7f7; +$white-c: #998; +$white-err: #a61717; +$white-err-bg: #e3d2d2; +$white-cm: #998; +$white-cp: #999; +$white-c1: #998; +$white-cs: #999; +$white-gd: $black; +$white-gd-bg: #fdd; +$white-gd-x: $black; +$white-gd-x-bg: #faa; +$white-gr: #a00; +$white-gh: #999; +$white-gi: $black; +$white-gi-bg: #dfd; +$white-gi-x: $black; +$white-gi-x-bg: #afa; +$white-go: #888; +$white-gp: #555; +$white-gu: #800080; +$white-gt: #a00; +$white-kt: #458; +$white-m: #099; +$white-s: #d14; +$white-n: #333; +$white-na: teal; +$white-nb: #0086b3; +$white-nc: #458; +$white-no: teal; +$white-ni: purple; +$white-ne: #900; +$white-nf: #900; +$white-nn: #555; +$white-nt: navy; +$white-nv: teal; +$white-w: #bbb; +$white-mf: #099; +$white-mh: #099; +$white-mi: #099; +$white-mo: #099; +$white-sb: #d14; +$white-sc: #d14; +$white-sd: #d14; +$white-s2: #d14; +$white-se: #d14; +$white-sh: #d14; +$white-si: #d14; +$white-sx: #d14; +$white-sr: #009926; +$white-s1: #d14; +$white-ss: #990073; +$white-bp: #999; +$white-vc: teal; +$white-vg: teal; +$white-vi: teal; +$white-il: #099; +$white-gc-color: #999; +$white-gc-bg: #eaf2f5; + + +@mixin matchLine { + color: $black-transparent; + background-color: $gray-light; +} + + // Line numbers +.line-numbers, +.diff-line-num { + background-color: $gray-light; +} + +.diff-line-num, +.diff-line-num a { + color: $black-transparent; +} + +// Code itself +pre.code, +.diff-line-num { + border-color: $white-normal; +} + +&, +pre.code, +.line_holder .line_content { + background-color: $white-light; + color: $white-code-color; +} + +// Diff line +.line_holder { + + &.match .line_content { + @include matchLine; + } + + .diff-line-num { + &.old { + background-color: $line-number-old; + border-color: $line-removed-dark; + + a { + color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); + } + } + + &.new { + background-color: $line-number-new; + border-color: $line-added-dark; + + a { + color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); + } + } + + &.is-over, + &.hll:not(.empty-cell).is-over { + background-color: $white-over-bg; + border-color: darken($white-over-bg, 5%); + + a { + color: darken($white-over-bg, 15%); + } + } + + &.hll:not(.empty-cell) { + background-color: $line-number-select; + border-color: $line-select-yellow-dark; + } + } + + &:not(.diff-expanded) + .diff-expanded, + &.diff-expanded + .line_holder:not(.diff-expanded) { + > .diff-line-num, + > .line_content { + border-top: 1px solid $white-expanded-border; + } + } + + &.diff-expanded { + > .diff-line-num, + > .line_content { + background: $white-expanded-bg; + border-color: $white-expanded-bg; + } + } + + .line_content { + &.old { + background-color: $line-removed; + + &::before { + color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); + } + + span.idiff { + background-color: $line-removed-dark; + } + } + + &.new { + background-color: $line-added; + + &::before { + color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); + } + + span.idiff { + background-color: $line-added-dark; + } + } + + &.match { + @include matchLine; + } + + &.hll:not(.empty-cell) { + background-color: $line-select-yellow; + } + } +} + +// highlight line via anchor +pre .hll { + background-color: $white-pre-hll-bg !important; +} + + // Search result highlight +span.highlight_word { + background-color: $white-highlight !important; +} + + // Links to URLs, emails, or dependencies +.line a { + color: $white-nb; +} + +.hll { background-color: $white-hll-bg; } +.c { color: $white-c; font-style: italic; } +.err { color: $white-err; background-color: $white-err-bg; } +.k { font-weight: $gl-font-weight-bold; } +.o { font-weight: $gl-font-weight-bold; } +.cm { color: $white-cm; font-style: italic; } +.cp { color: $white-cp; font-weight: $gl-font-weight-bold; } +.c1 { color: $white-c1; font-style: italic; } +.cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; } + +.gd { + color: $white-gd; + background-color: $white-gd-bg; + + .x { + color: $white-gd-x; + background-color: $white-gd-x-bg; + } +} + +.ge { font-style: italic; } +.gr { color: $white-gr; } +.gh { color: $white-gh; } + +.gi { + color: $white-gi; + background-color: $white-gi-bg; + + .x { + color: $white-gi-x; + background-color: $white-gi-x-bg; + } +} + +.go { color: $white-go; } +.gp { color: $white-gp; } +.gs { font-weight: $gl-font-weight-bold; } +.gu { color: $white-gu; font-weight: $gl-font-weight-bold; } +.gt { color: $white-gt; } +.kc { font-weight: $gl-font-weight-bold; } +.kd { font-weight: $gl-font-weight-bold; } +.kn { font-weight: $gl-font-weight-bold; } +.kp { font-weight: $gl-font-weight-bold; } +.kr { font-weight: $gl-font-weight-bold; } +.kt { color: $white-kt; font-weight: $gl-font-weight-bold; } +.m { color: $white-m; } +.s { color: $white-s; } +.n { color: $white-n; } +.na { color: $white-na; } +.nb { color: $white-nb; } +.nc { color: $white-nc; font-weight: $gl-font-weight-bold; } +.no { color: $white-no; } +.ni { color: $white-ni; } +.ne { color: $white-ne; font-weight: $gl-font-weight-bold; } +.nf { color: $white-nf; font-weight: $gl-font-weight-bold; } +.nn { color: $white-nn; } +.nt { color: $white-nt; } +.nv { color: $white-nv; } +.ow { font-weight: $gl-font-weight-bold; } +.w { color: $white-w; } +.mf { color: $white-mf; } +.mh { color: $white-mh; } +.mi { color: $white-mi; } +.mo { color: $white-mo; } +.sb { color: $white-sb; } +.sc { color: $white-sc; } +.sd { color: $white-sd; } +.s2 { color: $white-s2; } +.se { color: $white-se; } +.sh { color: $white-sh; } +.si { color: $white-si; } +.sx { color: $white-sx; } +.sr { color: $white-sr; } +.s1 { color: $white-s1; } +.ss { color: $white-ss; } +.bp { color: $white-bp; } +.vc { color: $white-vc; } +.vg { color: $white-vg; } +.vi { color: $white-vi; } +.il { color: $white-il; } +.gc { color: $white-gc-color; background-color: $white-gc-bg; } diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss new file mode 100644 index 00000000000..4de295629f4 --- /dev/null +++ b/app/assets/stylesheets/snippets.scss @@ -0,0 +1,132 @@ +@import "framework/variables"; + +.gitlab-embed-snippets { + @import "highlight/embedded"; + @import "framework/images"; + + $border-style: 1px solid $border-color; + + font-family: $regular_font; + font-size: $gl-font-size; + line-height: $code_line_height; + color: $gl-text-color; + margin: 20px; + font-weight: 200; + + .blob-viewer { + background-color: $white-light; + text-align: left; + } + + .file-content.code { + border: $border-style; + border-radius: 0 0 4px 4px; + display: flex; + box-shadow: none; + margin: 0; + padding: 0; + table-layout: fixed; + + .blob-content { + overflow-x: auto; + + pre { + padding: 10px; + border: 0; + border-radius: 0; + font-family: $monospace_font; + font-size: $code_font_size; + line-height: $code_line_height; + margin: 0; + overflow: auto; + overflow-y: hidden; + white-space: pre; + word-wrap: normal; + border-left: $border-style; + } + } + + .line-numbers { + padding: 10px; + text-align: right; + float: left; + + .diff-line-num { + font-family: $monospace_font; + display: block; + font-size: $code_font_size; + min-height: $code_line_height; + white-space: nowrap; + color: $black-transparent; + min-width: 30px; + } + + .diff-line-num:hover { + color: $almost-black; + cursor: pointer; + } + } + } + + .file-title-flex-parent { + display: flex; + align-items: center; + justify-content: space-between; + background-color: $gray-light; + border: $border-style; + border-bottom: 0; + padding: $gl-padding-top $gl-padding; + margin: 0; + border-radius: $border-radius-default $border-radius-default 0 0; + + .file-header-content { + .file-title-name { + font-weight: $gl-font-weight-bold; + } + + .gitlab-logo { + display: inline-block; + padding-left: 5px; + text-decoration: none; + color: $gl-text-color-secondary; + + .logo-text { + background: image_url('ext_snippet_icons/logo.png') no-repeat left center; + background-size: 18px; + font-weight: $gl-font-weight-normal; + padding-left: 24px; + } + } + } + + img { + display: inline-block; + vertical-align: middle; + } + } + + .btn-group { + a.btn { + background-color: $white-light; + text-decoration: none; + padding: 7px 9px; + border: $border-style; + border-right: 0; + + &:hover { + background-color: $white-normal; + border-color: $border-white-normal; + text-decoration: none; + } + + &:first-child { + border-radius: 3px 0 0 3px; + } + + &:last-child { + border-radius: 0 3px 3px 0; + border-right: $border-style; + } + } + } +} diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb index 9095cc7f783..120614739aa 100644 --- a/app/controllers/concerns/snippets_actions.rb +++ b/app/controllers/concerns/snippets_actions.rb @@ -17,6 +17,10 @@ module SnippetsActions end # rubocop:enable Gitlab/ModuleWithInstanceVariables + def js_request? + request.format.js? + end + private def convert_line_endings(content) diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 7c19aa7bb23..208a1d19862 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -5,6 +5,8 @@ class Projects::SnippetsController < Projects::ApplicationController include SnippetsActions include RendersBlob + skip_before_action :verify_authenticity_token, only: [:show], if: :js_request? + before_action :check_snippets_available! before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam] @@ -71,6 +73,7 @@ class Projects::SnippetsController < Projects::ApplicationController format.json do render_blob_json(blob) end + format.js { render 'shared/snippets/show'} end end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index be2d3f638ff..3d51520ddf4 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -6,6 +6,8 @@ class SnippetsController < ApplicationController include RendersBlob include PreviewMarkdown + skip_before_action :verify_authenticity_token, only: [:show], if: :js_request? + before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] # Allow read snippet @@ -77,6 +79,8 @@ class SnippetsController < ApplicationController format.json do render_blob_json(blob) end + + format.js { render 'shared/snippets/show' } end end diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index c5522ff7a69..67001c55e88 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -43,6 +43,10 @@ module IconsHelper content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{sprite_icon_path}##{icon_name}" } ), class: css_classes.empty? ? nil : css_classes) end + def external_snippet_icon(name) + content_tag(:img, "", src: "#{image_url('/assets/ext_snippet_icons')}/#{name}.png", width: '16px', height: '16px') + end + def audit_icon(names, options = {}) case names when "standard" diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 00e7e4230b9..830db46d67f 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -101,4 +101,39 @@ module SnippetsHelper # Return snippet with chunk array { snippet_object: snippet, snippet_chunks: snippet_chunks } end + + def snippet_embed + "" + end + + def embedded_snippet_raw_button + blob = @snippet.blob + return if blob.empty? || blob.raw_binary? || blob.stored_externally? + + snippet_raw_url = if @snippet.is_a?(PersonalSnippet) + raw_snippet_url(@snippet) + else + raw_project_snippet_url(@snippet.project, @snippet) + end + + link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw', data: { container: 'body' } + end + + def embedded_snippet_download_button + download_url = if @snippet.is_a?(PersonalSnippet) + raw_snippet_url(@snippet, inline: false) + else + raw_project_snippet_url(@snippet.project, @snippet, inline: false) + end + + link_to external_snippet_icon('download'), download_url, class: 'btn', target: '_blank', title: 'Download', data: { container: 'body' }, rel: 'noopener noreferrer' + end + + def public_snippet? + if @snippet.project_id? + can?(nil, :read_project_snippet, @snippet) + else + can?(nil, :read_personal_snippet, @snippet) + end + end end diff --git a/app/views/projects/blob/_viewer.html.haml b/app/views/projects/blob/_viewer.html.haml index cc85e5de40f..fc62c1f06eb 100644 --- a/app/views/projects/blob/_viewer.html.haml +++ b/app/views/projects/blob/_viewer.html.haml @@ -1,6 +1,7 @@ - hidden = local_assigns.fetch(:hidden, false) - render_error = viewer.render_error - load_async = local_assigns.fetch(:load_async, viewer.load_async? && render_error.nil?) +- external_embed = local_assigns.fetch(:external_embed, false) - viewer_url = local_assigns.fetch(:viewer_url) { url_for(params.merge(viewer: viewer.type, format: :json)) } if load_async .blob-viewer{ data: { type: viewer.type, url: viewer_url }, class: ('hidden' if hidden) } @@ -8,6 +9,8 @@ = render 'projects/blob/render_error', viewer: viewer - elsif load_async = render viewer.loading_partial_path, viewer: viewer + - elsif external_embed + = render 'projects/blob/viewers/highlight_embed', blob: viewer.blob - else - viewer.prepare! diff --git a/app/views/projects/blob/viewers/_highlight_embed.html.haml b/app/views/projects/blob/viewers/_highlight_embed.html.haml new file mode 100644 index 00000000000..9bd4ef6ad0b --- /dev/null +++ b/app/views/projects/blob/viewers/_highlight_embed.html.haml @@ -0,0 +1,7 @@ +.file-content.code.js-syntax-highlight + .line-numbers + - if blob.data.present? + - blob.data.each_line.each_with_index do |_, index| + %span.diff-line-num= index + 1 + .blob-content{ data: { blob_id: blob.id } } + = highlight(blob.path, blob.data, repository: nil, plain: blob.no_highlighting?) diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml new file mode 100644 index 00000000000..9a5bf4c9f04 --- /dev/null +++ b/app/views/shared/snippets/_embed.html.haml @@ -0,0 +1,23 @@ +- blob = @snippet.blob +.gitlab-embed-snippets + .js-file-title.file-title-flex-parent + .file-header-content + = external_snippet_icon('doc_text') + + %strong.file-title-name + = blob.name + + %small + = number_to_human_size(blob.raw_size) + %a.gitlab-logo{ href: url_for(only_path: false, overwrite_params: nil) } + on   + %span.logo-text + GitLab + + .file-actions.hidden-xs + .btn-group{ role: "group" }< + = embedded_snippet_raw_button + + = embedded_snippet_download_button + %article.file-holder.snippet-file-content + = render 'projects/blob/viewer', viewer: @snippet.blob.simple_viewer, load_async: false, external_embed: true diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 12df79a28c7..de14356b82f 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,3 +1,6 @@ +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('snippet_embed') + .detail-page-header .detail-page-header-body .snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } } @@ -27,3 +30,21 @@ = markdown_field(@snippet, :description) %textarea.hidden.js-task-list-field = @snippet.description + - if public_snippet? + .pull-right + .input-group + .input-group-btn + %button.btn.btn-default.dropdown-toggle{ 'data-toggle': 'dropdown' } + %span#embed-action Embed + = sprite_icon('angle-down', size: 16) + .dropdown-menu + %ul + %li + %a#embed-btn{ href: "#" }Embed + %li + %a#share-btn{ href: "#" }Share + %input#snippet-url-area.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } + .input-group-btn + %a#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } + = sprite_icon('duplicate', size: 16) + .clearfix diff --git a/app/views/shared/snippets/show.js.haml b/app/views/shared/snippets/show.js.haml new file mode 100644 index 00000000000..a9af732bbb5 --- /dev/null +++ b/app/views/shared/snippets/show.js.haml @@ -0,0 +1,2 @@ +document.write('#{escape_javascript(stylesheet_link_tag "#{stylesheet_url 'snippets'}")}'); +document.write('#{escape_javascript(render 'shared/snippets/embed')}'); diff --git a/config/application.rb b/config/application.rb index 918bd4d57cf..f7f0563519a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -114,6 +114,7 @@ module Gitlab config.assets.precompile << "performance_bar.css" config.assets.precompile << "lib/ace.js" config.assets.precompile << "test.css" + config.assets.precompile << "snippets.css" config.assets.precompile << "locale/**/app.js" # Version of your assets, change this if you want to expire all your assets diff --git a/config/webpack.config.js b/config/webpack.config.js index b01cfd6595e..de55350d804 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -57,6 +57,12 @@ function generateEntries() { protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', + ide: './ide/index.js', + sidebar: './sidebar/sidebar_bundle.js', + schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js', + schedules_index: './pipeline_schedules/pipeline_schedules_index_bundle.js', + snippet: './snippet/snippet_bundle.js', + snippet_embed: './snippet/snippet_embed.js', sketch_viewer: './blob/sketch_viewer.js', stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb index 2f23ed55d99..d87c81a28d1 100644 --- a/spec/helpers/icons_helper_spec.rb +++ b/spec/helpers/icons_helper_spec.rb @@ -162,4 +162,11 @@ describe IconsHelper do expect(file_type_icon_class('file', 0, 'CHANGELOG')).to eq 'file-text-o' end end + + describe '#external_snippet_icon' do + it 'returns external snippet icon' do + expect(external_snippet_icon('download').to_s) + .to eq("") + end + end end diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb new file mode 100644 index 00000000000..d432875b5ab --- /dev/null +++ b/spec/helpers/snippets_helper_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe SnippetsHelper do + include IconsHelper + + describe '#embedded_snippet_raw_button' do + it 'gives view raw button of embedded snippets for project snippets' do + @snippet = create(:project_snippet, :public) + + expect(embedded_snippet_raw_button.to_s).to eq("#{external_snippet_icon('doc_code')}") + end + + it 'gives view raw button of embedded snippets for personal snippets' do + @snippet = create(:personal_snippet, :public) + + expect(embedded_snippet_raw_button.to_s).to eq("#{external_snippet_icon('doc_code')}") + end + end + + describe '#embedded_snippet_download_button' do + it 'gives download button of embedded snippets for project snippets' do + @snippet = create(:project_snippet, :public) + + expect(embedded_snippet_download_button.to_s).to eq("#{external_snippet_icon('download')}") + end + + it 'gives download button of embedded snippets for personal snippets' do + @snippet = create(:personal_snippet, :public) + + expect(embedded_snippet_download_button.to_s).to eq("#{external_snippet_icon('download')}") + end + end +end -- cgit v1.2.1 From 4ee2f74ba05562b629f9cf5cb476c51e9e80e7be Mon Sep 17 00:00:00 2001 From: haseeb Date: Fri, 16 Feb 2018 15:20:12 +0530 Subject: embedd button related fixes --- app/assets/stylesheets/framework/snippets.scss | 4 ++++ app/views/shared/snippets/_header.html.haml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 30c15c231d5..c2d06b59b7f 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -46,3 +46,7 @@ .snippet-scope-menu .btn-new { margin-top: 15px; } + +.snippet-embed-input { + height: 35px; +} diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index de14356b82f..30ad8932e84 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -40,10 +40,10 @@ .dropdown-menu %ul %li - %a#embed-btn{ href: "#" }Embed + %a#embed-btn{ href: "#" } Embed %li - %a#share-btn{ href: "#" }Share - %input#snippet-url-area.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } + %a#share-btn{ href: "#" } Share + %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn %a#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } = sprite_icon('duplicate', size: 16) -- cgit v1.2.1 From bab24c2fcd54622ed09ffd076e38560b42510773 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 21 Feb 2018 19:39:33 +0530 Subject: embedded snippet title changes --- app/assets/images/ext_snippet_icons/doc_code.png | Bin 1058 -> 1592 bytes app/assets/images/ext_snippet_icons/doc_text.png | Bin 996 -> 1452 bytes app/assets/images/ext_snippet_icons/download.png | Bin 1098 -> 1671 bytes app/assets/stylesheets/snippets.scss | 9 +++++++++ app/views/shared/snippets/_embed.html.haml | 3 ++- 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/images/ext_snippet_icons/doc_code.png b/app/assets/images/ext_snippet_icons/doc_code.png index b1a589a848f..93a9b372f32 100644 Binary files a/app/assets/images/ext_snippet_icons/doc_code.png and b/app/assets/images/ext_snippet_icons/doc_code.png differ diff --git a/app/assets/images/ext_snippet_icons/doc_text.png b/app/assets/images/ext_snippet_icons/doc_text.png index aee32cc3620..f2357c17c90 100644 Binary files a/app/assets/images/ext_snippet_icons/doc_text.png and b/app/assets/images/ext_snippet_icons/doc_text.png differ diff --git a/app/assets/images/ext_snippet_icons/download.png b/app/assets/images/ext_snippet_icons/download.png index 045fb0f2e95..156c788509b 100644 Binary files a/app/assets/images/ext_snippet_icons/download.png and b/app/assets/images/ext_snippet_icons/download.png differ diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index 4de295629f4..b88a8158b35 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -84,6 +84,15 @@ font-weight: $gl-font-weight-bold; } + .gitlab-embedded-snippets-title { + text-decoration: none; + color: $gl-text-color; + + &:hover{ + text-decoration: underline; + } + } + .gitlab-logo { display: inline-block; padding-left: 5px; diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml index 9a5bf4c9f04..aacc7825534 100644 --- a/app/views/shared/snippets/_embed.html.haml +++ b/app/views/shared/snippets/_embed.html.haml @@ -5,7 +5,8 @@ = external_snippet_icon('doc_text') %strong.file-title-name - = blob.name + %a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) } + = blob.name %small = number_to_human_size(blob.raw_size) -- cgit v1.2.1 From cfdbfcc621a7e04c8604bd2d11c1e8a3c764f8c7 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 21 Feb 2018 22:34:45 +0530 Subject: fix for chrome issue --- app/views/shared/snippets/_header.html.haml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 30ad8932e84..9d55afba591 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -31,18 +31,17 @@ %textarea.hidden.js-task-list-field = @snippet.description - if public_snippet? - .pull-right + .embed-snippet.pull-right.col-md-6 .input-group .input-group-btn - %button.btn.btn-default.dropdown-toggle{ 'data-toggle': 'dropdown' } + %a.btn.btn-default.embed-toggle.dropdown-toggle{ 'data-toggle': 'dropdown' } %span#embed-action Embed - = sprite_icon('angle-down', size: 16) - .dropdown-menu - %ul - %li - %a#embed-btn{ href: "#" } Embed - %li - %a#share-btn{ href: "#" } Share + = sprite_icon('angle-down', size: 12) + %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right + %li + %a#embed-btn.btn.btn-transparent{ href: "#" } Embed + %li + %a#share-btn.btn.btn-transparent{ href: "#" } Share %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn %a#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } -- cgit v1.2.1 From 6520e2c78ff87f68029a7d71c616073ec640f130 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 21 Feb 2018 22:35:10 +0530 Subject: rightside border fix for dropdown --- app/assets/stylesheets/framework/snippets.scss | 18 ++++++++++++++++++ app/assets/stylesheets/snippets.scss | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index c2d06b59b7f..69828532bef 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -50,3 +50,21 @@ .snippet-embed-input { height: 35px; } + +.embed-snippet { + padding-right: 0; + + a.btn { + width: 100%; + } + + .form-control { + cursor: auto; + width: 101%; + border-left: 0; + } + + .embed-toggle { + height: 35px; + } +} diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index b88a8158b35..7d271d25fd9 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -87,8 +87,8 @@ .gitlab-embedded-snippets-title { text-decoration: none; color: $gl-text-color; - - &:hover{ + + &:hover { text-decoration: underline; } } -- cgit v1.2.1 From 20cebb8f2804273e0c9cefe23ae78ac5f584dd14 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 21 Feb 2018 23:47:46 +0530 Subject: snippet page modifications --- app/assets/stylesheets/framework/snippets.scss | 10 +++++++++- app/views/shared/snippets/_header.html.haml | 6 ++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 69828532bef..be060422b47 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -29,8 +29,10 @@ } .snippet-title { - font-size: 24px; + color: $gl-text-color; + font-size: 2em; font-weight: $gl-font-weight-bold; + min-height: $header-height; } .snippet-edited-ago { @@ -62,6 +64,12 @@ cursor: auto; width: 101%; border-left: 0; + outline: none; + box-shadow: none; + + &:active { + box-shadow: none; + } } .embed-toggle { diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 9d55afba591..2963156b2d7 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -22,14 +22,16 @@ %h2.snippet-title.prepend-top-0.append-bottom-0 = markdown_field(@snippet, :title) - - if @snippet.updated_at != @snippet.created_at - = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true) - if @snippet.description.present? .description .wiki = markdown_field(@snippet, :description) %textarea.hidden.js-task-list-field = @snippet.description + + - if @snippet.updated_at != @snippet.created_at + = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true) + - if public_snippet? .embed-snippet.pull-right.col-md-6 .input-group -- cgit v1.2.1 From 4f2f71db90db7b9d754d552ac30c39061da63039 Mon Sep 17 00:00:00 2001 From: haseeb Date: Thu, 22 Feb 2018 00:08:56 +0530 Subject: added top-padding to snippet embed block --- app/assets/stylesheets/framework/snippets.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index be060422b47..9a555ba1f15 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -55,6 +55,7 @@ .embed-snippet { padding-right: 0; + padding-top: $gl-padding; a.btn { width: 100%; @@ -66,7 +67,7 @@ border-left: 0; outline: none; box-shadow: none; - + &:active { box-shadow: none; } -- cgit v1.2.1 From 69459457e6ce7b537add7c8671ba5fcf425e6dd5 Mon Sep 17 00:00:00 2001 From: haseeb Date: Thu, 22 Feb 2018 00:17:16 +0530 Subject: [ci skip] changelog added --- changelogs/unreleased/8088_embedded_snippets_support.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/8088_embedded_snippets_support.yml diff --git a/changelogs/unreleased/8088_embedded_snippets_support.yml b/changelogs/unreleased/8088_embedded_snippets_support.yml new file mode 100644 index 00000000000..7bd77a69dbd --- /dev/null +++ b/changelogs/unreleased/8088_embedded_snippets_support.yml @@ -0,0 +1,5 @@ +--- +title: Adds Embedded Snippets Support +merge_request: 15695 +author: haseebeqx +type: added -- cgit v1.2.1 From 174ee4e182522545baf05669d7dd03ff3753d322 Mon Sep 17 00:00:00 2001 From: haseeb Date: Thu, 22 Feb 2018 01:03:10 +0530 Subject: fix incomplete shadow in input field --- app/assets/stylesheets/framework/snippets.scss | 12 +----------- app/views/shared/snippets/_header.html.haml | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 9a555ba1f15..462aca905d8 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -57,20 +57,10 @@ padding-right: 0; padding-top: $gl-padding; - a.btn { - width: 100%; - } - .form-control { cursor: auto; width: 101%; - border-left: 0; - outline: none; - box-shadow: none; - - &:active { - box-shadow: none; - } + margin-left: -1; } .embed-toggle { diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 2963156b2d7..da014da031d 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -36,7 +36,7 @@ .embed-snippet.pull-right.col-md-6 .input-group .input-group-btn - %a.btn.btn-default.embed-toggle.dropdown-toggle{ 'data-toggle': 'dropdown' } + %a.btn.embed-toggle{ 'data-toggle': 'dropdown' } %span#embed-action Embed = sprite_icon('angle-down', size: 12) %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right -- cgit v1.2.1 From 3bed273d60554be06a9c464ed6f04f71d91c7f28 Mon Sep 17 00:00:00 2001 From: haseeb Date: Thu, 22 Feb 2018 19:34:35 +0530 Subject: dropdown style fixes --- app/assets/javascripts/snippet/snippet_embed.js | 4 ++++ app/assets/stylesheets/framework/snippets.scss | 6 +++++- app/views/shared/snippets/_header.html.haml | 10 ++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index c032414552e..dc808b68123 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -4,12 +4,16 @@ $('#share-btn').click((event) => { event.preventDefault(); + $('#share-btn').addClass('is-active'); + $('#embed-btn').removeClass('is-active'); $('#snippet-url-area').val(`${protocol}//${host + pathname}`); $('#embed-action').html('Share'); }); $('#embed-btn').click((event) => { event.preventDefault(); + $('#embed-btn').addClass('is-active'); + $('#share-btn').removeClass('is-active'); const scriptTag = ``; $('#snippet-url-area').val(scriptTag); $('#embed-action').html('Embed'); diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 462aca905d8..74f56e6e2f9 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -60,7 +60,11 @@ .form-control { cursor: auto; width: 101%; - margin-left: -1; + margin-left: -1px; + } + + .embed-toggle-list li a { + padding: 8px 40px; } .embed-toggle { diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index da014da031d..3e7f6cf9f83 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -33,17 +33,19 @@ = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true) - if public_snippet? - .embed-snippet.pull-right.col-md-6 + .embed-snippet .input-group .input-group-btn %a.btn.embed-toggle{ 'data-toggle': 'dropdown' } %span#embed-action Embed = sprite_icon('angle-down', size: 12) - %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right + %ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list %li - %a#embed-btn.btn.btn-transparent{ href: "#" } Embed + %a#embed-btn.btn.btn-transparent.is-active{ href: "#" } + %strong.embed-toggle-list-item Embed %li - %a#share-btn.btn.btn-transparent{ href: "#" } Share + %a#share-btn.btn.btn-transparent{ href: "#" } + %strong.embed-toggle-list-item Share %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn %a#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } -- cgit v1.2.1 From deb82546324fba81b20c47073cb28ca0cd8fe318 Mon Sep 17 00:00:00 2001 From: haseeb Date: Thu, 22 Feb 2018 19:42:13 +0530 Subject: extra whitespace in haml fixed --- app/views/shared/snippets/_header.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 3e7f6cf9f83..b5ce5fec04d 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -44,7 +44,7 @@ %a#embed-btn.btn.btn-transparent.is-active{ href: "#" } %strong.embed-toggle-list-item Embed %li - %a#share-btn.btn.btn-transparent{ href: "#" } + %a#share-btn.btn.btn-transparent{ href: "#" } %strong.embed-toggle-list-item Share %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn -- cgit v1.2.1 From a6635fb6c8de329aa5e4701ce809e26ed78c91bb Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 28 Feb 2018 21:34:25 +0530 Subject: fix webpack.config.js --- config/webpack.config.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index de55350d804..4b516493954 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -57,11 +57,6 @@ function generateEntries() { protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', - ide: './ide/index.js', - sidebar: './sidebar/sidebar_bundle.js', - schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js', - schedules_index: './pipeline_schedules/pipeline_schedules_index_bundle.js', - snippet: './snippet/snippet_bundle.js', snippet_embed: './snippet/snippet_embed.js', sketch_viewer: './blob/sketch_viewer.js', stl_viewer: './blob/stl_viewer.js', -- cgit v1.2.1 From e0e62b0336b6518a04113af9af4dc20fe1d3059c Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 1 Mar 2018 18:20:14 +0000 Subject: Update and install gitlab-svgs dep --- app/assets/images/icons.json | 2 +- app/assets/images/icons.svg | 2 +- app/assets/images/illustrations/epics.svg | 2 +- app/assets/images/illustrations/epics/list.svg | 1 + app/assets/images/illustrations/epics/roadmap.svg | 1 + app/assets/images/illustrations/lock_promotion.svg | 1 + app/assets/images/illustrations/milestone_removing-page.svg | 1 + app/assets/images/illustrations/prometheus-graphs_empty.svg | 1 + app/assets/images/illustrations/web-ide_promotion.svg | 1 + yarn.lock | 4 ++-- 10 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 app/assets/images/illustrations/epics/list.svg create mode 100644 app/assets/images/illustrations/epics/roadmap.svg create mode 100644 app/assets/images/illustrations/lock_promotion.svg create mode 100644 app/assets/images/illustrations/milestone_removing-page.svg create mode 100644 app/assets/images/illustrations/prometheus-graphs_empty.svg create mode 100644 app/assets/images/illustrations/web-ide_promotion.svg diff --git a/app/assets/images/icons.json b/app/assets/images/icons.json index 19843d24e22..b78dc6a2f07 100644 --- a/app/assets/images/icons.json +++ b/app/assets/images/icons.json @@ -1 +1 @@ -{"iconCount":191,"spriteSize":86607,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","soft-unwrap","soft-wrap","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_notfound","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]} \ No newline at end of file +{"iconCount":196,"spriteSize":88552,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","blame","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","compress","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","error","expand","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor-lines","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","soft-unwrap","soft-wrap","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_notfound","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]} \ No newline at end of file diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg index 6aec54d0543..0dd09b4607f 100644 --- a/app/assets/images/icons.svg +++ b/app/assets/images/icons.svg @@ -1 +1 @@ - \ No newline at end of file +error \ No newline at end of file diff --git a/app/assets/images/illustrations/epics.svg b/app/assets/images/illustrations/epics.svg index 1a37e6bba5f..be6c3fb76a5 100644 --- a/app/assets/images/illustrations/epics.svg +++ b/app/assets/images/illustrations/epics.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/assets/images/illustrations/epics/list.svg b/app/assets/images/illustrations/epics/list.svg new file mode 100644 index 00000000000..be6c3fb76a5 --- /dev/null +++ b/app/assets/images/illustrations/epics/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/illustrations/epics/roadmap.svg b/app/assets/images/illustrations/epics/roadmap.svg new file mode 100644 index 00000000000..a14f14b91c9 --- /dev/null +++ b/app/assets/images/illustrations/epics/roadmap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/illustrations/lock_promotion.svg b/app/assets/images/illustrations/lock_promotion.svg new file mode 100644 index 00000000000..6f1f9b2b030 --- /dev/null +++ b/app/assets/images/illustrations/lock_promotion.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/illustrations/milestone_removing-page.svg b/app/assets/images/illustrations/milestone_removing-page.svg new file mode 100644 index 00000000000..a8cd54da0d3 --- /dev/null +++ b/app/assets/images/illustrations/milestone_removing-page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/illustrations/prometheus-graphs_empty.svg b/app/assets/images/illustrations/prometheus-graphs_empty.svg new file mode 100644 index 00000000000..0ef198f59cd --- /dev/null +++ b/app/assets/images/illustrations/prometheus-graphs_empty.svg @@ -0,0 +1 @@ +prometheus-graphs_empty \ No newline at end of file diff --git a/app/assets/images/illustrations/web-ide_promotion.svg b/app/assets/images/illustrations/web-ide_promotion.svg new file mode 100644 index 00000000000..ef61348057d --- /dev/null +++ b/app/assets/images/illustrations/web-ide_promotion.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4d7dc1be854..7fb19bfc890 100644 --- a/yarn.lock +++ b/yarn.lock @@ -55,8 +55,8 @@ to-fast-properties "^2.0.0" "@gitlab-org/gitlab-svgs@^1.8.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.8.0.tgz#95d6afa94395860699ddad60a82bd1bbbc2ba89f" + version "1.11.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.11.0.tgz#95615590b7b37b5eeb57e19272036f7f43a95337" "@types/jquery@^2.0.40": version "2.0.48" -- cgit v1.2.1 From 250fde7c13c9bd0f2fd2cdd117c81b03ee46c558 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 1 Mar 2018 18:21:01 +0000 Subject: Add deprecation warning to shared/milestones/top --- app/views/shared/milestones/_top.html.haml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index fd0760d83a5..574863800eb 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -30,6 +30,16 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" +.deprecation-warning.prepend-top-default + = image_tag 'illustrations/milestone_removing-page.svg' + .inline + %strong.append-top-default This page will be removed in a future release. + %p + Use group milestones to manage issues from multiple projects in the same milestone. + %br + = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link' + %div= link_to 'Learn more', class: 'btn btn-default' + .detail-page-description.milestone-detail %h2.title = markdown_field(milestone, :title) -- cgit v1.2.1 From 49fad054393cfc4281d1152e291c8ed99ac76b9e Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 1 Mar 2018 18:21:14 +0000 Subject: Remove padding from btn-link css class --- app/assets/stylesheets/framework/buttons.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index c4b046a6d68..95e3573b43d 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -422,6 +422,10 @@ } } +.btn-link { + padding: 0; +} + .btn-link.btn-secondary-hover-link { color: $gl-text-color-secondary; -- cgit v1.2.1 From 72e0cdd0c7db019a761624ad961db27714b7c0fd Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 1 Mar 2018 19:52:51 +0000 Subject: Correct styling of deprecation warning --- app/views/shared/milestones/_top.html.haml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 574863800eb..60d8f94ea0b 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -31,14 +31,14 @@ = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" .deprecation-warning.prepend-top-default - = image_tag 'illustrations/milestone_removing-page.svg' - .inline - %strong.append-top-default This page will be removed in a future release. - %p + = image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' + .inline.vertical-align-middle + %strong This page will be removed in a future release. + %p.append-bottom-default.prepend-top-default Use group milestones to manage issues from multiple projects in the same milestone. %br = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link' - %div= link_to 'Learn more', class: 'btn btn-default' + %div= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' .detail-page-description.milestone-detail %h2.title -- cgit v1.2.1 From 639e6144959553b69b0f1dade4aa0758fd87a4b6 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 1 Mar 2018 19:53:22 +0000 Subject: remove deprecation-warning container as its unused --- app/views/shared/milestones/_top.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 60d8f94ea0b..ad0d4d54a18 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -30,7 +30,7 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" -.deprecation-warning.prepend-top-default +.prepend-top-default = image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' .inline.vertical-align-middle %strong This page will be removed in a future release. -- cgit v1.2.1 From 9bac5c4a7ce21a4939b65f0b288bf5050118f5c1 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 12:11:19 +0530 Subject: jquery and webpack changed --- .../pages/projects/snippets/show/index.js | 2 ++ .../javascripts/pages/snippets/show/index.js | 2 ++ app/assets/javascripts/snippet/snippet_embed.js | 40 ++++++++++++---------- app/views/shared/snippets/_header.html.haml | 5 +-- config/webpack.config.js | 1 - 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js index a134599cb04..d85cb6439ee 100644 --- a/app/assets/javascripts/pages/projects/snippets/show/index.js +++ b/app/assets/javascripts/pages/projects/snippets/show/index.js @@ -2,10 +2,12 @@ import initNotes from '~/init_notes'; import ZenMode from '~/zen_mode'; import LineHighlighter from '../../../../line_highlighter'; import BlobViewer from '../../../../blob/viewer'; +import snippetEmbed from '../../../../snippet/snippet_embed'; document.addEventListener('DOMContentLoaded', () => { new LineHighlighter(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new initNotes(); new ZenMode(); // eslint-disable-line no-new + snippetEmbed(); }); diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js index f548b9fad65..e3eded8ab9d 100644 --- a/app/assets/javascripts/pages/snippets/show/index.js +++ b/app/assets/javascripts/pages/snippets/show/index.js @@ -2,10 +2,12 @@ import LineHighlighter from '../../../line_highlighter'; import BlobViewer from '../../../blob/viewer'; import ZenMode from '../../../zen_mode'; import initNotes from '../../../init_notes'; +import snippetEmbed from '../../../snippet/snippet_embed'; document.addEventListener('DOMContentLoaded', () => { new LineHighlighter(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new initNotes(); new ZenMode(); // eslint-disable-line no-new + snippetEmbed(); }); diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index dc808b68123..872a04555a9 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -1,22 +1,24 @@ -(() => { - $(() => { - const { protocol, host, pathname } = location; +export default () => { + const { protocol, host, pathname } = location; + const shareBtn = document.querySelector('#share-btn'); + const embedBtn = document.querySelector('#embed-btn'); + const snippetUrlArea = document.querySelector('#snippet-url-area'); + const embedAction = document.querySelector('#embed-action'); - $('#share-btn').click((event) => { - event.preventDefault(); - $('#share-btn').addClass('is-active'); - $('#embed-btn').removeClass('is-active'); - $('#snippet-url-area').val(`${protocol}//${host + pathname}`); - $('#embed-action').html('Share'); - }); + shareBtn.addEventListener('click', (event) => { + event.preventDefault(); + shareBtn.classList.add('is-active'); + embedBtn.classList.remove('is-active'); + snippetUrlArea.value = `${protocol}//${host + pathname}`; + embedAction.innerHTML = 'Share'; + }); - $('#embed-btn').click((event) => { - event.preventDefault(); - $('#embed-btn').addClass('is-active'); - $('#share-btn').removeClass('is-active'); - const scriptTag = ``; - $('#snippet-url-area').val(scriptTag); - $('#embed-action').html('Embed'); - }); + embedBtn.addEventListener('click', (event) => { + event.preventDefault(); + embedBtn.classList.add('is-active'); + shareBtn.classList.remove('is-active'); + const scriptTag = ``; + snippetUrlArea.value = scriptTag; + embedAction.innerHTML = 'Embed'; }); -}).call(window); +}; diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index b5ce5fec04d..53d5416d120 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('snippet_embed') - .detail-page-header .detail-page-header-body .snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } } @@ -36,7 +33,7 @@ .embed-snippet .input-group .input-group-btn - %a.btn.embed-toggle{ 'data-toggle': 'dropdown' } + %button.btn.embed-toggle{ 'data-toggle': 'dropdown' } %span#embed-action Embed = sprite_icon('angle-down', size: 12) %ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list diff --git a/config/webpack.config.js b/config/webpack.config.js index 4b516493954..b01cfd6595e 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -57,7 +57,6 @@ function generateEntries() { protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', - snippet_embed: './snippet/snippet_embed.js', sketch_viewer: './blob/sketch_viewer.js', stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', -- cgit v1.2.1 From 1b2d9c0edfcf9c207f5ecdda9068c35d43d05329 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 12:17:33 +0530 Subject: anchor changed to button --- app/assets/javascripts/snippet/snippet_embed.js | 2 -- app/assets/stylesheets/framework/dropdowns.scss | 2 +- app/assets/stylesheets/framework/snippets.scss | 2 +- app/views/shared/snippets/_header.html.haml | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index 872a04555a9..28a5ed306bd 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -6,7 +6,6 @@ export default () => { const embedAction = document.querySelector('#embed-action'); shareBtn.addEventListener('click', (event) => { - event.preventDefault(); shareBtn.classList.add('is-active'); embedBtn.classList.remove('is-active'); snippetUrlArea.value = `${protocol}//${host + pathname}`; @@ -14,7 +13,6 @@ export default () => { }); embedBtn.addEventListener('click', (event) => { - event.preventDefault(); embedBtn.classList.add('is-active'); shareBtn.classList.remove('is-active'); const scriptTag = ``; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 1d7b0b602cc..8dc3519a394 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -481,7 +481,7 @@ .dropdown-menu-selectable { li { - a { + a, button { padding: 8px 40px; position: relative; diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 74f56e6e2f9..606d4675f19 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -63,7 +63,7 @@ margin-left: -1px; } - .embed-toggle-list li a { + .embed-toggle-list li button { padding: 8px 40px; } diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 53d5416d120..2a704bb7e59 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -38,10 +38,10 @@ = sprite_icon('angle-down', size: 12) %ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list %li - %a#embed-btn.btn.btn-transparent.is-active{ href: "#" } + %button#embed-btn.btn.btn-transparent.is-active{ href: "#" } %strong.embed-toggle-list-item Embed %li - %a#share-btn.btn.btn-transparent{ href: "#" } + %button#share-btn.btn.btn-transparent{ href: "#" } %strong.embed-toggle-list-item Share %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn -- cgit v1.2.1 From 2ee641aacd0960ed6bb96cdbdc26a0cdef028954 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 12:20:48 +0530 Subject: one more button change --- app/views/shared/snippets/_header.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 2a704bb7e59..6379dbc7b1b 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -45,6 +45,6 @@ %strong.embed-toggle-list-item Share %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn - %a#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } + %button#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } = sprite_icon('duplicate', size: 16) .clearfix -- cgit v1.2.1 From 468da17361ff7c7628b48e5374ad13e41db6a37e Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 12:30:32 +0530 Subject: added title --- app/views/shared/snippets/_embed.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml index aacc7825534..2d93e51a2d9 100644 --- a/app/views/shared/snippets/_embed.html.haml +++ b/app/views/shared/snippets/_embed.html.haml @@ -10,7 +10,7 @@ %small = number_to_human_size(blob.raw_size) - %a.gitlab-logo{ href: url_for(only_path: false, overwrite_params: nil) } + %a.gitlab-logo{ href: url_for(only_path: false, overwrite_params: nil), title: 'view on gitlab' } on   %span.logo-text GitLab -- cgit v1.2.1 From 3cd80c37647ca8587bc46ac47d1a628d5e4e5446 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 13:30:08 +0530 Subject: removed unused variable --- app/assets/javascripts/snippet/snippet_embed.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index 28a5ed306bd..62af55addfc 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -5,14 +5,14 @@ export default () => { const snippetUrlArea = document.querySelector('#snippet-url-area'); const embedAction = document.querySelector('#embed-action'); - shareBtn.addEventListener('click', (event) => { + shareBtn.addEventListener('click', () => { shareBtn.classList.add('is-active'); embedBtn.classList.remove('is-active'); snippetUrlArea.value = `${protocol}//${host + pathname}`; embedAction.innerHTML = 'Share'; }); - embedBtn.addEventListener('click', (event) => { + embedBtn.addEventListener('click', () => { embedBtn.classList.add('is-active'); shareBtn.classList.remove('is-active'); const scriptTag = ``; -- cgit v1.2.1 From 097dc726d6ed8f8cfe1e55e47e60d7c813882402 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 14:53:42 +0530 Subject: use sprite for icons --- app/assets/images/ext_snippet_icons/doc_code.png | Bin 1592 -> 0 bytes app/assets/images/ext_snippet_icons/doc_text.png | Bin 1452 -> 0 bytes app/assets/images/ext_snippet_icons/download.png | Bin 1671 -> 0 bytes .../images/ext_snippet_icons/ext_snippet_icons.png | Bin 0 -> 1018 bytes app/assets/images/ext_snippet_icons/logo.png | Bin 736 -> 494 bytes app/assets/stylesheets/framework/dropdowns.scss | 3 ++- app/assets/stylesheets/snippets.scss | 18 +++++++++++++++++- app/helpers/icons_helper.rb | 2 +- 8 files changed, 20 insertions(+), 3 deletions(-) delete mode 100644 app/assets/images/ext_snippet_icons/doc_code.png delete mode 100644 app/assets/images/ext_snippet_icons/doc_text.png delete mode 100644 app/assets/images/ext_snippet_icons/download.png create mode 100644 app/assets/images/ext_snippet_icons/ext_snippet_icons.png diff --git a/app/assets/images/ext_snippet_icons/doc_code.png b/app/assets/images/ext_snippet_icons/doc_code.png deleted file mode 100644 index 93a9b372f32..00000000000 Binary files a/app/assets/images/ext_snippet_icons/doc_code.png and /dev/null differ diff --git a/app/assets/images/ext_snippet_icons/doc_text.png b/app/assets/images/ext_snippet_icons/doc_text.png deleted file mode 100644 index f2357c17c90..00000000000 Binary files a/app/assets/images/ext_snippet_icons/doc_text.png and /dev/null differ diff --git a/app/assets/images/ext_snippet_icons/download.png b/app/assets/images/ext_snippet_icons/download.png deleted file mode 100644 index 156c788509b..00000000000 Binary files a/app/assets/images/ext_snippet_icons/download.png and /dev/null differ diff --git a/app/assets/images/ext_snippet_icons/ext_snippet_icons.png b/app/assets/images/ext_snippet_icons/ext_snippet_icons.png new file mode 100644 index 00000000000..20380adc4e5 Binary files /dev/null and b/app/assets/images/ext_snippet_icons/ext_snippet_icons.png differ diff --git a/app/assets/images/ext_snippet_icons/logo.png b/app/assets/images/ext_snippet_icons/logo.png index 12f8315cad8..794c9cc2dbc 100644 Binary files a/app/assets/images/ext_snippet_icons/logo.png and b/app/assets/images/ext_snippet_icons/logo.png differ diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 8dc3519a394..5d2b8ab6651 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -481,7 +481,8 @@ .dropdown-menu-selectable { li { - a, button { + a, + button { padding: 8px 40px; position: relative; diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index 7d271d25fd9..e39ee13be8d 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -13,6 +13,21 @@ margin: 20px; font-weight: 200; + .gl-snippet-icon { + display: inline-block; + background: url(asset_path('ext_snippet_icons/ext_snippet_icons.png')) no-repeat; + overflow: hidden; + text-indent: -9999px; + text-align: left; + width: 16px; + height: 16px; + background-size: cover; + + &.gl-snippet-icon-doc_code { background-position: -0 -0; } + &.gl-snippet-icon-doc_text { background-position: -0 -16px; } + &.gl-snippet-icon-download { background-position: -0 -32px; } + } + .blob-viewer { background-color: $white-light; text-align: left; @@ -108,7 +123,8 @@ } } - img { + img, + .gl-snippet-icon { display: inline-block; vertical-align: middle; } diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 67001c55e88..2f304b040c7 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -44,7 +44,7 @@ module IconsHelper end def external_snippet_icon(name) - content_tag(:img, "", src: "#{image_url('/assets/ext_snippet_icons')}/#{name}.png", width: '16px', height: '16px') + content_tag(:span, "", class: "gl-snippet-icon gl-snippet-icon-#{name}") end def audit_icon(names, options = {}) -- cgit v1.2.1 From 45052a7215cb30dfea29912539e48700969c5120 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 3 Mar 2018 16:13:53 +0530 Subject: fixed failing test --- spec/helpers/icons_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb index d87c81a28d1..93d8e672f8c 100644 --- a/spec/helpers/icons_helper_spec.rb +++ b/spec/helpers/icons_helper_spec.rb @@ -166,7 +166,7 @@ describe IconsHelper do describe '#external_snippet_icon' do it 'returns external snippet icon' do expect(external_snippet_icon('download').to_s) - .to eq("") + .to eq("") end end end -- cgit v1.2.1 From 4f10cad9ffdc508dbcecd477e93921367a69477d Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Mon, 15 Jan 2018 07:46:19 +0100 Subject: Add Inherit quick action Closes #38450 --- app/services/quick_actions/interpret_service.rb | 18 ++++++ changelogs/unreleased/add-inherit-command.yml | 5 ++ doc/user/project/quick_actions.md | 3 +- .../quick_actions/interpret_service_spec.rb | 65 ++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/add-inherit-command.yml diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 1e9bd84e749..4abb936e14a 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -268,6 +268,24 @@ module QuickActions end end + desc 'Inherit (copy) labels and milestone from other issue' + explanation do |issue_id| + "Inherit (copy) labels and milestone from issue \"#{issue_id}\"." + end + params '#issue' + condition do + issuable.persisted? && + current_user.can?(:"update_#{issuable.to_ability_name}", issuable) + end + command :inherit do |issue_id| + issue = extract_references(issue_id, :issue).first + if issue.present? && issue.project_id == issuable.project_id + @updates[:add_label_ids] = issue.labels.map(&:id) + + @updates[:milestone_id] = issue.milestone.id if issue.milestone + end + end + desc 'Add a todo' explanation 'Adds a todo.' condition do diff --git a/changelogs/unreleased/add-inherit-command.yml b/changelogs/unreleased/add-inherit-command.yml new file mode 100644 index 00000000000..84f5bc97fbe --- /dev/null +++ b/changelogs/unreleased/add-inherit-command.yml @@ -0,0 +1,5 @@ +--- +title: Add Inherit quick action +merge_request: +author: Mateusz Bajorski +type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 442fc978284..3b6bdc8ca3d 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -40,4 +40,5 @@ do. | `/duplicate #issue` | Closes this issue and marks it as a duplicate of another issue | | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | -| `/shrug` | Append the comment with `¯\_(ツ)_/¯` | \ No newline at end of file +| `/shrug` | Append the comment with `¯\_(ツ)_/¯` | +| `/inherit #issue` | Inherit (copy) labels and milestone from other issue | diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index f793f55e51b..ebe0c7639a0 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -306,6 +306,23 @@ describe QuickActions::InterpretService do end end + shared_examples 'inherit command' do + it 'fetches issue and copies labels and milestone if content contains /inherit issue_reference' do + issue_father # populate the issue + todo_label # populate this label + inreview_label # populate this label + _, updates = service.execute(content, issuable) + + expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id]) + + if issue_father.milestone + expect(updates[:milestone_id]).to eq(issue_father.milestone.id) + else + expect(updates).not_to have_key(:milestone_id) + end + end + end + shared_examples 'shrug command' do it 'appends ¯\_(ツ)_/¯ to the comment' do new_content, _ = service.execute(content, issuable) @@ -757,6 +774,54 @@ describe QuickActions::InterpretService do let(:issuable) { issue } end + context '/inherit command' do + let!(:todo_label) { create(:label, project: project, title: 'To Do') } + let!(:inreview_label) { create(:label, project: project, title: 'In Review') } + + it_behaves_like 'inherit command' do + # Without milestone assignment + let(:issue_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } + + let(:content) { "/inherit #{issue_father.to_reference}" } + let(:issuable) { issue } + end + + it_behaves_like 'inherit command' do + # With milestone assignment + let(:issue_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + + let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { '/inherit' } + let(:issuable) { issue } + end + + context 'cross project references' do + it_behaves_like 'empty command' do + let(:other_project) { create(:project, :public) } + let(:issue_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } + let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { "/inherit imaginary#1234" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:other_project) { create(:project, :private) } + let(:issue_father) { create(:issue, project: other_project) } + + let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issuable) { issue } + end + end + end + context '/duplicate command' do it_behaves_like 'duplicate command' do let(:issue_duplicate) { create(:issue, project: project) } -- cgit v1.2.1 From a0adf87707e44fda83aca859b41ce18372a1c72b Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Sun, 21 Jan 2018 18:16:56 +0100 Subject: Changed command name to copy_metadata and added MR support --- app/services/quick_actions/interpret_service.rb | 16 ++++---- changelogs/unreleased/add-inherit-command.yml | 2 +- doc/user/project/quick_actions.md | 2 +- .../quick_actions/interpret_service_spec.rb | 46 +++++++++++----------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 4abb936e14a..28fa887bf2d 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -268,18 +268,20 @@ module QuickActions end end - desc 'Inherit (copy) labels and milestone from other issue' - explanation do |issue_id| - "Inherit (copy) labels and milestone from issue \"#{issue_id}\"." + desc 'Copy labels and milestone from other issue or merge request' + explanation do |issueable_id| + "Copy labels and milestone from issue or merge_request \"#{issueable_id}\"." end - params '#issue' + params '< #issue | !merge_request >' condition do issuable.persisted? && current_user.can?(:"update_#{issuable.to_ability_name}", issuable) end - command :inherit do |issue_id| - issue = extract_references(issue_id, :issue).first - if issue.present? && issue.project_id == issuable.project_id + command :copy_metadata do |issueable_id| + reference_type = issueable_id.include?("#") ? :issue : :merge_request + issue = extract_references(issueable_id, reference_type).first + + if issue.present? && issue.project.id == issuable.project.id @updates[:add_label_ids] = issue.labels.map(&:id) @updates[:milestone_id] = issue.milestone.id if issue.milestone diff --git a/changelogs/unreleased/add-inherit-command.yml b/changelogs/unreleased/add-inherit-command.yml index 84f5bc97fbe..d47aa8c7b36 100644 --- a/changelogs/unreleased/add-inherit-command.yml +++ b/changelogs/unreleased/add-inherit-command.yml @@ -1,5 +1,5 @@ --- title: Add Inherit quick action -merge_request: +merge_request: 16473 author: Mateusz Bajorski type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 3b6bdc8ca3d..e2e2b859d35 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -41,4 +41,4 @@ do. | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | | `/shrug` | Append the comment with `¯\_(ツ)_/¯` | -| `/inherit #issue` | Inherit (copy) labels and milestone from other issue | +| /copy_metadata < #issue | !merge_request > | copy_metadata labels and milestone from other issue or merge request | diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index ebe0c7639a0..9a8240f9491 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -306,17 +306,17 @@ describe QuickActions::InterpretService do end end - shared_examples 'inherit command' do - it 'fetches issue and copies labels and milestone if content contains /inherit issue_reference' do - issue_father # populate the issue + shared_examples 'copy_metadata command' do + it 'fetches issue or merge request and copies labels and milestone if content contains /copy_metadata reference' do + issueable_father # populate the issue todo_label # populate this label inreview_label # populate this label _, updates = service.execute(content, issuable) expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id]) - if issue_father.milestone - expect(updates[:milestone_id]).to eq(issue_father.milestone.id) + if issueable_father.milestone + expect(updates[:milestone_id]).to eq(issueable_father.milestone.id) else expect(updates).not_to have_key(:milestone_id) end @@ -774,49 +774,49 @@ describe QuickActions::InterpretService do let(:issuable) { issue } end - context '/inherit command' do + context '/copy_metadata command' do let!(:todo_label) { create(:label, project: project, title: 'To Do') } let!(:inreview_label) { create(:label, project: project, title: 'In Review') } - it_behaves_like 'inherit command' do - # Without milestone assignment - let(:issue_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } - - let(:content) { "/inherit #{issue_father.to_reference}" } + it_behaves_like 'empty command' do + let(:content) { '/copy_metadata' } let(:issuable) { issue } end - it_behaves_like 'inherit command' do - # With milestone assignment - let(:issue_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + it_behaves_like 'copy_metadata command' do + let(:issueable_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } - let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{issueable_father.to_reference}" } let(:issuable) { issue } end - it_behaves_like 'empty command' do - let(:content) { '/inherit' } - let(:issuable) { issue } + context 'when the parent issueable has a milestone' do + it_behaves_like 'copy_metadata command' do + let(:issueable_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + + let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:issuable) { issue } + end end context 'cross project references' do it_behaves_like 'empty command' do let(:other_project) { create(:project, :public) } - let(:issue_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } - let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:issueable_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } + let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } let(:issuable) { issue } end it_behaves_like 'empty command' do - let(:content) { "/inherit imaginary#1234" } + let(:content) { "/copy_metadata imaginary#1234" } let(:issuable) { issue } end it_behaves_like 'empty command' do let(:other_project) { create(:project, :private) } - let(:issue_father) { create(:issue, project: other_project) } + let(:issueable_father) { create(:issue, project: other_project) } - let(:content) { "/inherit #{issue_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } let(:issuable) { issue } end end -- cgit v1.2.1 From 26087ae91c0397054786bed7bcc078b03dd8752b Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Wed, 31 Jan 2018 18:41:06 +0100 Subject: Fixed typos and improved reference checking --- app/services/quick_actions/interpret_service.rb | 18 ++++++++-------- .../unreleased/add-copy-metadata-command.yml | 5 +++++ changelogs/unreleased/add-inherit-command.yml | 5 ----- doc/user/project/quick_actions.md | 2 +- .../quick_actions/interpret_service_spec.rb | 24 +++++++++++----------- 5 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 changelogs/unreleased/add-copy-metadata-command.yml delete mode 100644 changelogs/unreleased/add-inherit-command.yml diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 28fa887bf2d..87937fef8f4 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -269,22 +269,22 @@ module QuickActions end desc 'Copy labels and milestone from other issue or merge request' - explanation do |issueable_id| - "Copy labels and milestone from issue or merge_request \"#{issueable_id}\"." + explanation do |issuable_id| + "Copy labels and milestone from issue or merge_request \"#{issuable_id}\"." end - params '< #issue | !merge_request >' + params '#issue | !merge_request' condition do issuable.persisted? && current_user.can?(:"update_#{issuable.to_ability_name}", issuable) end - command :copy_metadata do |issueable_id| - reference_type = issueable_id.include?("#") ? :issue : :merge_request - issue = extract_references(issueable_id, reference_type).first + command :copy_metadata do |issuable_id| + source_issuable = extract_references(issuable_id, :issue).first + source_issuable = extract_references(issuable_id, :merge_request).first if !source_issuable.present? - if issue.present? && issue.project.id == issuable.project.id - @updates[:add_label_ids] = issue.labels.map(&:id) + if source_issuable.present? && source_issuable.project.id == issuable.project.id + @updates[:add_label_ids] = source_issuable.labels.map(&:id) - @updates[:milestone_id] = issue.milestone.id if issue.milestone + @updates[:milestone_id] = source_issuable.milestone.id if source_issuable.milestone end end diff --git a/changelogs/unreleased/add-copy-metadata-command.yml b/changelogs/unreleased/add-copy-metadata-command.yml new file mode 100644 index 00000000000..3bf25ae6ce0 --- /dev/null +++ b/changelogs/unreleased/add-copy-metadata-command.yml @@ -0,0 +1,5 @@ +--- +title: Add Copy metadata quick action +merge_request: 16473 +author: Mateusz Bajorski +type: added diff --git a/changelogs/unreleased/add-inherit-command.yml b/changelogs/unreleased/add-inherit-command.yml deleted file mode 100644 index d47aa8c7b36..00000000000 --- a/changelogs/unreleased/add-inherit-command.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Inherit quick action -merge_request: 16473 -author: Mateusz Bajorski -type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index e2e2b859d35..75799caacde 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -41,4 +41,4 @@ do. | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | | `/shrug` | Append the comment with `¯\_(ツ)_/¯` | -| /copy_metadata < #issue | !merge_request > | copy_metadata labels and milestone from other issue or merge request | +| /copy_metadata < #issue | !merge_request > | Copy labels and milestone from other issue or merge request | diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 9a8240f9491..4aad2aaef79 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -308,15 +308,15 @@ describe QuickActions::InterpretService do shared_examples 'copy_metadata command' do it 'fetches issue or merge request and copies labels and milestone if content contains /copy_metadata reference' do - issueable_father # populate the issue + source_issuable # populate the issue todo_label # populate this label inreview_label # populate this label _, updates = service.execute(content, issuable) expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id]) - if issueable_father.milestone - expect(updates[:milestone_id]).to eq(issueable_father.milestone.id) + if source_issuable.milestone + expect(updates[:milestone_id]).to eq(source_issuable.milestone.id) else expect(updates).not_to have_key(:milestone_id) end @@ -784,17 +784,17 @@ describe QuickActions::InterpretService do end it_behaves_like 'copy_metadata command' do - let(:issueable_father) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } + let(:source_issuable) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) } - let(:content) { "/copy_metadata #{issueable_father.to_reference}" } + let(:content) { "/copy_metadata #{source_issuable.to_reference}" } let(:issuable) { issue } end - context 'when the parent issueable has a milestone' do + context 'when the parent issuable has a milestone' do it_behaves_like 'copy_metadata command' do - let(:issueable_father) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } + let(:source_issuable) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) } - let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end end @@ -802,8 +802,8 @@ describe QuickActions::InterpretService do context 'cross project references' do it_behaves_like 'empty command' do let(:other_project) { create(:project, :public) } - let(:issueable_father) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } - let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:source_issuable) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } + let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end @@ -814,9 +814,9 @@ describe QuickActions::InterpretService do it_behaves_like 'empty command' do let(:other_project) { create(:project, :private) } - let(:issueable_father) { create(:issue, project: other_project) } + let(:source_issuable) { create(:issue, project: other_project) } - let(:content) { "/copy_metadata #{issueable_father.to_reference(project)}" } + let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end end -- cgit v1.2.1 From 593e3ffc325a600a070ce6c247e33412473ec651 Mon Sep 17 00:00:00 2001 From: Mateusz Bajorski Date: Thu, 1 Feb 2018 19:38:10 +0100 Subject: Fixed inconsistent descriptions and refactored reference checking --- app/services/quick_actions/interpret_service.rb | 4 ++-- doc/user/project/quick_actions.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 87937fef8f4..1a0e27d551a 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -270,7 +270,7 @@ module QuickActions desc 'Copy labels and milestone from other issue or merge request' explanation do |issuable_id| - "Copy labels and milestone from issue or merge_request \"#{issuable_id}\"." + "Copy labels and milestone from issue or merge_request #{issuable_id}." end params '#issue | !merge_request' condition do @@ -279,7 +279,7 @@ module QuickActions end command :copy_metadata do |issuable_id| source_issuable = extract_references(issuable_id, :issue).first - source_issuable = extract_references(issuable_id, :merge_request).first if !source_issuable.present? + source_issuable ||= extract_references(issuable_id, :merge_request).first if source_issuable.present? && source_issuable.project.id == issuable.project.id @updates[:add_label_ids] = source_issuable.labels.map(&:id) diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 75799caacde..3e3b699edc4 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -41,4 +41,4 @@ do. | `/move path/to/project` | Moves issue to another project | | `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` | | `/shrug` | Append the comment with `¯\_(ツ)_/¯` | -| /copy_metadata < #issue | !merge_request > | Copy labels and milestone from other issue or merge request | +| /copy_metadata #issue | !merge_request | Copy labels and milestone from other issue or merge request | -- cgit v1.2.1 From a87d76240410c865cda1e35ec377a19af29ed207 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 3 Mar 2018 22:06:27 +0100 Subject: Add small-triangle class and add popup footer --- app/assets/stylesheets/framework/popup.scss | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/popup.scss b/app/assets/stylesheets/framework/popup.scss index 5c76205095f..e21c32a90df 100644 --- a/app/assets/stylesheets/framework/popup.scss +++ b/app/assets/stylesheets/framework/popup.scss @@ -6,10 +6,31 @@ $popup-triangle-border-size ); - padding: $gl-padding; - background-color: $gray-lighter; + &.small-triangle { + @include triangle( + $gray-lighter, + $gray-darker, + $popup-triangle-size / 2, + $popup-triangle-border-size /2 + ); + } + border: 1px solid $gray-darker; - border-radius: $border-radius-default; box-shadow: 0 5px 8px $popup-box-shadow-color; position: relative; + + .body { + background-color: $gray-lighter; + padding: $gl-padding; + border-top-left-radius: $border-radius-default; + border-top-right-radius: $border-radius-default; + } + + .footer { + background-color: $white-light; + padding: $gl-padding; + border-bottom-left-radius: $border-radius-default; + border-bottom-right-radius: $border-radius-default; + border-top: 1px solid $white-dark; + } } -- cgit v1.2.1 From ed01f87c998a24312722972b6e4e84e63c68f137 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 3 Mar 2018 22:06:41 +0100 Subject: Custom deprecation message styles --- app/assets/stylesheets/pages/milestone.scss | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index e5afa8fffcb..ea67d3fcdeb 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -194,3 +194,27 @@ .issuable-row { background-color: $white-light; } + +.milestone-deprecation-message { + .popup { + max-width: 300px; + position: absolute; + left: 30px; + + .body { + padding: $gl-padding-8; + } + + .footer { + padding: $gl-padding-8 $gl-padding; + } + } + + .instructions-list { + padding: 0 0 0 30px; + } + + .text-container { + position: relative; + } +} \ No newline at end of file -- cgit v1.2.1 From 17e863be5279d452ac10df99ae6ce9f443d92375 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 3 Mar 2018 22:06:58 +0100 Subject: Create deprecation_message partial and use it --- .../milestones/_deprecation_message.html.haml | 23 ++++++++++++++++++++++ app/views/shared/milestones/_top.html.haml | 10 +--------- 2 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 app/views/shared/milestones/_deprecation_message.html.haml diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml new file mode 100644 index 00000000000..22fe445d64f --- /dev/null +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -0,0 +1,23 @@ +.milestone-deprecation-message.prepend-top-default.js-toggle-container + = image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' + .inline.vertical-align-middle.text-container + %strong This page will be removed in a future release. + %p.append-bottom-default.prepend-top-5 + Use group milestones to manage issues from multiple projects in the same milestone. + %br + = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link js-toggle-button' + + .popup.js-toggle-content.hide.small-triangle + .body + %ol.instructions-list + %li + Click any + %strong project name + in the project list below to navigate to the project milestone. + %li + Click the + %strong Promote + button in the top right corner to promote it to a group milestone. + .footer= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' + + %div= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' \ No newline at end of file diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index ad0d4d54a18..9761c9181f2 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -30,15 +30,7 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" -.prepend-top-default - = image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' - .inline.vertical-align-middle - %strong This page will be removed in a future release. - %p.append-bottom-default.prepend-top-default - Use group milestones to manage issues from multiple projects in the same milestone. - %br - = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link' - %div= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' += render 'shared/milestones/deprecation_message' .detail-page-description.milestone-detail %h2.title -- cgit v1.2.1 From 7a8ec42ea7c097ead08ddb35721308d3b736f4d5 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 3 Mar 2018 22:10:18 +0100 Subject: Remove small-triangle --- app/assets/stylesheets/framework/popup.scss | 9 --------- app/assets/stylesheets/framework/variables.scss | 2 +- app/views/shared/milestones/_deprecation_message.html.haml | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/framework/popup.scss b/app/assets/stylesheets/framework/popup.scss index e21c32a90df..faa827c7f62 100644 --- a/app/assets/stylesheets/framework/popup.scss +++ b/app/assets/stylesheets/framework/popup.scss @@ -6,15 +6,6 @@ $popup-triangle-border-size ); - &.small-triangle { - @include triangle( - $gray-lighter, - $gray-darker, - $popup-triangle-size / 2, - $popup-triangle-border-size /2 - ); - } - border: 1px solid $gray-darker; box-shadow: 0 5px 8px $popup-box-shadow-color; position: relative; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 54e13f9d95c..ed5aa5ad07d 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -731,7 +731,7 @@ $image-comment-cursor-top-offset: 12; /* Popup */ -$popup-triangle-size: 15px; +$popup-triangle-size: 8px; $popup-triangle-border-size: 1px; $popup-box-shadow-color: rgba(90, 90, 90, 0.05); diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index 22fe445d64f..eb4e27de20b 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -7,7 +7,7 @@ %br = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link js-toggle-button' - .popup.js-toggle-content.hide.small-triangle + .popup.js-toggle-content.hide .body %ol.instructions-list %li -- cgit v1.2.1 From 3d6484aded227e04f1f6690de7a91a44ee9e5c15 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 3 Mar 2018 22:13:53 +0100 Subject: Make popup shadow slightly darker and larger to make new design --- app/assets/stylesheets/framework/popup.scss | 2 +- app/assets/stylesheets/framework/variables.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/popup.scss b/app/assets/stylesheets/framework/popup.scss index faa827c7f62..a571151aef7 100644 --- a/app/assets/stylesheets/framework/popup.scss +++ b/app/assets/stylesheets/framework/popup.scss @@ -7,7 +7,7 @@ ); border: 1px solid $gray-darker; - box-shadow: 0 5px 8px $popup-box-shadow-color; + box-shadow: 0 5px 10px $popup-box-shadow-color; position: relative; .body { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index ed5aa5ad07d..26f03948bae 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -733,7 +733,7 @@ Popup */ $popup-triangle-size: 8px; $popup-triangle-border-size: 1px; -$popup-box-shadow-color: rgba(90, 90, 90, 0.05); +$popup-box-shadow-color: rgba(90, 90, 90, 0.3); /* Multi file editor -- cgit v1.2.1 From 67b0ac73ae131fec5296e3c05aad311b432ec03b Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 4 Mar 2018 09:41:41 +0100 Subject: Only render deprecation message for deprecated milestones --- app/views/shared/milestones/_top.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 9761c9181f2..021e291bdc2 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -30,7 +30,7 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" -= render 'shared/milestones/deprecation_message' += render 'shared/milestones/deprecation_message' if @milestone.legacy_group_milestone? || @milestone.dashboard_milestone? .detail-page-description.milestone-detail %h2.title -- cgit v1.2.1 From 4763f3153947610065eccc841aa85722d963d151 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 4 Mar 2018 09:44:15 +0100 Subject: Fix lints --- app/assets/stylesheets/framework/buttons.scss | 38 +++++++++++++-------------- app/assets/stylesheets/pages/milestone.scss | 4 +-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 95e3573b43d..b8a47b2cfdb 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -424,27 +424,27 @@ .btn-link { padding: 0; -} - -.btn-link.btn-secondary-hover-link { - color: $gl-text-color-secondary; - &:hover, - &:active, - &:focus { - color: $gl-link-color; - text-decoration: none; + &.btn-secondary-hover-link { + color: $gl-text-color-secondary; + + &:hover, + &:active, + &:focus { + color: $gl-link-color; + text-decoration: none; + } } -} - -.btn-link.btn-primary-hover-link { - color: inherit; - - &:hover, - &:active, - &:focus { - color: $gl-link-color; - text-decoration: none; + + &.btn-primary-hover-link { + color: inherit; + + &:hover, + &:active, + &:focus { + color: $gl-link-color; + text-decoration: none; + } } } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index ea67d3fcdeb..9b5087caacf 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -204,7 +204,7 @@ .body { padding: $gl-padding-8; } - + .footer { padding: $gl-padding-8 $gl-padding; } @@ -217,4 +217,4 @@ .text-container { position: relative; } -} \ No newline at end of file +} -- cgit v1.2.1 From fb7189e369e491436d6ad9cab5c1cea48ff6232f Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 4 Mar 2018 11:35:42 +0100 Subject: Remove all instance milestone variables in favour of local variables --- app/views/shared/milestones/_top.html.haml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 021e291bdc2..565d8807d0e 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -30,23 +30,23 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" -= render 'shared/milestones/deprecation_message' if @milestone.legacy_group_milestone? || @milestone.dashboard_milestone? += render 'shared/milestones/deprecation_message' if milestone.legacy_group_milestone? || milestone.dashboard_milestone? .detail-page-description.milestone-detail %h2.title = markdown_field(milestone, :title) - - if @milestone.group_milestone? && @milestone.description.present? + - if milestone.group_milestone? && milestone.description.present? %div .description .wiki - = markdown_field(@milestone, :description) + = markdown_field(milestone, :description) - if milestone.complete?(current_user) && milestone.active? .alert.alert-success.prepend-top-default - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' %span All issues for this milestone are closed. #{close_msg} -- if @milestone.legacy_group_milestone? || @milestone.dashboard_milestone? +- if milestone.legacy_group_milestone? || milestone.dashboard_milestone? .table-holder %table.table %thead @@ -69,7 +69,7 @@ Open %td = ms.expires_at -- elsif @milestone.group_milestone? +- elsif milestone.group_milestone? %br View = link_to 'Issues', issues_group_path(@group, milestone_title: milestone.title) -- cgit v1.2.1 From c647787bc3d16f6bcc24e02eea2a5b3aad76f51c Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 5 Mar 2018 11:11:44 +0000 Subject: Added is_dymanic_milestone to top partial --- app/views/shared/milestones/_top.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 565d8807d0e..67a41926580 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -1,6 +1,6 @@ - page_title milestone.title, "Milestones" - - group = local_assigns[:group] +- is_dynamic_milestone = milestone.legacy_group_milestone? || milestone.dashboard_milestone? .detail-page-header %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } @@ -30,7 +30,7 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" -= render 'shared/milestones/deprecation_message' if milestone.legacy_group_milestone? || milestone.dashboard_milestone? += render 'shared/milestones/deprecation_message' if is_dynamic_milestone .detail-page-description.milestone-detail %h2.title @@ -46,7 +46,7 @@ - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' %span All issues for this milestone are closed. #{close_msg} -- if milestone.legacy_group_milestone? || milestone.dashboard_milestone? +- if is_dynamic_milestone .table-holder %table.table %thead -- cgit v1.2.1 From eab81682b62eee96f14dd7391203627ea714a41d Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 5 Mar 2018 11:11:51 +0000 Subject: Add view spec for top --- spec/views/shared/milestones/_top.html.haml.rb | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 spec/views/shared/milestones/_top.html.haml.rb diff --git a/spec/views/shared/milestones/_top.html.haml.rb b/spec/views/shared/milestones/_top.html.haml.rb new file mode 100644 index 00000000000..50efe1d5377 --- /dev/null +++ b/spec/views/shared/milestones/_top.html.haml.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe 'shared/milestones/_top.html.haml' do + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + let(:milestone) { create(:milestone, project: project) } + + before do + allow(milestone).to receive(:milestones) { [] } + end + + it 'renders a deprecation message for a legacy milestone' do + allow(milestone).to receive(:legacy_group_milestone?) { true } + + render 'shared/milestones/top', milestone: milestone + + expect(rendered).to have_css('.milestone-deprecation-message') + end + + it 'renders a deprecation message for a dashboard milestone' do + allow(milestone).to receive(:dashboard_milestone?) { true } + + render 'shared/milestones/top', milestone: milestone + + expect(rendered).to have_css('.milestone-deprecation-message') + end + + it 'does not render a deprecation message for a non-legacy and non-dashboard milestone' do + assign :group, group + + render 'shared/milestones/top', milestone: milestone + + expect(rendered).not_to have_css('.milestone-deprecation-message') + end +end -- cgit v1.2.1 From f1d5af4319a0eead31555adccc994e3d41c10a82 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 5 Mar 2018 11:58:57 +0000 Subject: Fix lint --- app/assets/stylesheets/framework/buttons.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index b8a47b2cfdb..c358e9e93de 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -427,7 +427,7 @@ &.btn-secondary-hover-link { color: $gl-text-color-secondary; - + &:hover, &:active, &:focus { @@ -435,10 +435,10 @@ text-decoration: none; } } - + &.btn-primary-hover-link { color: inherit; - + &:hover, &:active, &:focus { -- cgit v1.2.1 From 52a3bcf9e8782061d402890f2234380c166f7ca6 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 5 Mar 2018 12:54:08 +0000 Subject: Fix lint --- app/views/shared/milestones/_deprecation_message.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index eb4e27de20b..668a4f09ee3 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -20,4 +20,4 @@ button in the top right corner to promote it to a group milestone. .footer= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' - %div= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' \ No newline at end of file + %div= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' -- cgit v1.2.1 From 3308419b5bcf5b3fe8ac52ce72f122e024b4a4ce Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 5 Mar 2018 12:56:37 +0000 Subject: Fix docs link --- app/views/shared/milestones/_deprecation_message.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index 668a4f09ee3..714739a1ca7 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -18,6 +18,6 @@ Click the %strong Promote button in the top right corner to promote it to a group milestone. - .footer= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' + .footer= link_to 'Learn more', help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' - %div= link_to 'Learn more', help_page_url('user/project/milestones', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' + %div= link_to 'Learn more', help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' -- cgit v1.2.1 From bf619dc62b7f848b1cbcaac41549f7bd5e616c87 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 7 Mar 2018 00:07:47 +0530 Subject: remove relative path --- app/assets/javascripts/pages/projects/snippets/show/index.js | 6 +++--- app/assets/javascripts/pages/snippets/show/index.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js index d85cb6439ee..c35b9c30058 100644 --- a/app/assets/javascripts/pages/projects/snippets/show/index.js +++ b/app/assets/javascripts/pages/projects/snippets/show/index.js @@ -1,8 +1,8 @@ import initNotes from '~/init_notes'; import ZenMode from '~/zen_mode'; -import LineHighlighter from '../../../../line_highlighter'; -import BlobViewer from '../../../../blob/viewer'; -import snippetEmbed from '../../../../snippet/snippet_embed'; +import LineHighlighter from '~/line_highlighter'; +import BlobViewer from '~/blob/viewer'; +import snippetEmbed from '~/snippet/snippet_embed'; document.addEventListener('DOMContentLoaded', () => { new LineHighlighter(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js index e3eded8ab9d..26936110402 100644 --- a/app/assets/javascripts/pages/snippets/show/index.js +++ b/app/assets/javascripts/pages/snippets/show/index.js @@ -1,8 +1,8 @@ -import LineHighlighter from '../../../line_highlighter'; -import BlobViewer from '../../../blob/viewer'; -import ZenMode from '../../../zen_mode'; -import initNotes from '../../../init_notes'; -import snippetEmbed from '../../../snippet/snippet_embed'; +import LineHighlighter from '~/line_highlighter'; +import BlobViewer from '~/blob/viewer'; +import ZenMode from '~/zen_mode'; +import initNotes from '~/init_notes'; +import snippetEmbed from '~/snippet/snippet_embed'; document.addEventListener('DOMContentLoaded', () => { new LineHighlighter(); // eslint-disable-line no-new -- cgit v1.2.1 From 460d03b75f5087e74494cb31b28a8c7489f1ce2c Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 7 Mar 2018 00:14:57 +0530 Subject: changes to url posiion --- app/assets/javascripts/snippet/snippet_embed.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index 62af55addfc..700247cfdb3 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -4,19 +4,20 @@ export default () => { const embedBtn = document.querySelector('#embed-btn'); const snippetUrlArea = document.querySelector('#snippet-url-area'); const embedAction = document.querySelector('#embed-action'); + const url = `${protocol}//${host + pathname}`; shareBtn.addEventListener('click', () => { shareBtn.classList.add('is-active'); embedBtn.classList.remove('is-active'); - snippetUrlArea.value = `${protocol}//${host + pathname}`; - embedAction.innerHTML = 'Share'; + snippetUrlArea.value = url; + embedAction.innerText = 'Share'; }); embedBtn.addEventListener('click', () => { embedBtn.classList.add('is-active'); shareBtn.classList.remove('is-active'); - const scriptTag = ``; + const scriptTag = ``; snippetUrlArea.value = scriptTag; - embedAction.innerHTML = 'Embed'; + embedAction.innerText = 'Embed'; }); }; -- cgit v1.2.1 From 768c4c7a6098ef458cade312a720a142277e505b Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 7 Mar 2018 00:32:02 +0530 Subject: removed unwated column from css --- app/assets/stylesheets/snippets.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index e39ee13be8d..14e6b5a1e1e 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -17,7 +17,6 @@ display: inline-block; background: url(asset_path('ext_snippet_icons/ext_snippet_icons.png')) no-repeat; overflow: hidden; - text-indent: -9999px; text-align: left; width: 16px; height: 16px; -- cgit v1.2.1 From c5cd31142bd40fe2529d2c5009cbb1ae664d9c1d Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 7 Mar 2018 00:58:15 +0530 Subject: button type and i18n support --- app/views/shared/snippets/_header.html.haml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 6379dbc7b1b..43567bae619 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -33,18 +33,18 @@ .embed-snippet .input-group .input-group-btn - %button.btn.embed-toggle{ 'data-toggle': 'dropdown' } - %span#embed-action Embed + %button.btn.embed-toggle{ 'data-toggle': 'dropdown', type: 'button' } + %span#embed-action= _("Embed") = sprite_icon('angle-down', size: 12) %ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list %li - %button#embed-btn.btn.btn-transparent.is-active{ href: "#" } - %strong.embed-toggle-list-item Embed + %button#embed-btn.btn.btn-transparent.is-active{ type: 'button' } + %strong.embed-toggle-list-item= _("Embed") %li - %button#share-btn.btn.btn-transparent{ href: "#" } - %strong.embed-toggle-list-item Share + %button#share-btn.btn.btn-transparent{ type: 'button' } + %strong.embed-toggle-list-item= _("Share") %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn - %button#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } + %button#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", type: "button", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } = sprite_icon('duplicate', size: 16) .clearfix -- cgit v1.2.1 From 101c37085f5342189b64a04f19c742cbd91e01f4 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 7 Mar 2018 18:36:16 +0530 Subject: changes based on review --- app/assets/javascripts/snippet/snippet_embed.js | 8 ++++---- app/assets/stylesheets/snippets.scss | 6 +++--- app/helpers/snippets_helper.rb | 2 +- app/views/shared/snippets/_header.html.haml | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index 700247cfdb3..81ec483f2d9 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -1,9 +1,9 @@ export default () => { const { protocol, host, pathname } = location; - const shareBtn = document.querySelector('#share-btn'); - const embedBtn = document.querySelector('#embed-btn'); - const snippetUrlArea = document.querySelector('#snippet-url-area'); - const embedAction = document.querySelector('#embed-action'); + const shareBtn = document.querySelector('.js-share-btn'); + const embedBtn = document.querySelector('.js-embed-btn'); + const snippetUrlArea = document.querySelector('.js-snippet-url-area'); + const embedAction = document.querySelector('.js-embed-action'); const url = `${protocol}//${host + pathname}`; shareBtn.addEventListener('click', () => { diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index 14e6b5a1e1e..0d6b0735f70 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -22,9 +22,9 @@ height: 16px; background-size: cover; - &.gl-snippet-icon-doc_code { background-position: -0 -0; } - &.gl-snippet-icon-doc_text { background-position: -0 -16px; } - &.gl-snippet-icon-download { background-position: -0 -32px; } + &.gl-snippet-icon-doc_code { background-position: 0 0; } + &.gl-snippet-icon-doc_text { background-position: 0 -16px; } + &.gl-snippet-icon-download { background-position: 0 -32px; } } .blob-viewer { diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 830db46d67f..59e0d192ffa 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -116,7 +116,7 @@ module SnippetsHelper raw_project_snippet_url(@snippet.project, @snippet) end - link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw', data: { container: 'body' } + link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw' end def embedded_snippet_download_button diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 43567bae619..836230ae8ee 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -34,17 +34,17 @@ .input-group .input-group-btn %button.btn.embed-toggle{ 'data-toggle': 'dropdown', type: 'button' } - %span#embed-action= _("Embed") + %span.js-embed-action= _("Embed") = sprite_icon('angle-down', size: 12) %ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list %li - %button#embed-btn.btn.btn-transparent.is-active{ type: 'button' } + %button.js-embed-btn.btn.btn-transparent.is-active{ type: 'button' } %strong.embed-toggle-list-item= _("Embed") %li - %button#share-btn.btn.btn-transparent{ type: 'button' } + %button.js-share-btn.btn.btn-transparent{ type: 'button' } %strong.embed-toggle-list-item= _("Share") - %input#snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } + %input.js-snippet-url-area.snippet-embed-input.form-control{ type: "text", autocomplete: 'off', value: snippet_embed } .input-group-btn - %button#clipboard-btn.btn.btn-default{ title: "Copy to clipboard", type: "button", data: { toggle: "tooltip", placement: "bottom", title: "Copy source to clipboard", container: "body", clipboard_target: '#snippet-url-area' } } + %button.js-clipboard-btn.btn.btn-default.has-tooltip{ title: "Copy to clipboard", 'data-clipboard-target': '#snippet-url-area' } = sprite_icon('duplicate', size: 16) .clearfix -- cgit v1.2.1 From d23337fd96058d38d6615e210b61830a3d334d46 Mon Sep 17 00:00:00 2001 From: haseeb Date: Wed, 7 Mar 2018 20:24:16 +0530 Subject: fix failing spec --- app/helpers/snippets_helper.rb | 2 +- spec/helpers/snippets_helper_spec.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 59e0d192ffa..733832c1bbb 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -126,7 +126,7 @@ module SnippetsHelper raw_project_snippet_url(@snippet.project, @snippet, inline: false) end - link_to external_snippet_icon('download'), download_url, class: 'btn', target: '_blank', title: 'Download', data: { container: 'body' }, rel: 'noopener noreferrer' + link_to external_snippet_icon('download'), download_url, class: 'btn', target: '_blank', title: 'Download', rel: 'noopener noreferrer' end def public_snippet? diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb index d432875b5ab..0323ffb641c 100644 --- a/spec/helpers/snippets_helper_spec.rb +++ b/spec/helpers/snippets_helper_spec.rb @@ -7,13 +7,13 @@ describe SnippetsHelper do it 'gives view raw button of embedded snippets for project snippets' do @snippet = create(:project_snippet, :public) - expect(embedded_snippet_raw_button.to_s).to eq("#{external_snippet_icon('doc_code')}") + expect(embedded_snippet_raw_button.to_s).to eq("#{external_snippet_icon('doc_code')}") end it 'gives view raw button of embedded snippets for personal snippets' do @snippet = create(:personal_snippet, :public) - expect(embedded_snippet_raw_button.to_s).to eq("#{external_snippet_icon('doc_code')}") + expect(embedded_snippet_raw_button.to_s).to eq("#{external_snippet_icon('doc_code')}") end end @@ -21,13 +21,13 @@ describe SnippetsHelper do it 'gives download button of embedded snippets for project snippets' do @snippet = create(:project_snippet, :public) - expect(embedded_snippet_download_button.to_s).to eq("#{external_snippet_icon('download')}") + expect(embedded_snippet_download_button.to_s).to eq("#{external_snippet_icon('download')}") end it 'gives download button of embedded snippets for personal snippets' do @snippet = create(:personal_snippet, :public) - expect(embedded_snippet_download_button.to_s).to eq("#{external_snippet_icon('download')}") + expect(embedded_snippet_download_button.to_s).to eq("#{external_snippet_icon('download')}") end end end -- cgit v1.2.1 From 35dc722f8071fad7731edd22e62c3c20a3daa9e4 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 9 Mar 2018 11:04:38 +0000 Subject: Change unneeded let to set --- spec/views/shared/milestones/_top.html.haml.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/views/shared/milestones/_top.html.haml.rb b/spec/views/shared/milestones/_top.html.haml.rb index 50efe1d5377..516d81c87ac 100644 --- a/spec/views/shared/milestones/_top.html.haml.rb +++ b/spec/views/shared/milestones/_top.html.haml.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'shared/milestones/_top.html.haml' do - let(:group) { create(:group) } + set(:group) { create(:group) } let(:project) { create(:project, group: group) } let(:milestone) { create(:milestone, project: project) } -- cgit v1.2.1 From 8bbaf266cc8d1ff03c4f7a4173ce9a7ac60c0cbb Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 8 Mar 2018 17:33:06 +0000 Subject: Handle empty states for job page --- app/views/projects/jobs/_empty_state.html.haml | 5 ++-- app/views/projects/jobs/_empty_status.html.haml | 29 ++++++++++++++++++++++ app/views/projects/jobs/show.html.haml | 23 +++-------------- .../unreleased/42568-pipeline-empty-state.yml | 5 ++++ db/fixtures/development/14_pipelines.rb | 4 +++ spec/features/projects/jobs_spec.rb | 27 ++++++++++++++++++++ 6 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 app/views/projects/jobs/_empty_status.html.haml create mode 100644 changelogs/unreleased/42568-pipeline-empty-state.yml diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml index c66313bdbf3..311934d9c33 100644 --- a/app/views/projects/jobs/_empty_state.html.haml +++ b/app/views/projects/jobs/_empty_state.html.haml @@ -1,7 +1,7 @@ - illustration = local_assigns.fetch(:illustration) - illustration_size = local_assigns.fetch(:illustration_size) - title = local_assigns.fetch(:title) -- content = local_assigns.fetch(:content) +- content = local_assigns.fetch(:content, nil) - action = local_assigns.fetch(:action, nil) .row.empty-state @@ -11,7 +11,8 @@ .col-xs-12 .text-content %h4.text-center= title - %p= content + - if content + %p= content - if action .text-center = action diff --git a/app/views/projects/jobs/_empty_status.html.haml b/app/views/projects/jobs/_empty_status.html.haml new file mode 100644 index 00000000000..707085cddd5 --- /dev/null +++ b/app/views/projects/jobs/_empty_status.html.haml @@ -0,0 +1,29 @@ +- if @build.playable? + = render 'empty_state', + illustration: 'illustrations/manual_action.svg', + illustration_size: 'svg-394', + title: _('This job requires a manual action'), + content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'), + action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') ) +- elsif @build.created? + = render 'empty_state', + illustration: 'illustrations/job_not_triggered.svg', + illustration_size: 'svg-306', + title: _('This job has not been triggered yet'), + content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered') +- elsif @build.canceled? + = render 'empty_state', + illustration: 'illustrations/canceled-job_empty.svg', + illustration_size: 'svg-430', + title: _('This job has been canceled') +- elsif @build.skipped? + = render 'empty_state', + illustration: 'illustrations/canceled-job_empty.svg', + illustration_size: 'svg-430', + title: _('This job has been skipped') +- else + = render 'empty_state', + illustration: 'illustrations/pending_job_empty.svg', + illustration_size: 'svg-430', + title: _('This job has not started yet'), + content: _('This job is in pending state and is waiting to be picked by a runner') diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 849c273db8c..04c28841511 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -54,7 +54,8 @@ Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)} - else Job has been erased #{time_ago_with_tooltip(@build.erased_at)} - - if @build.started? + + - if @build.has_trace? .build-trace-container.prepend-top-default .top-bar.js-top-bar .js-truncated-info.truncated-info.hidden-xs.pull-left.hidden< @@ -88,25 +89,9 @@ %pre.build-trace#build-trace %code.bash.js-build-output .build-loader-animation.js-build-refresh - - elsif @build.playable? - = render 'empty_state', - illustration: 'illustrations/manual_action.svg', - illustration_size: 'svg-394', - title: _('This job requires a manual action'), - content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'), - action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') ) - - elsif @build.created? - = render 'empty_state', - illustration: 'illustrations/job_not_triggered.svg', - illustration_size: 'svg-306', - title: _('This job has not been triggered yet'), - content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered') - else - = render 'empty_state', - illustration: 'illustrations/pending_job_empty.svg', - illustration_size: 'svg-430', - title: _('This job has not started yet'), - content: _('This job is in pending state and is waiting to be picked by a runner') + = render "empty_status" + = render "sidebar" .js-build-options{ data: javascript_build_options } diff --git a/changelogs/unreleased/42568-pipeline-empty-state.yml b/changelogs/unreleased/42568-pipeline-empty-state.yml new file mode 100644 index 00000000000..d36edcf1b37 --- /dev/null +++ b/changelogs/unreleased/42568-pipeline-empty-state.yml @@ -0,0 +1,5 @@ +--- +title: Improve empty state for canceled job +merge_request: 17646 +author: +type: fixed diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb index d3a63aa2a78..fe21b7aad0e 100644 --- a/db/fixtures/development/14_pipelines.rb +++ b/db/fixtures/development/14_pipelines.rb @@ -30,6 +30,10 @@ class Gitlab::Seeder::Pipelines queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, { name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true, queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'spinach:osx1', stage: 'test', status: :canceled, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'spinach:osx1', stage: 'test', status: :running, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, # deploy stage { name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success, diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 5d311f2dde3..aa4c2757a25 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -419,6 +419,33 @@ feature 'Jobs' do expect(page).to have_content('This job is in pending state and is waiting to be picked by a runner') end end + + context 'Canceled job' do + context 'with log' do + let(:job) { create(:ci_build, :canceled, :trace_artifact, pipeline: pipeline) } + + before do + visit project_job_path(project, job) + end + + it 'renders job log' do + expect(page).to have_selector('.js-build-output') + end + end + + context 'without log' do + let(:job) { create(:ci_build, :canceled, pipeline: pipeline) } + + before do + visit project_job_path(project, job) + end + + it 'renders empty state' do + expect(page).not_to have_selector('.js-build-output') + expect(page).to have_content('This job has been canceled') + end + end + end end describe "POST /:project/jobs/:id/cancel", :js do -- cgit v1.2.1 From cd6a58c8100356be95019be6a69724066469bd42 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 9 Mar 2018 17:59:59 +0000 Subject: Fix broken test --- features/steps/shared/builds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/shared/builds.rb b/features/steps/shared/builds.rb index a3e4459f169..f5950145348 100644 --- a/features/steps/shared/builds.rb +++ b/features/steps/shared/builds.rb @@ -11,7 +11,7 @@ module SharedBuilds step 'project has a recent build' do @pipeline = create(:ci_empty_pipeline, project: @project, sha: @project.commit.sha, ref: 'master') - @build = create(:ci_build, :running, :coverage, pipeline: @pipeline) + @build = create(:ci_build, :running, :coverage, :trace_artifact, pipeline: @pipeline) end step 'recent build is successful' do -- cgit v1.2.1 From f9dc1e86881f21b0960d0035c1e051c2e54fdd80 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 12 Mar 2018 14:40:17 +0000 Subject: Remove js-toggle classes --- app/views/shared/milestones/_deprecation_message.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index 714739a1ca7..9f049e7bde1 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -1,13 +1,13 @@ -.milestone-deprecation-message.prepend-top-default.js-toggle-container +.milestone-deprecation-message.prepend-top-default = image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' .inline.vertical-align-middle.text-container %strong This page will be removed in a future release. %p.append-bottom-default.prepend-top-5 Use group milestones to manage issues from multiple projects in the same milestone. %br - = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link js-toggle-button' + = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-blank' - .popup.js-toggle-content.hide + .popup.hide .body %ol.instructions-list %li -- cgit v1.2.1 From 9191f96ab762a5704165120a66131df24f61a272 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 12 Mar 2018 16:11:57 +0000 Subject: Fix illustration padding and center for mobiel --- app/assets/stylesheets/pages/milestone.scss | 8 ++++++++ app/views/shared/milestones/_deprecation_message.html.haml | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 9b5087caacf..b7fe269c7c8 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -217,4 +217,12 @@ .text-container { position: relative; } + + @media (max-width: $screen-xs-max) { + .illustration { + display: block; + text-align: center; + margin: $gl-padding 0; + } + } } diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index 9f049e7bde1..c9a5b273173 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -1,5 +1,5 @@ -.milestone-deprecation-message.prepend-top-default - = image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' +.milestone-deprecation-message.prepend-top-default.prepend-left-default + .illustration.inline= image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' .inline.vertical-align-middle.text-container %strong This page will be removed in a future release. %p.append-bottom-default.prepend-top-5 -- cgit v1.2.1 From e5f3696774d893bc2a83086939b090edd1775118 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 12 Mar 2018 16:19:08 +0000 Subject: Center everything on mobile instead and correct btn-link changes --- app/assets/stylesheets/framework/buttons.scss | 2 -- app/assets/stylesheets/pages/milestone.scss | 7 ++++++- app/views/shared/milestones/_deprecation_message.html.haml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index c358e9e93de..06dabcc77b5 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -423,8 +423,6 @@ } .btn-link { - padding: 0; - &.btn-secondary-hover-link { color: $gl-text-color-secondary; diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index b7fe269c7c8..1b9bab56979 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -218,10 +218,15 @@ position: relative; } + .btn-link { + padding: 0; + } + @media (max-width: $screen-xs-max) { + text-align: center; + .illustration { display: block; - text-align: center; margin: $gl-padding 0; } } diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index c9a5b273173..c32fca296bc 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -5,7 +5,7 @@ %p.append-bottom-default.prepend-top-5 Use group milestones to manage issues from multiple projects in the same milestone. %br - = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-blank' + = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link' .popup.hide .body -- cgit v1.2.1 From c7776a1d32621740f1eea17ec0385889280df65e Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 12 Mar 2018 19:13:13 +0000 Subject: Render skipped illustration for skipped state --- app/views/projects/jobs/_empty_status.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/jobs/_empty_status.html.haml b/app/views/projects/jobs/_empty_status.html.haml index 707085cddd5..9e144dbfe32 100644 --- a/app/views/projects/jobs/_empty_status.html.haml +++ b/app/views/projects/jobs/_empty_status.html.haml @@ -18,7 +18,7 @@ title: _('This job has been canceled') - elsif @build.skipped? = render 'empty_state', - illustration: 'illustrations/canceled-job_empty.svg', + illustration: 'illustrations/skipped-job_empty.svg', illustration_size: 'svg-430', title: _('This job has been skipped') - else -- cgit v1.2.1 From 5a68a96ecb3cdba0e3da7aa210f98369e02359ab Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 13 Mar 2018 14:43:22 +0000 Subject: i8n --- .../milestones/_deprecation_message.html.haml | 30 ++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index c32fca296bc..363ce62b73c 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -1,23 +1,15 @@ .milestone-deprecation-message.prepend-top-default.prepend-left-default .illustration.inline= image_tag 'illustrations/milestone_removing-page.svg', class: 'append-right-default' - .inline.vertical-align-middle.text-container - %strong This page will be removed in a future release. + .inline.vertical-align-middle + %strong= _('This page will be removed in a future release.') %p.append-bottom-default.prepend-top-5 - Use group milestones to manage issues from multiple projects in the same milestone. + = _('Use group milestones to manage issues from multiple projects in the same milestone.') %br - = button_tag 'Promote these project milestones into a group milestone.', class: 'btn-link' - - .popup.hide - .body - %ol.instructions-list - %li - Click any - %strong project name - in the project list below to navigate to the project milestone. - %li - Click the - %strong Promote - button in the top right corner to promote it to a group milestone. - .footer= link_to 'Learn more', help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' - - %div= link_to 'Learn more', help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' + = button_tag _('Promote these project milestones into a group milestone.'), class: 'btn-link popover-link' + %div= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' + + %script.milestone-deprecation-message-template{ type: 'text/template' } + %ol.instructions-list.append-bottom-0 + %li= _('Click any project name in the project list below to navigate to the project milestone.') + %li= _('Click the Promote button in the top right corner to promote it to a group milestone.') + .footer= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' \ No newline at end of file -- cgit v1.2.1 From ce3ff3b8450dbe8c33bdeb9eeabfb753228a1c2f Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 13 Mar 2018 14:43:27 +0000 Subject: add changellg --- changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml diff --git a/changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml b/changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml new file mode 100644 index 00000000000..3e1ac7b795d --- /dev/null +++ b/changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml @@ -0,0 +1,5 @@ +--- +title: Add deprecation message to dynamic milestone pages +merge_request: 17505 +author: +type: added -- cgit v1.2.1 From 607636b92080b3760568f36c341cbe71edfdc541 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 13 Mar 2018 14:43:39 +0000 Subject: Correct styling now we've changed to popover --- app/assets/stylesheets/pages/milestone.scss | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 1b9bab56979..755a1eb5ee3 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -196,26 +196,26 @@ } .milestone-deprecation-message { - .popup { - max-width: 300px; - position: absolute; - left: 30px; + .popover { + padding: 0; - .body { - padding: $gl-padding-8; + .popover-content { + padding: 0; } .footer { + background-color: $white-light; padding: $gl-padding-8 $gl-padding; + border-bottom-left-radius: $border-radius-default; + border-bottom-right-radius: $border-radius-default; + border-top: 1px solid $white-dark; } - } - .instructions-list { - padding: 0 0 0 30px; - } - - .text-container { - position: relative; + .instructions-list { + list-style-position: inside; + padding: $gl-padding-8 20px; + background-color: $gray-light; + } } .btn-link { -- cgit v1.2.1 From 978d553ad00fad714dd926b5e38fe927fb72051f Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 13 Mar 2018 14:44:06 +0000 Subject: Init deprecation message. Incomplete. --- app/assets/javascripts/milestone.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index b1d74250dfd..ab97fa50fd5 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -3,6 +3,7 @@ import flash from './flash'; export default class Milestone { constructor() { + Milestone.initDeprecationMessage(); this.bindTabsSwitching(); // Load merge request tab if it is active @@ -42,4 +43,37 @@ export default class Milestone { .catch(() => flash('Error loading milestone tab')); } } + + static initDeprecationMessage() { + const deprecationMesssage = document.querySelector('.milestone-deprecation-message'); + + if (!deprecationMesssage) return; + + const deprecationMesssageTemplate = deprecationMesssage.querySelector('.milestone-deprecation-message-template'); + const popoverLink = deprecationMesssage.querySelector('.popover-link'); + const $popoverLink = $(popoverLink); + + $popoverLink + .popover({ + html: true, + placement: 'bottom', + content: deprecationMesssageTemplate.innerHTML, + trigger: 'hover', + }) + .on('inserted.bs.popover', () => { + const $popover = $popoverLink.siblings('.popover').first(); + const $popoverContent = $('.popover-content', $popover); + + $popoverContent.on('mouseleave', () => { + $popoverContent.off('mouseleave'); + $popoverLink.popover('hide'); + }); + }) + .on('hidden.bs.popover', (event) => { + $(event.target).data('bs.popover').inState.click = false; + }) + .on('mouseleave', () => { + + }); + } } -- cgit v1.2.1 From 520c93717a40e887b57afd86376c82db77e33e72 Mon Sep 17 00:00:00 2001 From: haseeb Date: Sat, 17 Mar 2018 17:47:43 +0530 Subject: integration test added --- spec/features/snippets/embedded_snippet_spec.rb | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 spec/features/snippets/embedded_snippet_spec.rb diff --git a/spec/features/snippets/embedded_snippet_spec.rb b/spec/features/snippets/embedded_snippet_spec.rb new file mode 100644 index 00000000000..d67f47279c5 --- /dev/null +++ b/spec/features/snippets/embedded_snippet_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe 'Embedded Snippets' do + include RSpec::Rails::RequestExampleGroup + + let(:project) { create(:project, :repository) } + let(:snippet) { create(:personal_snippet, :public, file_name: 'popen.rb', content: content) } + let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data } + + after do + FileUtils.rm_f([File.join(File.dirname(__FILE__), 'embedded_snippet.html'), File.join(File.dirname(__FILE__), 'snippet.js')]) + end + + it 'loads snippet', :js do + get "#{snippet_path(snippet)}.js" + + File.write(File.join(File.dirname(__FILE__), 'snippet.js'), response.body) + + script_tag = "" + File.write(File.join(File.dirname(__FILE__), 'embedded_snippet.html'), script_tag) + + Capybara.app = Rack::File.new File.dirname __FILE__ + + visit('embedded_snippet.html') + + expect(page).to have_content("popen.rb") + + expect(page).to have_content("require 'fileutils'") + end +end -- cgit v1.2.1 From 18bce8fcfeaaeeda5aea37f21adedcdc49dd4147 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Mar 2018 10:07:30 +0000 Subject: Update partial name and remove changes to database fixtures --- app/views/projects/jobs/_empty_states.html.haml | 29 +++++++++++++++++++++++++ app/views/projects/jobs/_empty_status.html.haml | 29 ------------------------- app/views/projects/jobs/show.html.haml | 2 +- db/fixtures/development/14_pipelines.rb | 4 ---- 4 files changed, 30 insertions(+), 34 deletions(-) create mode 100644 app/views/projects/jobs/_empty_states.html.haml delete mode 100644 app/views/projects/jobs/_empty_status.html.haml diff --git a/app/views/projects/jobs/_empty_states.html.haml b/app/views/projects/jobs/_empty_states.html.haml new file mode 100644 index 00000000000..9e144dbfe32 --- /dev/null +++ b/app/views/projects/jobs/_empty_states.html.haml @@ -0,0 +1,29 @@ +- if @build.playable? + = render 'empty_state', + illustration: 'illustrations/manual_action.svg', + illustration_size: 'svg-394', + title: _('This job requires a manual action'), + content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'), + action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') ) +- elsif @build.created? + = render 'empty_state', + illustration: 'illustrations/job_not_triggered.svg', + illustration_size: 'svg-306', + title: _('This job has not been triggered yet'), + content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered') +- elsif @build.canceled? + = render 'empty_state', + illustration: 'illustrations/canceled-job_empty.svg', + illustration_size: 'svg-430', + title: _('This job has been canceled') +- elsif @build.skipped? + = render 'empty_state', + illustration: 'illustrations/skipped-job_empty.svg', + illustration_size: 'svg-430', + title: _('This job has been skipped') +- else + = render 'empty_state', + illustration: 'illustrations/pending_job_empty.svg', + illustration_size: 'svg-430', + title: _('This job has not started yet'), + content: _('This job is in pending state and is waiting to be picked by a runner') diff --git a/app/views/projects/jobs/_empty_status.html.haml b/app/views/projects/jobs/_empty_status.html.haml deleted file mode 100644 index 9e144dbfe32..00000000000 --- a/app/views/projects/jobs/_empty_status.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -- if @build.playable? - = render 'empty_state', - illustration: 'illustrations/manual_action.svg', - illustration_size: 'svg-394', - title: _('This job requires a manual action'), - content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'), - action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') ) -- elsif @build.created? - = render 'empty_state', - illustration: 'illustrations/job_not_triggered.svg', - illustration_size: 'svg-306', - title: _('This job has not been triggered yet'), - content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered') -- elsif @build.canceled? - = render 'empty_state', - illustration: 'illustrations/canceled-job_empty.svg', - illustration_size: 'svg-430', - title: _('This job has been canceled') -- elsif @build.skipped? - = render 'empty_state', - illustration: 'illustrations/skipped-job_empty.svg', - illustration_size: 'svg-430', - title: _('This job has been skipped') -- else - = render 'empty_state', - illustration: 'illustrations/pending_job_empty.svg', - illustration_size: 'svg-430', - title: _('This job has not started yet'), - content: _('This job is in pending state and is waiting to be picked by a runner') diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 04c28841511..1db01133900 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -90,7 +90,7 @@ %code.bash.js-build-output .build-loader-animation.js-build-refresh - else - = render "empty_status" + = render "empty_states" = render "sidebar" diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb index fe21b7aad0e..d3a63aa2a78 100644 --- a/db/fixtures/development/14_pipelines.rb +++ b/db/fixtures/development/14_pipelines.rb @@ -30,10 +30,6 @@ class Gitlab::Seeder::Pipelines queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, { name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true, queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, - { name: 'spinach:osx1', stage: 'test', status: :canceled, - queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, - { name: 'spinach:osx1', stage: 'test', status: :running, - queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, # deploy stage { name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success, -- cgit v1.2.1 From 9a304721d4aa735e9229d7a019b8e2187d0361d6 Mon Sep 17 00:00:00 2001 From: haseeb Date: Mon, 19 Mar 2018 19:00:53 +0530 Subject: updated integration test --- spec/features/snippets/embedded_snippet_spec.rb | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/spec/features/snippets/embedded_snippet_spec.rb b/spec/features/snippets/embedded_snippet_spec.rb index d67f47279c5..3cbefe556c7 100644 --- a/spec/features/snippets/embedded_snippet_spec.rb +++ b/spec/features/snippets/embedded_snippet_spec.rb @@ -1,30 +1,29 @@ require 'spec_helper' describe 'Embedded Snippets' do - include RSpec::Rails::RequestExampleGroup - let(:project) { create(:project, :repository) } let(:snippet) { create(:personal_snippet, :public, file_name: 'popen.rb', content: content) } let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data } - after do - FileUtils.rm_f([File.join(File.dirname(__FILE__), 'embedded_snippet.html'), File.join(File.dirname(__FILE__), 'snippet.js')]) - end - it 'loads snippet', :js do - get "#{snippet_path(snippet)}.js" + script_url = "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}/#{snippet_path(snippet, format: 'js')}" + embed_body = "" - File.write(File.join(File.dirname(__FILE__), 'snippet.js'), response.body) + rack_app = proc do + ['200', { 'Content-Type' => 'text/html' }, [embed_body]] + end - script_tag = "" - File.write(File.join(File.dirname(__FILE__), 'embedded_snippet.html'), script_tag) + server = Capybara::Server.new(rack_app) + server.boot - Capybara.app = Rack::File.new File.dirname __FILE__ - - visit('embedded_snippet.html') + visit("http://#{server.host}:#{server.port}/embedded_snippet.html") expect(page).to have_content("popen.rb") expect(page).to have_content("require 'fileutils'") + + expect(page).to have_link('Open raw') + + expect(page).to have_link('Download') end end -- cgit v1.2.1 From a0f585000fe2d96611e93d7ef3c16bba10f485b0 Mon Sep 17 00:00:00 2001 From: haseeb Date: Mon, 19 Mar 2018 19:26:18 +0530 Subject: removed creating project --- spec/features/snippets/embedded_snippet_spec.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/features/snippets/embedded_snippet_spec.rb b/spec/features/snippets/embedded_snippet_spec.rb index 3cbefe556c7..474d9fd8a33 100644 --- a/spec/features/snippets/embedded_snippet_spec.rb +++ b/spec/features/snippets/embedded_snippet_spec.rb @@ -1,9 +1,8 @@ require 'spec_helper' describe 'Embedded Snippets' do - let(:project) { create(:project, :repository) } - let(:snippet) { create(:personal_snippet, :public, file_name: 'popen.rb', content: content) } - let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data } + let(:snippet) { create(:personal_snippet, :public, file_name: 'random_dir.rb', content: content) } + let(:content) { "require 'fileutils'\nFileUtils.mkdir_p 'some/random_dir'\n" } it 'loads snippet', :js do script_url = "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}/#{snippet_path(snippet, format: 'js')}" @@ -18,7 +17,7 @@ describe 'Embedded Snippets' do visit("http://#{server.host}:#{server.port}/embedded_snippet.html") - expect(page).to have_content("popen.rb") + expect(page).to have_content("random_dir.rb") expect(page).to have_content("require 'fileutils'") -- cgit v1.2.1 From a5ab67228f07f6fa1a5db7da07601643b87f23ff Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 20 Mar 2018 18:36:58 +0000 Subject: Fix translation strings and add _blank as link targets --- app/views/shared/milestones/_deprecation_message.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml index 363ce62b73c..a92bfd3b9df 100644 --- a/app/views/shared/milestones/_deprecation_message.html.haml +++ b/app/views/shared/milestones/_deprecation_message.html.haml @@ -6,10 +6,10 @@ = _('Use group milestones to manage issues from multiple projects in the same milestone.') %br = button_tag _('Promote these project milestones into a group milestone.'), class: 'btn-link popover-link' - %div= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default' + %div= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default', target: '_blank' %script.milestone-deprecation-message-template{ type: 'text/template' } %ol.instructions-list.append-bottom-0 - %li= _('Click any project name in the project list below to navigate to the project milestone.') - %li= _('Click the Promote button in the top right corner to promote it to a group milestone.') - .footer= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link' \ No newline at end of file + %li= _('Click any project name in the project list below to navigate to the project milestone.').html_safe + %li= _('Click the Promote button in the top right corner to promote it to a group milestone.').html_safe + .footer= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn-link', target: '_blank' \ No newline at end of file -- cgit v1.2.1 From 0488d44cc2cc55808cf5aa17cb1b0c5423c958db Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 21 Mar 2018 18:23:33 +0000 Subject: Add popover --- app/assets/javascripts/shared/popover.js | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 app/assets/javascripts/shared/popover.js diff --git a/app/assets/javascripts/shared/popover.js b/app/assets/javascripts/shared/popover.js new file mode 100644 index 00000000000..48894c2b100 --- /dev/null +++ b/app/assets/javascripts/shared/popover.js @@ -0,0 +1,84 @@ +export default class Popover { + constructor(trigger, content) { + this.isOpen = false; + + this.$popover = $(trigger).popover({ + content, + html: true, + placement: 'bottom', + trigger: 'manual', + }); + } + + init() { + this.registerClickOpenListener(); + } + + openPopover() { + if (this.isOpen) return; + + this.$popover.popover('show'); + this.$popover.one('shown.bs.popover', this.enableClose.bind(this)); + this.isOpen = true; + } + + closePopover() { + if (!this.isOpen) return; + + this.$popover.popover('hide'); + this.disableClose(); + this.isOpen = false; + } + + closePopoverClick(event) { + const $target = $(event.target); + + if ($target.is(this.$popover) || + $target.is('.popover') || + $target.parents('.popover').length > 0) return; + + this.closePopover(); + } + + closePopoverMouseleave() { + setTimeout(() => { + if (this.$popover.is(':hover') || + (this.$popover.siblings('.popover').length > 0 && + this.$popover.siblings('.popover').is(':hover'))) return; + + this.closePopover(); + }, 1500); + } + + registerClickOpenListener() { + this.$popover.on('click.glPopover.open', this.openPopover.bind(this)); + } + + registerClickCloseListener() { + $(document.body).on('click.glPopover.close', this.closePopoverClick.bind(this)); + } + + registerMouseleaveCloseListener() { + this.$popover.on('mouseleave.glPopover.close', this.closePopoverMouseleave.bind(this)); + this.$popover.siblings('.popover').on('mouseleave.glPopover.close', this.closePopoverMouseleave.bind(this)); + } + + destroyMouseleaveCloseListener() { + this.$popover.off('mouseleave.glPopover.close'); + this.$popover.siblings('.popover').on('mouseleave.glPopover.close'); + } + + enableClose() { + this.registerClickCloseListener(); + this.registerMouseleaveCloseListener(); + } + + disableClose() { + Popover.destroyClickCloseListener(); + this.destroyMouseleaveCloseListener(); + } + + static destroyClickCloseListener() { + $(document.body).off('click.glPopover.close'); + } +} -- cgit v1.2.1 From 5595afe807c3c11d9c695c558da1210a25544387 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 21 Mar 2018 18:23:42 +0000 Subject: Impl popover for milestone deprecation --- app/assets/javascripts/milestone.js | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index ab97fa50fd5..0f1dcc7e9e9 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -1,5 +1,6 @@ import axios from './lib/utils/axios_utils'; import flash from './flash'; +import Popover from './shared/popover'; export default class Milestone { constructor() { @@ -49,31 +50,10 @@ export default class Milestone { if (!deprecationMesssage) return; - const deprecationMesssageTemplate = deprecationMesssage.querySelector('.milestone-deprecation-message-template'); + const deprecationMesssageTemplate = deprecationMesssage.querySelector('.milestone-deprecation-message-template').innerHTML; const popoverLink = deprecationMesssage.querySelector('.popover-link'); - const $popoverLink = $(popoverLink); - $popoverLink - .popover({ - html: true, - placement: 'bottom', - content: deprecationMesssageTemplate.innerHTML, - trigger: 'hover', - }) - .on('inserted.bs.popover', () => { - const $popover = $popoverLink.siblings('.popover').first(); - const $popoverContent = $('.popover-content', $popover); - - $popoverContent.on('mouseleave', () => { - $popoverContent.off('mouseleave'); - $popoverLink.popover('hide'); - }); - }) - .on('hidden.bs.popover', (event) => { - $(event.target).data('bs.popover').inState.click = false; - }) - .on('mouseleave', () => { - - }); + const popover = new Popover(popoverLink, deprecationMesssageTemplate); + popover.init(); } } -- cgit v1.2.1 From 51c64f3fc7180732621d60f939bfe6157165040f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 21 Mar 2018 10:05:08 +0000 Subject: Added staged files state to IDE Closes https://gitlab.com/gitlab-org/gitlab-ee/issues/4541 --- .../ide/components/commit_sidebar/empty_state.vue | 87 ++++++++++++++++++ .../ide/components/commit_sidebar/list.vue | 94 +++++++++++++++++-- .../components/commit_sidebar/list_collapsed.vue | 53 ++++++++--- .../ide/components/commit_sidebar/list_item.vue | 73 ++++++++------- .../ide/components/commit_sidebar/stage_button.vue | 49 ++++++++++ .../components/commit_sidebar/unstage_button.vue | 38 ++++++++ .../javascripts/ide/components/ide_context_bar.vue | 42 --------- .../ide/components/repo_commit_section.vue | 101 +++++++-------------- app/assets/javascripts/ide/stores/actions.js | 15 +++ app/assets/javascripts/ide/stores/actions/file.js | 8 ++ app/assets/javascripts/ide/stores/getters.js | 2 + .../ide/stores/modules/commit/actions.js | 12 ++- .../javascripts/ide/stores/mutation_types.js | 4 + app/assets/javascripts/ide/stores/mutations.js | 5 + .../javascripts/ide/stores/mutations/file.js | 28 ++++++ app/assets/javascripts/ide/stores/state.js | 1 + app/assets/javascripts/ide/stores/utils.js | 28 +++--- app/assets/stylesheets/pages/repo.scss | 41 +++++++-- .../projects/tree/create_directory_spec.rb | 2 + spec/features/projects/tree/create_file_spec.rb | 2 + .../components/commit_sidebar/empty_state_spec.js | 95 +++++++++++++++++++ .../commit_sidebar/list_collapsed_spec.js | 50 +++++++++- .../components/commit_sidebar/list_item_spec.js | 24 +++-- .../ide/components/commit_sidebar/list_spec.js | 42 ++++++++- .../components/commit_sidebar/stage_button_spec.js | 46 ++++++++++ .../commit_sidebar/unstage_button_spec.js | 39 ++++++++ .../ide/components/repo_commit_section_spec.js | 97 +++++++++++++++++++- spec/javascripts/ide/stores/actions_spec.js | 78 ++++++++++++++++ spec/javascripts/ide/stores/getters_spec.js | 14 +-- .../ide/stores/modules/commit/actions_spec.js | 20 +++- spec/javascripts/ide/stores/mutations/file_spec.js | 66 ++++++++++++++ spec/javascripts/ide/stores/mutations_spec.js | 10 ++ 32 files changed, 1035 insertions(+), 231 deletions(-) create mode 100644 app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue create mode 100644 app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue create mode 100644 app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue create mode 100644 spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js create mode 100644 spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js create mode 100644 spec/javascripts/ide/components/commit_sidebar/unstage_button_spec.js diff --git a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue new file mode 100644 index 00000000000..e69535335d4 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue @@ -0,0 +1,87 @@ + + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue index 453208f3f19..e3280bbb204 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -1,5 +1,5 @@ diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index 18934af004a..93c8fc00f28 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -1,38 +1,44 @@ diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue new file mode 100644 index 00000000000..0189358d82f --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue @@ -0,0 +1,49 @@ + + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue new file mode 100644 index 00000000000..fd7ec0366a2 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue @@ -0,0 +1,38 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue index 79a83b47994..627fbeb9adf 100644 --- a/app/assets/javascripts/ide/components/ide_context_bar.vue +++ b/app/assets/javascripts/ide/components/ide_context_bar.vue @@ -1,5 +1,4 @@ @@ -41,40 +33,6 @@ export default {
-
-
-
- - Staged -
-
- -
this.commitChanges()); + return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => + this.commitChanges(), + ); }, }, }; @@ -77,9 +60,6 @@ export default { diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index 7e920aa9f30..639195308b2 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -33,6 +33,13 @@ export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => { } }; +export const toggleRightPanelCollapsed = ({ dispatch, state }) => { + dispatch('setPanelCollapsedStatus', { + side: 'right', + collapsed: !state.rightPanelCollapsed, + }); +}; + export const setResizingStatus = ({ commit }, resizing) => { commit(types.SET_RESIZING_STATUS, resizing); }; @@ -108,6 +115,14 @@ export const scrollToTab = () => { }); }; +export const stageAllChanges = ({ state, commit }) => { + [...state.changedFiles].forEach(file => commit(types.STAGE_CHANGE, file)); +}; + +export const unstageAllChanges = ({ state, commit }) => { + [...state.stagedFiles].forEach(file => commit(types.UNSTAGE_CHANGE, file)); +}; + export const updateViewer = ({ commit }, viewer) => { commit(types.UPDATE_VIEWER, viewer); }; diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index ddc4b757bf9..61aa0e983fc 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -144,3 +144,11 @@ export const discardFileChanges = ({ state, commit }, path) => { eventHub.$emit(`editor.update.model.content.${file.path}`, file.raw); }; + +export const stageChange = ({ commit }, file) => { + commit(types.STAGE_CHANGE, file); +}; + +export const unstageChange = ({ commit }, file) => { + commit(types.UNSTAGE_CHANGE, file); +}; diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index eba325a31df..85f9b75636a 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -28,3 +28,5 @@ export const currentIcon = state => state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right'; export const hasChanges = state => !!state.changedFiles.length; + +export const unstagedFiles = state => state.changedFiles.filter(f => !f.staged); diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index f536ce6344b..5346bbcdfd9 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -131,9 +131,10 @@ export const updateFilesAfterCommit = ( ); }); - commit(rootTypes.REMOVE_ALL_CHANGES_FILES, null, { root: true }); - - if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH) { + if ( + state.commitAction === consts.COMMIT_TO_NEW_BRANCH && + rootGetters.activeFile + ) { router.push( `/project/${rootState.currentProjectId}/blob/${branch}/${ rootGetters.activeFile.path @@ -186,7 +187,6 @@ export const commitChanges = ({ } dispatch('setLastCommitMessage', data); - dispatch('updateCommitMessage', ''); if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR) { dispatch( @@ -204,6 +204,10 @@ export const commitChanges = ({ branch: getters.branchName, }); } + + commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true }); + + dispatch('discardDraft'); }) .catch(err => { let errMsg = __('Error committing changes. Please try again.'); diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index e28f190897c..49eb30302c6 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -41,3 +41,7 @@ export const SET_ENTRIES = 'SET_ENTRIES'; export const CREATE_TMP_ENTRY = 'CREATE_TMP_ENTRY'; export const UPDATE_VIEWER = 'UPDATE_VIEWER'; export const UPDATE_DELAY_VIEWER_CHANGE = 'UPDATE_DELAY_VIEWER_CHANGE'; + +export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES'; +export const STAGE_CHANGE = 'STAGE_CHANGE'; +export const UNSTAGE_CHANGE = 'UNSTAGE_CHANGE'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index da41fc9285c..5409ec1ec47 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -51,6 +51,11 @@ export default { lastCommitMsg, }); }, + [types.CLEAR_STAGED_CHANGES](state) { + Object.assign(state, { + stagedFiles: [], + }); + }, [types.SET_ENTRIES](state, entries) { Object.assign(state, { entries, diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index 2500f13db7c..8e739a83270 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -1,4 +1,5 @@ import * as types from '../mutation_types'; +import { findIndexOfFile, findEntry } from '../utils'; export default { [types.SET_FILE_ACTIVE](state, { path, active }) { @@ -75,6 +76,33 @@ export default { changedFiles: state.changedFiles.filter(f => f.path !== path), }); }, + [types.STAGE_CHANGE](state, file) { + const stagedFile = findEntry(state.stagedFiles, 'blob', file.name); + + Object.assign(file, { + staged: true, + }); + + if (stagedFile) { + Object.assign(stagedFile, { + ...file, + }); + } else { + state.stagedFiles.push({ + ...file, + }); + } + }, + [types.UNSTAGE_CHANGE](state, file) { + const indexOfStagedFile = findIndexOfFile(state.stagedFiles, file); + const changedFile = findEntry(state.changedFiles, 'blob', file.name); + + state.stagedFiles.splice(indexOfStagedFile, 1); + + Object.assign(changedFile, { + staged: false, + }); + }, [types.TOGGLE_FILE_CHANGED](state, { file, changed }) { Object.assign(state.entries[file.path], { changed, diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 6110f54951c..6c09324e4ba 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -2,6 +2,7 @@ export default () => ({ currentProjectId: '', currentBranchId: '', changedFiles: [], + stagedFiles: [], endpoints: {}, lastCommitMsg: '', lastCommitPath: '', diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 487ea1ead8e..da0069b63a8 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -13,6 +13,7 @@ export const dataStructure = () => ({ opened: false, active: false, changed: false, + staged: false, lastCommitPath: '', lastCommit: { id: '', @@ -38,7 +39,7 @@ export const dataStructure = () => ({ eol: '', }); -export const decorateData = (entity) => { +export const decorateData = entity => { const { id, projectId, @@ -57,7 +58,6 @@ export const decorateData = (entity) => { base64 = false, file_lock, - } = entity; return { @@ -80,24 +80,23 @@ export const decorateData = (entity) => { base64, file_lock, - }; }; -export const findEntry = (tree, type, name, prop = 'name') => tree.find( - f => f.type === type && f[prop] === name, -); +export const findEntry = (tree, type, name, prop = 'name') => + tree.find(f => f.type === type && f[prop] === name); -export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path); +export const findIndexOfFile = (state, file) => + state.findIndex(f => f.path === file.path); -export const setPageTitle = (title) => { +export const setPageTitle = title => { document.title = title; }; export const createCommitPayload = (branch, newBranch, state, rootState) => ({ branch, commit_message: state.commitMessage, - actions: rootState.changedFiles.map(f => ({ + actions: rootState.stagedFiles.map(f => ({ action: f.tempFile ? 'create' : 'update', file_path: f.path, content: f.content, @@ -120,6 +119,11 @@ const sortTreesByTypeAndName = (a, b) => { return 0; }; -export const sortTree = sortedTree => sortedTree.map(entity => Object.assign(entity, { - tree: entity.tree.length ? sortTree(entity.tree) : [], -})).sort(sortTreesByTypeAndName); +export const sortTree = sortedTree => + sortedTree + .map(entity => + Object.assign(entity, { + tree: entity.tree.length ? sortTree(entity.tree) : [], + }), + ) + .sort(sortTreesByTypeAndName); diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 7a8fbfc517d..57393cf65e7 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -449,9 +449,13 @@ flex: 1; } -.multi-file-commit-empty-state-container { - align-items: center; - justify-content: center; +.ide-commity-empty-state { + padding: 0 $gl-padding; +} + +.ide-commit-empty-state-container { + margin-top: auto; + margin-bottom: auto; } .multi-file-commit-panel-header { @@ -462,7 +466,8 @@ padding: $gl-btn-padding 0; &.is-collapsed { - border-bottom: 1px solid $white-dark; + margin-left: -$gl-padding; + margin-right: -$gl-padding; svg { margin-left: auto; @@ -480,7 +485,6 @@ .multi-file-commit-panel-header-title { display: flex; flex: 1; - padding: 0 $gl-btn-padding; svg { margin-right: $gl-btn-padding; @@ -489,6 +493,7 @@ .multi-file-commit-panel-collapse-btn { border-left: 1px solid $white-dark; + margin-left: auto; } .multi-file-commit-list { @@ -502,12 +507,14 @@ display: flex; padding: 0; align-items: center; + border-radius: $border-radius-default; .multi-file-discard-btn { display: none; + margin-top: -2px; margin-left: auto; + margin-right: $grid-size; color: $gl-link-color; - padding: 0 2px; &:focus, &:hover { @@ -519,7 +526,7 @@ background: $white-normal; .multi-file-discard-btn { - display: block; + display: flex; } } } @@ -535,10 +542,12 @@ .multi-file-commit-list-collapsed { display: flex; flex-direction: column; + padding: $gl-padding 0; > svg { margin-left: auto; margin-right: auto; + color: $theme-gray-700; } .file-status-icon { @@ -550,7 +559,7 @@ .multi-file-commit-list-path { padding: $grid-size / 2; - padding-left: $gl-padding; + padding-left: $grid-size; background: none; border: 0; text-align: left; @@ -740,6 +749,22 @@ } } +.ide-commit-list-container { + display: flex; + flex-direction: column; + width: 100%; + padding: 0 16px; + + &:not(.is-collapsed) { + flex: 1; + } +} + +.ide-staged-action-btn { + margin-left: auto; + color: $gl-link-color; +} + .ide-commit-radios { label { font-weight: normal; diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index d96c7e655ba..b242e41df1c 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -44,6 +44,8 @@ feature 'Multi-file editor new directory', :js do wait_for_requests + click_button 'Stage all' + fill_in('commit-message', with: 'commit message ide') click_button('Commit') diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index a4cbd5cf766..7d65456e049 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -34,6 +34,8 @@ feature 'Multi-file editor new file', :js do wait_for_requests + click_button 'Stage all' + fill_in('commit-message', with: 'commit message ide') click_button('Commit') diff --git a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js new file mode 100644 index 00000000000..b80d08de7b1 --- /dev/null +++ b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js @@ -0,0 +1,95 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import emptyState from '~/ide/components/commit_sidebar/empty_state.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { resetStore } from '../../helpers'; + +describe('IDE commit panel empty state', () => { + let vm; + + beforeEach(() => { + const Component = Vue.extend(emptyState); + + vm = createComponentWithStore(Component, store, { + noChangesStateSvgPath: 'no-changes', + committedStateSvgPath: 'committed-state', + }); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + describe('statusSvg', () => { + it('uses noChangesStateSvgPath when commit message is empty', () => { + expect(vm.statusSvg).toBe('no-changes'); + expect(vm.$el.querySelector('img').getAttribute('src')).toBe( + 'no-changes', + ); + }); + + it('uses committedStateSvgPath when commit message exists', done => { + vm.$store.state.lastCommitMsg = 'testing'; + + Vue.nextTick(() => { + expect(vm.statusSvg).toBe('committed-state'); + expect(vm.$el.querySelector('img').getAttribute('src')).toBe( + 'committed-state', + ); + + done(); + }); + }); + }); + + it('renders no changes text when last commit message is empty', () => { + expect(vm.$el.textContent).toContain('No changes'); + }); + + it('renders last commit message when it exists', done => { + vm.$store.state.lastCommitMsg = 'testing commit message'; + + Vue.nextTick(() => { + expect(vm.$el.textContent).toContain('testing commit message'); + + done(); + }); + }); + + describe('toggle button', () => { + it('calls store action', () => { + spyOn(vm, 'toggleRightPanelCollapsed'); + + vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click(); + + expect(vm.toggleRightPanelCollapsed).toHaveBeenCalled(); + }); + + it('renders collapsed class', done => { + vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click(); + + Vue.nextTick(() => { + expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull(); + + done(); + }); + }); + }); + + describe('collapsed state', () => { + beforeEach(done => { + vm.$store.state.rightPanelCollapsed = true; + + Vue.nextTick(done); + }); + + it('does not render text & svg', () => { + expect(vm.$el.querySelector('img')).toBeNull(); + expect(vm.$el.textContent).not.toContain('No changes'); + }); + }); +}); diff --git a/spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js index 5b402886b55..65b754b5e5f 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js @@ -10,10 +10,16 @@ describe('Multi-file editor commit sidebar list collapsed', () => { beforeEach(() => { const Component = Vue.extend(listCollapsed); - vm = createComponentWithStore(Component, store); - - vm.$store.state.changedFiles.push(file('file1'), file('file2')); - vm.$store.state.changedFiles[0].tempFile = true; + vm = createComponentWithStore(Component, store, { + files: [ + { + ...file('file1'), + tempFile: true, + }, + file('file2'), + ], + icon: 'staged', + }); vm.$mount(); }); @@ -25,4 +31,40 @@ describe('Multi-file editor commit sidebar list collapsed', () => { it('renders added & modified files count', () => { expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toBe('1 1'); }); + + describe('addedFilesLength', () => { + it('returns an length of temp files', () => { + expect(vm.addedFilesLength).toBe(1); + }); + }); + + describe('modifiedFilesLength', () => { + it('returns an length of modified files', () => { + expect(vm.modifiedFilesLength).toBe(1); + }); + }); + + describe('addedFilesIconClass', () => { + it('includes multi-file-addition when addedFiles is not empty', () => { + expect(vm.addedFilesIconClass).toContain('multi-file-addition'); + }); + + it('excludes multi-file-addition when addedFiles is empty', () => { + vm.files = []; + + expect(vm.addedFilesIconClass).not.toContain('multi-file-addition'); + }); + }); + + describe('modifiedFilesClass', () => { + it('includes multi-file-modified when addedFiles is not empty', () => { + expect(vm.modifiedFilesClass).toContain('multi-file-modified'); + }); + + it('excludes multi-file-modified when addedFiles is empty', () => { + vm.files = []; + + expect(vm.modifiedFilesClass).not.toContain('multi-file-modified'); + }); + }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js index 15b66952d99..f1e0bf80819 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js @@ -1,25 +1,33 @@ import Vue from 'vue'; +import store from '~/ide/stores'; import listItem from '~/ide/components/commit_sidebar/list_item.vue'; import router from '~/ide/ide_router'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { file } from '../../helpers'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { file, resetStore } from '../../helpers'; describe('Multi-file editor commit sidebar list item', () => { let vm; let f; - beforeEach(() => { + beforeEach(done => { const Component = Vue.extend(listItem); f = file('test-file'); - vm = mountComponent(Component, { + vm = createComponentWithStore(Component, store, { file: f, + actionComponent: 'stage-button', }); + + vm.$mount(); + + Vue.nextTick(done); }); afterEach(() => { vm.$destroy(); + + resetStore(vm.$store); }); it('renders file path', () => { @@ -28,12 +36,8 @@ describe('Multi-file editor commit sidebar list item', () => { ).toBe(f.path); }); - it('calls discardFileChanges when clicking discard button', () => { - spyOn(vm, 'discardFileChanges'); - - vm.$el.querySelector('.multi-file-discard-btn').click(); - - expect(vm.discardFileChanges).toHaveBeenCalled(); + it('renders actionn button', () => { + expect(vm.$el.querySelector('.multi-file-discard-btn')).not.toBeNull(); }); it('opens a closed file in the editor when clicking the file path', () => { diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js index a62c0a28340..189826cec3e 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js @@ -2,7 +2,7 @@ import Vue from 'vue'; import store from '~/ide/stores'; import commitSidebarList from '~/ide/components/commit_sidebar/list.vue'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { file } from '../../helpers'; +import { file, resetStore } from '../../helpers'; describe('Multi-file editor commit sidebar list', () => { let vm; @@ -13,6 +13,10 @@ describe('Multi-file editor commit sidebar list', () => { vm = createComponentWithStore(Component, store, { title: 'Staged', fileList: [], + icon: 'staged', + action: 'stageAllChanges', + actionBtnText: 'stage all', + itemActionComponent: 'stage-button', }); vm.$store.state.rightPanelCollapsed = false; @@ -22,6 +26,8 @@ describe('Multi-file editor commit sidebar list', () => { afterEach(() => { vm.$destroy(); + + resetStore(vm.$store); }); describe('with a list of files', () => { @@ -38,6 +44,12 @@ describe('Multi-file editor commit sidebar list', () => { }); }); + describe('empty files array', () => { + it('renders no changes text when empty', () => { + expect(vm.$el.textContent).toContain('No changes'); + }); + }); + describe('collapsed', () => { beforeEach(done => { vm.$store.state.rightPanelCollapsed = true; @@ -50,4 +62,32 @@ describe('Multi-file editor commit sidebar list', () => { expect(vm.$el.querySelector('.help-block')).toBeNull(); }); }); + + describe('with toggle', () => { + beforeEach(done => { + spyOn(vm, 'toggleRightPanelCollapsed'); + + vm.showToggle = true; + + Vue.nextTick(done); + }); + + it('calls setPanelCollapsedStatus when clickin toggle', () => { + vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click(); + + expect(vm.toggleRightPanelCollapsed).toHaveBeenCalled(); + }); + }); + + describe('action button', () => { + beforeEach(() => { + spyOn(vm, 'stageAllChanges'); + }); + + it('calls store action when clicked', () => { + vm.$el.querySelector('.ide-staged-action-btn').click(); + + expect(vm.stageAllChanges).toHaveBeenCalled(); + }); + }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js new file mode 100644 index 00000000000..6e34de1d959 --- /dev/null +++ b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js @@ -0,0 +1,46 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import stageButton from '~/ide/components/commit_sidebar/stage_button.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { file, resetStore } from '../../helpers'; + +describe('IDE stage file button', () => { + let vm; + let f; + + beforeEach(() => { + const Component = Vue.extend(stageButton); + f = file(); + + vm = createComponentWithStore(Component, store, { + file: f, + }); + + spyOn(vm, 'stageChange'); + spyOn(vm, 'discardFileChanges'); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + it('renders button to discard & stage', () => { + expect(vm.$el.querySelectorAll('.btn').length).toBe(2); + }); + + it('calls store with stage button', () => { + vm.$el.querySelectorAll('.btn')[0].click(); + + expect(vm.stageChange).toHaveBeenCalledWith(f); + }); + + it('calls store with discard button', () => { + vm.$el.querySelectorAll('.btn')[1].click(); + + expect(vm.discardFileChanges).toHaveBeenCalledWith(f); + }); +}); diff --git a/spec/javascripts/ide/components/commit_sidebar/unstage_button_spec.js b/spec/javascripts/ide/components/commit_sidebar/unstage_button_spec.js new file mode 100644 index 00000000000..50fa1de2b44 --- /dev/null +++ b/spec/javascripts/ide/components/commit_sidebar/unstage_button_spec.js @@ -0,0 +1,39 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import unstageButton from '~/ide/components/commit_sidebar/unstage_button.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { file, resetStore } from '../../helpers'; + +describe('IDE unstage file button', () => { + let vm; + let f; + + beforeEach(() => { + const Component = Vue.extend(unstageButton); + f = file(); + + vm = createComponentWithStore(Component, store, { + file: f, + }); + + spyOn(vm, 'unstageChange'); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + it('renders button to unstage', () => { + expect(vm.$el.querySelectorAll('.btn').length).toBe(1); + }); + + it('calls store with unnstage button', () => { + vm.$el.querySelector('.btn').click(); + + expect(vm.unstageChange).toHaveBeenCalledWith(f); + }); +}); diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index 113ade269e9..c6c565bb0f3 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -28,10 +28,24 @@ describe('RepoCommitSection', () => { }, }; + const files = [file('file1'), file('file2')].map(f => + Object.assign(f, { + type: 'blob', + }), + ); + vm.$store.state.rightPanelCollapsed = false; vm.$store.state.currentBranch = 'master'; - vm.$store.state.changedFiles = [file('file1'), file('file2')]; + vm.$store.state.changedFiles = [...files]; vm.$store.state.changedFiles.forEach(f => + Object.assign(f, { + changed: true, + content: 'changedFile testing', + }), + ); + + vm.$store.state.stagedFiles = [{ ...files[0] }, { ...files[1] }]; + vm.$store.state.stagedFiles.forEach(f => Object.assign(f, { changed: true, content: 'testing', @@ -94,20 +108,93 @@ describe('RepoCommitSection', () => { ...vm.$el.querySelectorAll('.multi-file-commit-list li'), ]; const submitCommit = vm.$el.querySelector('form .btn'); + const allFiles = vm.$store.state.changedFiles.concat( + vm.$store.state.stagedFiles, + ); expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull(); - expect(changedFileElements.length).toEqual(2); + expect(changedFileElements.length).toEqual(4); changedFileElements.forEach((changedFile, i) => { - expect(changedFile.textContent.trim()).toContain( - vm.$store.state.changedFiles[i].path, - ); + expect(changedFile.textContent.trim()).toContain(allFiles[i].path); }); expect(submitCommit.disabled).toBeTruthy(); expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull(); }); + it('adds changed files into staged files', done => { + vm.$el.querySelector('.ide-staged-action-btn').click(); + + Vue.nextTick(() => { + expect( + vm.$el.querySelector('.ide-commit-list-container').textContent, + ).toContain('No changes'); + + done(); + }); + }); + + it('stages a single file', done => { + vm.$el.querySelector('.multi-file-discard-btn .btn').click(); + + Vue.nextTick(() => { + expect( + vm.$el + .querySelector('.ide-commit-list-container') + .querySelectorAll('li').length, + ).toBe(1); + + done(); + }); + }); + + it('discards a single file', done => { + vm.$el.querySelectorAll('.multi-file-discard-btn .btn')[1].click(); + + Vue.nextTick(() => { + expect( + vm.$el.querySelector('.ide-commit-list-container').textContent, + ).not.toContain('file1'); + expect( + vm.$el + .querySelector('.ide-commit-list-container') + .querySelectorAll('li').length, + ).toBe(1); + + done(); + }); + }); + + it('removes all staged files', done => { + vm.$el.querySelectorAll('.ide-staged-action-btn')[1].click(); + + Vue.nextTick(() => { + expect( + vm.$el.querySelectorAll('.ide-commit-list-container')[1].textContent, + ).toContain('No changes'); + + done(); + }); + }); + + it('unstages a single file', done => { + vm.$el + .querySelectorAll('.multi-file-discard-btn')[2] + .querySelector('.btn') + .click(); + + Vue.nextTick(() => { + expect( + vm.$el + .querySelectorAll('.ide-commit-list-container')[1] + .querySelectorAll('li').length, + ).toBe(1); + + done(); + }); + }); + it('updates commitMessage in store on input', done => { const textarea = vm.$el.querySelector('textarea'); diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js index cec572f4507..0cbf9f3735f 100644 --- a/spec/javascripts/ide/stores/actions_spec.js +++ b/spec/javascripts/ide/stores/actions_spec.js @@ -292,6 +292,84 @@ describe('Multi-file store actions', () => { }); }); + describe('stageAllChanges', () => { + it('adds all files from changedFiles to stagedFiles', done => { + const f = file(); + store.state.changedFiles.push(f); + store.state.changedFiles.push(file('new')); + + store + .dispatch('stageAllChanges') + .then(() => { + expect(store.state.stagedFiles.length).toBe(2); + expect(store.state.stagedFiles[0]).toEqual(f); + + done(); + }) + .catch(done.fail); + }); + + it('sets all files from changedFiles as staged after adding to stagedFiles', done => { + store.state.changedFiles.push(file()); + store.state.changedFiles.push(file('new')); + + store + .dispatch('stageAllChanges') + .then(() => { + expect(store.state.changedFiles.length).toBe(2); + store.state.changedFiles.forEach(f => { + expect(f.staged).toBeTruthy(); + }); + + done(); + }) + .catch(done.fail); + }); + }); + + describe('unstageAllChanges', () => { + let f; + + beforeEach(() => { + f = { + ...file(), + type: 'blob', + staged: true, + }; + + store.state.changedFiles.push({ + ...f, + }); + }); + + it('sets staged to false in changedFiles when unstaging', done => { + store.state.stagedFiles.push(f); + + store + .dispatch('unstageAllChanges') + .then(() => { + expect(store.state.stagedFiles.length).toBe(0); + expect(store.state.changedFiles[0].staged).toBeFalsy(); + + done(); + }) + .catch(done.fail); + }); + + it('removes all files from stagedFiles after unstaging', done => { + store.state.stagedFiles.push(file()); + + store + .dispatch('unstageAllChanges') + .then(() => { + expect(store.state.stagedFiles.length).toBe(0); + + done(); + }) + .catch(done.fail); + }); + }); + describe('updateViewer', () => { it('updates viewer state', done => { store diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js index a613f3a21cc..0c6fca82b1f 100644 --- a/spec/javascripts/ide/stores/getters_spec.js +++ b/spec/javascripts/ide/stores/getters_spec.js @@ -37,19 +37,11 @@ describe('Multi-file store getters', () => { expect(modifiedFiles.length).toBe(1); expect(modifiedFiles[0].name).toBe('changed'); }); - }); - describe('addedFiles', () => { - it('returns a list of added files', () => { - localState.openFiles.push(file()); - localState.changedFiles.push(file('added')); - localState.changedFiles[0].changed = true; - localState.changedFiles[0].tempFile = true; + it('returns angle left when collapsed', () => { + localState.rightPanelCollapsed = true; - const modifiedFiles = getters.addedFiles(localState); - - expect(modifiedFiles.length).toBe(1); - expect(modifiedFiles[0].name).toBe('added'); + expect(getters.collapseButtonIcon(localState)).toBe('angle-double-left'); }); }); }); diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js index 90ded940227..780e46fe686 100644 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js @@ -359,12 +359,22 @@ describe('IDE commit module actions', () => { }, }, }; - store.state.changedFiles.push(file('changed')); - store.state.changedFiles[0].active = true; + + const f = { + ...file('changed'), + type: 'blob', + active: true, + }; + store.state.stagedFiles.push(f); + store.state.changedFiles = [ + { + ...f, + }, + ]; store.state.openFiles = store.state.changedFiles; - store.state.openFiles.forEach(f => { - store.state.entries[f.path] = f; + store.state.openFiles.forEach(localF => { + store.state.entries[localF.path] = localF; }); store.state.commit.commitAction = '2'; @@ -444,7 +454,7 @@ describe('IDE commit module actions', () => { .catch(done.fail); }); - it('adds commit data to changed files', done => { + it('adds commit data to files', done => { store .dispatch('commit/commitChanges') .then(() => { diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js index 131380248e8..f769bedf50d 100644 --- a/spec/javascripts/ide/stores/mutations/file_spec.js +++ b/spec/javascripts/ide/stores/mutations/file_spec.js @@ -144,6 +144,72 @@ describe('Multi-file store file mutations', () => { }); }); + describe('STAGE_CHANGE', () => { + it('adds file into stagedFiles array', () => { + const f = file(); + + mutations.STAGE_CHANGE(localState, f); + + expect(localState.stagedFiles.length).toBe(1); + expect(localState.stagedFiles[0]).toEqual(f); + }); + + it('updates changedFiles file to staged', () => { + const f = { + ...file(), + type: 'blob', + staged: false, + }; + + localState.changedFiles.push(f); + + mutations.STAGE_CHANGE(localState, f); + + expect(localState.changedFiles[0].staged).toBeTruthy(); + }); + + it('updates stagedFile if it is already staged', () => { + const f = file(); + f.type = 'blob'; + + mutations.STAGE_CHANGE(localState, f); + + f.raw = 'testing 123'; + + mutations.STAGE_CHANGE(localState, f); + + expect(localState.stagedFiles.length).toBe(1); + expect(localState.stagedFiles[0].raw).toEqual('testing 123'); + }); + }); + + describe('UNSTAGE_CHANGE', () => { + let f; + + beforeEach(() => { + f = { + ...file(), + type: 'blob', + staged: true, + }; + + localState.stagedFiles.push(f); + localState.changedFiles.push(f); + }); + + it('removes from stagedFiles array', () => { + mutations.UNSTAGE_CHANGE(localState, f); + + expect(localState.stagedFiles.length).toBe(0); + }); + + it('updates changedFiles array file to unstaged', () => { + mutations.UNSTAGE_CHANGE(localState, f); + + expect(localState.changedFiles[0].staged).toBeFalsy(); + }); + }); + describe('TOGGLE_FILE_CHANGED', () => { it('updates file changed status', () => { mutations.TOGGLE_FILE_CHANGED(localState, { diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js index 38162a470ad..26e7ed4535e 100644 --- a/spec/javascripts/ide/stores/mutations_spec.js +++ b/spec/javascripts/ide/stores/mutations_spec.js @@ -69,6 +69,16 @@ describe('Multi-file store mutations', () => { }); }); + describe('CLEAR_STAGED_CHANGES', () => { + it('clears stagedFiles array', () => { + localState.stagedFiles.push('a'); + + mutations.CLEAR_STAGED_CHANGES(localState); + + expect(localState.stagedFiles.length).toBe(0); + }); + }); + describe('UPDATE_VIEWER', () => { it('sets viewer state', () => { mutations.UPDATE_VIEWER(localState, 'diff'); -- cgit v1.2.1 From ba63bda9550d3d66545e71de1f78a3089c7dc014 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 21 Mar 2018 11:06:39 +0000 Subject: correctly stages and unstages files after a commit the files are correctly reset correctly enables the ability to have different staged & unstaged changes in the same file --- .../ide/components/commit_sidebar/list_item.vue | 3 +- .../ide/components/commit_sidebar/stage_button.vue | 8 ++--- .../components/commit_sidebar/unstage_button.vue | 6 ++-- .../ide/components/repo_commit_section.vue | 7 ++-- app/assets/javascripts/ide/lib/common/model.js | 7 ++-- app/assets/javascripts/ide/stores/actions.js | 4 +-- app/assets/javascripts/ide/stores/actions/file.js | 8 ++--- app/assets/javascripts/ide/stores/getters.js | 7 ++-- .../ide/stores/modules/commit/actions.js | 31 +++++------------- .../ide/stores/modules/commit/getters.js | 9 +++-- .../javascripts/ide/stores/mutation_types.js | 2 ++ app/assets/javascripts/ide/stores/mutations.js | 15 +++++++++ .../javascripts/ide/stores/mutations/file.js | 38 ++++++++++++++-------- app/assets/javascripts/ide/stores/utils.js | 1 - 14 files changed, 82 insertions(+), 64 deletions(-) diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index 93c8fc00f28..92fcb55f94f 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -1,4 +1,5 @@ diff --git a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue index e69535335d4..440f360280f 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue @@ -1,11 +1,15 @@