Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript strictFunctionTypes: true #167

Merged
merged 8 commits into from
Dec 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/components/CheckboxGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ export default class RadioGroup extends React.PureComponent<
render() {
const { children, selected, className, name, disabled } = this.props;

const _children = React.Children.map(
children,
(checkbox: React.ReactElement<CheckboxProps>) =>
React.cloneElement(checkbox, {
onChange: this.handleChange,
checked: selected.indexOf(checkbox.props.value) >= 0,
disabled
})
);
const _children = React.Children.map(children, _checkbox => {
// `_checkbox as React.ReactElement<CheckboxProps>` is a hack
// Because React does not allow us to specify what sort of elements
// you can allow as children and leaves it on you to figure out
// all various types of children provided.
const checkbox = _checkbox as React.ReactElement<CheckboxProps>;
return React.cloneElement(checkbox, {
onChange: this.handleChange,
checked: selected.indexOf(checkbox.props.value) >= 0,
disabled
});
});

return (
<div role="checkboxgroup" aria-label={name} className={className}>
Expand Down
5 changes: 4 additions & 1 deletion src/components/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ class DateInput extends React.PureComponent<DateInputProps> {
className={dateClass}
selected={value ? new Date(value) : undefined}
{...calendarProps}
onChange={(date: Date) => {
onChange={_date => {
// _date as Date is a hack.
// TODO:Aziz this is fixable, fix this.
const date = _date as Date;
this.onChange(date);
toggle();
}}
Expand Down
18 changes: 9 additions & 9 deletions src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import isBrowser from "is-in-browser";
import * as ReactDOM from "react-dom";

class Modal extends React.PureComponent<ModalProps> {
node: HTMLDivElement;
node = isBrowser ? document.createElement("div") : null;

componentDidMount() {
this.node = document.createElement("div");
document.body.appendChild(this.node);
if (this.node) {
document.body.appendChild(this.node);
}
}

componentWillUnmount() {
document.body.removeChild(this.node);
if (this.node) {
document.body.removeChild(this.node);
}
}

componentDidUpdate(prevProps: ModalProps) {
Expand All @@ -31,17 +34,14 @@ class Modal extends React.PureComponent<ModalProps> {
if (!isBrowser) return null;

const { children, visible, className } = this.props;

if (!this.node) {
this.node = document.createElement("div");
}
const node = this.node;

return visible
? ReactDOM.createPortal(
<div className={cx(modalContainer, "ReactPortal", className)}>
{children}
</div>,
this.node
node as NonNullable<typeof node>
)
: null;
}
Expand Down
21 changes: 12 additions & 9 deletions src/components/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ export default class RadioGroup extends React.PureComponent<RadioGroupProps> {
render() {
const { children, selected, className, name, disabled } = this.props;

const _children = React.Children.map(
children,
(radio: React.ReactElement<RadioProps>) =>
React.cloneElement(radio, {
onChange: this.handleChange,
checked: selected === radio.props.value,
disabled
})
);
const _children = React.Children.map(children, _radio => {
// `_radio as React.ReactElement<RadioProps>` is a hack
// Because React does not allow us to specify what sort of elements
// you can allow as children and leaves it on you to figure out
// all various types of children provided.
const radio = _radio as React.ReactElement<RadioProps>;
return React.cloneElement(radio, {
onChange: this.handleChange,
checked: selected === radio.props.value,
disabled
});
});

return (
<div role="radiogroup" aria-label={name} className={className}>
Expand Down
14 changes: 10 additions & 4 deletions src/components/TimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ const TimePicker: React.FunctionComponent<TimePickerProps> = props => {
>
{({ toggle }) => (
<OptionGroupRadio
onChange={(value: number) => {
onHourChange(value);
onChange={value => {
// `value as number` is a escape hack because we have to handle number | string
// and OptionGroupRadio cannot determine its type on its own.
// TODO: Remove all `as`.
onHourChange(value as number);
toggle();
}}
selected={selectedHour}
Expand All @@ -62,8 +65,11 @@ const TimePicker: React.FunctionComponent<TimePickerProps> = props => {
>
{({ toggle }) => (
<OptionGroupRadio
onChange={(value: number) => {
onMinuteChange(value);
onChange={value => {
// `value as number` is a escape hack because we have to handle number | string
// and OptionGroupRadio cannot determine its type on its own.
// TODO: Remove all `as`.
onMinuteChange(value as number);
toggle();
}}
selected={selectedMinute}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Toast extends React.PureComponent<ToastProps, ToastState> {
emitter.emit("showToast", { text, type });
}

showTimer: number | null;
showTimer?: number | null;

static hide() {
emitter.emit("hideToast");
Expand Down
42 changes: 23 additions & 19 deletions src/components/shared/OptionGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class OptionGroup extends React.PureComponent<
> {
optionRef: React.RefObject<HTMLDivElement> = React.createRef();
optionsRefsSet = new Map<number, React.RefObject<React.ReactInstance>>();
observer: IntersectionObserver;
observer?: IntersectionObserver;

state = {
selected: -1,
Expand Down Expand Up @@ -105,7 +105,9 @@ class OptionGroup extends React.PureComponent<
}

componentWillUnmount() {
this.observer.disconnect();
if (this.observer) {
this.observer.disconnect();
}
}

render() {
Expand All @@ -120,24 +122,26 @@ class OptionGroup extends React.PureComponent<
} = this.props;
const { isScrolled, selected } = this.state;

const _children = React.Children.map(
children,
(option: React.ReactElement<OptionProps>, i) => {
let ref = this.optionsRefsSet.get(i);
if (!ref) {
ref = React.createRef<HTMLDivElement>();
this.optionsRefsSet.set(i, ref);
}
return React.cloneElement(option, {
onChange: handleChange,
isActive: selected === i,
isSelected: isSelected(option.props.value),
multiSelect,
// @ts-ignore
ref
});
const _children = React.Children.map(children, (_option, i) => {
// `_option as React.ReactElement<OptionProps>` is a hack
// Because React does not allow us to specify what sort of elements
// you can allow as children and leaves it on you to figure out
// all various types of children provided.
const option = _option as React.ReactElement<OptionProps>;
let ref = this.optionsRefsSet.get(i);
if (!ref) {
ref = React.createRef<HTMLDivElement>();
this.optionsRefsSet.set(i, ref);
}
);
return React.cloneElement(option, {
onChange: handleChange,
isActive: selected === i,
isSelected: isSelected(option.props.value),
multiSelect,
// @ts-ignore
ref
});
});

const searchBoxClassName = cx(searchBoxWrapper, {
[searchBoxScrolledStyle]: isScrolled
Expand Down
8 changes: 4 additions & 4 deletions stories/colors.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ const section = css({
storiesOf("Theme/colors", module).add("List", () => (
<div className={wrapper}>
<h2 style={{ marginBottom: 40 }}>Colors</h2>
{Object.keys(colors).map((x: keyof Colors) => {
const group = colors[x];
{Object.keys(colors).map(x => {
const group = colors[x as keyof Colors];
return (
<div key={x}>
<h4 style={{ marginBottom: 10 }}>{x}</h4>
<div style={{ overflow: "auto" }}>
{Object.keys(colors[x]).map((y: keyof typeof group) => {
const color = group[y];
{Object.keys(colors[x as keyof Colors]).map(y => {
const color = group[y as keyof typeof group];
return (
<div className={section} key={y}>
<div
Expand Down
67 changes: 33 additions & 34 deletions stories/optionGroupSection.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ interface GroupedOptionGroupProps<D extends DataPoint, G extends Group<D>> {
}

interface GroupedOptionGroupState<D extends DataPoint, G extends Group<D>> {
selectedGroups: G[];
selectedDataPoints: D[];
selectedDataPoints: Set<D>;
isSelected: (value: number | string) => boolean;
groupMapping: Map<string, G>;
dataPointMapping: Map<number, D>;
Expand Down Expand Up @@ -59,8 +58,7 @@ class GroupedOptionGroup<
GroupedOptionGroupState<D, G>
> {
state: Readonly<GroupedOptionGroupState<D, G>> = {
selectedGroups: [],
selectedDataPoints: [],
selectedDataPoints: new Set<D>(),
isSelected: () => false,
groupMapping: new Map(),
dataPointMapping: new Map(),
Expand All @@ -87,7 +85,7 @@ class GroupedOptionGroup<
}
const dataPointMapping = new Map<number, D>(
props.options
.reduce((arr, cur) => [...arr, ...cur.options], [])
.reduce((arr, cur) => [...arr, ...cur.options], [] as D[])
.map(dataPointPair)
);

Expand All @@ -104,30 +102,30 @@ class GroupedOptionGroup<
);
}
if (props.selected !== state.propSelected) {
const selectedDataPoints = props.selected
.map(id => newState.dataPointMapping.get(id) as D)
.filter(f => f);
const selectedDataPoints = new Set<D>(
props.selected
.map(id => newState.dataPointMapping.get(id) as D)
.filter(f => f)
);

const selectedGroups = props.options.filter(group => {
return (
group.group_label &&
!group.options.some(
dataPoint => !selectedDataPoints.includes(dataPoint)
)
);
});
const selectedGroups = new Set<G>(
props.options.filter(group => {
return (
group.group_label &&
!group.options.some(dataPoint => !selectedDataPoints.has(dataPoint))
);
})
);

newState.isSelected = (value: number | string) => {
if (typeof value === "string") {
const group = newState.groupMapping.get(value) as G;
return selectedGroups.includes(group);
return selectedGroups.has(group);
}
const dataPoint = newState.dataPointMapping.get(value) as D;
return selectedDataPoints.includes(dataPoint);
return selectedDataPoints.has(dataPoint);
};

newState.selectedDataPoints = selectedDataPoints;
newState.selectedGroups = selectedGroups;
}
return newState;
}
Expand All @@ -140,33 +138,34 @@ class GroupedOptionGroup<
checked: boolean;
}) => {
const { selectedDataPoints } = this.state;
const _selectedDataPoints = new Set(selectedDataPoints);
if (typeof value === "string") {
const group = this.state.groupMapping.get(value) as G;
if (checked) {
return this.triggerOnChange(
selectedDataPoints.concat(
group.options.filter(d => !selectedDataPoints.includes(d))
)
);
group.options.forEach(d => {
_selectedDataPoints.add(d);
});
return this.triggerOnChange(_selectedDataPoints);
} else {
return this.triggerOnChange(
selectedDataPoints.filter(d => !group.options.includes(d))
);
group.options.forEach(d => {
_selectedDataPoints.delete(d);
});
return this.triggerOnChange(_selectedDataPoints);
}
} else {
const dataPoint = this.state.dataPointMapping.get(value) as D;
if (checked) {
return this.triggerOnChange(selectedDataPoints.concat([dataPoint]));
_selectedDataPoints.add(dataPoint);
return this.triggerOnChange(_selectedDataPoints);
} else {
return this.triggerOnChange(
selectedDataPoints.filter(d => d !== dataPoint)
);
_selectedDataPoints.delete(dataPoint);
return this.triggerOnChange(_selectedDataPoints);
}
}
};

triggerOnChange(selectedDataPoints: D[]) {
this.props.onChange(selectedDataPoints.map(d => d.id));
triggerOnChange(selectedDataPoints: Set<D>) {
this.props.onChange(Array.from(selectedDataPoints).map(d => d.id));
}

onSearchBoxValueChange = (v: string) => {
Expand Down
4 changes: 2 additions & 2 deletions stories/select.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ storiesOf("Components/Select", module)
withState<State>({ searchQuery: "", multiSelected: undefined })(
({ store }) => (
<Select
onChange={(val: number[] | string[]) => {
onChange={val => {
action("onSelect")(val);
store.set({ multiSelected: val });
store.set({ multiSelected: val as number[] | string[] });
}}
placeholder="Choose Option"
multiSelect
Expand Down
10 changes: 5 additions & 5 deletions stories/slider.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ const wrapperStyle = css({
});

interface State {
value: [number, number];
values: number[];
}

storiesOf("Components/Slider", module).add(
"simple",
withState<State>({ value: [10, 20] })(({ store }) => (
withState<State>({ values: [10, 20] })(({ store }) => (
<Slider
onValuesUpdated={({ min, max }) => store.set({ value: [min, max] })}
onValuesUpdated={({ values }) => store.set({ values })}
min={number("min", 0)}
max={number("max", 100)}
values={store.state.value}
values={store.state.values}
disabled={boolean("disabled", false)}
large={boolean("large", false)}
className={wrapperStyle}
title={text("title", "Select Range")}
valueLabelExtractor={() =>
`${store.state.value[0]}-${store.state.value[1]}`
`${store.state.values[0]}-${store.state.values[1]}`
}
/>
))
Expand Down
Loading