mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-07-30 09:48:47 -04:00
Enable smooth scrolling to top of paginated lists in compatibility questions and adjust font size for improved readability.
This commit is contained in:
@@ -13,7 +13,7 @@ import {User} from 'common/user'
|
||||
import {shortenNumber} from 'common/util/format'
|
||||
import {keyBy, sortBy} from 'lodash'
|
||||
import {PinIcon} from 'lucide-react'
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {RefObject, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import {AddCompatibilityQuestionButton} from 'web/components/answers/add-compatibility-question-button'
|
||||
import DropdownMenu from 'web/components/comments/dropdown-menu'
|
||||
@@ -62,6 +62,25 @@ import {PinQuestionButton} from './pin-question-button'
|
||||
const NUM_QUESTIONS_TO_SHOW = 8
|
||||
const NUM_PINNED_QUESTIONS_TO_SHOW = 4
|
||||
|
||||
// Matches the `scroll-mt-24` on the paginated lists (6rem), which is what keeps a scrolled-to first prompt
|
||||
// clear of the sticky header.
|
||||
const LIST_SCROLL_OFFSET = 96
|
||||
|
||||
/**
|
||||
* Changing page is a request for new content to read, and reading starts at the first prompt — so put the
|
||||
* top of that list back in view instead of leaving the reader parked at the tail of the new page. The
|
||||
* paginator itself is not lost: it sits where they left it, one scroll down past the prompts they came for.
|
||||
*
|
||||
* Only scrolls when the top has actually drifted out of reach (short list, or the reader is already up
|
||||
* there) — a jump that changes nothing reads as a glitch.
|
||||
*/
|
||||
const scrollListIntoView = (ref: RefObject<HTMLDivElement | null>) => {
|
||||
const el = ref.current
|
||||
if (!el) return
|
||||
if (el.getBoundingClientRect().top >= LIST_SCROLL_OFFSET) return
|
||||
el.scrollIntoView({block: 'start', behavior: 'smooth'})
|
||||
}
|
||||
|
||||
export function CompatibilityQuestionsDisplay(props: {
|
||||
isCurrentUser: boolean
|
||||
user: User
|
||||
@@ -145,7 +164,16 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
)
|
||||
}, [answers, compatibilityQuestions, comparedAnswers, searchTerm, sort])
|
||||
|
||||
// Each list scrolls to its own top, not to a shared one — paging the pinned list must not yank the
|
||||
// reader down to the main list, or vice versa.
|
||||
const listRef = useRef<HTMLDivElement>(null)
|
||||
const pinnedListRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [page, setPage] = useState(0)
|
||||
const goToPage = (p: number) => {
|
||||
setPage(p)
|
||||
scrollListIntoView(listRef)
|
||||
}
|
||||
const currentSlice = page * NUM_QUESTIONS_TO_SHOW
|
||||
const shownAnswers = sortedAndFilteredAnswers.slice(
|
||||
currentSlice,
|
||||
@@ -160,6 +188,10 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
}, [answers, pinnedQuestionIds])
|
||||
|
||||
const [pinnedPage, setPinnedPage] = useState(0)
|
||||
const goToPinnedPage = (p: number) => {
|
||||
setPinnedPage(p)
|
||||
scrollListIntoView(pinnedListRef)
|
||||
}
|
||||
const pinnedCurrentSlice = pinnedPage * NUM_PINNED_QUESTIONS_TO_SHOW
|
||||
const shownPinnedAnswers = pinnedAnswers.slice(
|
||||
pinnedCurrentSlice,
|
||||
@@ -177,7 +209,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
{pinnedAnswers.length > 0 && (
|
||||
<Col className="gap-3">
|
||||
<PinIcon />
|
||||
<Col>
|
||||
<Col ref={pinnedListRef} className="scroll-mt-24">
|
||||
{shownPinnedAnswers.map((answer) => (
|
||||
<CompatibilityAnswerBlock
|
||||
key={`pinned-${answer.question_id}`}
|
||||
@@ -197,7 +229,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
page={pinnedPage}
|
||||
pageSize={NUM_PINNED_QUESTIONS_TO_SHOW}
|
||||
totalItems={pinnedAnswers.length}
|
||||
setPage={setPinnedPage}
|
||||
setPage={goToPinnedPage}
|
||||
/>
|
||||
)}
|
||||
<div className="border-canvas-200 border-b" />
|
||||
@@ -266,7 +298,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<Col>
|
||||
<Col ref={listRef} className="scroll-mt-24">
|
||||
{shownAnswers.map((answer) => {
|
||||
return (
|
||||
<CompatibilityAnswerBlock
|
||||
@@ -300,7 +332,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
page={page}
|
||||
pageSize={NUM_QUESTIONS_TO_SHOW}
|
||||
totalItems={sortedAndFilteredAnswers.length}
|
||||
setPage={setPage}
|
||||
setPage={goToPage}
|
||||
/>
|
||||
)}
|
||||
{isCurrentUser && !fromProfilePage && (
|
||||
@@ -450,7 +482,7 @@ export function CompatibilityAnswerBlock(props: {
|
||||
// `font-figtree` is the body face: globals.css sets every h1–h6 to Newsreader at weight 700,
|
||||
// so a heading element opts out of the serif explicitly or it does not opt out at all.
|
||||
className="font-figtree text-ink-600 min-w-0 font-normal"
|
||||
style={{fontSize: '16px', lineHeight: '1.45'}}
|
||||
style={{fontSize: '18px', lineHeight: '1.45'}}
|
||||
data-testid="profile-compatibility-question"
|
||||
>
|
||||
{question.question}
|
||||
|
||||
Reference in New Issue
Block a user