diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-13 00:18:30 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-13 00:18:30 +0300 |
commit | ac48f7c24110a7a1e0a0aa49fc7838ab03c28374 (patch) | |
tree | 0d323ff3b3317315241fd1c784d82bfa0577711e /app | |
parent | 6ce6d20cf0b81275bad7bf8e95cf49bd475c5c4f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
28 files changed, 116 insertions, 77 deletions
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue index 306183ebcca..aa349186014 100644 --- a/app/assets/javascripts/header_search/components/app.vue +++ b/app/assets/javascripts/header_search/components/app.vue @@ -286,6 +286,7 @@ export default { :max="searchOptions.length - 1" :min="$options.FIRST_DROPDOWN_INDEX" :default-index="defaultIndex" + :enable-cycle="true" /> <header-search-default-items v-if="showDefaultItems" diff --git a/app/assets/javascripts/packages_and_registries/shared/utils.js b/app/assets/javascripts/packages_and_registries/shared/utils.js index 76623377d90..adffab277cc 100644 --- a/app/assets/javascripts/packages_and_registries/shared/utils.js +++ b/app/assets/javascripts/packages_and_registries/shared/utils.js @@ -55,15 +55,6 @@ export const renderBreadcrumb = (router, apolloProvider, RegistryBreadcrumb) => RegistryBreadcrumb, }, render(createElement) { - // FIXME(@tnir): this is a workaround until the MR gets merged: - // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115 - const parentEl = breadCrumbEl.parentElement.parentElement; - if (parentEl) { - parentEl.classList.remove('breadcrumbs-container'); - parentEl.classList.add('gl-display-flex'); - parentEl.classList.add('w-100'); - } - // End of FIXME(@tnir) return createElement('registry-breadcrumb', { class: breadCrumbEl.className, props: { diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index 2f8c2a8e86f..6d5460da2e5 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -17,6 +17,7 @@ import createStore from '~/code_navigation/store'; import { generateRefDestinationPath } from '~/repository/utils/ref_switcher_utils'; import RefSelector from '~/ref/components/ref_selector.vue'; import { joinPaths, visitUrl } from '~/lib/utils/url_utility'; +import { parseBoolean } from '~/lib/utils/common_utils'; Vue.use(Vuex); Vue.use(VueApollo); @@ -100,6 +101,7 @@ const initForkInfo = () => { sourceName, sourcePath, sourceDefaultBranch, + canSyncBranch, aheadComparePath, behindComparePath, canUserCreateMrInFork, @@ -110,6 +112,7 @@ const initForkInfo = () => { render(h) { return h(ForkInfo, { props: { + canSyncBranch: parseBoolean(canSyncBranch), projectPath, selectedBranch, sourceName, diff --git a/app/assets/javascripts/repository/components/fork_info.vue b/app/assets/javascripts/repository/components/fork_info.vue index a7795c8da0a..07a29bd3b96 100644 --- a/app/assets/javascripts/repository/components/fork_info.vue +++ b/app/assets/javascripts/repository/components/fork_info.vue @@ -94,6 +94,11 @@ export default { required: false, default: '', }, + canSyncBranch: { + type: Boolean, + required: false, + default: false, + }, aheadComparePath: { type: String, required: false, @@ -187,6 +192,7 @@ export default { hasUpdateButton() { return ( this.glFeatures.synchronizeFork && + this.canSyncBranch && ((this.sourceName && this.forkDetails && this.behind) || this.isUnknownDivergence) ); }, diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js index b1217881bc3..b5568393313 100644 --- a/app/assets/javascripts/repository/index.js +++ b/app/assets/javascripts/repository/index.js @@ -75,6 +75,7 @@ export default function setupVueRepositoryList() { sourcePath, sourceDefaultBranch, createMrPath, + canSyncBranch, aheadComparePath, behindComparePath, canUserCreateMrInFork, @@ -85,6 +86,7 @@ export default function setupVueRepositoryList() { render(h) { return h(ForkInfo, { props: { + canSyncBranch: parseBoolean(canSyncBranch), projectPath, selectedBranch, sourceName, diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js index 3d798d278bf..ba5495ba014 100644 --- a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js +++ b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js @@ -32,7 +32,7 @@ const hide = (sidebar, toggle, isUserAction) => { setTimeout(() => { sidebar.classList.add(SIDEBAR_VISIBILITY_CLASS); if (isUserAction) { - toggle.focus(); + toggle?.focus(); } }, SIDEBAR_TRANSITION_DURATION); }; diff --git a/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue b/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue index 1da84df022f..b920af593df 100644 --- a/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue +++ b/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue @@ -27,6 +27,12 @@ export default { type: Number, required: true, }, + /* enable possibility to cycle around */ + enableCycle: { + type: Boolean, + required: false, + default: false, + }, }, watch: { max() { @@ -64,15 +70,34 @@ export default { return; } - const nextIndex = Math.max(this.min, Math.min(this.index + val, this.max)); + let nextIndex = Math.max(this.min, Math.min(this.index + val, this.max)); - // Return if the index didn't change if (nextIndex === this.index) { - return; + // Return if the index didn't change and cycle is not enabled + if (!this.enableCycle) { + return; + } + // Update nextIndex if the cycle is enabled + nextIndex = this.cycle(nextIndex, val); } this.$emit('change', nextIndex); }, + cycle(nextIndex, val) { + if (val === 1 && nextIndex === this.max) { + // if we are moving down +1 and we reached bottom (max) + // return top most index (min) + return this.min; + } + + if (val === -1 && nextIndex === this.min) { + // if we are moving up -1 and we reached top (min) + // return bottom most index (max) + return this.max; + } + + return nextIndex; + }, }, render() { return this.$scopedSlots.default?.(); diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 4eb5a686d43..0d18835ac0f 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -312,22 +312,12 @@ $search-input-field-x-min-width: 200px; margin-top: $dropdown-vertical-offset; } -.breadcrumbs { - display: flex; - min-height: $breadcrumb-min-height; - color: $gl-text-color; -} - -.breadcrumbs-container { - display: flex; - width: 100%; - padding-top: $gl-padding / 2; - padding-bottom: $gl-padding / 2; - align-items: center; +.top-bar-container { + min-height: $top-bar-height; border-bottom: 1px solid $border-color; } -.breadcrumbs-links { +.breadcrumbs { flex: 1; min-width: 0; align-self: center; @@ -549,7 +539,7 @@ $search-input-field-x-min-width: 200px; @include media-breakpoint-down(sm) { @include gl-display-block; - + .breadcrumbs-links { + + .breadcrumbs { @include gl-pl-4; @include gl-border-l-1; @include gl-border-l-solid; diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss index 06532d6e1f5..db59a482c64 100644 --- a/app/assets/stylesheets/framework/system_messages.scss +++ b/app/assets/stylesheets/framework/system_messages.scss @@ -68,7 +68,7 @@ .boards-list, .board-swimlanes { - height: calc(100vh - (#{$header-height} + #{$breadcrumb-min-height} + #{$performance-bar-height} + #{$system-footer-height} + #{$gl-padding-32})); + height: calc(100vh - (#{$header-height} + #{$top-bar-height} + #{$performance-bar-height} + #{$system-footer-height} + #{$gl-padding-32})); } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index a4d9bcccb38..d9e36735f2c 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -484,7 +484,7 @@ $mr-review-bar-height: calc(2rem + 13px); $flash-height: 52px; $flash-container-top: 48px; $context-header-height: 60px; -$breadcrumb-min-height: 48px; +$top-bar-height: 48px; $home-panel-title-row-height: 64px; $home-panel-avatar-mobile-size: 24px; $issuable-title-max-width: 350px; @@ -694,7 +694,7 @@ $issue-boards-filter-height: 68px; The following heights are used in environment_logs.scss and are used for calculation of the log viewer height. */ $environment-logs-breadcrumbs-height: 63px; -$environment-logs-breadcrumbs-height-md: $breadcrumb-min-height; +$environment-logs-breadcrumbs-height-md: $top-bar-height; $environment-logs-difference-xs-up: calc(#{$header-height} + #{$environment-logs-breadcrumbs-height}); $environment-logs-difference-md-up: calc(#{$header-height} + #{$environment-logs-breadcrumbs-height-md}); diff --git a/app/assets/stylesheets/pages/registry.scss b/app/assets/stylesheets/pages/registry.scss index 31c6dbd2970..36b86771295 100644 --- a/app/assets/stylesheets/pages/registry.scss +++ b/app/assets/stylesheets/pages/registry.scss @@ -2,7 +2,7 @@ // until this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079 // // See app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue when this is changed. -.breadcrumbs-container .gl-breadcrumbs { +.breadcrumbs .gl-breadcrumbs { padding: 0; box-shadow: none; } diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index c8e98e6aa1c..0631c02355e 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -18,7 +18,7 @@ class Projects::TreeController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) - push_frontend_feature_flag(:synchronize_fork, @project) + push_frontend_feature_flag(:synchronize_fork, @project.fork_source) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 04b51a2cba1..a6bc754d09e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -38,7 +38,7 @@ class ProjectsController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) - push_frontend_feature_flag(:synchronize_fork, @project) + push_frontend_feature_flag(:synchronize_fork, @project&.fork_source) push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks) push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies) push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) diff --git a/app/graphql/mutations/projects/sync_fork.rb b/app/graphql/mutations/projects/sync_fork.rb index 121c16df87b..05332457e8c 100644 --- a/app/graphql/mutations/projects/sync_fork.rb +++ b/app/graphql/mutations/projects/sync_fork.rb @@ -7,8 +7,6 @@ module Mutations include FindsProject - authorize :push_code - argument :project_path, GraphQL::Types::ID, required: true, description: 'Full path of the project to initialize.' @@ -22,9 +20,10 @@ module Mutations description: 'Updated fork details.' def resolve(project_path:, target_branch:) - project = authorized_find!(project_path) + project = authorized_find!(project_path, target_branch) - return respond(nil, ['Feature flag is disabled']) unless Feature.enabled?(:synchronize_fork, project) + return respond(nil, ['Feature flag is disabled']) unless Feature.enabled?(:synchronize_fork, + project.fork_source) details_resolver = Resolvers::Projects::ForkDetailsResolver.new(object: project, context: context, field: nil) details = details_resolver.resolve(ref: target_branch) @@ -56,6 +55,14 @@ module Mutations def respond(details, errors) { details: details, errors: errors } end + + def authorized_find!(project_path, target_branch) + project = find_object(project_path) + + return project if ::Gitlab::UserAccess.new(current_user, container: project).can_update_branch?(target_branch) + + raise_resource_not_available_error! + end end end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index fd684ee5ecb..42c9481828c 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -356,6 +356,7 @@ module ApplicationSettingsHelper :shared_runners_text, :sign_in_text, :signup_enabled, + :silent_mode_enabled, :sourcegraph_enabled, :sourcegraph_url, :sourcegraph_public_only, diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 21a736bf68a..567cca7cab1 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -137,6 +137,7 @@ module ProjectsHelper source_name: source_project.full_name, source_path: project_path(source_project), source_default_branch: source_default_branch, + can_sync_branch: ::Gitlab::UserAccess.new(current_user, container: project).can_update_branch?(ref).to_s, ahead_compare_path: project_compare_path( project, from: source_default_branch, to: ref, from_project_id: source_project.id ), diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 9aab1d96083..c11cd0191df 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -439,6 +439,9 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord allow_nil: false, inclusion: { in: [true, false], message: N_('must be a boolean value') } + validates :silent_mode_enabled, + inclusion: { in: [true, false], message: N_('must be a boolean value') } + Gitlab::SSHPublicKey.supported_types.each do |type| validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } end diff --git a/app/validators/json_schemas/build_report_result_data.json b/app/validators/json_schemas/build_report_result_data.json index d109389a046..d9ef7633acd 100644 --- a/app/validators/json_schemas/build_report_result_data.json +++ b/app/validators/json_schemas/build_report_result_data.json @@ -8,10 +8,7 @@ "format": "float" }, "tests": { - "type": "object", - "items": { - "$ref": "./build_report_result_data_tests.json" - } + "$ref": "./build_report_result_data_tests.json" } }, "additionalProperties": false diff --git a/app/validators/json_schemas/build_report_result_data_tests.json b/app/validators/json_schemas/build_report_result_data_tests.json index 3b6a2688313..456b651dd6c 100644 --- a/app/validators/json_schemas/build_report_result_data_tests.json +++ b/app/validators/json_schemas/build_report_result_data_tests.json @@ -7,7 +7,7 @@ "type": "string" }, "duration": { - "type": "string" + "type": "number" }, "failed": { "type": "integer" @@ -20,6 +20,16 @@ }, "success": { "type": "integer" + }, + "suite_error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 0677a91f385..4c3ea0f292e 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -1,4 +1,4 @@ -- @hide_breadcrumbs = true +- @hide_top_bar = true - @hide_top_links = true - page_title _('New Group') - header_title _("Groups"), dashboard_groups_path diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 7e7f8a6a871..1a647249eb7 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -35,8 +35,8 @@ = dispensable_render_if_exists "shared/new_user_signups_cap_reached_alert" = yield :page_level_alert = yield :group_invite_members_banner - - unless @hide_breadcrumbs - = render "layouts/nav/breadcrumbs" + - unless @hide_top_bar + = render "layouts/nav/top_bar" %div{ class: "#{container_class unless @no_container} #{@content_class}" } %main.content{ id: "content-body", **page_itemtype } = render "layouts/flash", extra_flash_class: 'limit-container-width' diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml deleted file mode 100644 index 60c5cb1af68..00000000000 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -- container = @no_breadcrumb_container ? 'container-fluid' : container_class -- hide_top_links = @hide_top_links || false -- unless @skip_current_level_breadcrumb - - push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link) - -.gl-relative - .breadcrumbs{ class: [container, @content_class] } - .breadcrumbs-container - - if show_super_sidebar? - = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'js-super-sidebar-toggle super-sidebar-toggle gl-ml-n3 gl-mr-2', title: _('Expand sidebar'), aria: { controls: 'super-sidebar', expanded: 'false', label: _('Navigation sidebar') }, data: {toggle: 'tooltip', placement: 'right' } }) - - elsif defined?(@left_sidebar) - = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'toggle-mobile-nav gl-ml-n3 gl-mr-2', data: { qa_selector: 'toggle_mobile_nav_button' }, aria: { label: _('Open sidebar') } }) - %nav.breadcrumbs-links{ 'aria-label': _('Breadcrumbs'), data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } } - %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list - - unless hide_top_links - = header_title - - if @breadcrumbs_extra_links - - @breadcrumbs_extra_links.each do |extra| - = breadcrumb_list_item link_to(extra[:text], extra[:link]) - = render "layouts/nav/breadcrumbs/collapsed_inline_list", location: :after - - unless @skip_current_level_breadcrumb - %li{ data: { testid: 'breadcrumb-current-link', qa_selector: 'breadcrumb_current_link' } } - = link_to @breadcrumb_title, breadcrumb_title_link - -# haml-lint:disable InlineJavaScript - %script{ type: 'application/ld+json' } - :plain - #{schema_breadcrumb_json} - = yield :header_content diff --git a/app/views/layouts/nav/_top_bar.html.haml b/app/views/layouts/nav/_top_bar.html.haml new file mode 100644 index 00000000000..e7a6566a7ff --- /dev/null +++ b/app/views/layouts/nav/_top_bar.html.haml @@ -0,0 +1,10 @@ +- container = @no_top_bar_container ? 'container-fluid' : container_class + +.gl-relative + %div{ class: [container, @content_class] } + .top-bar-container.gl-display-flex.gl-align-items-center + - if show_super_sidebar? + = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'js-super-sidebar-toggle super-sidebar-toggle gl-ml-n3 gl-mr-2', title: _('Expand sidebar'), aria: { controls: 'super-sidebar', expanded: 'false', label: _('Navigation sidebar') }, data: {toggle: 'tooltip', placement: 'right' } }) + - elsif defined?(@left_sidebar) + = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'toggle-mobile-nav gl-ml-n3 gl-mr-2', data: { qa_selector: 'toggle_mobile_nav_button' }, aria: { label: _('Open sidebar') } }) + = render "layouts/nav/breadcrumbs/breadcrumbs" diff --git a/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml b/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml new file mode 100644 index 00000000000..b5f067cf42f --- /dev/null +++ b/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml @@ -0,0 +1,20 @@ +- hide_top_links = @hide_top_links || false +- unless @skip_current_level_breadcrumb + - push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link) + +%nav.breadcrumbs{ 'aria-label': _('Breadcrumbs'), data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } } + %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list + - unless hide_top_links + = header_title + - if @breadcrumbs_extra_links + - @breadcrumbs_extra_links.each do |extra| + = breadcrumb_list_item link_to(extra[:text], extra[:link]) + = render "layouts/nav/breadcrumbs/collapsed_inline_list", location: :after + - unless @skip_current_level_breadcrumb + %li{ data: { testid: 'breadcrumb-current-link', qa_selector: 'breadcrumb_current_link' } } + = link_to @breadcrumb_title, breadcrumb_title_link + -# haml-lint:disable InlineJavaScript + %script{ type: 'application/ld+json' } + :plain + #{schema_breadcrumb_json} += yield :header_content diff --git a/app/views/layouts/terms.html.haml b/app/views/layouts/terms.html.haml index 032be73f70c..71c622d7a62 100644 --- a/app/views/layouts/terms.html.haml +++ b/app/views/layouts/terms.html.haml @@ -1,6 +1,6 @@ !!! 5 - add_page_specific_style 'page_bundles/terms' -- @hide_breadcrumbs = true +- @hide_top_bar = true - body_classes = [user_application_theme] %html{ lang: I18n.locale, class: page_class } = render "layouts/head" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 004b9b824d9..e64ed2c7b8f 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,4 +1,4 @@ -- @hide_breadcrumbs = true +- @hide_top_bar = true - @hide_top_links = true - page_title _('New Project') - header_title _("Projects"), dashboard_projects_path diff --git a/app/views/shared/_auto_devops_callout.html.haml b/app/views/shared/_auto_devops_callout.html.haml index 93f919f01d9..c468b3a2001 100644 --- a/app/views/shared/_auto_devops_callout.html.haml +++ b/app/views/shared/_auto_devops_callout.html.haml @@ -1,4 +1,4 @@ -- container = @no_breadcrumb_container ? 'container-fluid' : container_class +- container = @no_top_bar_container ? 'container-fluid' : container_class %div{ class: [container, @content_class, 'gl-pt-5!'] } = render Pajamas::BannerComponent.new(button_text: s_('AutoDevOps|Enable in settings'), diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index c3835386d5a..e5aa4c58da1 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -1,5 +1,5 @@ - board = local_assigns.fetch(:board, nil) -- @no_breadcrumb_container = true +- @no_top_bar_container = true - @no_container = true - @content_wrapper_class = "#{@content_wrapper_class} gl-relative gl-pb-0" - @content_class = "issue-boards-content js-focus-mode-board" |