From 1a51fc60b748b58dec4d768de089ca04df843edd Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 14 Nov 2023 20:05:31 -0700 Subject: [PATCH] Add "Read more" button to readmes ... and fix user action button alignment Changelog: changed --- .../javascripts/pages/users/show/index.js | 3 ++ app/assets/javascripts/read_more.js | 10 ++++ .../stylesheets/page_bundles/profile.scss | 33 +++++++++++++ app/views/users/_follow_user.html.haml | 10 ++-- app/views/users/_overview.html.haml | 26 +++++----- locale/gitlab.pot | 3 ++ spec/frontend/read_more_spec.js | 48 ++++++++++++++++--- 7 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 app/assets/javascripts/pages/users/show/index.js diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js new file mode 100644 index 00000000000000..dbb5e29c689ce1 --- /dev/null +++ b/app/assets/javascripts/pages/users/show/index.js @@ -0,0 +1,3 @@ +import initReadMore from '~/read_more'; + +initReadMore(); diff --git a/app/assets/javascripts/read_more.js b/app/assets/javascripts/read_more.js index 692f375bb940bc..92ceeec9d99e57 100644 --- a/app/assets/javascripts/read_more.js +++ b/app/assets/javascripts/read_more.js @@ -16,6 +16,8 @@ * * * + * If data-show-button-height is present it will use it to determine if the button should be shown or not. + * */ export default function initReadMore(triggerSelector = '.js-read-more-trigger') { const triggerEls = document.querySelectorAll(triggerSelector); @@ -29,6 +31,14 @@ export default function initReadMore(triggerSelector = '.js-read-more-trigger') return; } + if ( + Object.hasOwn(targetEl.dataset, 'showButtonHeight') && + Number(targetEl.dataset.showButtonHeight) > targetEl.clientHeight + ) { + triggerEl.remove(); + return; + } + triggerEl.addEventListener( 'click', () => { diff --git a/app/assets/stylesheets/page_bundles/profile.scss b/app/assets/stylesheets/page_bundles/profile.scss index 573f9dbc69b968..18f33fde2fccd9 100644 --- a/app/assets/stylesheets/page_bundles/profile.scss +++ b/app/assets/stylesheets/page_bundles/profile.scss @@ -78,6 +78,39 @@ max-width: 600px; } +.profile-readme-wrapper { + $height: 22rem; + $scrim: 2rem; + $readme-bar-height: 2.5rem; + + .profile-readme:not(.is-expanded) { + max-height: $height; + + // only appears when size is > $height. + &::after { + content: ''; + display: block; + position: absolute; + left: 0; + right: 0; + top: calc(#{$height} - #{$scrim} - #{$readme-bar-height}); + height: $scrim; + background: linear-gradient(180deg, transparent, $white); + + .gl-dark & { + background: linear-gradient(180deg, transparent, $black); + } + } + } + + // only appears when size is > $height. + .read-more-bar { + bottom: 1px; + left: 1px; + right: 1px; + } +} + .user-calendar-activities { direction: ltr; diff --git a/app/views/users/_follow_user.html.haml b/app/views/users/_follow_user.html.haml index 71f8a462cbf7bf..953e74c1f27c16 100644 --- a/app/views/users/_follow_user.html.haml +++ b/app/views/users/_follow_user.html.haml @@ -1,11 +1,9 @@ -- link_classes = "flex-grow-1 gl-display-inline-block" - - if current_user&.following_users_allowed?(@user) - if current_user.following?(@user) - = form_tag user_unfollow_path(@user, :json), class: link_classes do - = render Pajamas::ButtonComponent.new(type: :submit, button_options: { class: 'gl-w-full', data: { track_action: 'click_button', track_label: 'unfollow_from_profile' } }) do + = form_tag user_unfollow_path(@user, :json) do + = render Pajamas::ButtonComponent.new(type: :submit, button_options: { data: { track_action: 'click_button', track_label: 'unfollow_from_profile' } }) do = _('Unfollow') - else - = form_tag user_follow_path(@user, :json), class: link_classes do - = render Pajamas::ButtonComponent.new(variant: :confirm, type: :submit, button_options: { class: 'gl-w-full', data: { testid: 'follow-user-link', track_action: 'click_button', track_label: 'follow_from_profile' } }) do + = form_tag user_follow_path(@user, :json) do + = render Pajamas::ButtonComponent.new(variant: :confirm, type: :submit, button_options: { data: { testid: 'follow-user-link', track_action: 'click_button', track_label: 'follow_from_profile' } }) do = _('Follow') diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml index a1043ead49e00c..5310752e71e5a6 100644 --- a/app/views/users/_overview.html.haml +++ b/app/views/users/_overview.html.haml @@ -1,6 +1,6 @@ - if can?(current_user, :read_cross_project) && @user.user_readme&.rich_viewer .profile-readme-wrapper.gl-relative.gl-overflow-hidden.gl-mb-5 - .profile-readme.read-more-container.gl-relative.justify-content-center.gl-border.gl-rounded-base.gl-py-5.gl-px-6.gl-overflow-hidden + .profile-readme.read-more-container.gl-relative.justify-content-center.gl-border.gl-rounded-base.gl-py-5.gl-px-6.gl-overflow-hidden{ data: { 'show-button-height': 320 } } .gl-display-flex %ol.breadcrumb.gl-breadcrumb-list.gl-mb-4 %li.gl-breadcrumb-item @@ -12,16 +12,10 @@ .gl-ml-auto = link_to _('Edit file'), edit_blob_path(@user.user_project, @user.user_project.default_branch, @user.user_readme.path) = render 'projects/blob/viewer', viewer: @user.user_readme.rich_viewer, load_async: false - -- unless Feature.enabled?(:security_auto_fix) && @user.bot? - - if @user.personal_projects.any? - .projects-block - .gl-display-flex.gl-align-items-baseline - %h4.gl-font-lg.gl-flex-grow-1 - = s_('UserProfile|Personal projects') - = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all" - .overview-content-list{ data: { href: user_projects_path } } - = gl_loading_icon(size: 'md', css_class: 'loading') + .js-read-more-trigger.read-more-bar.gl-h-8.gl-absolute.gl-bottom-0.gl-z-index-2.gl-bg-white.gl-px-6.gl-rounded-bottom-base + = render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'gl-mt-4 gl-ml-n1', 'aria-label': _("Expand Readme") }) do + = sprite_icon('chevron-down', size: 14, css_class: 'gl-mr-1 gl-mb-n1') + = _("Read more") - if can?(current_user, :read_cross_project) .gl-align-self-start @@ -41,3 +35,13 @@ .user-calendar-activities .overview-content-list.user-activity-content.gl-mb-5{ data: { href: user_activity_path, testid: 'user-activity-content' } } = gl_loading_icon(size: 'md', css_class: 'loading') + +- unless Feature.enabled?(:security_auto_fix) && @user.bot? + - if @user.personal_projects.any? + .projects-block + .gl-display-flex.gl-align-items-baseline + %h4.gl-font-lg.gl-flex-grow-1 + = s_('UserProfile|Personal projects') + = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all" + .overview-content-list{ data: { href: user_projects_path } } + = gl_loading_icon(size: 'md', css_class: 'loading') diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ef57ea577cf6d9..6fd08fce657103 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -19829,6 +19829,9 @@ msgstr "" msgid "Expand AI-generated summary" msgstr "" +msgid "Expand Readme" +msgstr "" + msgid "Expand all" msgstr "" diff --git a/spec/frontend/read_more_spec.js b/spec/frontend/read_more_spec.js index 5f7bd32e23101b..ab1b9b713d12a4 100644 --- a/spec/frontend/read_more_spec.js +++ b/spec/frontend/read_more_spec.js @@ -3,6 +3,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import initReadMore from '~/read_more'; describe('Read more click-to-expand functionality', () => { + const findTarget = () => document.querySelector('.read-more-container'); const findTrigger = () => document.querySelector('.js-read-more-trigger'); afterEach(() => { @@ -15,13 +16,11 @@ describe('Read more click-to-expand functionality', () => { }); it('adds "is-expanded" class to target element', () => { - const target = document.querySelector('.read-more-container'); - const trigger = findTrigger(); initReadMore(); - trigger.click(); + findTrigger().click(); - expect(target.classList.contains('is-expanded')).toEqual(true); + expect(findTarget().classList.contains('is-expanded')).toEqual(true); }); }); @@ -34,8 +33,7 @@ describe('Read more click-to-expand functionality', () => { `); - const trigger = findTrigger(); - const nestedElement = trigger.firstElementChild; + const nestedElement = findTrigger().firstElementChild; initReadMore(); nestedElement.click(); @@ -46,3 +44,41 @@ describe('Read more click-to-expand functionality', () => { }); }); }); + +describe('data-show-button-height defines when to show the read-more button', () => { + const findTrigger = () => document.querySelectorAll('.js-read-more-trigger'); + + afterEach(() => { + resetHTMLFixture(); + }); + + it('if not set shows button all the time', () => { + setHTMLFixture(` +
+

Occaecat voluptate exercitation aliqua et duis eiusmod mollit esse ea laborum amet consectetur officia culpa anim. Fugiat laboris eu irure deserunt excepteur laboris irure quis. Occaecat nostrud irure do officia ea laborum velit sunt. Aliqua incididunt non deserunt proident magna aliqua sunt laborum laborum eiusmod ullamco. Et elit commodo irure. Labore eu nisi proident.

+
+ + `); + + initReadMore(); + + expect(findTrigger().length).toBe(1); + }); + + it('if set hides button as threshold is met', () => { + setHTMLFixture(` +
+

Occaecat voluptate exercitation aliqua et duis eiusmod mollit esse ea laborum amet consectetur officia culpa anim. Fugiat laboris eu irure deserunt excepteur laboris irure quis. Occaecat nostrud irure do officia ea laborum velit sunt. Aliqua incididunt non deserunt proident magna aliqua sunt laborum laborum eiusmod ullamco. Et elit commodo irure. Labore eu nisi proident.

+
+ + `); + + initReadMore(); + + expect(findTrigger().length).toBe(0); + }); +}); -- GitLab