-
-
Notifications
You must be signed in to change notification settings - Fork 740
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(1-3262): initial impl of new month/range picker (#9122)
This PR implements a first version of the new month/range picker for the data usage graphs. It's minimally hooked up to the existing functionality to not take anything away. This primary purpose of this PR is to get the design and interaction out on sandbox so that UX can have a look and we can make adjustments. As such, there are a few things in the code that we'll want to clean up before removing the flag later: - for faster iteration, I've used a lot of CSS nesting and element selectors. this isn't usually how we do it here, so we'll probably want to extract into styled components later - there is a temporary override of the value in the period selector so that you can select ranges. It won't affect the chart state, but it affects the selector state. Again, this lets you see how it acts and works. - I've added a `NewHeader` component because the existing setup smushed the selector (it's a MUI grid setup, which isn't very flexible). I don't know what we want to do with this in the end, but the existing chart *does* have some problems when you resize your window, at least (although this is likely due to the chart, and can be solved in the same way that we did for the personal dashboards). ![image](https://github.com/user-attachments/assets/f3ce3ff9-bab3-4d00-afbe-56f5624fbe16)
- Loading branch information
1 parent
08a28c9
commit 857c91b
Showing
6 changed files
with
286 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
frontend/src/component/admin/network/NetworkTrafficUsage/PeriodSelector.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
import { styled } from '@mui/material'; | ||
import { type FC, useState } from 'react'; | ||
|
||
export type Period = { | ||
key: string; | ||
dayCount: number; | ||
label: string; | ||
year: number; | ||
month: number; | ||
selectable: boolean; | ||
shortLabel: string; | ||
}; | ||
|
||
export const toSelectablePeriod = ( | ||
date: Date, | ||
label?: string, | ||
selectable = true, | ||
): Period => { | ||
const year = date.getFullYear(); | ||
const month = date.getMonth(); | ||
const period = `${year}-${(month + 1).toString().padStart(2, '0')}`; | ||
const dayCount = new Date(year, month + 1, 0).getDate(); | ||
return { | ||
key: period, | ||
year, | ||
month, | ||
dayCount, | ||
shortLabel: date.toLocaleString('en-US', { | ||
month: 'short', | ||
}), | ||
label: | ||
label || | ||
date.toLocaleString('en-US', { month: 'long', year: 'numeric' }), | ||
selectable, | ||
}; | ||
}; | ||
|
||
const currentDate = new Date(Date.now()); | ||
const currentPeriod = toSelectablePeriod(currentDate, 'Current month'); | ||
|
||
const getSelectablePeriods = (): Period[] => { | ||
const selectablePeriods = [currentPeriod]; | ||
for ( | ||
let subtractMonthCount = 1; | ||
subtractMonthCount < 12; | ||
subtractMonthCount++ | ||
) { | ||
// JavaScript wraps around the year, so we don't need to handle that. | ||
const date = new Date( | ||
currentDate.getFullYear(), | ||
currentDate.getMonth() - subtractMonthCount, | ||
1, | ||
); | ||
selectablePeriods.push( | ||
toSelectablePeriod(date, undefined, date > new Date('2024-03-31')), | ||
); | ||
} | ||
return selectablePeriods; | ||
}; | ||
|
||
const Wrapper = styled('article')(({ theme }) => ({ | ||
borderRadius: theme.shape.borderRadiusLarge, | ||
border: `2px solid ${theme.palette.divider}`, | ||
padding: theme.spacing(3), | ||
display: 'flex', | ||
flexFlow: 'column', | ||
gap: theme.spacing(2), | ||
button: { | ||
cursor: 'pointer', | ||
border: 'none', | ||
background: 'none', | ||
fontSize: theme.typography.body1.fontSize, | ||
padding: theme.spacing(0.5), | ||
borderRadius: theme.shape.borderRadius, | ||
|
||
'&.selected': { | ||
backgroundColor: theme.palette.secondary.light, | ||
}, | ||
}, | ||
'button:disabled': { | ||
cursor: 'default', | ||
}, | ||
})); | ||
|
||
const MonthSelector = styled('article')(({ theme }) => ({ | ||
border: 'none', | ||
hgroup: { | ||
h3: { | ||
margin: 0, | ||
fontSize: theme.typography.h3.fontSize, | ||
}, | ||
p: { | ||
color: theme.palette.text.secondary, | ||
fontSize: theme.typography.body2.fontSize, | ||
}, | ||
|
||
marginBottom: theme.spacing(1), | ||
}, | ||
})); | ||
|
||
const MonthGrid = styled('ul')(({ theme }) => ({ | ||
listStyle: 'none', | ||
padding: 0, | ||
display: 'grid', | ||
gridTemplateColumns: 'repeat(4, 1fr)', | ||
rowGap: theme.spacing(1), | ||
columnGap: theme.spacing(2), | ||
})); | ||
|
||
const RangeSelector = styled('article')(({ theme }) => ({ | ||
display: 'flex', | ||
flexFlow: 'column', | ||
gap: theme.spacing(0.5), | ||
h4: { | ||
fontSize: theme.typography.body2.fontSize, | ||
margin: 0, | ||
color: theme.palette.text.secondary, | ||
}, | ||
})); | ||
|
||
const RangeList = styled('ul')(({ theme }) => ({ | ||
listStyle: 'none', | ||
padding: 0, | ||
'li + li': { | ||
marginTop: theme.spacing(1), | ||
}, | ||
|
||
button: { | ||
marginLeft: `-${theme.spacing(0.5)}`, | ||
}, | ||
})); | ||
|
||
type Selection = | ||
| { | ||
type: 'month'; | ||
value: string; | ||
} | ||
| { | ||
type: 'range'; | ||
monthsBack: number; | ||
}; | ||
|
||
type Props = { | ||
selectedPeriod: string; | ||
setPeriod: (period: string) => void; | ||
}; | ||
|
||
export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => { | ||
const selectablePeriods = getSelectablePeriods(); | ||
|
||
// this is for dev purposes; only to show how the design will work when you select a range. | ||
const [tempOverride, setTempOverride] = useState<Selection | null>(); | ||
|
||
const select = (value: Selection) => { | ||
if (value.type === 'month') { | ||
setTempOverride(null); | ||
setPeriod(value.value); | ||
} else { | ||
setTempOverride(value); | ||
} | ||
}; | ||
|
||
const rangeOptions = [3, 6, 12].map((monthsBack) => ({ | ||
value: monthsBack, | ||
label: `Last ${monthsBack} months`, | ||
})); | ||
|
||
return ( | ||
<Wrapper> | ||
<MonthSelector> | ||
<hgroup> | ||
<h3>Select month</h3> | ||
<p>Last 12 months</p> | ||
</hgroup> | ||
<MonthGrid> | ||
{selectablePeriods.map((period, index) => ( | ||
<li key={period.label}> | ||
<button | ||
className={ | ||
!tempOverride && | ||
period.key === selectedPeriod | ||
? 'selected' | ||
: '' | ||
} | ||
type='button' | ||
disabled={!period.selectable} | ||
onClick={() => { | ||
select({ | ||
type: 'month', | ||
value: period.key, | ||
}); | ||
}} | ||
> | ||
{period.shortLabel} | ||
</button> | ||
</li> | ||
))} | ||
</MonthGrid> | ||
</MonthSelector> | ||
<RangeSelector> | ||
<h4>Range</h4> | ||
|
||
<RangeList> | ||
{rangeOptions.map((option) => ( | ||
<li key={option.label}> | ||
<button | ||
className={ | ||
tempOverride && | ||
tempOverride.type === 'range' && | ||
option.value === tempOverride.monthsBack | ||
? 'selected' | ||
: '' | ||
} | ||
type='button' | ||
onClick={() => { | ||
select({ | ||
type: 'range', | ||
monthsBack: option.value, | ||
}); | ||
}} | ||
> | ||
Last {option.value} months | ||
</button> | ||
</li> | ||
))} | ||
</RangeList> | ||
</RangeSelector> | ||
</Wrapper> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters