Skip to content

Commit

Permalink
feat(select): implement new Select
Browse files Browse the repository at this point in the history
  • Loading branch information
jigsawye committed Apr 17, 2019
1 parent 60bcdd9 commit c80f5fa
Show file tree
Hide file tree
Showing 19 changed files with 1,092 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
node_modules
npm-debug.log
yarn-error.log
lerna-debug.log
coverage
jest-report
lib
Expand Down
245 changes: 245 additions & 0 deletions docs/lab/Select.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
---
name: Select
menu: Lab
route: /lab/select
---

import { PropsTable, Playground, Link } from 'docz';
import { range } from 'ramda';
import { Select } from '@tailor-ui/lab';

# Select

Select component to select value from options.

## When To Use

- A dropdown menu for displaying choices - an elegant alternative to the native `<select>` element.
- Utilizing <Link to="/components/radio">Radio</Link> is recommended when there are fewer total options (less than 5).

## Examples

```js
import { Select } from '@tailor-ui/lab';
```

### Basic

<Playground>
{() => {
const [value, setValue] = React.useState({ label: 'Banana', value: 'Banana' });

return (
<Select
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
);

}}

</Playground>

### other sizes

<Playground>
{() => {
const [value, setValue] = React.useState({ label: 'Banana', value: 'Banana' });

return (
<div>
<Select
size="sm"
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
<br />
<br />
<Select
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
<br />
<br />
<Select
size="lg"
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
</div>
);

}}

</Playground>

### Searchable

<Playground>
{() => {
const [value, setValue] = React.useState({ label: 'Banana', value: 'Banana' });

return (
<Select
searchable
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
);

}}

</Playground>

### Creatable

<Playground>
{() => {
const [loading, setLoading] = React.useState(false);
const [value, setValue] = React.useState({ label: 'Banana', value: 'Banana' });
const [options, setOptions] = React.useState([
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]);

return (
<Select
creatable
loading={loading}
value={value}
onChange={newValue => setValue(newValue)}
options={options}
isValidNewOption={name =>
!options.map(option => option.value).includes(name) && name.trim() !== ''
}
onCreateOption={name => {
const newOption = { label: name, value: name };
setLoading(true);
setTimeout(() => {
setOptions([...options, newOption]);
setValue(newOption);
setLoading(false);
}, 3000)
}
}
/>
);

}}

</Playground>

### disabled

<Playground>
{() => {
const [value, setValue] = React.useState({ label: 'Banana', value: 'Banana' });

return (
<Select
disabled
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
);

}}

</Playground>

### With placeholder & clearable

<Playground>
{() => {
const [value, setValue] = React.useState(null);

return (
<Select
clearable
placeholder="どれ"
value={value}
onChange={newValue => setValue(newValue)}
options={[
{ label: 'Banana', value: 'Banana' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Apple', value: 'Apple' },
{ label: 'Mango', value: 'Mango' },
]}
/>
);

}}

</Playground>

### Large items & custom menu

<Playground>
{() => {
const defaultItems = Array
.from({ length: 9999 })
.fill(0)
.map((_, index) => ({
label: `item #${index}`,
value: String(index)
}));

const [value, setValue] = React.useState({ label: 'item #9527', value: '9527' });

return (
<Select
value={value}
menu={<div style={{ padding: 8, borderTop: '1px solid #efefef' }}>I am cutsom menu!</div>}
onChange={newValue => {
console.log(newValue)
setValue(newValue)
}}
options={defaultItems}
/>
);

}}

</Playground>

## API

<PropsTable of={Select} />
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@types/fuzzaldrin-plus": "^0.6.0",
"@types/jest": "^24.0.11",
"@types/lodash.debounce": "^4.0.6",
"@types/ramda": "^0.26.6",
Expand Down
4 changes: 4 additions & 0 deletions packages/tailor-ui-lab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@
},
"dependencies": {
"@tailor-ui/utils": "^0.1.1",
"downshift": "^3.2.7",
"fuzzaldrin-plus": "^0.6.0",
"lodash.debounce": "^4.0.8",
"polished": "^3.2.0",
"react-icons": "^3.5.0",
"react-tiny-virtual-list": "^2.2.0",
"styled-system": "^4.1.0"
}
}
23 changes: 16 additions & 7 deletions packages/tailor-ui-lab/src/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
useKeydown,
} from 'tailor-ui';

import { PopoverContent, PopoverHeader, StyledPopover } from './styles';
import { PopoverHeader, StyledPopover, StyledPopoverProps } from './styles';

interface PopoverPopup {
style: CSSProperties;
Expand All @@ -46,16 +46,14 @@ const PopoverPopup = memo(
</Heading.h6>
</PopoverHeader>
)}
<PopoverContent>
{content instanceof Function ? content(handleClose) : content}
</PopoverContent>
{content instanceof Function ? content(handleClose) : content}
</StyledPopover>
</animated.div>
);
})
);

export interface PopoverProps {
export type PopoverProps = StyledPopoverProps & {
/**
* Whether the floating popover card is visible by default. Only support when the trigger is `click`
*/
Expand All @@ -72,14 +70,19 @@ export interface PopoverProps {
* The position base on the children component
*/
position?: Positions;
/**
* A string or react component inside this popover.
* If you are using click to trigger, it can be a
* function that with `hide` callback as first argument
*/
title?: ReactNode | ((handleClose: () => void) => ReactNode);
/**
* A string or react component inside this popover.
* If you are using click to trigger, it can be a
* function that with `hide` callback as first argument
*/
content: ReactNode | ((handleClose: () => void) => ReactNode);
}
};

const Popover: FunctionComponent<PopoverProps> = ({
children,
Expand All @@ -89,14 +92,19 @@ const Popover: FunctionComponent<PopoverProps> = ({
defaultVisible = false,
visible: visibleFromProps,
onVisibleChange,
...otherProps
}) => {
const childrenRef = useRef(null);
const childrenRefFromSelf = useRef(null);
const popupRef = useRef(null);
const [visibleFromSelf, setVisibleFromSelf] = useState(defaultVisible);

const hasVisibleFromProps = typeof visibleFromProps !== 'undefined';

const visible = hasVisibleFromProps ? visibleFromProps : visibleFromSelf;
const childrenRef =
children && (children as any).ref
? (children as any).ref
: childrenRefFromSelf;

const handleOpen = () => {
if (onVisibleChange) {
Expand Down Expand Up @@ -174,6 +182,7 @@ const Popover: FunctionComponent<PopoverProps> = ({
title={title}
content={content}
handleClose={handleClose}
{...otherProps}
/>
)}
>
Expand Down
30 changes: 23 additions & 7 deletions packages/tailor-ui-lab/src/Popover/styles.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import styled from 'styled-components';
import {
MinHeightProps,
MinWidthProps,
SpaceProps,
minHeight,
minWidth,
space,
} from 'styled-system';

export const StyledPopover = styled.div`
export type StyledPopoverProps = SpaceProps & MinWidthProps & MinHeightProps;

export const StyledPopover = styled.div<StyledPopoverProps>`
overflow: hidden;
border: ${p => p.theme.borders.base};
border-radius: ${p => p.theme.radii.lg};
border-color: ${p => p.theme.colors.gray300};
background-color: ${p => p.theme.colors.light};
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 4px rgba(100, 120, 168, 0.3);
color: ${p => p.theme.colors.gray700};
font-size: ${p => p.theme.fontSizes.sm};
text-align: left;
white-space: nowrap;
${space}
${minWidth}
${minHeight}
`;

StyledPopover.defaultProps = {
p: 2,
};

export const PopoverHeader = styled.div`
padding: ${p => p.theme.space[1]} ${p => p.theme.space[2]};
margin-bottom: ${p => p.theme.space[1]};
padding-bottom: ${p => p.theme.space[1]};
border-bottom: ${p => p.theme.borders.base};
border-color: ${p => p.theme.colors.gray300};
`;

export const PopoverContent = styled.div`
padding: ${p => p.theme.space[2]};
`;
Loading

0 comments on commit c80f5fa

Please sign in to comment.