diff --git a/packages/styled-components/src/components/primitives/Dropdown/Dropdown.stories.tsx b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.stories.tsx new file mode 100644 index 000000000..a37b50e22 --- /dev/null +++ b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.stories.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { Dropdown } from '.'; +import { text } from '@storybook/addon-knobs'; +import { LinkItem } from 'components/primitives/List/Items'; +import { favorite as Favorite, Icon } from 'components/primitives/Icon'; +import styled from 'styled-components'; + +export default { + title: 'Primitives/Dropdown', + component: Dropdown, +}; + +export const Titles = () => ( + <> +

Titles

+ + + + + + + +); + +export const Icons = () => ( + <> +

Icons

+ + } /> + } /> + } /> + } /> + + +); + +const StyledFlex = styled.div` + display: flex; +`; +export const Columns = () => ( + <> +

Columns

+ + +
+ {' '} + } + /> + } + /> + } + /> + } + /> +
+
+ {' '} + } + /> + } + /> + } + /> + } + /> +
+
+
+ +); + +export const Playground = () => ( + <> +

Playground

+ + {text('Content', 'Content')} + + +); diff --git a/packages/styled-components/src/components/primitives/Dropdown/Dropdown.styled.ts b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.styled.ts new file mode 100644 index 000000000..6d170b405 --- /dev/null +++ b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.styled.ts @@ -0,0 +1,23 @@ +import styled from 'styled-components'; +import { Icon } from 'components/primitives/Icon'; +import { List } from 'components/primitives/List'; + +export const StyledWrapper = styled.div` + position: relative; +`; + +export const StyledAnchor = styled.a` + display: flex; + align-items: middle; + flex: 1 1 0%; +`; + +export const StyledIcon = styled(Icon)` + fill: currentColor; + margin-left: 0.5rem; + pointer-events: none; +`; + +export const StyledList = styled(List)` + min-width: 10rem; +`; diff --git a/packages/styled-components/src/components/primitives/Dropdown/Dropdown.test.tsx b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.test.tsx new file mode 100644 index 000000000..4fc437966 --- /dev/null +++ b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.test.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import { Dropdown } from './Dropdown'; + +describe('Dropdown', () => { + it('should render children when dropdown is open', () => { + render( + +
foo
+
+ ); + const expandMore = screen.getByTestId('expandMore'); + fireEvent.click(expandMore); + screen.getByText('foo'); + }); + it('should render title', () => { + render(); + screen.getByText('foo'); + }); + it('should render ExpandMore icon', () => { + render(); + screen.getByTestId('expandMore'); + }); + it('should render ExpandLess icon on click', () => { + render(); + const expandMore = screen.getByTestId('expandMore'); + fireEvent.click(expandMore); + screen.getByTestId('expandLess'); + }); + it('should close menu when click outside', () => { + render(); + const expandMore = screen.getByTestId('expandMore'); + fireEvent.click(expandMore); + screen.getByTestId('expandLess'); + fireEvent.click(document); + screen.getByTestId('expandMore'); + }); +}); diff --git a/packages/styled-components/src/components/primitives/Dropdown/Dropdown.tsx b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.tsx new file mode 100644 index 000000000..09338f17a --- /dev/null +++ b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.tsx @@ -0,0 +1,58 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Icon, Typography } from 'components/primitives'; +import { + expand_more as ExpandMore, + expand_less as ExpandLess, +} from 'components/primitives/Icon'; +import { Props } from './Dropdown.types'; +import { + StyledWrapper, + StyledAnchor, + StyledIcon, + StyledList, +} from './Dropdown.styled'; + +export const Dropdown = ({ + title, + className, + children, + testId = 'dropdown', +}: Props) => { + const node = useRef(null); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + const handleClose = (e) => { + if (node.current && !node.current.contains(e.target)) { + setIsOpen(false); + } + }; + + document.addEventListener('click', handleClose); + return () => { + document.removeEventListener('click', handleClose); + }; + }, [node]); + + return ( + + setIsOpen((isOpen) => !isOpen)} + > + + {title} + + + {isOpen ? ( + + ) : ( + + )} + + + {isOpen && {children}} + + ); +}; diff --git a/packages/styled-components/src/components/primitives/Dropdown/Dropdown.types.ts b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.types.ts new file mode 100644 index 000000000..fcf7de3dc --- /dev/null +++ b/packages/styled-components/src/components/primitives/Dropdown/Dropdown.types.ts @@ -0,0 +1,8 @@ +import { ReactNode } from 'react'; + +export type Props = { + title: string; + className?: string; + children?: ReactNode; + testId?: string; +}; diff --git a/packages/styled-components/src/components/primitives/Dropdown/index.ts b/packages/styled-components/src/components/primitives/Dropdown/index.ts new file mode 100644 index 000000000..2f29bad4e --- /dev/null +++ b/packages/styled-components/src/components/primitives/Dropdown/index.ts @@ -0,0 +1 @@ +export * from './Dropdown'; diff --git a/packages/styled-components/src/components/primitives/index.ts b/packages/styled-components/src/components/primitives/index.ts index f02ba3077..d73fcfff4 100644 --- a/packages/styled-components/src/components/primitives/index.ts +++ b/packages/styled-components/src/components/primitives/index.ts @@ -4,6 +4,7 @@ export * from './Badges/IconicBadge'; export * from './Badges/NumericBadge'; export * from './Button'; export * from './Chat/ConversationSummary'; +export * from './Dropdown'; export * from './Icon'; export * from './Image'; export * from './Link';