Skip to content

Commit

Permalink
fix #384: Rework Dropdown using Popper (#411)
Browse files Browse the repository at this point in the history
* unrelated: use local typescript version

* Button: fix inline usage in static pages

* Button: restructure again to allow adding "endElement" + fix disabled link styles

* fix #384: reworked `Dropdown` with `Popper`

* fix typecheck after Button changes

* wrap pickers on small resolutions
  • Loading branch information
rtrembecky authored Nov 21, 2024
1 parent 2ec9fe3 commit 536e93a
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 138 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"git.branchProtection": [
"master"
],
"git.branchProtectionPrompt": "alwaysCommitToNewBranch"
"git.branchProtectionPrompt": "alwaysCommitToNewBranch",
"typescript.tsdk": "node_modules/typescript/lib"
}
40 changes: 26 additions & 14 deletions src/components/Clickable/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import {Box, SxProps, Theme, Typography, TypographyProps} from '@mui/material'
import {ButtonHTMLAttributes, FC, ReactNode} from 'react'
import {Box, Stack, SxProps, Theme, Typography, TypographyProps} from '@mui/material'
import {ButtonHTMLAttributes, FC, MouseEventHandler, ReactNode} from 'react'

import {buttonTextSx, getButtonWrapperSx} from './buttonStyles'
import {buttonInnerSx, getButtonWrapperSx} from './buttonStyles'

type ButtonProps = {
onClick?: () => void
onClick?: MouseEventHandler<HTMLButtonElement>
disabled?: boolean
children: ReactNode
children?: ReactNode
endElement?: ReactNode
variant?: TypographyProps['variant']
invertColors?: boolean
sx?: SxProps<Theme>
textSx?: SxProps<Theme>
} & Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'type'>

export const Button: FC<ButtonProps> = ({children, onClick, disabled, type, variant, invertColors, sx, textSx}) => {
export const Button: FC<ButtonProps> = ({
children,
endElement,
onClick,
disabled,
type,
variant,
invertColors,
sx,
textSx,
}) => {
return (
// tento wrapper je tu kvoli lepsej kontrole paddingov atd.
<Box
Expand All @@ -26,15 +37,16 @@ export const Button: FC<ButtonProps> = ({children, onClick, disabled, type, vari
...sx,
}}
>
<Typography
variant={variant ?? 'button3'}
sx={{
...buttonTextSx,
...textSx,
}}
<Stack
direction="row"
// toto pridava bottom border
sx={buttonInnerSx}
>
{children}
</Typography>
<Typography variant={variant ?? 'button3'} sx={textSx}>
{children}
</Typography>
{endElement}
</Stack>
</Box>
)
}
46 changes: 28 additions & 18 deletions src/components/Clickable/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Box, SxProps, Theme, Typography, TypographyProps} from '@mui/material'
import NextLink from 'next/link'
import {ComponentProps, FC, ReactNode} from 'react'

import {buttonTextSx, getButtonWrapperSx} from './buttonStyles'
import {buttonInnerSx, getButtonWrapperSx} from './buttonStyles'

type LinkProps = {
href?: string
Expand All @@ -16,22 +16,32 @@ type LinkProps = {
} & Pick<ComponentProps<typeof NextLink>, 'target'>

export const Link: FC<LinkProps> = ({children, href, disabled, target, variant, invertColors, active, sx, textSx}) => {
// https://a11y-guidelines.orange.com/en/articles/disable-elements/#disable-a-link
return disabled ? (
<Typography
variant={variant ?? 'button3'}
component="a"
aria-disabled
role="link"
sx={{
...getButtonWrapperSx({invertColors, disabled, active}),
...buttonTextSx,
...sx,
}}
>
{children}
</Typography>
) : (
if (disabled) {
return (
<Box
sx={{
...getButtonWrapperSx({invertColors, disabled, active}),
...sx,
}}
>
{/* https://a11y-guidelines.orange.com/en/articles/disable-elements/#disable-a-link */}
<Typography
variant={variant ?? 'button3'}
component="a"
aria-disabled
role="link"
sx={{
...buttonInnerSx,
...textSx,
}}
>
{children}
</Typography>
</Box>
)
}

return (
// tento wrapper je tu kvoli lepsej kontrole paddingov atd.
<Box
component={NextLink}
Expand All @@ -45,7 +55,7 @@ export const Link: FC<LinkProps> = ({children, href, disabled, target, variant,
<Typography
variant={variant ?? 'button3'}
sx={{
...buttonTextSx,
...buttonInnerSx,
...textSx,
}}
>
Expand Down
9 changes: 5 additions & 4 deletions src/components/Clickable/buttonStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const getButtonWrapperSx = ({
const invert = !!invertColors !== !!active

return {
display: 'flex',
display: 'inline-flex',
cursor: disabled ? 'default' : 'pointer',
// reset default <button> styles
padding: 0,
Expand All @@ -29,11 +29,12 @@ export const getButtonWrapperSx = ({
'--bgcolor': disabled ? '#ccc' : invert ? 'white' : 'black',
'--color': disabled ? 'white' : invert ? 'black' : 'white',
},
px: '10px',
pb: '4px',
} satisfies SxProps<Theme>
}

export const buttonTextSx = {
mx: '10px',
mb: '4px',
export const buttonInnerSx = {
borderBottom: '5px solid',
alignItems: 'center',
} satisfies SxProps<Theme>
2 changes: 1 addition & 1 deletion src/components/Problems/Discussion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export const Discussion: FC<DiscussionProps> = ({problemId, problemNumber, close
<>
<textarea className={styles.textArea} value={commentText} onChange={handleCommentChange} />
<Stack alignSelf="end">
<Button variant="button2" onClick={addComment}>
<Button variant="button2" onClick={() => addComment()}>
Odoslať
</Button>
</Stack>
Expand Down
58 changes: 0 additions & 58 deletions src/components/SemesterPicker/Dropdown.module.scss

This file was deleted.

14 changes: 0 additions & 14 deletions src/components/SemesterPicker/Dropdown.module.scss.d.ts

This file was deleted.

92 changes: 65 additions & 27 deletions src/components/SemesterPicker/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {Typography} from '@mui/material'
import clsx from 'clsx'
import {Box, ClickAwayListener, Popper, Stack, Typography} from '@mui/material'
import Link from 'next/link'
import {FC, MouseEvent, useState} from 'react'

import styles from './Dropdown.module.scss'
import ArrowDown from '@/svg/ArrowDown.svg'

import {Button} from '../Clickable/Button'

export interface DropdownOption {
id: number
Expand All @@ -13,36 +14,73 @@ export interface DropdownOption {
}

export const Dropdown: FC<{title: string; options: DropdownOption[]}> = ({title, options}) => {
const [display, setDisplay] = useState(false)
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

const handleClick = (event: MouseEvent) => {
event.preventDefault()
setDisplay((prevDisplay) => !prevDisplay)
const handleClick = (event: MouseEvent<HTMLElement>) => {
setAnchorEl((prev) => (prev ? null : event.currentTarget))
}

const handleMouseLeave = () => {
setDisplay(false)
const handleClose = () => {
setAnchorEl(null)
}

return (
<div className={styles.dropdown} onClick={handleClick} onMouseLeave={handleMouseLeave}>
<Typography variant="button2">
<>
<Button
variant="button2"
onClick={handleClick}
endElement={
<Box
sx={{
ml: '1rem',
// roughly aligned with button2 font sizes
width: {xs: '12px', md: '13px', lg: '16px', xl: '20px'},
}}
>
<ArrowDown />
</Box>
}
>
{title}
<div className={styles.arrow} />
</Typography>
<div className={clsx(styles.options, display && styles.displayOptions)}>
{options.map((option) => {
return (
<Link
href={option.link}
key={option.id}
className={clsx(styles.option, option.selected && styles.selectedOption)}
>
<Typography variant="button2">{option.text}</Typography>
</Link>
)
})}
</div>
</div>
</Button>
<Popper open={!!anchorEl} anchorEl={anchorEl} placement="bottom-end">
<ClickAwayListener onClickAway={handleClose}>
<Stack
sx={{
bgcolor: 'white',
border: '5px solid black',
borderWidth: '5px',
// random constant to make sure the dropdown is not too long (around 9 options)
maxHeight: '20rem',
overflowY: 'auto',
}}
>
{options.map((option) => (
<Box
key={option.id}
component={Link}
href={option.link}
sx={{
bgcolor: 'white',
color: 'black',
padding: '5px',
':hover': {
bgcolor: '#ccc',
},
...(option.selected && {
bgcolor: 'black',
color: 'white',
// ziadny hover feedback na ciernej aktualne vybratej option
':hover': {},
}),
}}
>
<Typography variant="button2">{option.text}</Typography>
</Box>
))}
</Stack>
</ClickAwayListener>
</Popper>
</>
)
}
13 changes: 12 additions & 1 deletion src/components/SemesterPicker/SemesterPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,18 @@ export const SemesterPicker: FC<{page: 'zadania' | 'vysledky' | 'admin/opravovan
}

return (
<Stack sx={{flexDirection: 'row', alignItems: 'center', gap: 2, zIndex: 1000, userSelect: 'none'}}>
<Stack
sx={{
flexDirection: 'row',
alignItems: 'center',
columnGap: 2,
// wrap na nizsich rozliseniach
flexWrap: 'wrap',
rowGap: 1,
zIndex: 1000,
userSelect: 'none',
}}
>
{page !== 'admin/opravovanie' && <Dropdown title={'Séria'} options={dropdownSeriesList} />}
<Dropdown title={'Semester'} options={dropdownSemesterList} />
</Stack>
Expand Down
3 changes: 3 additions & 0 deletions src/svg/ArrowDown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 536e93a

Please sign in to comment.