Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch state to params #5829

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions frontend/src/landing/LandingPageSummaries.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React from 'react'
import { useQuery } from '@apollo/client'

import { LANDING_PAGE_SUMMARIES, GET_HISTORICAL_CHART_SUMMARIES } from '../graphql/queries'
Expand All @@ -9,12 +9,18 @@ import { Box } from '@chakra-ui/react'
import { HistoricalSummariesGraph } from '../summaries/HistoricalSummariesGraph'
import { ErrorBoundary } from 'react-error-boundary'
import { ABTestVariant, ABTestWrapper } from '../app/ABTestWrapper'
import useSearchParam from '../utilities/useSearchParam'

export function LandingPageSummaries() {
const [progressChartRange, setProgressChartRange] = useState('LAST30DAYS')
const { loading, error, data } = useQuery(LANDING_PAGE_SUMMARIES)
const { searchValue: progressChartRangeParam, setSearchParams: setProgressChartRangeParam } = useSearchParam({
name: 'summary-range',
validOptions: ['last30days', 'lastyear', 'ytd'],
defaultValue: 'last30days',
})

const { data: historicalSummaries, loading: histSumLoading } = useQuery(GET_HISTORICAL_CHART_SUMMARIES, {
variables: { month: progressChartRange, year: new Date().getFullYear().toString() },
variables: { month: progressChartRangeParam.toUpperCase(), year: new Date().getFullYear().toString() },
})

if (loading) return <LoadingMessage />
Expand Down Expand Up @@ -43,7 +49,8 @@ export function LandingPageSummaries() {
<ErrorBoundary FallbackComponent={ErrorFallbackMessage}>
<HistoricalSummariesGraph
data={historicalSummaries?.findChartSummaries}
setRange={setProgressChartRange}
setRange={setProgressChartRangeParam}
selectedRange={progressChartRangeParam}
width={1200}
height={500}
/>
Expand Down
20 changes: 13 additions & 7 deletions frontend/src/organizationDetails/OrganizationDetails.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect } from 'react'
import { useQuery } from '@apollo/client'
import { Trans } from '@lingui/macro'
import {
Expand Down Expand Up @@ -33,16 +33,21 @@ import { RequestOrgInviteModal } from '../organizations/RequestOrgInviteModal'
import { useUserVar } from '../utilities/userState'
import { HistoricalSummariesGraph } from '../summaries/HistoricalSummariesGraph'
import { ABTestVariant, ABTestWrapper } from '../app/ABTestWrapper'
import useSearchParam from '../utilities/useSearchParam'
import { bool } from 'prop-types'

export default function OrganizationDetails({ loginRequired }) {
const { isLoggedIn } = useUserVar()
const { orgSlug, activeTab } = useParams()
const history = useHistory()
const { isOpen, onOpen, onClose } = useDisclosure()
const [progressChartRange, setProgressChartRange] = useState('LAST30DAYS')
const tabNames = ['summary', 'dmarc_phases', 'domains', 'users']
const defaultActiveTab = tabNames[0]
const { searchValue: progressChartRangeParam, setSearchParams: setProgressChartRangeParam } = useSearchParam({
name: 'summary-range',
validOptions: ['LAST30DAYS', 'LASTYEAR', 'YTD'],
defaultValue: 'LAST30DAYS',
})

useDocumentTitle(`${orgSlug}`)

Expand All @@ -52,12 +57,12 @@ export default function OrganizationDetails({ loginRequired }) {
})

const { data: orgSummariesData, loading: orgSummariesLoading } = useQuery(GET_HISTORICAL_ORG_SUMMARIES, {
variables: { orgSlug, month: progressChartRange, year: new Date().getFullYear().toString() },
variables: { orgSlug, month: progressChartRangeParam, year: new Date().getFullYear().toString() },
// errorPolicy: 'ignore', // allow partial success
})

useEffect(() => {
if (!activeTab) {
if (!activeTab || !tabNames.includes(activeTab)) {
history.replace(`/organizations/${orgSlug}/${defaultActiveTab}`)
}
}, [activeTab, history, orgSlug, defaultActiveTab])
Expand All @@ -78,7 +83,7 @@ export default function OrganizationDetails({ loginRequired }) {
const changeActiveTab = (index) => {
const tab = tabNames[index]
if (activeTab !== tab) {
history.replace(`/organizations/${orgSlug}/${tab}`)
history.push(`/organizations/${orgSlug}/${tab}`)
}
}

Expand Down Expand Up @@ -125,7 +130,7 @@ export default function OrganizationDetails({ loginRequired }) {
<Tabs
isFitted
variant="enclosed-colored"
defaultIndex={activeTab ? tabNames.indexOf(activeTab) : tabNames[0]}
index={tabNames.indexOf(activeTab) > -1 ? tabNames.indexOf(activeTab) : 0}
onChange={(i) => changeActiveTab(i)}
>
<TabList mb="4">
Expand Down Expand Up @@ -158,7 +163,8 @@ export default function OrganizationDetails({ loginRequired }) {
<ErrorBoundary FallbackComponent={ErrorFallbackMessage}>
<HistoricalSummariesGraph
data={orgSummariesData?.findOrganizationBySlug?.historicalSummaries}
setRange={setProgressChartRange}
setRange={setProgressChartRangeParam}
selectedRange={progressChartRangeParam}
width={1200}
height={500}
/>
Expand Down
58 changes: 42 additions & 16 deletions frontend/src/summaries/HistoricalSummariesGraph.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useState } from 'react'
import React, { useCallback } from 'react'
import { Box, Flex, Select, Text } from '@chakra-ui/react'
import { number, object } from 'prop-types'
import { number, object, string } from 'prop-types'
import { extent, bisector } from 'd3-array'
import theme from '../theme/canada'

Expand All @@ -16,6 +16,7 @@ import { timeFormat } from '@visx/vendor/d3-time-format'
import { GlyphCircle } from '@visx/glyph'
import { Trans, t } from '@lingui/macro'
import { func } from 'prop-types'
import useSearchParam from '../utilities/useSearchParam'

const getDate = ({ date }) => new Date(date)

Expand Down Expand Up @@ -48,11 +49,21 @@ const tieredSummaries = {
three: ['web', 'mail'],
}

export function HistoricalSummariesGraph({ data, setRange, width = 1200, height = 500 }) {
export function HistoricalSummariesGraph({ data, setRange, selectedRange = 'last30days', width = 1200, height = 500 }) {
const { colors } = theme
const [scoreType, setScoreType] = useState('percentage')
const [summaryTier, setSummaryTier] = useState('one')
const summaries = getSummaries(data, tieredSummaries[summaryTier], scoreType)

const { searchValue: scoreTypeParam, setSearchParams: setScoreTypeParam } = useSearchParam({
name: 'score-type',
validOptions: ['percentage', 'count'],
defaultValue: 'percentage',
})
const { searchValue: summaryTierParam, setSearchParams: setSummaryTierParam } = useSearchParam({
name: 'summary-tier',
validOptions: Object.keys(tieredSummaries),
defaultValue: 'one',
})

const summaries = getSummaries(data, tieredSummaries[summaryTierParam], scoreTypeParam)
summaries.sort((a, b) => getDate(a) - getDate(b))

const summaryNames = {
Expand All @@ -77,7 +88,7 @@ export function HistoricalSummariesGraph({ data, setRange, width = 1200, height
const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom

const series = getSeries(summaries, tieredSummaries[summaryTier])
const series = getSeries(summaries, tieredSummaries[summaryTierParam])

// colors for lines
const graphColours = [
Expand Down Expand Up @@ -114,7 +125,9 @@ export function HistoricalSummariesGraph({ data, setRange, width = 1200, height
const rdScale = scaleLinear({
range: [innerHeight, 0],
domain:
scoreType === 'percentage' ? [0, 100] : [Math.min(...summaries.map(getRD)), Math.max(...summaries.map(getRD))],
scoreTypeParam === 'percentage'
? [0, 100]
: [Math.min(...summaries.map(getRD)), Math.max(...summaries.map(getRD))],
nice: true,
})

Expand Down Expand Up @@ -151,21 +164,27 @@ export function HistoricalSummariesGraph({ data, setRange, width = 1200, height
<Text fontSize="lg" fontWeight="bold" textAlign="center">
<Trans>Range:</Trans>
</Text>
<Select mx="2" maxW="20%" borderColor="black" onChange={(e) => setRange(e.target.value)}>
<option value="LAST30DAYS">
<Select mx="2" maxW="20%" borderColor="black" value={selectedRange} onChange={(e) => setRange(e.target.value)}>
<option value="last30days">
<Trans>Last 30 Days of Data</Trans>
</option>
<option value="LASTYEAR">
<option value="lastyear">
<Trans>Last 365 Days of Data</Trans>
</option>
<option value="YTD">
<option value="ytd">
<Trans>Year to Date</Trans>
</option>
</Select>
<Text fontSize="lg" fontWeight="bold" textAlign="center">
<Trans>Data:</Trans>
</Text>
<Select mx="2" maxW="20%" borderColor="black" onChange={(e) => setScoreType(e.target.value)}>
<Select
mx="2"
maxW="20%"
borderColor="black"
value={scoreTypeParam}
onChange={(e) => setScoreTypeParam(e.target.value)}
>
<option value="percentage">
<Trans>Percentage</Trans>
</option>
Expand All @@ -176,7 +195,13 @@ export function HistoricalSummariesGraph({ data, setRange, width = 1200, height
<Text ml="2" fontSize="lg" fontWeight="bold" textAlign="center">
<Trans>Summary Tier:</Trans>
</Text>
<Select mx="2" maxW="20%" borderColor="black" onChange={(e) => setSummaryTier(e.target.value)}>
<Select
mx="2"
maxW="20%"
borderColor="black"
value={summaryTierParam}
onChange={(e) => setSummaryTierParam(e.target.value)}
>
<option value="one">
<Trans>Tier 1: Minimum Requirements</Trans>
</option>
Expand Down Expand Up @@ -285,7 +310,7 @@ export function HistoricalSummariesGraph({ data, setRange, width = 1200, height
{tooltipData.map((d, i) => (
<Text fontWeight="bold" key={i} color={graphColours[i]}>{`${summaryNames[d.type]}: ${getRD(
tooltipData[i],
)}${scoreType === 'percentage' ? '%' : ''}`}</Text>
)}${scoreTypeParam === 'percentage' ? '%' : ''}`}</Text>
))}
</TooltipWithBounds>
)}
Expand All @@ -296,7 +321,8 @@ export function HistoricalSummariesGraph({ data, setRange, width = 1200, height

HistoricalSummariesGraph.propTypes = {
data: object.isRequired,
setRange: func,
setRange: func.isRequired,
selectedRange: string,
width: number,
height: number,
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { setupI18n } from '@lingui/core'

import { historicalSummariesData } from '../../fixtures/summaryListData'
import { HistoricalSummariesGraph } from '../HistoricalSummariesGraph'
import { MemoryRouter } from 'react-router-dom'

// ** need to mock the ResizeObserver and polute the window object to avoid errors
class ResizeObserver {
Expand Down Expand Up @@ -40,7 +41,9 @@ describe('<HistoricalSummariesGraph />', () => {
const { queryByText } = render(
<ChakraProvider theme={theme}>
<I18nProvider i18n={i18n}>
<HistoricalSummariesGraph data={historicalSummariesData} />
<MemoryRouter initialEntries={['/']} initialIndex={0}>
<HistoricalSummariesGraph data={historicalSummariesData} setRange={() => {}} selectedRange="last30days" />
</MemoryRouter>
</I18nProvider>
</ChakraProvider>,
)
Expand Down
40 changes: 40 additions & 0 deletions frontend/src/utilities/useSearchParam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'

function useSearchParam({ name, validOptions, defaultValue }) {
const { search } = useLocation()
const history = useHistory()

const searchParams = React.useMemo(() => {
return new URLSearchParams(search)
}, [search])

const value = searchParams.get(name) || defaultValue
const searchValue = !validOptions || validOptions.includes(value) ? value : defaultValue

const setSearchParams = React.useCallback(
(value) => {
if (value == null || value === '' || (validOptions && !validOptions.includes(value))) {
searchParams.delete(name)
} else {
searchParams.set(name, value)
}
history.replace({ search: searchParams.toString() })
},
[searchParams, history, name, validOptions],
)

useEffect(() => {
if ((validOptions && !validOptions.includes(value)) || value === '') {
if (value != null) {
setSearchParams(null)
} else {
setSearchParams(defaultValue)
}
}
}, [value, validOptions, defaultValue, setSearchParams, name])

return { searchValue, setSearchParams }
}

export default useSearchParam