Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
marco committed Mar 10, 2021
1 parent 49146b1 commit 96e3d83
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 38 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"create-react-context": "^0.2.3",
"downshift": "^6.1.0",
"moment": "^2.24.0",
"react-id-generator": "^3.0.1",
"styled-system": "^5.1.5"
},
"peerDependencies": {
Expand Down
126 changes: 113 additions & 13 deletions src/Input/DownshiftSelect/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { SecondaryColor } from '../../Utils/Colors';
import { Button } from '../../General/Button/Button';
import { Box } from '../../Layout/Box';
import { sample } from 'lodash';
import { TextField } from '../..';
// import { TextField } from '../..';

const StoryContainer = styled(BaseContainer)`
min-height: 250px;
Expand Down Expand Up @@ -59,7 +59,6 @@ const CustomStyledItem = styled(Select.Components.Item)`
`;

const CustomItem: React.FC<ItemProps> = props => {
console.log(props);
return (
<CustomStyledItem {...props}>
<span>{props.children}</span>
Expand Down Expand Up @@ -250,32 +249,133 @@ ControlledIsOpen.parameters = {
},
};

export const ControlledInputValue: Story<Props> = () => {
const [inputValue, setInputValue] = useState<string>();
// Disabled temporarily because we're using onInputValueChange internally already
// export const ControlledInputValue: Story<Props> = () => {
// const [inputValue, setInputValue] = useState<string>();
// return (
// <>
// <Box mb={8}>
// <TextField
// label="Input Value"
// onChange={(e: any) => setInputValue(e.target.value)}
// />
// </Box>
// <Box mb={8}>
// Input value is <code>{JSON.stringify(inputValue)}</code>
// </Box>
// <Select
// items={items}
// inputValue={inputValue}
// onInputValueChange={(inputValue: string) => setInputValue(inputValue)}
// />
// </>
// );
// };
// ControlledInputValue.parameters = {
// docs: {
// description: {
// story:
// "Use <code>inputValue</code> and <code>setInputValue</code> to control the input value (this value is used for the search function, don't confuse it with <code>selectedItem</code>",
// },
// },
// };

export const Invalid = Template.bind({});
Invalid.args = {
label: 'Username',
invalid: true,
helperText: 'Username is required',
};
Invalid.parameters = {
docs: {
description: {
story:
'Use <code>invalid=true</code> to toggle the Select into an error state. It is recommended that you use <code>helperText</code> to describe the error when you do this.',
},
},
};

export const DisableAutocomplete = Template.bind({});
DisableAutocomplete.args = {
autocomplete: 'off',
};
DisableAutocomplete.parameters = {
docs: {
description: {
story:
"Intrinsic props are usually passed down to the internal <code>input</code> element, so to disable autocompletion just pass <code>autocomplete='off'</code>.",
},
},
};

export const FocusCallbacks: Story<Props> = () => {
const [onFocusCalls, setOnFocusCalls] = useState<number>(0);
const [onBlurCalls, setOnBlurCalls] = useState<number>(0);
return (
<>
<Box mb={8}>
<TextField
label="Input Value"
onChange={(e: any) => setInputValue(e.target.value)}
/>
<code>onFocus</code> called {onFocusCalls} times.
</Box>
<Box mb={8}>
Input value is <code>{JSON.stringify(inputValue)}</code>
<code>onBlur</code> called {onBlurCalls} times.
</Box>
<Select
items={items}
inputValue={inputValue}
onInputValueChange={(inputValue: string) => setInputValue(inputValue)}
onFocus={() => setOnFocusCalls(onFocusCalls + 1)}
onBlur={() => setOnBlurCalls(onBlurCalls + 1)}
/>
</>
);
};
ControlledInputValue.parameters = {
FocusCallbacks.parameters = {
docs: {
description: {
story:
'Intrinsic props are usually passed down to the internal <code>input</code> element, so you can simply use <code>onFocus</code> and <code>onBlur</code> to capture those events.',
},
},
};

export const OnClearCallback: Story<Props> = () => {
const [onClearCalls, setOnClearCalls] = useState<number>(0);
return (
<>
<Box mb={8}>
<code>onClear</code> called {onClearCalls} times.
</Box>
<Select items={items} onClear={() => setOnClearCalls(onClearCalls + 1)} />
</>
);
};
OnClearCallback.parameters = {
docs: {
description: {
story:
'Use <code>onClear</code> to capture the when the clear-button is clicked.',
},
},
};

const ShortContainer = styled(Select.Components.Container)`
max-width: 280px;
`;

export const ConfigurableWidth = Template.bind({});
ConfigurableWidth.args = {
components: { Container: ShortContainer },
items: [
...items,
{
...items[0],
label: "This Option Is Long! I Hope It Doesn't Get Truncated!",
},
],
};
ConfigurableWidth.parameters = {
docs: {
description: {
story:
"Use <code>inputValue</code> and <code>setInputValue</code> to control the input value (this value is used for the search function, don't confuse it with <code>selectedItem</code>",
"By default, the select has a 100% width. To change the width, just pass a custom <code>Container</code> subcomponent. Note that you might also have to adjust other subcomponent's styles if you want to make it really short (less than 300px).",
},
},
};
74 changes: 54 additions & 20 deletions src/Input/DownshiftSelect/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useCombobox, UseComboboxProps } from 'downshift';
import * as internalComponents from './SelectStyle';
import { ArrowDownIcon, ArrowUpIcon, CloseCircleSolidIcon } from '../..';
import { isFunction, isUndefined } from 'lodash';
import { useId } from 'react-id-generator';

export const defaultTransformFunction = (items: Item[], inputValue: string) =>
items.filter(item =>
Expand All @@ -13,6 +14,7 @@ export const defaultTransformFunction = (items: Item[], inputValue: string) =>
export const defaultItemToString = (item: Item) => item.label;

export interface Components {
Container: typeof internalComponents.Container;
Label: typeof internalComponents.Label;
Combobox: typeof internalComponents.Combobox;
Input: typeof internalComponents.Input;
Expand All @@ -32,6 +34,7 @@ export interface Item {
[x: string]: any;
}

export type ContainerProps = HTMLAttributes<HTMLElement>;
export type LabelProps = HTMLAttributes<HTMLElement>;
export type ComboboxProps = HTMLAttributes<HTMLElement>;
export type InputProps = HTMLAttributes<HTMLElement>;
Expand All @@ -43,7 +46,7 @@ export type MenuProps = HTMLAttributes<HTMLElement>;
export interface ItemProps extends HTMLAttributes<HTMLElement> {
item: Item;
}
export type HelperTextProps = HTMLAttributes<HTMLElement>;
export type HelperTextProps = HTMLAttributes<HTMLInputElement>;

export interface Props extends ComboboxProps {
/** The items to choose from. */
Expand Down Expand Up @@ -80,14 +83,18 @@ export interface Props extends ComboboxProps {
/** */
disableTyping?: boolean;

invalid?: boolean;

isOpen?: boolean;
onIsOpenChange?: (isOpen: boolean) => void;

selectedItem?: Item;
onSelectedItemChange?: (selectedItem: Item) => void;

inputValue?: string;
onInputValueChange?: (inputValue: string) => void;
// inputValue?: string;
// onInputValueChange?: (inputValue: string) => void;

onClear?: () => void;
}

export const Select: React.FC<Props> & { Components: Components } = ({
Expand All @@ -103,11 +110,13 @@ export const Select: React.FC<Props> & { Components: Components } = ({
disabled = false,
disableTyping = false,
isOpen: isOpenExternal,
invalid = false,
onIsOpenChange: onIsOpenChangeExternal,
selectedItem: selectedItemExternal,
onSelectedItemChange: onSelectedItemChangeExternal,
inputValue: inputValueExternal,
onInputValueChange: onInputValueChangeExternal,
// inputValue: inputValueExternal,
// onInputValueChange: onInputValueChangeExternal,
onClear,
...props
}) => {
const mergedComponents = {
Expand All @@ -116,6 +125,7 @@ export const Select: React.FC<Props> & { Components: Components } = ({
};

const {
Container,
Label,
Combobox,
Input,
Expand Down Expand Up @@ -160,39 +170,58 @@ export const Select: React.FC<Props> & { Components: Components } = ({
onSelectedItemChangeExternal(selectedItem),
}),

...(!isUndefined(inputValueExternal) && {
inputValue: inputValueExternal,
}),
...(isFunction(onInputValueChangeExternal) && {
onInputValueChange: ({ inputValue }) =>
onInputValueChangeExternal(inputValue),
}),
// ...(!isUndefined(inputValueExternal) && {
// inputValue: inputValueExternal,
// }),
// ...(isFunction(onInputValueChangeExternal) && {
// onInputValueChange: ({ inputValue }) => {
// onInputValueChangeExternal(inputValue);
// },
// }),

...downshift,
});

const [helperId] = useId(1, 'description-');

const handleClear = () => {
if (isFunction(onClear)) {
onClear();
}
reset();
};

return (
<>
<Container>
{label && (
<Label
{...getLabelProps()}
data-active={isOpen}
data-disabled={disabled}
data-invalid={invalid}
>
{label}
</Label>
)}
<Combobox {...getComboboxProps()} {...props} data-disabled={disabled}>
<Combobox
{...getComboboxProps()}
data-disabled={disabled}
data-status={status}
data-invalid={invalid}
>
<Input
{...getInputProps({ disabled: disabled || disableTyping })}
{...getInputProps({ disabled: disabled || disableTyping, ...props })}
placeholder={placeholder}
aria-invalid={invalid}
{...(helperText && { 'aria-describedBy': helperId })}
{...(selectedItem && { title: selectedItem.label })}
/>
<IndicatorsContainer>
{isLoading && <LoadingIndicator />}
{selectedItem && (
<ClearButton
type="button"
onClick={reset}
onClick={handleClear}
aria-label="clear selection"
>
<CloseCircleSolidIcon />
Expand All @@ -211,20 +240,25 @@ export const Select: React.FC<Props> & { Components: Components } = ({
{isOpen &&
displayItems.map((item, index) => (
<Item
key={item.value}
item={item}
{...getItemProps({
item,
index,
disabled: disabled || item.disabled,
})}
key={item.value}
item={item}
title={item.label}
>
{defaultItemToString(item)}
</Item>
))}
</Menu>
{helperText && <HelperText>{helperText}</HelperText>}
</>
{helperText && (
<HelperText id={helperId} data-invalid={invalid}>
{helperText}
</HelperText>
)}
</Container>
);
};

Expand Down
Loading

0 comments on commit 96e3d83

Please sign in to comment.