Skip to content

Commit

Permalink
Proposal votes: add search by validator name
Browse files Browse the repository at this point in the history
  • Loading branch information
csillag committed May 9, 2024
1 parent a8531a9 commit f4a54ba
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 10 deletions.
70 changes: 70 additions & 0 deletions src/app/components/Proposals/VoterSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { FC } from 'react'
import TextField from '@mui/material/TextField'
import SearchIcon from '@mui/icons-material/Search'
import HighlightOffIcon from '@mui/icons-material/HighlightOff'
import InputAdornment from '@mui/material/InputAdornment'
import { COLORS } from '../../../styles/theme/colors'
import { SearchVariant } from '../Search'
import { useTranslation } from 'react-i18next'
import IconButton from '@mui/material/IconButton'

export interface SearchBarProps {
variant: SearchVariant
value: string
onChange: (value: string) => void
}

export const VoterSearchBar: FC<SearchBarProps> = ({ variant, value, onChange }) => {
const { t } = useTranslation()
const startAdornment = variant === 'button' && (
<InputAdornment
position="start"
disablePointerEvents // Pass clicks through, so it focuses the input
>
<SearchIcon sx={{ color: COLORS.grayDark }} />
</InputAdornment>
)

const onClearValue = () => onChange('')

const endAdornment = (
<InputAdornment position="end">
<>
{value && (
<IconButton color="inherit" onClick={onClearValue}>
<HighlightOffIcon />
</IconButton>
)}
</>
</InputAdornment>
)

return (
<>
<TextField
value={value}
onChange={e => onChange(e.target.value)}
InputProps={{
inputProps: {
sx: {
p: 0,
marginRight: 2,
},
},
startAdornment,
endAdornment,
}}
placeholder={t('networkProposal.searchForVoters')}
// FormHelperTextProps={{
// component: 'div', // replace p with div tag
// sx: {
// marginTop: 0,
// marginBottom: 0,
// marginLeft: variant === 'button' ? '48px' : '17px',
// marginRight: variant === 'button' ? '48px' : '17px',
// },
// }}
/>
</>
)
}
12 changes: 10 additions & 2 deletions src/app/components/Validators/DeferredValidatorLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ export const DeferredValidatorLink: FC<{
address: string
validator: Validator | undefined
isError: boolean
}> = ({ network, address, validator, isError }) => {
highlightedPart?: string | undefined
}> = ({ network, address, validator, isError, highlightedPart }) => {
const scope: SearchScope = { network, layer: Layer.consensus }

if (isError) {
console.log('Warning: failed to look up validators!')
}

return <ValidatorLink address={address} network={scope.network} name={validator?.media?.name} />
return (
<ValidatorLink
address={address}
network={scope.network}
name={validator?.media?.name}
highlightedPart={highlightedPart}
/>
)
}
31 changes: 26 additions & 5 deletions src/app/components/Validators/ValidatorLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,37 @@ import { RouteUtils } from '../../utils/route-utils'
import Typography from '@mui/material/Typography'
import { COLORS } from '../../../styles/theme/colors'
import { Network } from '../../../types/network'
import { HighlightedText } from '../HighlightedText'

type ValidatorLinkProps = {
address: string
name?: string
network: Network
alwaysTrim?: boolean
highlightedPart?: string
}

export const ValidatorLink: FC<ValidatorLinkProps> = ({ address, name, network, alwaysTrim }) => {
export const ValidatorLink: FC<ValidatorLinkProps> = ({
address,
name,
network,
alwaysTrim,
highlightedPart,
}) => {
const { isTablet } = useScreenSize()
const to = RouteUtils.getValidatorRoute(network, address)
return (
<Typography variant="mono" component="span" sx={{ color: COLORS.brandDark, fontWeight: 700 }}>
{isTablet ? (
<TabletValidatorLink address={address} name={name} to={to} />
<TabletValidatorLink address={address} name={name} to={to} highlightedPart={highlightedPart} />
) : (
<DesktopValidatorLink address={address} alwaysTrim={alwaysTrim} name={name} to={to} />
<DesktopValidatorLink
address={address}
alwaysTrim={alwaysTrim}
name={name}
to={to}
highlightedPart={highlightedPart}
/>
)}
</Typography>
)
Expand All @@ -42,6 +56,7 @@ type TabletValidatorLinkProps = {
address: string
name?: string
to: string
highlightedPart?: string // TODO: add support for highlighting on tablet
}

const TabletValidatorLink: FC<TabletValidatorLinkProps> = ({ address, name, to }) => {
Expand All @@ -55,13 +70,19 @@ type DesktopValidatorLinkProps = TabletValidatorLinkProps & {
alwaysTrim?: boolean
}

const DesktopValidatorLink: FC<DesktopValidatorLinkProps> = ({ address, name, to, alwaysTrim }) => {
const DesktopValidatorLink: FC<DesktopValidatorLinkProps> = ({
address,
name,
to,
alwaysTrim,
highlightedPart,
}) => {
if (alwaysTrim) {
return <TrimLinkLabel label={address} to={to} />
}
return (
<Link component={RouterLink} to={to}>
{name ?? address}
{name ? <HighlightedText text={name} pattern={highlightedPart} /> : address}
</Link>
)
}
21 changes: 19 additions & 2 deletions src/app/pages/ProposalDetailsPage/ProposalVotesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ import { TablePaginationProps } from '../../components/Table/TablePagination'
import { useTranslation } from 'react-i18next'
import { Table, TableCellAlign, TableColProps } from '../../components/Table'
import { ExtendedVote, ProposalVoteValue } from '../../../types/vote'
import { PAGE_SIZE, useAllVotes, useDisplayedVotes, useWantedVoteType } from './hooks'
import {
PAGE_SIZE,
useAllVotes,
useDisplayedVotes,
useVoterSearch,
useVoterSearchPattern,
useWantedVoteType,
} from './hooks'
import { ProposalVoteIndicator } from '../../components/Proposals/ProposalVoteIndicator'
import { DeferredValidatorLink } from '../../components/Validators/DeferredValidatorLink'
import { CardHeaderWithResponsiveActions } from '../../components/CardHeaderWithResponsiveActions'
import { VoteTypeFilter } from '../../components/Proposals/VoteTypeFilter'
import { AppErrors } from '../../../types/errors'
import { ErrorBoundary } from '../../components/ErrorBoundary'
import { VoterSearchBar } from '../../components/Proposals/VoterSearchBar'

type ProposalVotesProps = {
isLoading: boolean
Expand All @@ -25,6 +33,8 @@ const ProposalVotes: FC<ProposalVotesProps> = ({ isLoading, votes, rowsNumber, p
const { t } = useTranslation()
const scope = useRequiredScopeParam()

const voterNameFragment = useVoterSearchPattern()

const tableColumns: TableColProps[] = [
{ key: 'index', content: <></>, width: '50px' },
{ key: 'voter', content: t('common.voter'), align: TableCellAlign.Left },
Expand All @@ -46,6 +56,7 @@ const ProposalVotes: FC<ProposalVotesProps> = ({ isLoading, votes, rowsNumber, p
address={vote.address}
isError={vote.haveValidatorsFailed}
validator={vote.validator}
highlightedPart={voterNameFragment}
/>
),
},
Expand Down Expand Up @@ -98,11 +109,17 @@ export const ProposalVotesCard: FC = () => {
const { t } = useTranslation()

const [wantedVoteType, setWantedVoteType] = useWantedVoteType()
const [voterSearchInput, setVoterSearchPattern] = useVoterSearch()

return (
<SubPageCard>
<CardHeaderWithResponsiveActions
action={<VoteTypeFilter onSelect={setWantedVoteType} value={wantedVoteType} />}
action={
<>
<VoterSearchBar variant={'button'} value={voterSearchInput} onChange={setVoterSearchPattern} />
<VoteTypeFilter onSelect={setWantedVoteType} value={wantedVoteType} />
</>
}
disableTypography
component="h3"
title={t('common.votes')}
Expand Down
19 changes: 18 additions & 1 deletion src/app/pages/ProposalDetailsPage/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,24 @@ export const useWantedVoteType = () =>
deleteParams: ['page'],
})

const useWantedVoteFilter = (): VoteFilter => getFilterForVoteType(useWantedVoteType()[0])
export const useVoterSearch = () => useTypedSearchParam('voter', '', { deleteParams: ['page'] })

export const useVoterSearchPattern = () => {
const voterSearchInput = useVoterSearch()[0]
return voterSearchInput.length < 3 ? undefined : voterSearchInput
}

const useWantedVoteFilter = (): VoteFilter => {
const typeFilter = getFilterForVoteType(useWantedVoteType()[0])
const voterSearchPattern = useVoterSearchPattern()

if (!voterSearchPattern) {
return typeFilter
} else {
return (vote: ExtendedVote) =>
typeFilter(vote) && !!vote.validator?.media?.name?.toLowerCase().includes(voterSearchPattern)
}
}

export const PAGE_SIZE = NUMBER_OF_ITEMS_ON_SEPARATE_PAGE

Expand Down
1 change: 1 addition & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
"passed": "Passed",
"rejected": "Rejected"
},
"searchForVoters": "Search for voters",
"type": {
"upgrade": "Upgrade",
"parameterUpgrade": "Parameter upgrade",
Expand Down

0 comments on commit f4a54ba

Please sign in to comment.