Skip to content

Commit

Permalink
feat(your-work): GitHub repo filter (#8899)
Browse files Browse the repository at this point in the history
* feat(your-work): GitHub repo filter

* Refactor poker github repo filter

* Add icon

* fix spacing and padding

* CR: Remove unnecessary GQL fragment

* CR: Switch to filter icon

* Style changes
  • Loading branch information
jmtaber129 authored Oct 6, 2023
1 parent 8341d3d commit 5406a0d
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 96 deletions.
109 changes: 109 additions & 0 deletions packages/client/components/GitHubRepoSearchFilterMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, {useMemo} from 'react'
import graphql from 'babel-plugin-relay/macro'
import {useFragment} from 'react-relay'
import {MenuProps} from '../hooks/useMenu'
import {GitHubRepoSearchFilterMenu_teamMember$key} from '../__generated__/GitHubRepoSearchFilterMenu_teamMember.graphql'
import useGetRepoContributions from '../hooks/useGetRepoContributions'
import useSearchFilter from '../hooks/useSearchFilter'
import Checkbox from './Checkbox'
import {EmptyDropdownMenuItemLabel} from './EmptyDropdownMenuItemLabel'
import Menu from './Menu'
import MenuItem from './MenuItem'
import MenuItemLabel from './MenuItemLabel'
import styled from '@emotion/styled'
import {SearchMenuItem} from './SearchMenuItem'
import TypeAheadLabel from './TypeAheadLabel'

const StyledCheckBox = styled(Checkbox)({
marginLeft: -8,
marginRight: 8
})
const StyledMenuItemLabel = styled(MenuItemLabel)({})

const StyledMenu = styled(Menu)({
maxWidth: '100%',
minWidth: '300px',
padding: '8px',
maxHeight: '100%'
})

const getValue = (item: {nameWithOwner?: string}) => {
return item.nameWithOwner || 'Unknown Repo'
}

const MAX_REPOS = 10

interface Props {
selectedRepos: string[]
onToggleRepo: (repoName: string, isSelected: boolean) => void
teamMemberRef: GitHubRepoSearchFilterMenu_teamMember$key
menuProps: MenuProps
menuLabel?: string
}

const GitHubRepoSearchFilterMenu = (props: Props) => {
const {teamMemberRef, menuProps, selectedRepos, onToggleRepo, menuLabel} = props
const teamMember = useFragment(
graphql`
fragment GitHubRepoSearchFilterMenu_teamMember on TeamMember {
...useGetRepoContributions_teamMember
}
`,
teamMemberRef
)

const repoContributions = useGetRepoContributions(teamMember)

const {
query: searchQuery,
filteredItems: filteredRepoContributions,
onQueryChange
} = useSearchFilter(repoContributions, getValue)

const selectedAndFilteredRepos = useMemo(() => {
const adjustedMax = selectedRepos.length >= MAX_REPOS ? selectedRepos.length + 1 : MAX_REPOS
const repos = filteredRepoContributions.map(({nameWithOwner}) =>
nameWithOwner.toLowerCase().trim()
)
return Array.from(new Set([...selectedRepos, ...repos])).slice(0, adjustedMax)
}, [filteredRepoContributions])

const {portalStatus, isDropdown} = menuProps
return (
<StyledMenu
keepParentFocus
ariaLabel='Define the GitHub search query'
portalStatus={portalStatus}
isDropdown={isDropdown}
>
{menuLabel && (
<div className='mx-2 mb-2 text-sm font-semibold text-slate-600'>{menuLabel}</div>
)}
<SearchMenuItem
placeholder='Search your GitHub repos'
onChange={onQueryChange}
value={searchQuery}
/>
{repoContributions.length === 0 && (
<EmptyDropdownMenuItemLabel key='no-results'>No repos found!</EmptyDropdownMenuItemLabel>
)}
{selectedAndFilteredRepos.map((repo) => {
const isSelected = selectedRepos.includes(repo)
return (
<MenuItem
key={repo}
label={
<StyledMenuItemLabel>
<StyledCheckBox active={isSelected} />
<TypeAheadLabel query={searchQuery} label={repo} />
</StyledMenuItemLabel>
}
onClick={() => onToggleRepo(repo, isSelected)}
/>
)
})}
</StyledMenu>
)
}

export default GitHubRepoSearchFilterMenu
117 changes: 25 additions & 92 deletions packages/client/components/GitHubScopingSearchFilterMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,12 @@
import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import React, {useMemo} from 'react'
import React from 'react'
import {commitLocalUpdate, PreloadedQuery, usePreloadedQuery} from 'react-relay'
import useSearchFilter from '~/hooks/useSearchFilter'
import useAtmosphere from '../hooks/useAtmosphere'
import useGetRepoContributions from '../hooks/useGetRepoContributions'
import {MenuProps} from '../hooks/useMenu'
import SearchQueryId from '../shared/gqlIds/SearchQueryId'
import getReposFromQueryStr from '../utils/getReposFromQueryStr'
import {GitHubScopingSearchFilterMenuQuery} from '../__generated__/GitHubScopingSearchFilterMenuQuery.graphql'
import Checkbox from './Checkbox'
import {EmptyDropdownMenuItemLabel} from './EmptyDropdownMenuItemLabel'
import Menu from './Menu'
import MenuItem from './MenuItem'
import MenuItemLabel from './MenuItemLabel'
import {SearchMenuItem} from './SearchMenuItem'
import TypeAheadLabel from './TypeAheadLabel'

const StyledCheckBox = styled(Checkbox)({
marginLeft: -8,
marginRight: 8
})
const StyledMenuItemLabel = styled(MenuItemLabel)({})

const StyledMenu = styled(Menu)({
maxWidth: '100%'
})
import GitHubRepoSearchFilterMenu from './GitHubRepoSearchFilterMenu'

interface Props {
menuProps: MenuProps
Expand All @@ -38,12 +19,6 @@ type GitHubSearchQuery = NonNullable<
>['githubSearchQuery']
>

const MAX_REPOS = 10

const getValue = (item: {nameWithOwner?: string}) => {
return item.nameWithOwner || 'Unknown Repo'
}

const GitHubScopingSearchFilterMenu = (props: Props) => {
const {menuProps, queryRef} = props
const query = usePreloadedQuery<GitHubScopingSearchFilterMenuQuery>(
Expand All @@ -59,86 +34,44 @@ const GitHubScopingSearchFilterMenu = (props: Props) => {
}
}
teamMember(teamId: $teamId) {
...useGetRepoContributions_teamMember
...GitHubRepoSearchFilterMenu_teamMember
}
}
}
`,
queryRef
)

const repoContributions = useGetRepoContributions(query.viewer.teamMember!)
const meeting = query?.viewer?.meeting
const meetingId = meeting?.id ?? ''
const githubSearchQuery = meeting?.githubSearchQuery
const queryString = githubSearchQuery?.queryString ?? null
const atmosphere = useAtmosphere()

const {
query: searchQuery,
filteredItems: filteredRepoContributions,
onQueryChange
} = useSearchFilter(repoContributions, getValue)

// TODO parse the query string & extract out the repositories
const selectedRepos = getReposFromQueryStr(queryString)
const selectedAndFilteredRepos = useMemo(() => {
const adjustedMax = selectedRepos.length >= MAX_REPOS ? selectedRepos.length + 1 : MAX_REPOS
const repos = filteredRepoContributions.map(({nameWithOwner}) =>
nameWithOwner.toLowerCase().trim()
)
return Array.from(new Set([...selectedRepos, ...repos])).slice(0, adjustedMax)
}, [filteredRepoContributions])

const {portalStatus, isDropdown} = menuProps
return (
<StyledMenu
keepParentFocus
ariaLabel='Define the GitHub search query'
portalStatus={portalStatus}
isDropdown={isDropdown}
>
<SearchMenuItem
placeholder='Search your GitHub repos'
onChange={onQueryChange}
value={searchQuery}
/>
{repoContributions.length === 0 && (
<EmptyDropdownMenuItemLabel key='no-results'>No repos found!</EmptyDropdownMenuItemLabel>
)}
{selectedAndFilteredRepos.map((repo) => {
const isSelected = selectedRepos.includes(repo)
const handleClick = () => {
commitLocalUpdate(atmosphere, (store) => {
const searchQueryId = SearchQueryId.join('github', meetingId)
const githubSearchQuery = store.get<GitHubSearchQuery>(searchQueryId)!
const newFilters = isSelected
? selectedRepos.filter((name) => name !== repo)
: selectedRepos.concat(repo)
const queryString = githubSearchQuery.getValue('queryString')
const queryWithoutRepos = queryString
.trim()
.split(' ')
.filter((str) => !str.includes('repo:'))
const newRepos = newFilters.map((name) => `repo:${name}`)
const newQueryStr = queryWithoutRepos.concat(newRepos).join(' ')
githubSearchQuery.setValue(newQueryStr, 'queryString')
})
}
return (
<MenuItem
key={repo}
label={
<StyledMenuItemLabel>
<StyledCheckBox active={isSelected} />
<TypeAheadLabel query={searchQuery} label={repo} />
</StyledMenuItemLabel>
}
onClick={handleClick}
/>
)
})}
</StyledMenu>
<GitHubRepoSearchFilterMenu
selectedRepos={selectedRepos}
onToggleRepo={(repo, isSelected) => {
commitLocalUpdate(atmosphere, (store) => {
const searchQueryId = SearchQueryId.join('github', meetingId)
const githubSearchQuery = store.get<GitHubSearchQuery>(searchQueryId)!
const newFilters = isSelected
? selectedRepos.filter((name) => name !== repo)
: selectedRepos.concat(repo)
const queryString = githubSearchQuery.getValue('queryString')
const queryWithoutRepos = queryString
.trim()
.split(' ')
.filter((str) => !str.includes('repo:'))
const newRepos = newFilters.map((name) => `repo:${name}`)
const newQueryStr = queryWithoutRepos.concat(newRepos).join(' ')
githubSearchQuery.setValue(newQueryStr, 'queryString')
})
}}
teamMemberRef={query.viewer.teamMember!}
menuProps={menuProps}
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import GitHubClientManager from '../../../utils/GitHubClientManager'
import useAtmosphere from '../../../hooks/useAtmosphere'
import useMutationProps from '../../../hooks/useMutationProps'
import GitHubIntegrationResultsRoot from './GitHubIntegrationResultsRoot'
import GitHubRepoFilterBar from './GitHubRepoFilterBar'

const GITHUB_QUERY_TABS: {key: 'issue' | 'pullRequest'; label: string}[] = [
{
Expand Down Expand Up @@ -37,6 +38,7 @@ const GitHubIntegrationPanel = (props: Props) => {
isActive
}
}
...GitHubRepoFilterBar_teamMember
}
}
}
Expand All @@ -47,6 +49,7 @@ const GitHubIntegrationPanel = (props: Props) => {
const teamMember = meeting.viewerMeetingMember?.teamMember

const [githubType, setGithubType] = useState<'issue' | 'pullRequest'>('issue')
const [selectedRepos, setSelectedRepos] = useState<string[]>([])

const atmosphere = useAtmosphere()
const mutationProps = useMutationProps()
Expand All @@ -63,7 +66,12 @@ const GitHubIntegrationPanel = (props: Props) => {
<>
{teamMember?.integrations.github?.isActive ? (
<>
<div className='my-4 flex w-full gap-2 px-4'>
<GitHubRepoFilterBar
teamMemberRef={teamMember}
selectedRepos={selectedRepos}
setSelectedRepos={setSelectedRepos}
/>
<div className='mb-4 flex w-full gap-2 px-4'>
{GITHUB_QUERY_TABS.map((tab) => (
<div
key={tab.key}
Expand All @@ -79,7 +87,11 @@ const GitHubIntegrationPanel = (props: Props) => {
</div>
))}
</div>
<GitHubIntegrationResultsRoot teamId={teamMember.teamId} queryType={githubType} />
<GitHubIntegrationResultsRoot
teamId={teamMember.teamId}
queryType={githubType}
selectedRepos={selectedRepos}
/>
</>
) : (
<div className='-mt-14 flex h-full flex-col items-center justify-center gap-2'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {renderLoader} from '~/utils/relay/renderLoader'
interface Props {
teamId: string
queryType: 'issue' | 'pullRequest'
selectedRepos: string[]
}

const GITHUB_QUERY_MAPPING = {
Expand All @@ -18,10 +19,11 @@ const GITHUB_QUERY_MAPPING = {
}

const GitHubIntegrationResultsRoot = (props: Props) => {
const {teamId, queryType} = props
const {teamId, queryType, selectedRepos} = props
const repoQueryString = selectedRepos.map((repo) => `repo:${repo}`).join(' ')
const queryRef = useQueryLoaderNow<GitHubIntegrationResultsQuery>(gitHubIntegrationResultsQuery, {
teamId: teamId,
searchQuery: GITHUB_QUERY_MAPPING[queryType]
searchQuery: `${GITHUB_QUERY_MAPPING[queryType]} ${repoQueryString}`
})
return (
<ErrorBoundary>
Expand Down
Loading

0 comments on commit 5406a0d

Please sign in to comment.