Skip to content
This repository has been archived by the owner on Sep 9, 2022. It is now read-only.

Commit

Permalink
fix(Dropdown): added Dropdown component
Browse files Browse the repository at this point in the history
  • Loading branch information
cecilia-sanare committed Aug 24, 2021
1 parent e6ae0c6 commit cc26958
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 4 deletions.
2 changes: 1 addition & 1 deletion stories/Button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ a.button {
position: relative;

height: $min-component-size;
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-family: $font-family;
font-weight: 700;
font-size: 15px;
border: 0;
Expand Down
4 changes: 2 additions & 2 deletions stories/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export function Button({
)}
layout={Layout.Horizontal}
type={'button'}
onClick={() => {
onClick={onClick ? () => {
if (!isDisabled) onClick()
}}
} : null}
{...props}
>
<Grid
Expand Down
2 changes: 1 addition & 1 deletion stories/Card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
padding: $padding;
box-shadow: 0px 2px 2px 0px rgb(0 0 0 / 50%);

font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-family: $font-family;
}
57 changes: 57 additions & 0 deletions stories/Dropdown.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@use './variables' as *;
@use './mixins' as *;

.dropdown {
position: relative;
display: inline-block;
font-family: $font-family;

&.left .list {
left: 0;
}

&.right .list {
right: 0;
}

.list {
@include transition(transform, opacity);

position: absolute;
top: calc(100% + #{$margin});
overflow: hidden;
min-width: 200px;

background: $primary;
color: $primary-accent;
border-radius: $border-radius;

transform: translateY(-$margin);
opacity: 0;

> * {
padding: $padding;
}
}

&:not(.visible) .list {
pointer-events: none;
}

&.visible .list {
transform: translateY(0);
opacity: 1;
}
}

.dropdownItem {
color: inherit;
text-decoration: none;
position: relative;

&.clickable {
@include hover();

cursor: pointer;
}
}
36 changes: 36 additions & 0 deletions stories/Dropdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';

import { Dropdown, DropdownItem, DropdownProps } from './Dropdown';
import { Button } from './Button';
import { Alignment } from './constants';

export default {
title: 'Components/Dropdown',
component: Dropdown,
};

const Template = (args: DropdownProps) => <Dropdown {...args} />;
export const Simple = Template.bind({});
Simple.args = {
align: Alignment.Left,
children: (
<>
<DropdownItem
href='https://google.com'
type='a'
>
One
</DropdownItem>
<DropdownItem
href='https://google.com'
type='a'
>
Two
</DropdownItem>
<DropdownItem>
Three
</DropdownItem>
</>
),
button: <Button>Click meee!</Button>,
};
73 changes: 73 additions & 0 deletions stories/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import classNames from 'classnames';
import React, { ReactNode, useEffect, useRef, useState } from 'react';
import { Alignment, Layout } from './constants';

import styles from './Dropdown.scss';
import { Grid } from './Grid';
export { DropdownItem } from './DropdownItem';

export interface DropdownProps {
align: Alignment;
button: ReactNode;
children: ReactNode;
}

export function Dropdown({
align,
button,
children,
}: DropdownProps): JSX.Element {
const elementRef = useRef<HTMLDivElement>();
const listenerRef = useRef<(event: MouseEvent) => void>();
const [visible, setVisible] = useState<boolean>();

useEffect(() => {
if (visible) {
listenerRef.current = (event: MouseEvent): void => {
const path = event.composedPath();
if (!path.includes(elementRef.current)) {
setVisible(false);
}
};

window.addEventListener('click', listenerRef.current, {
passive: true,
});
} else if (listenerRef.current) {
window.removeEventListener('click', listenerRef.current);
listenerRef.current = null;
}

return () => {
if (listenerRef.current) {
window.removeEventListener('click', listenerRef.current);
}
}
}, [visible]);

return (
<div
ref={elementRef}
className={classNames(
styles.dropdown,
styles[align],
visible && styles.visible,
)}
>
<div
onClick={() => {
setVisible(!visible);
}}
>
{button}
</div>
<Grid
className={styles.list}
gap={0}
layout={Layout.Vertical}
>
{children}
</Grid>
</div>
);
}
30 changes: 30 additions & 0 deletions stories/DropdownItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import classNames from 'classnames';
import React, { ElementType, ReactNode } from 'react';

import styles from './Dropdown.scss';

export interface DropdownProps {
[key: string]: any;
children: ReactNode;
type?: ElementType;
}

export function DropdownItem({
children,
type: Type = 'div',
...props
}: DropdownProps): JSX.Element {
const isClickable = Type === 'a' || props.onClick || props.href || props.to;

return (
<Type
className={classNames(
styles.dropdownItem,
isClickable && styles.clickable,
)}
{...props}
>
{children}
</Type>
);
}
1 change: 1 addition & 0 deletions stories/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ $min-component-size: 36px;
$padding: 10px;
$margin: 10px;
$font-size: 16px;
$font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
1 change: 1 addition & 0 deletions stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export { Separator } from './Separator';
export { Tooltip } from './Tooltip';
export { Typography, TypographyStyles } from './Typography';
export { Toasts, AddToastNotification } from './Toasts';
export { Dropdown, DropdownItem } from './Dropdown';

0 comments on commit cc26958

Please sign in to comment.