mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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.  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:
		
							parent
							
								
									b870333990
								
							
						
					
					
						commit
						f4556839c8
					
				@ -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>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user