1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-04 00:18:01 +01:00

feat(1-3281): wraps the new datepicker in a dropdown (#9169)

Wraps the datepicker in a popover, making it function largely the same
as a dropdown list.

The dropdown displays one of:
- "current month" if you've selected the current month
- "<month> <year>" (e.g. "December 2024") if you've selected a month
that isn't the current month
- "Last n months" (e.g. "Last 3 months") if you have selected a range

Additionally, the range selections have been updated to span the whole
row, aligning with the look of generic dropdown lists.


![image](https://github.com/user-attachments/assets/d356aec5-d51b-42fa-9591-60e2b5038a8e)

Like with the rest of this file (`PeriodSelector`), the code is rough
and not according to Unleash standards. However, I'm prioritizing fast
changes so UX can have a look before I clean up the code to switch to
using styled components etc later. It's still behind a flag, so I'm not
very worried about it.
This commit is contained in:
Thomas Heartman 2025-01-29 15:29:30 +01:00 committed by GitHub
parent b870333990
commit f4556839c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,6 +1,12 @@
import { styled } from '@mui/material';
import { styled, Button, Popover, Box, type Theme } from '@mui/material';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp';
import type { ChartDataSelection } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
import type { FC } from 'react';
import { useRef, useState, type FC } from 'react';
import { format } from 'date-fns';
const dropdownWidth = '15rem';
const dropdownInlinePadding = (theme: Theme) => theme.spacing(3);
export type Period = {
key: string;
@ -60,9 +66,8 @@ const getSelectablePeriods = (): Period[] => {
};
const Wrapper = styled('article')(({ theme }) => ({
borderRadius: theme.shape.borderRadiusLarge,
border: `2px solid ${theme.palette.divider}`,
padding: theme.spacing(3),
width: dropdownWidth,
paddingBlock: theme.spacing(2),
display: 'flex',
flexFlow: 'column',
gap: theme.spacing(2),
@ -84,13 +89,17 @@ const Wrapper = styled('article')(({ theme }) => ({
cursor: 'default',
color: theme.palette.text.disabled,
},
'button:hover': {
'button:hover:not(:disabled)': {
backgroundColor: theme.palette.action.hover,
},
'button:focus': {
outline: `2px solid ${theme.palette.primary.main}`,
},
}));
const MonthSelector = styled('article')(({ theme }) => ({
border: 'none',
paddingInline: dropdownInlinePadding(theme),
hgroup: {
h3: {
margin: 0,
@ -116,9 +125,11 @@ const MonthGrid = styled('ul')(({ theme }) => ({
const RangeSelector = styled('article')(({ theme }) => ({
display: 'flex',
width: '100%',
flexFlow: 'column',
gap: theme.spacing(0.5),
gap: theme.spacing(0),
h4: {
paddingInline: dropdownInlinePadding(theme),
fontSize: theme.typography.body2.fontSize,
margin: 0,
color: theme.palette.text.secondary,
@ -127,31 +138,34 @@ const RangeSelector = styled('article')(({ theme }) => ({
const RangeList = styled('ul')(({ theme }) => ({
listStyle: 'none',
margin: 0,
padding: 0,
'li + li': {
marginTop: theme.spacing(1),
width: '100%',
li: {
width: '100%',
},
button: {
marginLeft: `-${theme.spacing(0.5)}`,
width: '100%',
paddingBlock: theme.spacing(1),
textAlign: 'left',
borderRadius: 0,
paddingInline: dropdownInlinePadding(theme),
},
}));
type Selection =
| {
type: 'month';
value: string;
}
| {
type: 'range';
monthsBack: number;
};
type Props = {
selectedPeriod: ChartDataSelection;
setPeriod: (period: ChartDataSelection) => void;
};
const StyledPopover = styled(Popover)(({ theme }) => ({
'& .MuiPaper-root': {
borderRadius: theme.shape.borderRadiusLarge,
border: `1px solid ${theme.palette.divider}`,
},
}));
export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
const selectablePeriods = getSelectablePeriods();
@ -160,7 +174,62 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
label: `Last ${monthsBack} months`,
}));
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const selectPeriod = (period: ChartDataSelection) => {
setPeriod(period);
setOpen(false);
};
const buttonText =
selectedPeriod.grouping === 'daily'
? selectedPeriod.month === format(new Date(), 'yyyy-MM')
? 'Current month'
: new Date(selectedPeriod.month).toLocaleDateString('en-US', {
month: 'long',
year: 'numeric',
})
: `Last ${selectedPeriod.monthsBack} months`;
return (
<Box ref={ref}>
<Button
endIcon={open ? <ArrowDropUpIcon /> : <ArrowDropDownIcon />}
sx={(theme) => ({
whiteSpace: 'nowrap',
width: dropdownWidth,
justifyContent: 'space-between',
fontWeight: 'normal',
color: theme.palette.text.primary,
borderColor: theme.palette.divider,
borderWidth: '2px',
':focus-within': {
borderColor: theme.palette.primary.main,
},
':hover': {
borderWidth: '2px', // Prevent the border from changing width on hover
},
})}
variant='outlined'
disableRipple
onClick={() => setOpen(true)}
>
{buttonText}
</Button>
<StyledPopover
open={open}
anchorEl={ref.current}
onClose={() => setOpen(false)}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
<Wrapper>
<MonthSelector>
<hgroup>
@ -172,7 +241,8 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
<li key={period.label}>
<button
className={
selectedPeriod.grouping === 'daily' &&
selectedPeriod.grouping ===
'daily' &&
period.key === selectedPeriod.month
? 'selected'
: ''
@ -180,7 +250,7 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
type='button'
disabled={!period.selectable}
onClick={() => {
setPeriod({
selectPeriod({
grouping: 'daily',
month: period.key,
});
@ -200,14 +270,16 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
<li key={option.label}>
<button
className={
selectedPeriod.grouping === 'monthly' &&
option.value === selectedPeriod.monthsBack
selectedPeriod.grouping ===
'monthly' &&
option.value ===
selectedPeriod.monthsBack
? 'selected'
: ''
}
type='button'
onClick={() => {
setPeriod({
selectPeriod({
grouping: 'monthly',
monthsBack: option.value,
});
@ -219,6 +291,8 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
))}
</RangeList>
</RangeSelector>
</Wrapper>
</Wrapper>{' '}
</StyledPopover>
</Box>
);
};