Branch search and styles (#50)

* Add LatestCommit component

* Add branches search

* Proper link for root
This commit is contained in:
Tan Nhu 2022-11-01 00:53:46 -07:00 committed by GitHub
parent 2d4db78991
commit 3f7fc109d0
13 changed files with 188 additions and 109 deletions

View File

@ -38,13 +38,13 @@
"@blueprintjs/select": "3.12.3",
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@harness/design-system": "1.0.0",
"@harness/icons": "^1.59.0",
"@harness/design-system": "1.4.0",
"@harness/icons": "1.71.0",
"@harness/monaco-yaml": ">=1.0.0",
"@harness/ng-tooltip": ">=1.31.25",
"@harness/telemetry": ">=1.0.42",
"@harness/uicore": "3.75.0",
"@harness/use-modal": ">=1.1.0",
"@harness/uicore": "3.84.0",
"@harness/use-modal": "1.3.0",
"@popperjs/core": "^2.4.2",
"@projectstorm/react-diagrams-core": "^6.6.0",
"@uiw/react-markdown-preview": "^4.1.2",

View File

@ -90,10 +90,7 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
const { showError } = useToaster()
const { mutate: createRepo, loading: submitLoading } = useMutate<TypesRepository>({
verb: 'POST',
path: `/api/v1/repos?spacePath=${space}`,
queryParams: {
// spacePath: space
}
path: `/api/v1/repos?spacePath=${space}`
})
const {
data: gitignores,

View File

@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react'
import React, { ChangeEvent, useEffect, useMemo, useState } from 'react'
import {
Container,
Layout,
@ -19,6 +19,8 @@ import { useGet } from 'restful-react'
import { useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import type { RepoBranch, TypesRepository } from 'services/scm'
import { BRANCH_PER_PAGE } from 'utils/Utils'
import { GitIcon } from 'utils/GitUtils'
import css from './ContentHeader.module.scss'
interface ContentHeaderProps {
@ -31,11 +33,14 @@ export function ContentHeader({ repoMetadata, gitRef, resourcePath = '' }: Conte
const { getString } = useStrings()
const { routes } = useAppContext()
const history = useHistory()
// const [searchTerm, setSearchTerm] = useState('README.md')
const [branch, setBranch] = useState(gitRef || repoMetadata.defaultBranch)
const { data } = useGet<RepoBranch[]>({
path: `/api/v1/repos/${repoMetadata.path}/+/branches?sort=date&direction=desc&per_page=20&page=1`
})
const [query, setQuery] = useState('')
const [activeBranch, setActiveBranch] = useState(gitRef || repoMetadata.defaultBranch)
const path = useMemo(
() =>
`/api/v1/repos/${repoMetadata.path}/+/branches?sort=date&direction=desc&per_page=${BRANCH_PER_PAGE}&page=1&query=${query}`,
[query, repoMetadata.path]
)
const { data, loading } = useGet<RepoBranch[]>({ path })
// defaultBranches is computed using repository default branch, and gitRef in URL, if it exists
const defaultBranches = useMemo(
() => [repoMetadata.defaultBranch].concat(gitRef ? gitRef : []),
@ -60,11 +65,29 @@ export function ContentHeader({ repoMetadata, gitRef, resourcePath = '' }: Conte
<Container className={css.folderHeader}>
<Layout.Horizontal spacing="medium">
<DropDown
icon="git-branch"
value={branch}
icon={GitIcon.BRANCH}
value={activeBranch}
items={branches}
{...{
inputProps: {
leftElement: (
<Icon name={loading ? 'steps-spinner' : 'thinner-search'} size={12} color={Color.GREY_500} />
),
placeholder: getString('search'),
onInput: (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.value !== query) {
setQuery(event.target.value)
}
},
onBlur: (event: ChangeEvent<HTMLInputElement>) => {
setTimeout(() => {
setQuery(event.target.value || '')
}, 250)
}
}
}}
onChange={({ value: switchBranch }) => {
setBranch(switchBranch as string)
setActiveBranch(switchBranch as string)
history.push(
routes.toSCMRepository({
repoPath: repoMetadata.path as string,
@ -77,23 +100,23 @@ export function ContentHeader({ repoMetadata, gitRef, resourcePath = '' }: Conte
/>
<Container>
<Layout.Horizontal spacing="small">
<Link to={routes.toSCMRepository({ repoPath: repoMetadata.path as string })}>
<Link to={routes.toSCMRepository({ repoPath: repoMetadata.path as string, gitRef })}>
<Icon name="main-folder" />
</Link>
<Text color={Color.GREY_900}>/</Text>
<ReactJoin separator={<Text color={Color.GREY_900}>/</Text>}>
{resourcePath.split('/').map((path, index, paths) => {
{resourcePath.split('/').map((_path, index, paths) => {
const pathAtIndex = paths.slice(0, index + 1).join('/')
return (
<Link
key={path + index}
key={_path + index}
to={routes.toSCMRepository({
repoPath: repoMetadata.path as string,
gitRef,
resourcePath: pathAtIndex
})}>
<Text color={Color.GREY_900}>{path}</Text>
<Text color={Color.GREY_900}>{_path}</Text>
</Link>
)
})}
@ -117,3 +140,5 @@ export function ContentHeader({ repoMetadata, gitRef, resourcePath = '' }: Conte
</Container>
)
}
// TODO: Optimize branch fetching when first fetch return less than request PER_PAGE

View File

@ -4,6 +4,7 @@ import { SourceCodeViewer } from 'components/SourceCodeViewer/SourceCodeViewer'
import type { OpenapiGetContentOutput, RepoFileContent } from 'services/scm'
import { GitIcon } from 'utils/GitUtils'
import { filenameToLanguage } from 'utils/Utils'
import { LatestCommit } from '../LatestCommit/LatestCommit'
import css from './FileContent.module.scss'
interface FileContentProps {
@ -12,6 +13,8 @@ interface FileContentProps {
export function FileContent({ contentInfo }: FileContentProps): JSX.Element {
return (
<Layout.Vertical spacing="small">
<LatestCommit latestCommit={contentInfo.latestCommit} standaloneStyle />
<Container className={css.container} background={Color.WHITE}>
<Layout.Horizontal padding="small" className={css.heading}>
<Heading level={5}>{contentInfo.name}</Heading>
@ -29,5 +32,6 @@ export function FileContent({ contentInfo }: FileContentProps): JSX.Element {
</Container>
)}
</Container>
</Layout.Vertical>
)
}

View File

@ -1,15 +1,4 @@
.folderContent {
.lastCommit {
display: flex;
align-items: center;
margin-left: var(--spacing-medium);
margin-right: var(--spacing-xxlarge);
.shaBtn {
border: 1px solid var(--grey-200) !important;
}
}
.table {
background-color: var(--white) !important;
@ -26,9 +15,15 @@
height: 20px;
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
overflow: hidden;
padding-top: 5px;
padding-bottom: 25px;
border-radius: 0;
padding-top: 10px;
margin-bottom: 0;
padding-bottom: 25px;
.rowText {
font-size: 13px;
font-weight: 400;
}
}
}

View File

@ -2,10 +2,9 @@
// this is an auto-generated file
declare const styles: {
readonly folderContent: string
readonly lastCommit: string
readonly shaBtn: string
readonly table: string
readonly row: string
readonly rowText: string
readonly readmeContainer: string
readonly heading: string
readonly readmeContent: string

View File

@ -1,26 +1,15 @@
import React, { useMemo } from 'react'
import {
Container,
Color,
Layout,
Button,
ButtonSize,
FlexExpander,
ButtonVariation,
TableV2 as Table,
Text,
FontVariation
} from '@harness/uicore'
import { Container, Color, TableV2 as Table, Text } from '@harness/uicore'
import type { CellProps, Column } from 'react-table'
import { sortBy } from 'lodash-es'
import ReactTimeago from 'react-timeago'
import { Link, useHistory } from 'react-router-dom'
import { useHistory } from 'react-router-dom'
import { useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import type { OpenapiContentInfo, OpenapiDirContent, OpenapiGetContentOutput, TypesRepository } from 'services/scm'
import { formatDate } from 'utils/Utils'
import { findReadmeInfo, GitIcon, isFile } from 'utils/GitUtils'
import { Readme } from './Readme'
import { LatestCommit } from '../LatestCommit/LatestCommit'
import css from './FolderContent.module.scss'
interface FolderContentProps {
@ -37,11 +26,11 @@ export function FolderContent({ repoMetadata, contentInfo, gitRef }: FolderConte
() => [
{
Header: getString('name'),
accessor: (row: OpenapiContentInfo) => row.name,
width: '30%',
width: '40%',
Cell: ({ row }: CellProps<OpenapiContentInfo>) => {
return (
<Text
className={css.rowText}
color={Color.BLACK}
icon={isFile(row.original) ? GitIcon.FILE : GitIcon.FOLDER}
lineClamp={1}
@ -53,11 +42,10 @@ export function FolderContent({ repoMetadata, contentInfo, gitRef }: FolderConte
},
{
Header: getString('commits'),
accessor: row => row.latestCommit,
width: '55%',
width: 'calc(60% - 100px)',
Cell: ({ row }: CellProps<OpenapiContentInfo>) => {
return (
<Text color={Color.BLACK} lineClamp={1}>
<Text color={Color.BLACK} lineClamp={1} className={css.rowText}>
{row.original.latestCommit?.title}
</Text>
)
@ -65,12 +53,14 @@ export function FolderContent({ repoMetadata, contentInfo, gitRef }: FolderConte
},
{
Header: getString('repos.lastChange'),
accessor: row => row.latestCommit?.author?.when,
width: '15%',
width: '100px',
Cell: ({ row }: CellProps<OpenapiContentInfo>) => {
return <Text lineClamp={1}>{formatDate(row.original.latestCommit?.author?.when as string)}</Text>
},
disableSortBy: true
return (
<Text lineClamp={1} color={Color.GREY_500} className={css.rowText}>
{formatDate(row.original.latestCommit?.author?.when as string)}
</Text>
)
}
}
],
[getString]
@ -79,27 +69,11 @@ export function FolderContent({ repoMetadata, contentInfo, gitRef }: FolderConte
return (
<Container className={css.folderContent}>
<Container>
<Layout.Horizontal spacing="medium" padding={{ bottom: 'medium' }} className={css.lastCommit}>
<Text font={{ variation: FontVariation.SMALL_SEMI }}>
{contentInfo.latestCommit?.author?.identity?.name || contentInfo.latestCommit?.author?.identity?.email}
</Text>
<Link to="">{contentInfo.latestCommit?.title}</Link>
<FlexExpander />
<Button
className={css.shaBtn}
text={contentInfo.latestCommit?.sha?.substring(0, 6)}
variation={ButtonVariation.SECONDARY}
size={ButtonSize.SMALL}
/>
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_400}>
<ReactTimeago date={contentInfo.latestCommit?.author?.when as string} />
</Text>
</Layout.Horizontal>
</Container>
<LatestCommit latestCommit={contentInfo?.latestCommit} />
<Table<OpenapiContentInfo>
className={css.table}
hideHeaders
columns={columns}
data={sortBy((contentInfo.content as OpenapiDirContent)?.entries || [], ['type', 'name'])}
onRowClick={data => {

View File

@ -0,0 +1,27 @@
.latestCommit {
display: flex;
align-items: center;
margin-left: var(--spacing-medium);
margin-right: var(--spacing-xxlarge);
background-color: var(--primary-1) !important;
align-items: center;
padding: var(--spacing-small) var(--spacing-large) !important;
box-shadow: var(--elevation-3);
border-top-left-radius: 4px;
border-top-right-radius: 4px;
&.standalone {
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
border-radius: 4px;
}
.shaBtn span {
font-size: 12px !important;
}
.commitLink {
font-weight: 500;
font-size: 12px;
color: var(--primary-8);
}
}

View File

@ -0,0 +1,9 @@
/* eslint-disable */
// this is an auto-generated file
declare const styles: {
readonly latestCommit: string
readonly standalone: string
readonly shaBtn: string
readonly commitLink: string
}
export default styles

View File

@ -0,0 +1,47 @@
import React from 'react'
import {
Container,
Color,
Layout,
Button,
ButtonSize,
FlexExpander,
ButtonVariation,
Text,
FontVariation
} from '@harness/uicore'
import { Link } from 'react-router-dom'
import ReactTimeago from 'react-timeago'
import cx from 'classnames'
import type { RepoCommit } from 'services/scm'
import css from './LatestCommit.module.scss'
interface LatestCommitProps {
latestCommit?: RepoCommit
standaloneStyle?: boolean
}
export function LatestCommit({ latestCommit, standaloneStyle }: LatestCommitProps): JSX.Element | null {
return latestCommit ? (
<Container>
<Layout.Horizontal spacing="small" className={cx(css.latestCommit, standaloneStyle ? css.standalone : '')}>
<Text font={{ variation: FontVariation.SMALL_BOLD }}>
{latestCommit.author?.identity?.name || latestCommit.author?.identity?.email}
</Text>
<Link to="" className={css.commitLink}>
{latestCommit.title}
</Link>
<FlexExpander />
<Button
className={css.shaBtn}
text={latestCommit.sha?.substring(0, 6)}
variation={ButtonVariation.SECONDARY}
size={ButtonSize.SMALL}
/>
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_400}>
<ReactTimeago date={latestCommit.author?.when as string} />
</Text>
</Layout.Horizontal>
</Container>
) : null
}

View File

@ -25,7 +25,8 @@ export const GitIcon: Readonly<Record<string, IconName>> = {
PULL_REQUEST: 'git-pull',
SETTINGS: 'cog',
FOLDER: 'main-folder',
EDIT: 'edit'
EDIT: 'edit',
BRANCH: 'git-branch'
}
export type Nullable<T> = T | undefined | null

View File

@ -4,7 +4,8 @@ import { get } from 'lodash-es'
import moment from 'moment'
import langMap from 'lang-map'
export const LIST_FETCHING_PER_PAGE = 5
export const LIST_FETCHING_PER_PAGE = 20
export const BRANCH_PER_PAGE = 100
export const DEFAULT_DATE_FORMAT = 'MM/DD/YYYY hh:mm a'
export const X_TOTAL = 'x-total'
export const X_TOTAL_PAGES = 'x-total-pages'

View File

@ -1918,15 +1918,15 @@
dependencies:
loader-utils "^2.0.0"
"@harness/design-system@1.0.0":
version "1.0.0"
resolved "https://npm.pkg.github.com/download/@harness/design-system/1.0.0/e463410da2c7ad9bff84eaa43fbe4ba9ba347da1#e463410da2c7ad9bff84eaa43fbe4ba9ba347da1"
integrity sha512-sMsLP5Rxg08t87dJamTgpzEfRAef06C14bZO92SGqiXYtXsK01evjnXaOAiUS4gO36gMGi5cYJAqkJ0XBkERIg==
"@harness/design-system@1.4.0":
version "1.4.0"
resolved "https://npm.pkg.github.com/download/@harness/design-system/1.4.0/b2a77f73696d71a53765c71efd0a5b28039fa1cf#b2a77f73696d71a53765c71efd0a5b28039fa1cf"
integrity sha512-LuzuPEHPkE6xgIuXxn16RCCvPY1NDXF3o1JWlIjxmepoDTkgFuwnV1OhBdQftvAVBawJ5wJP10IIKUL161LdYg==
"@harness/icons@^1.59.0":
version "1.68.1"
resolved "https://npm.pkg.github.com/download/@harness/icons/1.68.1/56d8f98170aad544c44b3a77d15ffe0cbd6b5931#56d8f98170aad544c44b3a77d15ffe0cbd6b5931"
integrity sha512-9PLPVE/LffliSgYrP0TEWMWTZJX5ANYMTyqSSS4115tw8K52Wbl35C9Wg0ftLrzvJrt7ok+i+RZAdjc123kwpw==
"@harness/icons@1.71.0":
version "1.71.0"
resolved "https://npm.pkg.github.com/download/@harness/icons/1.71.0/86c3b579e3785106a03af048a15a93ae684e7358#86c3b579e3785106a03af048a15a93ae684e7358"
integrity sha512-mnUvcI1o8mi7A1437/fG8xXS2spzVte4PZbKlbguZ5WtojKGxVe4FMT6xhUt7goSihEz9cu91WY5V/KE7Cc5Ew==
"@harness/jarvis@^0.12.0":
version "0.12.0"
@ -1967,12 +1967,12 @@
resolved "https://npm.pkg.github.com/download/@harness/telemetry/1.0.44/55e75d8caccbcdcb0a11226c813edd631578d9af#55e75d8caccbcdcb0a11226c813edd631578d9af"
integrity sha512-t6N3Ie/F9Nw/tANAmptsunebGYBkC3j865bc75MZVL2ZqFM0CBRxFR1MG8zC+hU6uDpr8Drqsn81NmdlVlBSmA==
"@harness/uicore@3.75.0":
version "3.75.0"
resolved "https://npm.pkg.github.com/download/@harness/uicore/3.75.0/fecb45629a1d55783bf6088424957bbdf81af743#fecb45629a1d55783bf6088424957bbdf81af743"
integrity sha512-j61mbRnfkJAWuBrVXBDaio+MR+lS//klVY277EiGb4iWRo0opX9nyBmRhd4hD+rtfux9JSS7BhqUHdG1pzCQ/A==
"@harness/uicore@3.84.0":
version "3.84.0"
resolved "https://npm.pkg.github.com/download/@harness/uicore/3.84.0/9c02acd3106254a08530f68065317d031cd15e67#9c02acd3106254a08530f68065317d031cd15e67"
integrity sha512-3AvETaoDHGGKSbnFN9rZAg0HgWWBKMeqGpbonxWj/6gUEahlMnzd8QGvpjUdbYOVCAtPbUVxrtI0Yf4VQPuf5g==
"@harness/use-modal@>=1.1.0":
"@harness/use-modal@1.3.0":
version "1.3.0"
resolved "https://npm.pkg.github.com/download/@harness/use-modal/1.3.0/fdedeb097e62000c856ed9e73368ff439692549f#fdedeb097e62000c856ed9e73368ff439692549f"
integrity sha512-2CxYlv4rLtHNAZz3Kg7zC+UyMaTeyLwYh3cr7xUtBVJsYpggm4GgyuJSui+gRaO75Os9mdtKQzBt+paPgY8krQ==