diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index 3477e188cbc4..b30d04035b06 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -116,7 +116,7 @@ See COPYRIGHT and LICENSE files for more details. <% end %>
-
+ <%= content_tag :main, id: "content-wrapper", class: initial_classes, data: stimulus_content_data do %> <%# Primerized flash messages are being rendered separately %>
<%= render_primerized_flash %> @@ -134,8 +134,7 @@ See COPYRIGHT and LICENSE files for more details. <% end %> <%= content_tag :div, id: 'content', - class: "#{initial_classes} #{'content--split' if content_for?(:content_body_right)}", - data: stimulus_content_data do %> + class: "#{initial_classes} #{'content--split' if content_for?(:content_body_right)}" do %>

<%= t(:label_content) %>

<% if content_for?(:content_header) %>
@@ -162,7 +161,7 @@ See COPYRIGHT and LICENSE files for more details. <%= content_for :content_body_right %> <% end %> <% end %> -
+ <% end %> diff --git a/docs/development/concepts/stimulus/README.md b/docs/development/concepts/stimulus/README.md index af7199d220ff..5bb9b78b1c50 100644 --- a/docs/development/concepts/stimulus/README.md +++ b/docs/development/concepts/stimulus/README.md @@ -44,7 +44,7 @@ You need to take care to prefix all actions, values etc. with the exact same pat ### Requiring a page controller -If you have a single controller used in a partial, we have added a helper to use in a partial in order to append a controller to the `#content`tag. This is useful if your template doesn't have a single DOM root. For example, to load the dynamic `project-storage-form` controller and provide a custom value to it: +If you have a single controller used in a partial, we have added a helper to use in a partial in order to append a controller to the `#content-wrapper` tag. This is useful if your template doesn't have a single DOM root. For example, to load the dynamic `project-storage-form` controller and provide a custom value to it: ```erb <% content_controller 'project-storage-form', diff --git a/frontend/src/stimulus/controllers/poll-for-changes.controller.ts b/frontend/src/stimulus/controllers/poll-for-changes.controller.ts index b7b9407b5823..811dc91926fd 100644 --- a/frontend/src/stimulus/controllers/poll-for-changes.controller.ts +++ b/frontend/src/stimulus/controllers/poll-for-changes.controller.ts @@ -36,11 +36,17 @@ export default class PollForChangesController extends ApplicationController { url: String, interval: Number, reference: String, + autoscrollEnabled: Boolean, }; + static targets = ['reloadButton']; + + declare reloadButtonTarget:HTMLLinkElement; + declare referenceValue:string; declare urlValue:string; declare intervalValue:number; + declare autoscrollEnabledValue:boolean; private interval:number; @@ -52,6 +58,10 @@ export default class PollForChangesController extends ApplicationController { void this.triggerTurboStream(); }, this.intervalValue || 10_000); } + + if (this.autoscrollEnabledValue) { + window.addEventListener('DOMContentLoaded', this.autoscrollToLastKnownPosition.bind(this)); + } } disconnect() { @@ -59,6 +69,10 @@ export default class PollForChangesController extends ApplicationController { clearInterval(this.interval); } + reloadButtonTargetConnected() { + this.reloadButtonTarget.addEventListener('click', this.rememberCurrentScrollPosition.bind(this)); + } + triggerTurboStream() { void fetch(`${this.urlValue}?reference=${this.referenceValue}`, { headers: { @@ -73,4 +87,29 @@ export default class PollForChangesController extends ApplicationController { } }); } + + rememberCurrentScrollPosition() { + const currentPosition = document.getElementById('content-body')?.scrollTop; + + if (currentPosition !== undefined) { + sessionStorage.setItem(this.scrollPositionKey(), currentPosition.toString()); + } + } + + autoscrollToLastKnownPosition() { + const lastKnownPos = sessionStorage.getItem(this.scrollPositionKey()); + if (lastKnownPos) { + const content = document.getElementById('content-body'); + + if (content) { + content.scrollTop = parseInt(lastKnownPos, 10); + } + } + + sessionStorage.removeItem(this.scrollPositionKey()); + } + + private scrollPositionKey():string { + return `${this.urlValue}/scrollPosition`; + } } diff --git a/modules/meeting/app/components/meetings/header_component.html.erb b/modules/meeting/app/components/meetings/header_component.html.erb index 13a451beba3b..c27700bbe91c 100644 --- a/modules/meeting/app/components/meetings/header_component.html.erb +++ b/modules/meeting/app/components/meetings/header_component.html.erb @@ -1,16 +1,14 @@ <%= - params = { - controller: "poll-for-changes", - poll_for_changes_reference_value: @meeting.changed_hash, - poll_for_changes_url_value: check_for_updates_meeting_path(@meeting), - poll_for_changes_interval_value: check_for_updates_interval - } + helpers.content_controller "poll-for-changes", + poll_for_changes_reference_value: @meeting.changed_hash, + poll_for_changes_url_value: check_for_updates_meeting_path(@meeting), + poll_for_changes_interval_value: check_for_updates_interval, + poll_for_changes_autoscroll_enabled_value: true component_wrapper do render(Primer::OpenProject::PageHeader.new( test_selector: "meeting-page-header", - state: @state, - data: params + state: @state )) do |header| header.with_title do |title| title.with_editable_form(model: @meeting, diff --git a/modules/meeting/app/components/meetings/update_flash_component.rb b/modules/meeting/app/components/meetings/update_flash_component.rb index 31c991ed513f..0b25370a52e3 100644 --- a/modules/meeting/app/components/meetings/update_flash_component.rb +++ b/modules/meeting/app/components/meetings/update_flash_component.rb @@ -43,7 +43,7 @@ def call banner.with_action_button( tag: :a, href: helpers.meeting_path(meeting), - data: { turbo: false }, + data: { turbo: false, poll_for_changes_target: "reloadButton" }, size: :medium ) { I18n.t("label_meeting_reload") } diff --git a/modules/meeting/spec/support/pages/structured_meeting/show.rb b/modules/meeting/spec/support/pages/structured_meeting/show.rb index 10a9367420d4..34e531761a98 100644 --- a/modules/meeting/spec/support/pages/structured_meeting/show.rb +++ b/modules/meeting/spec/support/pages/structured_meeting/show.rb @@ -43,7 +43,7 @@ def trigger_dropdown_menu_item(name) def trigger_change_poll script = <<~JS - var target = document.querySelector('[data-test-selector="meeting-page-header"]'); + var target = document.querySelector('#content-wrapper'); var controller = window.Stimulus.getControllerForElementAndIdentifier(target, 'poll-for-changes') controller.triggerTurboStream(); JS