diff --git a/app/components/rapid_diffs/diff_file_component.html.haml b/app/components/rapid_diffs/diff_file_component.html.haml index 65f6a9948e654988f4719e877b80272575a6cbd3..d770d97314b525f8dcf749915b6052ef9fd0fcaa 100644 --- a/app/components/rapid_diffs/diff_file_component.html.haml +++ b/app/components/rapid_diffs/diff_file_component.html.haml @@ -2,7 +2,7 @@ %diff-file.rd-diff-file-component{ id: id, data: { testid: 'rd-diff-file', **server_data } } .rd-diff-file - = render RapidDiffs::DiffFileHeaderComponent.new(diff_file: @diff_file) + = header || default_header -# extra wrapper needed so content-visibility: hidden doesn't require removing border or other styles %div{ data: { file_body: '' } } .rd-diff-file-body diff --git a/app/components/rapid_diffs/diff_file_component.rb b/app/components/rapid_diffs/diff_file_component.rb index a8a5bf7ca1de69ce1decd8bf9d564f40c161b373..f109b5f5c7cdb0e8f8ca0478da8918bcf763366e 100644 --- a/app/components/rapid_diffs/diff_file_component.rb +++ b/app/components/rapid_diffs/diff_file_component.rb @@ -4,6 +4,8 @@ module RapidDiffs class DiffFileComponent < ViewComponent::Base include TreeHelper + renders_one :header + def initialize(diff_file:, parallel_view: false) @diff_file = diff_file @parallel_view = parallel_view @@ -33,5 +35,9 @@ def viewer_component Viewers::NoPreviewComponent end + + def default_header + render RapidDiffs::DiffFileHeaderComponent.new(diff_file: @diff_file) + end end end diff --git a/app/components/rapid_diffs/diff_file_header_component.html.haml b/app/components/rapid_diffs/diff_file_header_component.html.haml index bfe99e0d7d61707c8bfa9394211dbdffe7076512..4b922320815f78f8c221133d62d7339b627fba87 100644 --- a/app/components/rapid_diffs/diff_file_header_component.html.haml +++ b/app/components/rapid_diffs/diff_file_header_component.html.haml @@ -11,10 +11,6 @@ -# * toggle file comments -# * submodule compare -- view_title = _('View file @ %{commitSha}') % { commitSha: Commit.truncate_sha(@diff_file.content_sha) } -- view_href = project_blob_path(@diff_file.repository.project, helpers.tree_join(@diff_file.content_sha, @diff_file.new_path)) -- options_menu_items = [{ "text": "#{view_title}", "href": "#{view_href}" }].to_json - .rd-diff-file-header{ data: { testid: 'rd-diff-file-header' } } .rd-diff-file-toggle< = render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'chevron-down', button_options: { class: 'rd-diff-file-toggle-button', data: { opened: '', click: 'toggleFile' }, aria: { label: _('Hide file contents') } }) @@ -59,6 +55,6 @@ -# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182850#note_2387011092 -# haml-lint:disable InlineJavaScript %script{ type: "application/json" } - = options_menu_items.html_safe + = menu_items.map { |item| item.except(:position) }.to_json.html_safe - button_params = { icon: 'ellipsis_v', button_options: { data: { click: 'toggleOptionsMenu' }, aria: { label: _('Options') } } } = render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, **button_params) diff --git a/app/components/rapid_diffs/diff_file_header_component.rb b/app/components/rapid_diffs/diff_file_header_component.rb index 5b0a88002c2d886cd2bb80585fecc833839b9805..94b97f06d28d6d5d3ff55ab436f4aa3c0070c276 100644 --- a/app/components/rapid_diffs/diff_file_header_component.rb +++ b/app/components/rapid_diffs/diff_file_header_component.rb @@ -4,8 +4,9 @@ module RapidDiffs class DiffFileHeaderComponent < ViewComponent::Base include ButtonHelper - def initialize(diff_file:) + def initialize(diff_file:, additional_menu_items: []) @diff_file = diff_file + @additional_menu_items = additional_menu_items end def copy_path_button @@ -18,5 +19,23 @@ def copy_path_button testid: "rd-diff-file-copy-clipboard" ) end + + def menu_items + base_items = [ + { + text: helpers.safe_format( + _('View file @ %{commitSha}'), + commitSha: Commit.truncate_sha(@diff_file.content_sha) + ), + href: helpers.project_blob_path( + @diff_file.repository.project, + helpers.tree_join(@diff_file.content_sha, @diff_file.new_path) + ), + position: 0 + } + ] + + [*base_items, *@additional_menu_items].sort_by { |item| item[:position] || Float::INFINITY } + end end end diff --git a/app/components/rapid_diffs/merge_request_diff_file_component.html.haml b/app/components/rapid_diffs/merge_request_diff_file_component.html.haml index fbb39bb1afe3bd1ad407f399a0ae64895acfadfd..8de679514080ab8913085b91ad079200735a4dba 100644 --- a/app/components/rapid_diffs/merge_request_diff_file_component.html.haml +++ b/app/components/rapid_diffs/merge_request_diff_file_component.html.haml @@ -1 +1,4 @@ -= render RapidDiffs::DiffFileComponent.new(diff_file: @diff_file, parallel_view: @parallel_view) += render RapidDiffs::DiffFileComponent.new(diff_file:, parallel_view: @parallel_view) do |c| + - c.with_header do + = render RapidDiffs::DiffFileHeaderComponent.new(diff_file:, additional_menu_items:) + end diff --git a/app/components/rapid_diffs/merge_request_diff_file_component.rb b/app/components/rapid_diffs/merge_request_diff_file_component.rb index 8c6781d99b26777bc76e95e842ac188712fedb31..ea997634a7497add1b7482cb7732844fc8869dd1 100644 --- a/app/components/rapid_diffs/merge_request_diff_file_component.rb +++ b/app/components/rapid_diffs/merge_request_diff_file_component.rb @@ -4,10 +4,28 @@ module RapidDiffs class MergeRequestDiffFileComponent < ViewComponent::Base with_collection_parameter :diff_file + attr_reader :diff_file + def initialize(diff_file:, merge_request:, parallel_view: false) @diff_file = diff_file @merge_request = merge_request @parallel_view = parallel_view end + + def additional_menu_items + editor_path = helpers.project_edit_blob_path( + @diff_file.repository.project, + helpers.tree_join(@merge_request.source_branch, @diff_file.new_path), + from_merge_request_iid: @merge_request.iid + ) + + [ + { + text: _('Edit in single-file editor'), + href: editor_path, + position: 1 + } + ] + end end end diff --git a/spec/components/rapid_diffs/diff_file_component_spec.rb b/spec/components/rapid_diffs/diff_file_component_spec.rb index 2afaff289976b90a22c74c70c3c4ab55d91ba59e..e693c821bfe3460252c4f1a85f54e93a021cf290 100644 --- a/spec/components/rapid_diffs/diff_file_component_spec.rb +++ b/spec/components/rapid_diffs/diff_file_component_spec.rb @@ -7,7 +7,34 @@ RSpec.describe RapidDiffs::DiffFileComponent, type: :component, feature_category: :code_review_workflow do include_context "with diff file component tests" - def render_component(**args) - render_inline(described_class.new(diff_file: diff_file, **args)) + describe 'header slot' do + let_it_be(:diff_file) { build(:diff_file) } + + it 'renders the default header when no custom header is provided' do + allow_next_instance_of( + RapidDiffs::DiffFileHeaderComponent, + diff_file: diff_file + ) do |instance| + allow(instance).to receive(:render_in).and_return('diff-file-header') + end + + result = render_component + + expect(result.to_html).to include('diff-file-header') + end + + it 'renders a custom header when provided' do + custom_header = '
Custom Header
'.html_safe + + result = render_component do |c| + c.with_header { custom_header } + end + + expect(result.css('.custom-header').text).to eq('Custom Header') + end + end + + def render_component(**args, &block) + render_inline(described_class.new(diff_file: diff_file, **args), &block) end end diff --git a/spec/components/rapid_diffs/diff_file_header_component_spec.rb b/spec/components/rapid_diffs/diff_file_header_component_spec.rb index 42c8ca34ec714af3af811c1a3e5210600146a2ff..146cb71c0c50b24eb5dd05fd457dbe2dc79e9151 100644 --- a/spec/components/rapid_diffs/diff_file_header_component_spec.rb +++ b/spec/components/rapid_diffs/diff_file_header_component_spec.rb @@ -68,7 +68,53 @@ expect(page.find('[data-testid="js-file-deletion-line"]')).to have_text(diff_file.removed_lines) end - def render_component - render_inline(described_class.new(diff_file: diff_file)) + describe 'menu items' do + let(:content_sha) { 'abc123' } + + before do + allow(diff_file).to receive(:content_sha).and_return(content_sha) + end + + it 'renders default menu items' do + render_component + + options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text) + + expect(options_menu_items.length).to eq(1) + expect(options_menu_items[0]['text']).to eq("View file @ #{content_sha}") + expect(options_menu_items[0]).not_to have_key('position') + end + + it 'renders additional menu items with respective order' do + menu_items = [ + { + text: 'First item', + href: '/first', + position: -1 + }, + { + text: 'Last item', + href: '/last', + position: 2 + } + ] + + render_component(additional_menu_items: menu_items) + + options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text) + + expect(options_menu_items.length).to eq(3) + expect(options_menu_items[0]['text']).to eq('First item') + expect(options_menu_items[1]['text']).to eq("View file @ #{content_sha}") + expect(options_menu_items[2]['text']).to eq('Last item') + + options_menu_items.each do |item| + expect(item).not_to have_key('position') + end + end + end + + def render_component(**args) + render_inline(described_class.new(diff_file:, **args)) end end diff --git a/spec/components/rapid_diffs/merge_request_diff_file_component_spec.rb b/spec/components/rapid_diffs/merge_request_diff_file_component_spec.rb index 4a0236725f6b7bd80db068085573fea482cdb06b..a53219d3a2c0ec3fbe9ced2cd0d3a5346b73eaf2 100644 --- a/spec/components/rapid_diffs/merge_request_diff_file_component_spec.rb +++ b/spec/components/rapid_diffs/merge_request_diff_file_component_spec.rb @@ -6,7 +6,37 @@ RSpec.describe RapidDiffs::MergeRequestDiffFileComponent, type: :component, feature_category: :code_review_workflow do include_context "with diff file component tests" - let_it_be(:merge_request) { build(:merge_request) } + + let(:merge_request) { build(:merge_request, source_project: project, target_project: project) } + let(:content_sha) { 'abc123' } + let(:edit_path_base) { '/-/edit/feature-branch/path/to/file.rb?from_merge_request_iid=' } + + before do + allow(diff_file).to receive(:repository).and_return(repository) + allow(repository).to receive(:commit).with(RepoHelpers.sample_commit.id).and_return(sample_commit) + allow(merge_request).to receive_messages( + source_branch: 'feature-branch', + iid: 123 + ) + allow(diff_file).to receive_messages( + new_path: 'path/to/file.rb', + content_sha: content_sha, + repository: repository + ) + end + + describe 'rendering' do + it 'renders additional options in the header menu' do + render_component + + options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text) + + expect(options_menu_items.length).to eq(2) + expect(options_menu_items[0]['text']).to eq('View file @ abc123') + expect(options_menu_items[1]['text']).to eq('Edit in single-file editor') + expect(options_menu_items[1]['href']).to include("#{edit_path_base}#{merge_request.iid}") + end + end def render_component(**args) render_inline(described_class.new(diff_file:, merge_request:, **args)) diff --git a/spec/components/rapid_diffs/shared.rb b/spec/components/rapid_diffs/shared.rb index 99dfe3be9c6e41cfa77f6d6876e03491a5961a61..6eab9a352a261e62542394765c30a50a9e2d1ed3 100644 --- a/spec/components/rapid_diffs/shared.rb +++ b/spec/components/rapid_diffs/shared.rb @@ -9,6 +9,11 @@ let(:repository) { diff_file.repository } let(:project) { repository.container } let(:namespace) { project.namespace } + let(:sample_commit) { instance_double(Commit, raw_diffs: [diff_file]) } + + before do + allow(repository).to receive(:commit).with(RepoHelpers.sample_commit.id).and_return(sample_commit) + end # This should be overridden in the including spec def render_component