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

Menu typescript story #815

Merged
merged 4 commits into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import React, { useEffect } from 'react'
import { withKnobs } from '@storybook/addon-knobs'
import React, { EventHandler, useEffect } from 'react'
import { action } from '@storybook/addon-actions'
import styled from 'styled-components'
import { Menu, Typography, Button, Icon, TopBar } from '@equinor/eds-core-react'
import {
Menu,
MenuProps,
Typography,
Button,
Icon,
TopBar,
} from '@equinor/eds-core-react'
import { Story, Meta } from '@storybook/react'

import { tokens } from '@equinor/eds-tokens'

Expand Down Expand Up @@ -54,29 +61,11 @@ const Anchor = styled.div.attrs({ tabIndex: 0 })`
width: fit-content;
`

const FloatingAnchor = styled(Anchor)`
position: absolute;
`

const onClick = (event) => {
const onClick = (event: React.MouseEvent) => {
action('clicked')(event)
event.stopPropagation()
}

export default {
title: 'Components/Menu',
component: Menu,
decorators: [withKnobs],
}

const simpleMenuTemplate = (
<>
<MenuItem onClick={onClick}>Item 1</MenuItem>
<MenuItem onClick={onClick}>Item 2</MenuItem>
<MenuItem onClick={onClick}>Item 3</MenuItem>
</>
)

const bigMenuTemplate = (
<>
<MenuItem onClick={onClick}>
Expand Down Expand Up @@ -191,97 +180,16 @@ const bigMenuTemplate = (
</>
)

export const EdgeDetection = () => {
const [state, setState] = React.useState({
topLeft: null,
topRight: null,
bottomLeft: null,
bottomRight: null,
})

const topLeftRef = React.useRef()
const topRightRef = React.useRef()
const bottomLeftRef = React.useRef()
const bottomRightRef = React.useRef()

useEffect(() => {
setState({
topLeft: topLeftRef.current,
topRight: topRightRef.current,
bottomLeft: bottomLeftRef.current,
bottomRight: bottomRightRef.current,
})
}, [state.topLeft])

const { topLeft, topRight, bottomLeft, bottomRight } = state

return (
<Grid>
<FloatingAnchor
id="anchor-topleft"
aria-controls="menu-topleft"
aria-haspopup="true"
ref={topLeftRef}
style={{ left: 0, top: 0 }}
>
Top left
</FloatingAnchor>
<Menu id="menu-topleft" open={Boolean(topLeft)} anchorEl={topLeft}>
{simpleMenuTemplate}
</Menu>

<FloatingAnchor
id="anchor-topright"
aria-controls="menu-topright"
aria-haspopup="true"
ref={topRightRef}
style={{ top: 0, right: 0 }}
>
Top Right
</FloatingAnchor>
<Menu id="menu-topright" open={Boolean(topRight)} anchorEl={topRight}>
{simpleMenuTemplate}
</Menu>

<FloatingAnchor
id="anchor-bottomleft"
aria-controls="menu-bottomleft"
aria-haspopup="true"
ref={bottomLeftRef}
style={{ bottom: 0, left: 0 }}
>
Bottom Left
</FloatingAnchor>
<Menu
id="menu-bottomleft"
open={Boolean(bottomLeft)}
anchorEl={bottomLeft}
>
{simpleMenuTemplate}
</Menu>

<FloatingAnchor
id="anchor-bottomright"
aria-controls="menu-bottomright"
aria-haspopup="true"
ref={bottomRightRef}
style={{ bottom: 0, right: 0 }}
>
Bottom right
</FloatingAnchor>
<Menu
id="menu-bottomright"
open={Boolean(bottomRight)}
anchorEl={bottomRight}
>
{simpleMenuTemplate}
</Menu>
</Grid>
)
}
export default {
title: 'Components/Menu',
component: Menu,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try using something like this to fix menus in story?

Suggested change
component: Menu,
component: Menu,
decorators: [
(Story) => (
<div style={{ margin: '3rem' }}>
<Story />
</div>
),
],

} as Meta

export const ButtonToggle = () => {
const [state, setState] = React.useState({
export const ButtonToggle: Story<MenuProps> = () => {
const [state, setState] = React.useState<{
buttonEl: HTMLButtonElement
focus: any
}>({
focus: 'first',
buttonEl: null,
})
Expand All @@ -297,7 +205,7 @@ export const ButtonToggle = () => {
setState({ ...state, buttonEl: null, focus })
}

const onKeyPress = (e) => {
const onKeyPress = (e: React.KeyboardEvent<HTMLButtonElement>) => {
const { key } = e
e.preventDefault()
switch (key) {
Expand All @@ -317,14 +225,14 @@ export const ButtonToggle = () => {

return (
<Grid style={{ gridAutoFlow: 'row' }}>
<Typography variant="h4">Opened with Button</Typography>
<Typography variant="h4">Click button to open Menu</Typography>
<Button
variant="ghost_icon"
id="menuButton"
aria-controls="menu-on-button"
aria-haspopup="true"
aria-expanded={isOpen}
onClick={(e) => (isOpen ? closeMenu() : openMenu(e))}
onClick={(e) => (isOpen ? closeMenu() : openMenu(e, null))}
onKeyDown={onKeyPress}
>
Menu
Expand All @@ -343,8 +251,11 @@ export const ButtonToggle = () => {
)
}

export const InTopbar = () => {
const [state, setState] = React.useState({
export const InTopbar: Story<MenuProps> = () => {
const [state, setState] = React.useState<{
buttonEl: HTMLButtonElement
focus: any
}>({
focus: 'first',
buttonEl: null,
})
Expand All @@ -356,14 +267,14 @@ export const InTopbar = () => {

const closeMenu = () => setState({ ...state, buttonEl: null })

const onKeyPress = (e) => {
const onKeyPress = (e: React.KeyboardEvent<HTMLButtonElement>) => {
const { key } = e
switch (key) {
case 'ArrowDown':
isOpen ? closeMenu() : openMenu(e, 'first')
isOpen ? closeMenu() : openMenu(e)
break
case 'ArrowUp':
isOpen ? closeMenu() : openMenu(e, 'last')
isOpen ? closeMenu() : openMenu(e)
break
case 'Escape':
closeMenu()
Expand Down Expand Up @@ -405,7 +316,7 @@ export const InTopbar = () => {
)
}

export const Examples = () => {
export const Examples: Story<MenuProps> = () => {
const [state, setState] = React.useState({
one: null,
two: null,
Expand Down
124 changes: 58 additions & 66 deletions libraries/core-react/src/Menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, ReactNode } from 'react'
import React, { useEffect, useRef, ReactNode, HTMLAttributes } from 'react'
import styled, { css } from 'styled-components'
import { useMenu } from './Menu.context'
import { Paper } from '../Paper'
Expand Down Expand Up @@ -43,76 +43,68 @@ export type MenuProps = {
focus?: FocusTarget
/** onClose handler */
onClose?: (e?: React.MouseEvent<ReactNode, MouseEvent>) => void
} & React.HTMLAttributes<HTMLDivElement>

export const Menu = React.forwardRef<HTMLDivElement, MenuProps>(
function EdsMenu(
{ children, anchorEl, onClose: onCloseCallback, open = false, ...rest },
ref,
) {
const listRef = useRef<HTMLLIElement>(null)

const {
setPosition,
position,
isPositioned,
setOnClose,
onClose,
} = useMenu()
useOutsideClick(listRef, () => {
if (open && onClose !== null) {
onClose()
}
})

useEffect(() => {
if (anchorEl && listRef.current) {
const menuRect = listRef.current.getBoundingClientRect()
const anchorRect = anchorEl.getBoundingClientRect()
setPosition(anchorRect, menuRect, window)
}

if (onClose === null && onCloseCallback) {
setOnClose(onCloseCallback)
}

document.addEventListener('keydown', handleGlobalKeyPress, true)

return () => {
document.removeEventListener('keydown', handleGlobalKeyPress, true)
}
}, [anchorEl, listRef.current])

const handleGlobalKeyPress = (e: KeyboardEvent) => {
const { key } = e

switch (key) {
case 'Escape':
onClose()
break
default:
break
}
} & HTMLAttributes<HTMLDivElement>

export const Menu = React.forwardRef<HTMLDivElement, MenuProps>(function Menu(
{ children, anchorEl, onClose: onCloseCallback, open = false, ...rest },
ref,
) {
const listRef = useRef<HTMLLIElement>(null)

const { setPosition, position, isPositioned, setOnClose, onClose } = useMenu()
useOutsideClick(listRef, () => {
if (open && onClose !== null) {
onClose()
}
})

useEffect(() => {
if (anchorEl && listRef.current) {
const menuRect = listRef.current.getBoundingClientRect()
const anchorRect = anchorEl.getBoundingClientRect()
setPosition(anchorRect, menuRect, window)
}

const paperProps = {
...position,
open,
isPositioned,
if (onClose === null && onCloseCallback) {
setOnClose(onCloseCallback)
}

const menuProps = {
...rest,
document.addEventListener('keydown', handleGlobalKeyPress, true)

return () => {
document.removeEventListener('keydown', handleGlobalKeyPress, true)
}
}, [anchorEl, listRef.current])

return (
<StyledPaper {...paperProps} elevation="raised">
<MenuList {...menuProps} ref={useCombinedRefs(ref, listRef)}>
{children}
</MenuList>
</StyledPaper>
)
},
)
const handleGlobalKeyPress = (e: KeyboardEvent) => {
const { key } = e

switch (key) {
case 'Escape':
onClose()
break
default:
break
}
}

const paperProps = {
...position,
open,
isPositioned,
}

const menuProps = {
...rest,
}

return (
<StyledPaper {...paperProps} elevation="raised">
<MenuList {...menuProps} ref={useCombinedRefs(ref, listRef)}>
{children}
</MenuList>
</StyledPaper>
)
})

// Menu.displayName = 'EdsMenu'
4 changes: 2 additions & 2 deletions libraries/core-react/src/Menu/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const Content = styled.div`
align-items: center;
`

export type Props = {
export type MenuItemProps = {
/** @ignore */
index?: number
/** Is active */
Expand All @@ -98,7 +98,7 @@ export type Props = {
} & React.HTMLAttributes<HTMLLIElement>

export const MenuItem = React.memo(
React.forwardRef<HTMLLIElement, Props>(function EdsMenuItem(
React.forwardRef<HTMLLIElement, MenuItemProps>(function MenuItem(
{ children, disabled, index = 0, onClick, ...rest },
ref,
) {
Expand Down
Loading