Skip to content

Commit

Permalink
Merge pull request #1357 from oasisprotocol/csillag/proposal-show-vot…
Browse files Browse the repository at this point in the history
…es-with-search

Network proposal votes: add search by validator name
  • Loading branch information
csillag authored May 16, 2024
2 parents 4f656d6 + 9dc098c commit 1dccca2
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 44 deletions.
1 change: 1 addition & 0 deletions .changelog/1357.trivial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Network proposal votes: add search by validator name
17 changes: 14 additions & 3 deletions src/app/components/CardEmptyState/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react'
import { FC, ReactNode } from 'react'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
import { styled } from '@mui/material/styles'
Expand All @@ -23,11 +23,22 @@ const StyledBox = styled(Box)(({ theme }) => ({

type CardEmptyStateProps = {
label: string
action?: ReactNode
}

export const CardEmptyState: FC<CardEmptyStateProps> = ({ label }) => (
export const CardEmptyState: FC<CardEmptyStateProps> = ({ label, action }) => (
<StyledBox>
<ReportProblemIcon sx={{ color: COLORS.warningColor, fontSize: '60px' }} />
<Typography sx={{ color: COLORS.grayDark }}>{label}</Typography>
<Typography
sx={{
color: COLORS.grayDark,
display: 'block',
alignItems: 'center',
verticalAlign: 'middle',
}}
>
{label}
{action}
</Typography>
</StyledBox>
)
8 changes: 7 additions & 1 deletion src/app/components/CardHeaderWithResponsiveActions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ import CardHeader, { cardHeaderClasses } from '@mui/material/CardHeader'
import { styled } from '@mui/material/styles'

export const CardHeaderWithResponsiveActions = styled(CardHeader)(({ theme }) => ({
[theme.breakpoints.down('sm')]: {
[theme.breakpoints.down('md')]: {
display: 'inline',
alignItems: 'flex-start',
flexDirection: 'column',
[`.${cardHeaderClasses.content}`]: {
display: 'inline',
marginRight: theme.spacing(4),
},
[`.${cardHeaderClasses.action}`]: {
display: 'inline',
marginTop: theme.spacing(4),
marginBottom: theme.spacing(4),
},
Expand Down
4 changes: 2 additions & 2 deletions src/app/components/HighlightedText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export interface HighlightOptions {

const defaultHighlightStyle: SxProps = {
background: '#FFFF5480',
padding: '4px',
margin: '-4px',
padding: '2px',
margin: '-2px',
}

const defaultHighlight: HighlightOptions = {
Expand Down
4 changes: 2 additions & 2 deletions src/app/components/Proposals/VoteTypeFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const VoteTypeFilter: FC<VoteTypeFilterProps> = ({ onSelect, value }) =>
]

return (
<>
<Box sx={{ display: 'inline-flex' }}>
{options.map(option => {
const selected = option.value === value
return (
Expand All @@ -59,6 +59,6 @@ export const VoteTypeFilter: FC<VoteTypeFilterProps> = ({ onSelect, value }) =>
/>
)
})}
</>
</Box>
)
}
133 changes: 133 additions & 0 deletions src/app/components/Search/TableSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { FC, useEffect, useState } 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 IconButton from '@mui/material/IconButton'
import { useScreenSize } from '../../hooks/useScreensize'
import WarningIcon from '@mui/icons-material/WarningAmber'
import { typingDelay } from '../../../styles/theme'
import Typography from '@mui/material/Typography'
import { useTranslation } from 'react-i18next'
import Button from '@mui/material/Button'
import { CardEmptyState } from '../CardEmptyState'
import { inputBaseClasses } from '@mui/material/InputBase'

export interface TableSearchBarProps {
placeholder: string
warning?: string
value: string
onChange: (value: string) => void
}

export const TableSearchBar: FC<TableSearchBarProps> = ({ value, onChange, placeholder, warning }) => {
const { isTablet } = useScreenSize()

const [isWarningFresh, setIsWarningFresh] = useState(false)

useEffect(() => {
if (warning) {
const timeout = setTimeout(() => {
setIsWarningFresh(false)
}, typingDelay)
return () => clearTimeout(timeout)
} else {
setIsWarningFresh(true)
}
}, [warning])

const startAdornment = (
<InputAdornment
position="start"
disablePointerEvents // Pass clicks through, so it focuses the input
>
<SearchIcon sx={{ color: COLORS.grayDark, ml: -3 }} />
</InputAdornment>
)

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

const endAdornment = (
<InputAdornment position="end">
{value ? (
<IconButton color="inherit" sx={{ mt: -3, mb: -3, mr: -1 }} onClick={onClearValue}>
<HighlightOffIcon />
</IconButton>
) : (
<span style={{ width: '38px' }} />
)}
</InputAdornment>
)

const helperText = isWarningFresh ? undefined : (
<Typography
component="span"
sx={{
display: 'inline-flex',
color: COLORS.warningColor,
fontSize: 12,
lineHeight: 2,
alignItems: 'center',
verticalAlign: 'middle',
mt: 3,
mb: 4,
width: isTablet ? '160px' : undefined,
}}
>
<WarningIcon sx={{ mr: 3 }} />
{warning}
</Typography>
)

return (
<TextField
sx={{
backgroundColor: COLORS.white,
marginLeft: 4,
marginRight: helperText ? '25px' : '25px',
'&:focus-within': {
boxShadow: '3px 3px 3px 3px rgb(0, 0, 98, 0.25) !important',
},
[`.${inputBaseClasses.root}`]: {
border: '1px solid',
borderColor: COLORS.inactiveStroke,
},
...(helperText
? {
border: '1px solid',
borderColor: COLORS.inactiveStroke,
marginBottom: isTablet ? '-99px' : '-50px',
}
: {}),
zIndex: 10,
}}
variant={'outlined'}
value={value}
onChange={e => onChange(e.target.value)}
InputProps={{
inputProps: {
sx: {
p: 0,
width: isTablet ? 110 : 300,
margin: 2,
},
},
startAdornment,
endAdornment,
}}
placeholder={placeholder}
helperText={helperText}
/>
)
}

export const NoMatchingDataMaybeClearFilters: FC<{ clearFilters: () => void }> = ({ clearFilters }) => {
const { t } = useTranslation()
const clearButton = (
<Button variant={'text'} onClick={() => clearFilters()}>
{t('tableSearch.clearFilters')}
</Button>
)
return <CardEmptyState label={t('tableSearch.noMatchingResults')} action={clearButton} />
}
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}
/>
)
}
40 changes: 31 additions & 9 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 @@ -32,21 +46,23 @@ export const ValidatorLink: FC<ValidatorLinkProps> = ({ address, name, network,
type TrimValidatorEndLinkLabelProps = {
name: string
to: string
highlightedPart?: string
}

const TrimValidatorEndLinkLabel: FC<TrimValidatorEndLinkLabelProps> = ({ name, to }) => (
<TrimEndLinkLabel label={name} to={to} trimStart={14} />
const TrimValidatorEndLinkLabel: FC<TrimValidatorEndLinkLabelProps> = ({ name, to, highlightedPart }) => (
<TrimEndLinkLabel label={name} to={to} trimStart={14} highlightedPart={highlightedPart} />
)

type TabletValidatorLinkProps = {
address: string
name?: string
to: string
highlightedPart?: string
}

const TabletValidatorLink: FC<TabletValidatorLinkProps> = ({ address, name, to }) => {
const TabletValidatorLink: FC<TabletValidatorLinkProps> = ({ address, name, to, highlightedPart }) => {
if (name) {
return <TrimValidatorEndLinkLabel name={name} to={to} />
return <TrimValidatorEndLinkLabel name={name} to={to} highlightedPart={highlightedPart} />
}
return <TrimLinkLabel label={address} to={to} />
}
Expand All @@ -55,13 +71,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>
)
}
Loading

0 comments on commit 1dccca2

Please sign in to comment.