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

Create categoryTile/categoryList talon #1755

Merged
merged 14 commits into from
Sep 26, 2019
39 changes: 39 additions & 0 deletions packages/peregrine/lib/talons/CategoryList/useCategoryList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useEffect } from 'react';
import { useQuery } from '../../hooks/useQuery';

/**
* Returns props necessary to render a CategoryList component.
*
* @param {object} props
* @param {object} props.query - category data
* @param {string} props.id - category id
* @return {{ data: object, error: boolean, loading: boolean }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is error a boolean or an Error object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look and see how we're using it in categoryList and update the output accordingly.

*/
export const useCategoryList = props => {
const { query, id } = props;
const [queryResult, queryApi] = useQuery(query);
const { data, error, loading } = queryResult;
const { runQuery, setLoading } = queryApi;

useEffect(() => {
const fetchCategories = async () => {
setLoading(true);

await runQuery({
variables: {
id
}
});

setLoading(false);
};

fetchCategories();
}, [runQuery, setLoading, id]);

return {
data,
error,
loading
};
};
49 changes: 49 additions & 0 deletions packages/peregrine/lib/talons/CategoryList/useCategoryTile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useMemo } from 'react';

// TODO: get categoryUrlSuffix from graphql storeOptions when it is ready
const categoryUrlSuffix = '.html';
const previewImageSize = 480;

/**
* Returns props necessary to render a CategoryTile component.
*
* @returns {Object} props necessary to render a category tile
* @returns {Object} .image - an object containing url, type and width for the category image
* @returns {Object} .item - an object containing name and url for the category tile
*/
export const useCategoryTile = props => {
const { item } = props;
const { image, productImagePreview } = item;

const imageObj = useMemo(() => {
const previewProduct = productImagePreview.items[0];
if (image) {
return {
url: image,
type: 'image-category',
width: previewImageSize
};
} else if (previewProduct) {
return {
url: previewProduct.small_image,
type: 'image-product',
width: previewImageSize
};
} else {
return null;
}
}, [image, productImagePreview]);

const itemObject = useMemo(
() => ({
name: item.name,
url: `/${item.url_key}${categoryUrlSuffix}`
}),
[item]
);

return {
image: imageObj,
item: itemObject
};
};
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
import React from 'react';
import waitForExpect from 'wait-for-expect';
import TestRenderer from 'react-test-renderer';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from 'react-apollo/test-utils';
import { createTestInstance } from '@magento/peregrine';

import LoadingIndicator from '../../LoadingIndicator';
import CategoryTile from '../categoryTile';
import CategoryList from '../categoryList';
import getCategoryList from '../../../queries/getCategoryList.graphql';
import { useCategoryList } from '@magento/peregrine/lib/talons/CategoryList/useCategoryList';
import { useCategoryTile } from '@magento/peregrine/lib/talons/CategoryList/useCategoryTile';

jest.mock('@magento/venia-drivers');
jest.mock('../../../classify');
jest.mock('@magento/peregrine/lib/talons/CategoryList/useCategoryTile', () => {
return {
useCategoryTile: jest.fn()
};
});

jest.mock('@magento/peregrine/lib/talons/CategoryList/useCategoryList', () => {
return {
useCategoryList: jest.fn()
};
});

const withRouterAndApolloClient = (mocks, renderFn) => (
<MemoryRouter initialIndex={0} initialEntries={['/']}>
<MockedProvider mocks={mocks} addTypename={false}>
{renderFn()}
</MockedProvider>
</MemoryRouter>
);
useCategoryTile.mockReturnValue({
image: {},
item: {}
});

useCategoryList.mockReturnValue({
data: {
category: {
children: []
}
},
loading: false,
error: false
});

test('renders a header', () => {
const title = 'foo';
const { root } = TestRenderer.create(
withRouterAndApolloClient([], () => (
<CategoryList id={2} title={title} />
))
);
const { root } = createTestInstance(<CategoryList id={2} title={title} />);

const list = root.findByProps({ className: 'root' });
const header = list.findByProps({ className: 'header' });
Expand All @@ -35,91 +48,83 @@ test('renders a header', () => {
});

test('omits the header if there is no title', () => {
const { root } = TestRenderer.create(
withRouterAndApolloClient([], () => <CategoryList id={2} />)
);
const { root } = createTestInstance(<CategoryList id={2} />);

expect(root.findAllByProps({ className: 'header' })).toHaveLength(0);
});

test('renders category tiles', async () => {
const mocks = [
{
request: {
query: getCategoryList,
variables: {
id: 2
}
},
result: {
data: {
category: {
id: 2,
children: [
{
id: 15,
name: 'foo',
url_key: 'foo-url.html',
url_path: '/foo-url.html',
children_count: 0,
path: '1/2/15',
image: 'media/foo.png',
productImagePreview: {
items: [
{
small_image: {
url: 'media/foo-product.jpg'
}
}
]
}
},
test('renders a loading indicator', () => {
useCategoryList.mockReturnValueOnce({
loading: true
});

const { root } = createTestInstance(<CategoryList id={2} title="foo" />);

expect(root.findAllByType(LoadingIndicator)).toBeTruthy();
});

test('renders category tiles', () => {
const data = {
category: {
id: 2,
children: [
{
id: 15,
name: 'foo',
url_key: 'foo-url.html',
url_path: '/foo-url.html',
children_count: 0,
path: '1/2/15',
image: 'media/foo.png',
productImagePreview: {
items: [
{
id: 16,
name: 'bar',
url_key: 'bar-url.html',
url_path: '/bar-url.html',
children_count: 0,
path: '1/2/16',
image: null,
productImagePreview: {
items: [
{
small_image: {
url: 'media/bar-product.jpg'
}
}
]
small_image: {
url: 'media/foo-product.jpg'
}
},
}
]
}
},
{
id: 16,
name: 'bar',
url_key: 'bar-url.html',
url_path: '/bar-url.html',
children_count: 0,
path: '1/2/16',
image: null,
productImagePreview: {
items: [
{
id: 17,
name: 'baz',
url_key: 'baz-url.html',
url_path: '/baz-url.html',
children_count: 0,
path: '1/2/17',
image: null,
productImagePreview: {
items: []
small_image: {
url: 'media/bar-product.jpg'
}
}
]
}
},
{
id: 17,
name: 'baz',
url_key: 'baz-url.html',
url_path: '/baz-url.html',
children_count: 0,
path: '1/2/17',
image: null,
productImagePreview: {
items: []
}
}
}
]
}
];
};

const { root } = TestRenderer.create(
withRouterAndApolloClient(mocks, () => (
<CategoryList id={2} title="foo" />
))
);
useCategoryList.mockReturnValueOnce({
data
});

expect(root.findByType(LoadingIndicator)).toBeTruthy();
const { root } = createTestInstance(<CategoryList id={2} title="foo" />);

await waitForExpect(() => {
expect(root.findAllByType(CategoryTile)).toHaveLength(3);
});
expect(root.findAllByType(CategoryTile)).toHaveLength(3);
});
Loading