Skip to content

Commit

Permalink
fix(input): animation race condition
Browse files Browse the repository at this point in the history
  • Loading branch information
gokselpirnal committed Mar 25, 2024
1 parent 330dff7 commit 23c832a
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 84 deletions.
16 changes: 8 additions & 8 deletions src/components/Input/Input.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { act } from 'react-test-renderer';
import { fireEvent, render } from '../../test-utils';
import { fireEvent, render, waitFor } from '../../test-utils';
import theme from '../../theme';
import Input, { TextInputHandles } from './Input';

Expand Down Expand Up @@ -115,7 +115,7 @@ describe('Input', () => {
expect(toJSON()).toMatchSnapshot();
});

test('should focus input correctly', () => {
test('should focus input correctly', async () => {
// when
const { getByTestId } = render(<Input label="label" />);

Expand All @@ -125,14 +125,14 @@ describe('Input', () => {
);

// when
act(() => {
fireEvent(getByTestId('input'), 'onFocus');
});
fireEvent(getByTestId('input'), 'onFocus');

// then
expect(getByTestId('input-box').props.style[0].borderColor).toBe(
theme.colors.primaryKey,
);
await waitFor(() => {
expect(getByTestId('input-box').props.style[0].borderColor).toBe(
theme.colors.primaryKey,
);
});
});

test('should not focus and blur input when input is disabled', () => {
Expand Down
12 changes: 8 additions & 4 deletions src/components/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,13 @@ const Input = forwardRef<TextInputHandles, InputProps>(
}
}, [error, success, icon]);

const handleFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
const handleFocus = async (
e: NativeSyntheticEvent<TextInputFocusEventData>,
) => {
if (disabled || !editable) {
return;
}
startAnimation();
await startAnimation();

setFocused(true);
setVariantIconName(null);
Expand All @@ -189,13 +191,15 @@ const Input = forwardRef<TextInputHandles, InputProps>(
rest.onFocus?.(e);
};

const handleBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
const handleBlur = async (
e: NativeSyntheticEvent<TextInputFocusEventData>,
) => {
if (disabled || !editable) {
return;
}

if (!value) {
stopAnimation();
await stopAnimation();
}

setFocused(false);
Expand Down
64 changes: 34 additions & 30 deletions src/components/Input/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,39 +117,43 @@ export const useOutlineLabelVisibility = ({
).current;

/* istanbul ignore next */
const startAnimation = () => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionFillValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize - 2,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue - 2,
...commonAnimatedProps,
}),
]).start();
const startAnimation = async () => {
return new Promise(res => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionFillValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize - 2,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue - 2,
...commonAnimatedProps,
}),
]).start(res);
});
};

/* istanbul ignore next */
const stopAnimation = () => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionEmptyValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue,
...commonAnimatedProps,
}),
]).start();
const stopAnimation = async () => {
return new Promise(res => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionEmptyValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue,
...commonAnimatedProps,
}),
]).start(res);
});
};

let viewHeight = inputHeight + 6;
Expand Down
16 changes: 8 additions & 8 deletions src/components/TextArea/TextArea.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { act } from 'react-test-renderer';
import { fireEvent, render } from '../../test-utils';
import { fireEvent, render, waitFor } from '../../test-utils';
import theme from '../../theme';
import TextArea, { TextInputHandles } from './TextArea';

Expand Down Expand Up @@ -81,7 +81,7 @@ describe('TextArea', () => {
expect(toJSON()).toMatchSnapshot();
});

test('should focus TextArea correctly', () => {
test('should focus TextArea correctly', async () => {
// when
const { getByTestId } = render(<TextArea label="label" />);

Expand All @@ -91,14 +91,14 @@ describe('TextArea', () => {
);

// when
act(() => {
fireEvent(getByTestId('textArea'), 'onFocus');
});
fireEvent(getByTestId('textArea'), 'onFocus');

// then
expect(getByTestId('textArea-box').props.style[0].borderColor).toBe(
theme.colors.primaryKey,
);
await waitFor(() => {
expect(getByTestId('textArea-box').props.style[0].borderColor).toBe(
theme.colors.primaryKey,
);
});
});

test('should not focus and blur TextArea when TextArea is disabled', () => {
Expand Down
12 changes: 8 additions & 4 deletions src/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,29 @@ const TextArea = forwardRef<TextInputHandles, TextAreaProps>(
setErrorState(error);
}, [error]);

const handleFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
const handleFocus = async (
e: NativeSyntheticEvent<TextInputFocusEventData>,
) => {
if (disabled || !editable) {
return;
}
startAnimation();
await startAnimation();

setFocused(true);
setErrorState(false);

rest.onFocus?.(e);
};

const handleBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
const handleBlur = async (
e: NativeSyntheticEvent<TextInputFocusEventData>,
) => {
if (disabled || !editable) {
return;
}

if (!value) {
stopAnimation();
await stopAnimation();
}

setFocused(false);
Expand Down
64 changes: 34 additions & 30 deletions src/components/TextArea/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,39 +90,43 @@ export const useOutlineLabelVisibility = ({
).current;

/* istanbul ignore next */
const startAnimation = () => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionFillValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize - 2,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue - 2,
...commonAnimatedProps,
}),
]).start();
const startAnimation = async () => {
return new Promise(res => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionFillValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize - 2,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue - 2,
...commonAnimatedProps,
}),
]).start(res);
});
};

/* istanbul ignore next */
const stopAnimation = () => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionEmptyValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue,
...commonAnimatedProps,
}),
]).start();
const stopAnimation = async () => {
return new Promise(res => {
Animated.parallel([
Animated.timing(labelPositionRef, {
toValue: labelPositionEmptyValue,
...commonAnimatedProps,
}),
Animated.timing(fontSizeRef, {
toValue: labelFontSize,
...commonAnimatedProps,
}),
Animated.timing(lineHeightRef, {
toValue: labelLineHeightValue,
...commonAnimatedProps,
}),
]).start(res);
});
};

let viewHeight = textAreaHeight + 6;
Expand Down

0 comments on commit 23c832a

Please sign in to comment.