Skip to content

Commit

Permalink
Reuse one component for sticky headings
Browse files Browse the repository at this point in the history
  • Loading branch information
majakomel committed Apr 4, 2024
1 parent a2cab7e commit d69f878
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 147 deletions.
24 changes: 21 additions & 3 deletions components/SharedStyledComponents.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Button, Container, Flex, Heading } from 'ooni-components'
import styled from 'styled-components'
import { Button } from 'ooni-components'

export const StyledSticky = styled.div`
position: sticky;
Expand All @@ -15,14 +15,32 @@ top: 0;
z-index: 100;
`

export const StyledStickySubMenu = styled.div`
export const StyledStickySubMenu = styled(Box)`
position: sticky;
top: 66px;
top: 65.5px;
background: white;
z-index: 99;
border-bottom: 1px solid ${props => props.theme.colors.gray3};
`

const StyledContainer = styled(Container)`
border-bottom: 1px solid ${props => props.theme.colors.gray3};
`
export const StickySubMenu = ({ title, children }) => {
return (
<StyledStickySubMenu mt={[0, 4]} mb={2}>
<Flex justifyContent='space-between' alignItems='center' flexDirection={['column', 'column', 'row']}>
<Heading h={1} mt={1} mb={0} fontSize={[4, 5]}>
{title}
</Heading>
<Box>
{children}
</Box>
</Flex>
</StyledStickySubMenu>
)
}

// port the design to ooni-components
export const StyledHollowButton = styled(Button)`
&:hover {
Expand Down
66 changes: 19 additions & 47 deletions pages/countries.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,22 @@ import CountryList from 'components/CountryBox'
import { StyledStickySubMenu } from 'components/SharedStyledComponents'
import countryUtil from 'country-util'
import { getLocalisedRegionName } from 'utils/i18nCountries'

const CountryLink = styled(Link)`
color: ${props => props.theme.colors.black};
text-decoration: none;
&:hover {
color: ${props => props.theme.colors.blue5};
}
`

const StyledCountryCard = styled(Box)`
border: 1px solid ${props => props.theme.colors.gray3};
`

const Divider = styled.div`
border: 1px solid ${props => props.theme.colors.gray3};
margin-bottom: 12px;
`
import { StickySubMenu } from '../components/SharedStyledComponents'

// To compenstate for the sticky navigation bar
// :target selector applies only the element with id that matches
// the current URL fragment (e.g '/#Africa')
const RegionHeaderAnchor = styled.div`
:target::before {
content: ' ';
display: block;
width: 0;
/* Height of the combined header (NavBar and Regions) */
/* This is needed to compensate the for the sticky navbar and region
links bar when scrolling to the selected region. And the height of these
bars changes in the mobile layout. This has evolved to be a bad design
that needs to be replaced. */
height: 145px;
margin-top: -145px;
@media(max-width: 768px) {
height: 375px;
margin-top: -375px;
}
/* Height of the combined header (NavBar and Regions) */
/* This is needed to compensate the for the sticky navbar and region
links bar when scrolling to the selected region. And the height of these
bars changes in the mobile layout. This has evolved to be a bad design
that needs to be replaced. */
height: 172px;
margin-top: -172px;
@media(max-width: 768px) {
height: 375px;
margin-top: -375px;
}
`

Expand All @@ -74,7 +53,7 @@ const RegionBlock = ({regionCode, countries}) => {
return (
<Box my={3}>
<RegionHeaderAnchor id={regionName} />
<Heading h={1} center py={2}>{regionName}</Heading>
<Heading h={2} py={2}>{regionName}</Heading>
<CountryList
countries={countries.filter((c => ( measuredCountriesInRegion.indexOf(c.alpha_2) > -1 ))).map((c) => ({country: c.alpha_2, measurements: c.count}))}
itemsPerRow={4}
Expand All @@ -86,18 +65,13 @@ const RegionBlock = ({regionCode, countries}) => {
const StyledRegionLink = styled.a`
display: block;
color: ${(props) => props.theme.colors.blue5};
text-decoration: none;
border-bottom: 2px solid transparent;
:hover {
border-bottom: 2px solid ${(props) => props.theme.colors.blue5};
width: 100%;
}
text-decoration: underline;
`

const RegionLink = ({ href, label }) => (
<Box px={[2,3]} py={[2,2,4]}>
<Box px={[2,3]}>
<StyledRegionLink href={href}>
<Text fontSize={[16, 20]}>{label}</Text>
<Text fontSize={1}>{label}</Text>
</StyledRegionLink>
</Box>
)
Expand Down Expand Up @@ -154,8 +128,8 @@ const Countries = ({countries}) => {
<Head>
<title>{intl.formatMessage({id: 'Countries.PageTitle'})}</title>
</Head>
<StyledStickySubMenu>
<Container>
<Container>
<StickySubMenu title="Countries">
<Flex
flexDirection={['column', 'column', 'row']}
justifyContent={['flex-start', 'flex-end']}
Expand All @@ -180,10 +154,8 @@ const Countries = ({countries}) => {
<RegionLink href={`#${getLocalisedRegionName('AQ', intl.locale)}`} label={getLocalisedRegionName('AQ', intl.locale)} />
</Flex>
</Flex>
</Container>
</StyledStickySubMenu>

<Container>
</StickySubMenu>

{
// Show a message when there are no countries to show, when search is empty
(filteredCountries.length === 0)
Expand Down
81 changes: 40 additions & 41 deletions pages/domains.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CategoryBadge } from 'components/Badge'
import GridLoader from 'components/GridLoader'
import { StyledStickySubMenu } from 'components/SharedStyledComponents'
import { StickySubMenu } from 'components/SharedStyledComponents'
import { getCategoryCodesMap } from 'components/utils/categoryCodes'
import useFilterWithSort from 'hooks/useFilterWithSort'
import Head from 'next/head'
import { Box, Container, Flex, Heading, Input, Select, Text } from 'ooni-components'
import { Box, Container, Flex, Input, Select, Text } from 'ooni-components'
import { useMemo } from 'react'
import { useIntl } from 'react-intl'
import { simpleFetcher } from 'services/fetchers'
Expand Down Expand Up @@ -92,48 +92,47 @@ const Domains = () => {
<title>{intl.formatMessage({id: 'General.OoniExplorer'})} | {intl.formatMessage({id: 'Domains.Title'})}</title>
</Head>
<Container>
<StyledStickySubMenu>
<Flex mt={[0, 5]} mb={2} justifyContent='space-between' alignItems='baseline' flexDirection={['column', 'column', 'row']}>
<Heading h={1} mt={1} mb={0} fontSize={[4, 5]}>
{
intl.formatMessage({id: 'Domains.Title'})
} {!!sortedAndFilteredData.length &&
<StickySubMenu
title={
<>
{ intl.formatMessage({id: 'Domains.Title'})}{' '}{!!sortedAndFilteredData.length &&
<>({new Intl.NumberFormat().format(sortedAndFilteredData.length)})</>
}
</Heading>
<Flex sx={{gap: 3}} flexDirection={['column', 'column', 'row']} width={[1, 'auto']}>
<Box>
<Input
onChange={(e) => debouncedSearchHandler(e.target.value)}
placeholder={intl.formatMessage({id: 'Domains.SearchPlaceholder'})}
error={
(searchValue && sortedAndFilteredData?.length === 0) &&
<>{intl.formatMessage({id: 'Domains.SearchError'})}</>
}
/>
</Box>
<Box>
<Select onChange={e => setCategoryValue(e.target.value)}>
<option value=''>{intl.formatMessage({id: 'Domains.AllCategories'})}</option>
{
categoryCodes.map((key) => (
<option key={key} value={key}>
{intl.formatMessage({id: `CategoryCode.${key}.Name`})}
</option>
))
}
</Select>
</Box>
<Box>
<Select value={sortValue} onChange={e => setSortValue(e.target.value)}>
{sortOptions.map(({key, intlKey}) => (
<option key={key} value={key}>{intl.formatMessage({id: intlKey})}</option>
))}
</Select>
</Box>
</Flex>
</>
}
>
<Flex sx={{gap: 3}} flexDirection={['column', 'column', 'row']} width={[1, 'auto']}>
<Box>
<Input
onChange={(e) => debouncedSearchHandler(e.target.value)}
placeholder={intl.formatMessage({id: 'Domains.SearchPlaceholder'})}
error={
(searchValue && sortedAndFilteredData?.length === 0) &&
<>{intl.formatMessage({id: 'Domains.SearchError'})}</>
}
/>
</Box>
<Box>
<Select onChange={e => setCategoryValue(e.target.value)}>
<option value=''>{intl.formatMessage({id: 'Domains.AllCategories'})}</option>
{
categoryCodes.map((key) => (
<option key={key} value={key}>
{intl.formatMessage({id: `CategoryCode.${key}.Name`})}
</option>
))
}
</Select>
</Box>
<Box>
<Select value={sortValue} onChange={e => setSortValue(e.target.value)}>
{sortOptions.map(({key, intlKey}) => (
<option key={key} value={key}>{intl.formatMessage({id: intlKey})}</option>
))}
</Select>
</Box>
</Flex>
</StyledStickySubMenu>
</StickySubMenu>
<Box mt={4}>
{!!displayData.length ?
<Box mt={4}><VirtualizedGrid data={sortedAndFilteredData} /></Box>:
Expand Down
49 changes: 23 additions & 26 deletions pages/findings/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StyledStickySubMenu } from 'components/SharedStyledComponents'
import { StickySubMenu } from 'components/SharedStyledComponents'
import HighlightBox from 'components/landing/HighlightBox'
import SpinLoader from 'components/vendor/SpinLoader'
import useFilterWithSort from 'hooks/useFilterWithSort'
Expand Down Expand Up @@ -93,32 +93,29 @@ const Index = () => {
{user?.role === 'admin' && (
<Flex justifyContent="end" mt={3}><NLink href="/findings/dashboard"><Button hollow>{intl.formatMessage({id: 'Findings.Dashboard.ShortTitle'})}</Button></NLink></Flex>
)}
<StyledStickySubMenu>
<Flex mt={user?.role === 'admin' ? 0 : [0, 5]} mb={2} justifyContent='space-between' alignItems='baseline' flexDirection={['column', 'column', 'row']}>
<Heading h={1} mt={1} mb={0} fontSize={[4, 5]}>
{intl.formatMessage({id: 'Findings.Index.Title'}, {amount: sortedAndFilteredData.length})}
</Heading>
<Flex sx={{gap: 3}} flexDirection={['column', 'column', 'row']} width={[1, 'auto']}>
<Box>
<Input
onChange={(e) => debouncedSearchHandler(e.target.value)}
placeholder={intl.formatMessage({id: 'Findings.Index.SearchPlaceholder'})}
error={
(searchValue && sortedAndFilteredData?.length === 0) &&
<>{intl.formatMessage({id: 'Findings.Index.SearchError'})}</>
}
/>
</Box>
<Box>
<Select value={sortValue} onChange={e => setSortValue(e.target.value)}>
{sortOptions.map(({key, intlKey}) => (
<option key={key} value={key}>{intl.formatMessage({id: intlKey})}</option>
))}
</Select>
</Box>
</Flex>
<StickySubMenu
title={<>{intl.formatMessage({id: 'Findings.Index.Title'}, {amount: sortedAndFilteredData.length})}</>}
>
<Flex sx={{gap: 3}} flexDirection={['column', 'column', 'row']} width={[1, 'auto']}>
<Box>
<Input
onChange={(e) => debouncedSearchHandler(e.target.value)}
placeholder={intl.formatMessage({id: 'Findings.Index.SearchPlaceholder'})}
error={
(searchValue && sortedAndFilteredData?.length === 0) &&
<>{intl.formatMessage({id: 'Findings.Index.SearchError'})}</>
}
/>
</Box>
<Box>
<Select value={sortValue} onChange={e => setSortValue(e.target.value)}>
{sortOptions.map(({key, intlKey}) => (
<option key={key} value={key}>{intl.formatMessage({id: intlKey})}</option>
))}
</Select>
</Box>
</Flex>
</StyledStickySubMenu>
</StickySubMenu>
{isLoading &&
<Container pt={6}><SpinLoader /></Container>
}
Expand Down
59 changes: 29 additions & 30 deletions pages/networks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from 'react'

import GridLoader from 'components/GridLoader'
import { StyledStickySubMenu } from 'components/SharedStyledComponents'
import { StickySubMenu } from 'components/SharedStyledComponents'
import VirtualizedGrid from 'components/VirtualizedGrid'
import useFilterWithSort from 'hooks/useFilterWithSort'
import Head from 'next/head'
Expand Down Expand Up @@ -74,36 +74,35 @@ const Networks = () => {
<title>{intl.formatMessage({id: 'General.OoniExplorer'})} | {intl.formatMessage({id: 'Networks.Title'})}</title>
</Head>
<Container>
<StyledStickySubMenu>
<Flex mt={[0, 5]} mb={2} justifyContent='space-between' alignItems='baseline' flexDirection={['column', 'column', 'row']}>
<Heading h={1} mt={1} mb={0} fontSize={[4, 5]}>
{
intl.formatMessage({id: 'Networks.Title'})
} {!!sortedAndFilteredData.length &&
<>({new Intl.NumberFormat().format(sortedAndFilteredData.length)})</>
}
</Heading>
<Flex sx={{gap: 3}} flexDirection={['column', 'column', 'row']} width={[1, 'auto']}>
<Box>
<Input
onChange={(e) => debouncedSearchHandler(e.target.value)}
placeholder={intl.formatMessage({id: 'Networks.SearchPlaceholder'})}
error={
(searchValue && !sortedAndFilteredData.length) &&
<>{intl.formatMessage({id: 'Networks.SearchError'})} </>
}
/>
</Box>
<Box>
<Select value={sortValue} onChange={e => setSortValue(e.target.value)}>
{sortOptions.map(({key, intlKey}) => (
<option key={key} value={key}>{intl.formatMessage({id: intlKey})}</option>
))}
</Select>
</Box>
</Flex>
<StickySubMenu
title={<>
{
intl.formatMessage({id: 'Networks.Title'})
} {!!sortedAndFilteredData.length &&
<>({new Intl.NumberFormat().format(sortedAndFilteredData.length)})</>
}
</>}
>
<Flex sx={{gap: 3}} flexDirection={['column', 'column', 'row']} width={[1, 'auto']}>
<Box>
<Input
onChange={(e) => debouncedSearchHandler(e.target.value)}
placeholder={intl.formatMessage({id: 'Networks.SearchPlaceholder'})}
error={
(searchValue && !sortedAndFilteredData.length) &&
<>{intl.formatMessage({id: 'Networks.SearchError'})} </>
}
/>
</Box>
<Box>
<Select value={sortValue} onChange={e => setSortValue(e.target.value)}>
{sortOptions.map(({key, intlKey}) => (
<option key={key} value={key}>{intl.formatMessage({id: intlKey})}</option>
))}
</Select>
</Box>
</Flex>
</StyledStickySubMenu>
</StickySubMenu>
<Box mt={4}>
{!!displayData.length ?
<VirtualizedGrid data={sortedAndFilteredData} />:
Expand Down

0 comments on commit d69f878

Please sign in to comment.