Skip to content

Commit

Permalink
Merge pull request #11430 from storybookjs/11427-controls-debounce
Browse files Browse the repository at this point in the history
Controls: Fix interaction lag & CJK input
  • Loading branch information
shilman authored Jul 6, 2020
2 parents 40a319d + 25de639 commit d706f55
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 24 deletions.
17 changes: 15 additions & 2 deletions lib/components/src/blocks/ArgsTable/ArgControl.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useCallback } from 'react';
import React, { FC, useCallback, useState, useEffect } from 'react';
import { Args, ArgType } from './types';
import {
ArrayControl,
Expand All @@ -22,17 +22,30 @@ const NoControl = () => <>-</>;

export const ArgControl: FC<ArgControlProps> = ({ row, arg, updateArgs }) => {
const { name, control } = row;

const [isFocused, setFocused] = useState(false);
// box because arg can be a fn (e.g. actions) and useState calls fn's
const [boxedValue, setBoxedValue] = useState({ value: arg });

useEffect(() => {
if (!isFocused) setBoxedValue({ value: arg });
}, [isFocused, arg]);

const onChange = useCallback(
(argName: string, argVal: any) => {
setBoxedValue({ value: argVal });
updateArgs({ [name]: argVal });
return argVal;
},
[updateArgs, name]
);

const onBlur = useCallback(() => setFocused(false), []);
const onFocus = useCallback(() => setFocused(true), []);

if (!control || control.disable) return <NoControl />;

const props = { name, argType: row, value: arg, onChange };
const props = { name, argType: row, value: boxedValue.value, onChange, onBlur, onFocus };
switch (control.type) {
case 'array':
return <ArrayControl {...props} {...control} />;
Expand Down
12 changes: 10 additions & 2 deletions lib/components/src/controls/Array.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ const Wrapper = styled.label({
});

export type ArrayProps = ControlProps<ArrayValue> & ArrayConfig;
export const ArrayControl: FC<ArrayProps> = ({ name, value, onChange, separator = ',' }) => {
export const ArrayControl: FC<ArrayProps> = ({
name,
value,
onChange,
argType,
separator = ',',
onBlur,
onFocus,
}) => {
const handleChange = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>): void => {
const { value: newVal } = e.target;
Expand All @@ -29,11 +37,11 @@ export const ArrayControl: FC<ArrayProps> = ({ name, value, onChange, separator
<Wrapper>
<Form.Textarea
id={name}
name={name}
value={format(value, separator)}
onChange={handleChange}
size="flex"
placeholder="Adjust array dynamically"
{...{ name, onBlur, onFocus }}
/>
</Wrapper>
);
Expand Down
4 changes: 2 additions & 2 deletions lib/components/src/controls/Boolean.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ const format = (value: BooleanValue): string | null => (value ? String(value) :
const parse = (value: string | null) => value === 'true';

export type BooleanProps = ControlProps<BooleanValue> & BooleanConfig;
export const BooleanControl: FC<BooleanProps> = ({ name, value, onChange }) => (
export const BooleanControl: FC<BooleanProps> = ({ name, value, onChange, onBlur, onFocus }) => (
<Label htmlFor={name} title={value ? 'Change to false' : 'Change to true'}>
<input
id={name}
name={name}
type="checkbox"
onChange={(e) => onChange(name, e.target.checked)}
checked={value}
{...{ name, onBlur, onFocus }}
/>
<span>True</span>
<span>False</span>
Expand Down
3 changes: 2 additions & 1 deletion lib/components/src/controls/Color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const format = (color: ColorResult) =>
`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`;

export type ColorProps = ControlProps<ColorValue> & ColorConfig;
export const ColorControl: FC<ColorProps> = ({ name, value, onChange }) => {
export const ColorControl: FC<ColorProps> = ({ name, value, onChange, onFocus, onBlur }) => {
const [showPicker, setShowPicker] = useState(false);

return (
Expand All @@ -48,6 +48,7 @@ export const ColorControl: FC<ColorProps> = ({ name, value, onChange }) => {
<SketchPicker
color={value}
onChange={(color: ColorResult) => onChange(name, format(color))}
{...{ onFocus, onBlur }}
/>
</Popover>
) : null}
Expand Down
4 changes: 3 additions & 1 deletion lib/components/src/controls/Date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const FlexSpaced = styled.div(({ theme }) => ({
}));

export type DateProps = ControlProps<DateValue> & DateConfig;
export const DateControl: FC<DateProps> = ({ name, value, onChange }) => {
export const DateControl: FC<DateProps> = ({ name, value, onChange, onFocus, onBlur }) => {
const [valid, setValid] = useState(true);
const dateRef = useRef<HTMLInputElement>();
const timeRef = useRef<HTMLInputElement>();
Expand Down Expand Up @@ -102,13 +102,15 @@ export const DateControl: FC<DateProps> = ({ name, value, onChange }) => {
id={`${name}date`}
name={`${name}date`}
onChange={onDateChange}
{...{ onFocus, onBlur }}
/>
<Form.Input
type="time"
id={`${name}time`}
name={`${name}time`}
ref={timeRef as RefObject<HTMLInputElement>}
onChange={onTimeChange}
{...{ onFocus, onBlur }}
/>
{!valid ? <div>invalid</div> : null}
</FlexSpaced>
Expand Down
17 changes: 11 additions & 6 deletions lib/components/src/controls/Number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,28 @@ export const parse = (value: string) => {

export const format = (value: NumberValue) => (value != null ? String(value) : '');

export const NumberControl: FC<NumberProps> = ({ name, value, onChange, min, max, step }) => {
export const NumberControl: FC<NumberProps> = ({
name,
value,
onChange,
min,
max,
step,
onBlur,
onFocus,
}) => {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange(name, parse(event.target.value));
};

return (
<Wrapper>
<Form.Input
value={value}
type="number"
name={name}
min={min}
max={max}
step={step}
onChange={handleChange}
size="flex"
placeholder="Adjust number dynamically"
{...{ name, value, min, max, step, onFocus, onBlur }}
/>
</Wrapper>
);
Expand Down
11 changes: 9 additions & 2 deletions lib/components/src/controls/Object.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ const Wrapper = styled.label({
});

export type ObjectProps = ControlProps<ObjectValue> & ObjectConfig;
export const ObjectControl: FC<ObjectProps> = ({ name, argType, value, onChange }) => {
export const ObjectControl: FC<ObjectProps> = ({
name,
argType,
value,
onChange,
onBlur,
onFocus,
}) => {
const [valid, setValid] = useState(true);
const [text, setText] = useState(format(value));

Expand All @@ -49,12 +56,12 @@ export const ObjectControl: FC<ObjectProps> = ({ name, argType, value, onChange
return (
<Wrapper>
<Form.Textarea
name={name}
valid={valid ? undefined : 'error'}
value={text}
onChange={handleChange}
size="flex"
placeholder="Adjust object dynamically"
{...{ name, onBlur, onFocus }}
/>
</Wrapper>
);
Expand Down
8 changes: 3 additions & 5 deletions lib/components/src/controls/Range.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ export const RangeControl: FC<RangeProps> = ({
min = 0,
max = 100,
step = 1,
onBlur,
onFocus,
}) => {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange(name, parse(event.target.value));
Expand All @@ -166,13 +168,9 @@ export const RangeControl: FC<RangeProps> = ({
<RangeWrapper>
<RangeLabel>{min}</RangeLabel>
<RangeInput
value={value}
type="range"
name={name}
min={min}
max={max}
step={step}
onChange={handleChange}
{...{ name, value, min, max, step, onFocus, onBlur }}
/>
<RangeLabel>{`${value} / ${max}`}</RangeLabel>
</RangeWrapper>
Expand Down
5 changes: 2 additions & 3 deletions lib/components/src/controls/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ const Wrapper = styled.label({
display: 'flex',
});

export const TextControl: FC<TextProps> = ({ name, value, onChange }) => {
export const TextControl: FC<TextProps> = ({ name, value, onChange, onFocus, onBlur }) => {
const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
onChange(name, event.target.value);
};
return (
<Wrapper>
<Form.Textarea
id={name}
name={name}
value={value}
onChange={handleChange}
size="flex"
placeholder="Adjust string dynamically"
{...{ name, value, onFocus, onBlur }}
/>
</Wrapper>
);
Expand Down
2 changes: 2 additions & 0 deletions lib/components/src/controls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface ControlProps<T> {
defaultValue?: T;
argType?: ArgType;
onChange: (name: string, value: T) => T | void;
onFocus?: (evt: any) => void;
onBlur?: (evt: any) => void;
}

export type ArrayValue = string[] | readonly string[];
Expand Down

0 comments on commit d706f55

Please sign in to comment.