Skip to content

Commit

Permalink
[Carousel] Props change and revert (#15)
Browse files Browse the repository at this point in the history
* Carousel prop changes

* Commit reverts
  • Loading branch information
akwong2 authored Aug 3, 2022
1 parent da83513 commit d4ad5bf
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 37 deletions.
8 changes: 0 additions & 8 deletions environments/react-18.2.js/src/App.test.js

This file was deleted.

9 changes: 4 additions & 5 deletions src/api/recommendations.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import axios from 'axios';

// CONFIGS
import { BreinifyGlobalConfigs } from '../setup';
import { RECOMMENDATION_URL } from '../configs';

// UTILS
import { isSetupComplete } from '../utils';
// SETUP
import { BreinifyGlobalConfigs, isSetupComplete } from '../setup';

// TYPES
import { RecommendationQuery } from '../types';
Expand All @@ -31,8 +30,8 @@ export function getRecommendations({
recommendations,
};
return axios.post(RECOMMENDATION_URL, data).then((response) => {
const { data, status } = response;
if (status === 200) return data;
const { data } = response;
if (data?.statusCode === 200) return data;
throw data;
});
}
23 changes: 17 additions & 6 deletions src/components/Carousel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import './carousel-styles.scss';

const DefaultCarouselComponent = ({ onButton, ...props }: any) => {
const DefaultCarouselComponent = ({ onButtonClick, ...props }: any) => {
const { title, image, description } = props;

function onClick() {
if (onButtonClick instanceof Function) onButtonClick(props);
}

return (
<div className='breinify-recommendation'>
<div className='br-rec-content'>
Expand All @@ -27,7 +32,7 @@ const DefaultCarouselComponent = ({ onButton, ...props }: any) => {
<span>{description}</span>
</div>
)}
<div className='br-rec-footer' onClick={() => onButton(props)}>
<div className='br-rec-footer' onClick={onClick}>
<div className='br-rec-button'>View Product</div>
</div>
</div>
Expand Down Expand Up @@ -67,7 +72,8 @@ export interface CarouselProps extends Settings {
recommendationQuery: RecommendationQuery;
containerClassName?: string;
containerStyles?: React.CSSProperties;
onButton?(props: any): void;
onError?(error: any): void;
onButtonClick?(props: any): void;
}

export default function Carousel({
Expand All @@ -77,15 +83,20 @@ export default function Carousel({
recommendationQuery,
containerClassName = 'breinify-carousel',
containerStyles,
onButton = () => {},
onButtonClick,
onError,
...sliderProps
}: CarouselProps) {
const { data, getRecs, isFailure, isLoading, isSuccess } = useRecommendations(null);
const { data, getRecs, isFailure, isLoading, isSuccess, error } = useRecommendations(null);

React.useEffect(() => {
getRecs(recommendationQuery, (response) => response.result);
}, []);

React.useEffect(() => {
if (isFailure && onError instanceof Function) onError(error);
}, [isFailure]);

if (isFailure) {
return null;
}
Expand All @@ -97,7 +108,7 @@ export default function Carousel({
<Slider {...sliderProps}>
{Array.isArray(data) &&
data.map((each, idx) => (
<Component {...getComponentProps(each)} onButton={onButton} key={idx} />
<Component {...getComponentProps(each)} onButtonClick={onButtonClick} key={idx} />
))}
</Slider>
)}
Expand Down
174 changes: 174 additions & 0 deletions src/hooks/helpers/useLoader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { renderHook, act } from '@testing-library/react';
import { useLoader } from './useLoader';
import { STATUS } from '../../types';

describe('useLoader', () => {
describe('initialization', () => {
test('loadingStatus should be INIT', () => {
const defaultStatus = STATUS.INIT;
const { result } = renderHook(() => useLoader());

expect(result.current.loadingStatus.status).toEqual(defaultStatus);
});

test('data should be null', () => {
const defaultDataState = null;
const { result } = renderHook(() => useLoader());

expect(result.current.data).toEqual(defaultDataState);
});

test('passing defaultStatus should change the default loadingStatus', () => {
const defaultStatus = STATUS.REQUESTING;
const { result } = renderHook(() => useLoader(defaultStatus));

expect(result.current.loadingStatus.status).toEqual(defaultStatus);
});

test('passing defaultDataState should change the default data', () => {
const defaultDataState = {};
const { result } = renderHook(() => useLoader(undefined, defaultDataState));

expect(result.current.data).toEqual(defaultDataState);
});
});

describe('setLoading', () => {
test('should set loadingStatus to REQUESTING', () => {
const { result } = renderHook(() => useLoader());
act(() => {
result.current.setLoading();
});

expect(result.current.loadingStatus.status).toEqual(STATUS.REQUESTING);
});

test('after success, setLoading with nothing passed should preserve data', () => {
const { result } = renderHook(() => useLoader());
const response = 'response';
act(() => {
result.current.setSuccess(response);
});
act(() => {
result.current.setLoading();
});

expect(result.current.data).toEqual(response);
});

test('isRefetch: true should change loadingStatus to REFETCH', () => {
const { result } = renderHook(() => useLoader());
act(() => {
result.current.setLoading(true);
});

expect(result.current.loadingStatus.status).toEqual(STATUS.REFETCH);
});

test('after success, setLoading with shouldClearData: true should clear data', () => {
const { result } = renderHook(() => useLoader());
const response = 'response';
act(() => {
result.current.setSuccess(response);
});
act(() => {
result.current.setLoading(false, true);
});

expect(result.current.data).toEqual(null);
});
});

describe('setSuccess', () => {
test('should set loadingStatus to SUCCESS', () => {
const { result } = renderHook(() => useLoader());
act(() => {
result.current.setSuccess();
});

expect(result.current.loadingStatus.status).toEqual(STATUS.SUCCESS);
});

test('passing the response should set data with the response', () => {
const { result } = renderHook(() => useLoader());
const response = 'response';
act(() => {
result.current.setSuccess(response);
});

expect(result.current.data).toEqual(response);
});

test('passing no response should set data to null', () => {
const { result } = renderHook(() => useLoader());
act(() => {
result.current.setSuccess();
});

expect(result.current.loadingStatus.status).toEqual(STATUS.SUCCESS);
expect(result.current.data).toEqual(null);
});
});

describe('onError', () => {
test('should set loadingStatus to FAILURE', () => {
const { result } = renderHook(() => useLoader());
act(() => {
result.current.setError();
});

expect(result.current.loadingStatus.status).toEqual(STATUS.FAILURE);
});

test('after success, passing error should set error', () => {
const { result } = renderHook(() => useLoader());
const error = 'error';
act(() => {
result.current.setError(error);
});

expect(result.current.loadingStatus.reason).toEqual(error);
});

test('after success, error should preserve data', () => {
const { result } = renderHook(() => useLoader());
const response = 'response';
const error = 'error';
act(() => {
result.current.setSuccess(response);
});
act(() => {
result.current.setError(error);
});

expect(result.current.data).toEqual(response);
});

test("after success, passing no error should set error to ''", () => {
const { result } = renderHook(() => useLoader());
const response = 'response';
act(() => {
result.current.setSuccess(response);
});
act(() => {
result.current.setError();
});

expect(result.current.loadingStatus.reason).toEqual('');
});

test('after success, shouldClearData: true should clear data', () => {
const { result } = renderHook(() => useLoader());
const response = 'hello';
const error = 'error';
act(() => {
result.current.setSuccess(response);
});
act(() => {
result.current.setError(error, true);
});

expect(result.current.data).toEqual(null);
});
});
});
10 changes: 5 additions & 5 deletions src/hooks/helpers/useLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@ import { useState, useCallback } from 'react';
// TYPES
import { LoadingState, STATUS } from '../../types';

export const useLoader = (defaultStatus: STATUS = STATUS.REQUESTING, defaultDataState: any = null) => {
export const useLoader = (defaultStatus: STATUS = STATUS.INIT, defaultDataState: any = null) => {
const [loadingStatus, setLoadingStatus] = useState<LoadingState>({ status: defaultStatus, reason: '' });
const [data, setData] = useState(defaultDataState);

const setLoading = useCallback(
(isRefetch = false, shouldClearData = false) => {
setLoadingStatus({ status: isRefetch ? STATUS.REFETCH : STATUS.REQUESTING, reason: '' });
if (shouldClearData) setData(null);
if (shouldClearData) setData(defaultDataState);
},
[setLoadingStatus]
);
const setSuccess = useCallback(
(data: any) => {
(data: any = defaultDataState) => {
setData(data);
setLoadingStatus({ status: STATUS.SUCCESS, reason: '' });
},
[setLoadingStatus, setData]
);
const setError = useCallback(
(reason: any, shouldClearData = false) => {
(reason: any = '', shouldClearData = false) => {
setLoadingStatus({ status: STATUS.FAILURE, reason });
if (shouldClearData) setData(null);
if (shouldClearData) setData(defaultDataState);
},
[setLoadingStatus, setData]
);
Expand Down
90 changes: 90 additions & 0 deletions src/hooks/recommendations/useRecommendations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// testing utilities
import { renderHook, act } from '@testing-library/react';

// mocks
import { mockAxios } from '../../mocks/axios';

// constants
import { RECOMMENDATION_URL } from '../../configs';

import { useRecommendations } from './useRecommendations';

const TIMEOUT = 100;

describe('useRecommendations', () => {
const API_KEY = 'API_KEY';
const SECRET = 'SECRET';

beforeEach(() => {
const Setup = require('../../setup');
Setup.BreinifySetup({ apiKey: API_KEY, secret: SECRET });
});

test('init state when calling useRecommendations', () => {
const defaultData = {};
const { result } = renderHook(({ defaultDataState }) => useRecommendations(defaultDataState), {
initialProps: { defaultDataState: defaultData },
});

expect(result.current.data).toEqual(defaultData);
expect(result.current.isInit).toEqual(true);
expect(result.current.isLoading).toEqual(false);
expect(result.current.isSuccess).toEqual(false);
expect(result.current.isFailure).toEqual(false);
expect(result.current.error).toEqual('');
});

test('getRecs should succeed if statusCode: 200 is returned', async () => {
const mock = mockAxios();
const { result } = renderHook(() => useRecommendations());
const response = {
response: {},
statusCode: 200,
};
mock.onPost(RECOMMENDATION_URL).reply(200, response);

await act(async () => {
result.current.getRecs({ recommendation: {} });
});

expect(result.current.data).toEqual(response);
expect(result.current.isInit).toEqual(false);
expect(result.current.isLoading).toEqual(false);
expect(result.current.isSuccess).toEqual(true);
expect(result.current.isFailure).toEqual(false);
expect(result.current.error).toEqual('');
});

const failedCodes = [100, 300, 400, 500, 600, 700, 800, 900];
test.each(failedCodes)('getRecs should fail if statusCode === %s', async (statusCode) => {
const { result } = renderHook(() => useRecommendations());
const mock = mockAxios({ delayResponse: TIMEOUT });
const response = {
response: {
errorResponse: 'hi',
errorCode: 123,
},
statusCode,
};
mock.onPost(RECOMMENDATION_URL).reply(200, response);
jest.useFakeTimers();

await act(async () => {
result.current.getRecs({ recommendation: {} });
});

// test loading states
expect(result.current.isLoading).toEqual(true);

await act(() => {
jest.runAllTimers(); // trigger setTimeout
});

expect(result.current.data).toEqual(null);
expect(result.current.isInit).toEqual(false);
expect(result.current.isLoading).toEqual(false);
expect(result.current.isSuccess).toEqual(false);
expect(result.current.isFailure).toEqual(true);
expect(result.current.error).toEqual(response);
});
});
Loading

0 comments on commit d4ad5bf

Please sign in to comment.