Skip to content

Commit

Permalink
feat(js): scroll-to on feed len change (#403)
Browse files Browse the repository at this point in the history
Signed-off-by: Elizabeth Kelen <erskelen@gmail.com>
  • Loading branch information
ekelen authored Oct 12, 2020
1 parent cb394fc commit 237b097
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 28 deletions.
1 change: 1 addition & 0 deletions web/.babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@babel/plugin-syntax-dynamic-import",
["transform-react-remove-prop-types",{ "removeImport": true }],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-optional-chaining",
"react-hot-loader/babel"
]
},
Expand Down
45 changes: 41 additions & 4 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@babel/core": "7.9.0",
"@babel/node": "7.8.7",
"@babel/plugin-proposal-class-properties": "7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/plugin-transform-react-constant-elements": "7.9.0",
"@babel/plugin-transform-runtime": "7.9.0",
Expand Down
141 changes: 117 additions & 24 deletions web/src/ui/components/BuildList.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React, {
Fragment, useMemo, useContext, useState,
Fragment,
useMemo,
useContext,
useState,
useRef,
useEffect,
} from 'react'
import { ChevronDown, ChevronUp } from 'react-feather'
import {
Expand All @@ -17,6 +22,64 @@ import BuildContainer from './Build/BuildContainer'
import Divider from './Divider/Divider'

import { ThemeContext } from '../../store/ThemeStore'
import { usePrevious } from '../../util/misc'

const Feed = ({
displayFeed,
indexOfLatestMasterBuildsForProjects,
buildsByMrWithDateFlags,
builds,
}) => {
const { theme } = useContext(ThemeContext)
const scrollTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}

const scrollToTopStyle = {
backgroundColor: theme.bg.btnPrimary,
color: theme.text.btnPrimary,
padding: '0.3rem 0.7rem',
display: 'flex',
alignItems: 'center',
marginBottom: '1rem',
}

return (
<>
{(displayFeed === true || !indexOfLatestMasterBuildsForProjects.length)
&& buildsByMrWithDateFlags.map((
build,
i, // The i is important to get day dividers, so don't change length of this array
) => (
<Fragment key={`${build.id}-${i}`}>
{build.buildIsFirstOfDay
&& !indexOfLatestMasterBuildsForProjects.includes(i) && (
<Divider dividerText={getDayFormat(build.created_at)} />
)}
{!indexOfLatestMasterBuildsForProjects.includes(i) && (
<BuildContainer
build={build}
toCollapse={indexOfLatestMasterBuildsForProjects.length > 0}
hasRunningBuilds={buildsStateIsRunning(
build.allBuildsForMr,
builds,
)}
/>
)}
</Fragment>
))}
{buildsByMrWithDateFlags?.length > 5 && displayFeed && (
<button
className="btn btn-primary"
style={scrollToTopStyle}
onClick={scrollTop}
>
Scroll to top
</button>
)}
</>
)
}

const FeedDisplayToggler = ({
loaded = false,
Expand Down Expand Up @@ -60,15 +123,19 @@ const FeedDisplayToggler = ({

const BuildList = ({ builds = [], loaded }) => {
const [displayFeed, setDisplayFeed] = useState(false)
const ref = useRef()
const refOpen = useRef()
const oneBuildInResultsHasMaster = useMemo(
() => oneBuildResultHasBranchMaster(builds),
[builds],
)
const prevDisplay = usePrevious(displayFeed)
const buildsByMr = useMemo(() => groupBuildsByMr(builds), [builds])
const buildsByMrWithDateFlags = useMemo(
() => flagBuildsFirstOfDay(buildsByMr),
[buildsByMr],
)

const indexOfLatestMasterBuildsForProjects = !getIsArrayWithN(buildsByMrWithDateFlags, 2) || !oneBuildInResultsHasMaster
? []
: getLatestMasterBuildsForProjects(buildsByMrWithDateFlags)
Expand All @@ -78,6 +145,31 @@ const BuildList = ({ builds = [], loaded }) => {
setDisplayFeed(!displayFeed)
}

useEffect(() => {
// On hide feed, scroll to view 'show feed' was clicked from
if (
ref?.current
&& displayFeed === false
&& prevDisplay === true
&& builds.length > 0
) {
ref.current.scrollIntoView({
behavior: 'smooth',
block: 'end',
})
}
}, [displayFeed, prevDisplay, builds.length])

useEffect(() => {
// On show feed, scroll to feed with 'hide feed' button at top of screen
if (refOpen?.current && displayFeed === true && prevDisplay === false) {
refOpen.current.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
}, [displayFeed, refOpen, prevDisplay])

return !builds.length > 0 && loaded ? (
<NoBuilds />
) : (
Expand All @@ -100,7 +192,14 @@ const BuildList = ({ builds = [], loaded }) => {
)}
/>
))}

<div
ref={refOpen}
style={{
visibility: 'hidden',
height: 0,
transform: 'translateY(-10px)',
}}
/>
{loaded && indexOfLatestMasterBuildsForProjects.length > 0 && (
<FeedDisplayToggler
{...{
Expand All @@ -111,28 +210,22 @@ const BuildList = ({ builds = [], loaded }) => {
}}
/>
)}
{(displayFeed === true || !indexOfLatestMasterBuildsForProjects.length)
&& buildsByMrWithDateFlags.map((
build,
i, // The i is important to get day dividers, so don't change length of this array
) => (
<Fragment key={`${build.id}-${i}`}>
{build.buildIsFirstOfDay
&& !indexOfLatestMasterBuildsForProjects.includes(i) && (
<Divider dividerText={getDayFormat(build.created_at)} />
)}
{!indexOfLatestMasterBuildsForProjects.includes(i) && (
<BuildContainer
build={build}
toCollapse={indexOfLatestMasterBuildsForProjects.length > 0}
hasRunningBuilds={buildsStateIsRunning(
build.allBuildsForMr,
builds,
)}
/>
)}
</Fragment>
))}
<div
ref={ref}
style={{
visibility: 'hidden',
height: 0,
transform: 'translateY(10px)',
}}
/>
<Feed
{...{
indexOfLatestMasterBuildsForProjects,
buildsByMrWithDateFlags,
builds,
displayFeed,
}}
/>
</div>
)
}
Expand Down
10 changes: 10 additions & 0 deletions web/src/util/misc.js
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
import { useEffect, useRef } from 'react'

export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

export function usePrevious(value) {
const ref = useRef()
useEffect(() => {
ref.current = value
})
return ref.current
}

0 comments on commit 237b097

Please sign in to comment.