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

Layered Navigation - Desktop Optimizations #3137

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3c50dab
Adding FilterSidebar component for desktop, refactor category and sea…
justinconabree Apr 19, 2021
2a8e9eb
Adding prop to FilterSidebar to allow easy changing of open filter co…
justinconabree Apr 19, 2021
e6cc96f
Fix current filters, cleanup search page, change sort button styles a…
justinconabree Apr 21, 2021
57579ea
Updating snapshots to pass, fix useCategoryContent to handle no data,…
justinconabree Apr 21, 2021
8f20e76
Fixing sidebar positioning (#3115)
justinconabree Apr 21, 2021
66d3ec2
Fixing lint and prettier issues
justinconabree Apr 21, 2021
2b85f41
Merge branch 'develop' into absolunet/desktop-layered-nav-optimizations
justinconabree May 11, 2021
d0b11fa
Fixing merge issue with chevron color, show more label and prettier
justinconabree May 11, 2021
699fb18
Refactor for new useFilterSidebar, changing to more appropriate prop …
justinconabree May 12, 2021
eb9de1d
Merge branch 'develop' into absolunet/desktop-layered-nav-optimizations
sirugh May 12, 2021
b47a3fa
fix prettier and lint errors
sirugh May 12, 2021
c6e96be
Fix jest test snapshots for searchpage and useFilterSidebar
justinconabree May 12, 2021
d1047d5
Merge branch 'absolunet/desktop-layered-nav-optimizations' of github.…
justinconabree May 12, 2021
e077965
Merge branch 'develop' into absolunet/desktop-layered-nav-optimizations
justinconabree May 12, 2021
294a70c
Fixing merge, adding change to FilterSidebar
justinconabree May 12, 2021
2285567
Adding tests for useFilterSidebar, useFilterBlock and useFilterList t…
justinconabree May 17, 2021
a7bc565
Fixing useFilterSidebar test, running prettier
justinconabree May 17, 2021
d48d5a7
Adding test for filterModel and filterSidebar
justinconabree May 17, 2021
f48b9e7
Merge branch 'develop' of github.com:magento/pwa-studio into absolune…
anthoula May 17, 2021
58482cc
Adding test coverage for filterItem
justinconabree May 17, 2021
7c27800
Merge branch 'absolunet/desktop-layered-nav-optimizations' of github.…
justinconabree May 17, 2021
4f49967
Merge branch 'develop' of github.com:magento/pwa-studio into absolune…
anthoula May 18, 2021
ae9e52d
Merge branch 'develop' into absolunet/desktop-layered-nav-optimizations
dpatil-magento May 19, 2021
637be1c
Merge branch 'develop' of github.com:magento/pwa-studio into absolune…
anthoula May 26, 2021
62b4c01
Merge branch 'develop' of github.com:magento/pwa-studio into absolune…
anthoula May 26, 2021
938c0eb
Merge branch 'develop' of github.com:magento/pwa-studio into absolune…
anthoula May 26, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"cartTrigger.ariaLabel": "Basculer le mini-panier. Vous avez {count} articles dans votre panier.",
"categoryContent.filter": "Filtre",
"categoryContent.itemsSortedBy": "Articles triés par ",
"categoryContent.resultCount": "{count} Résultats",
"categoryLeaf.allLabel": "Tout(e) {name}",
"categoryList.noResults": "Aucune catégorie enfant trouvée.",
"checkoutPage.accountSuccessfullyCreated": "Compte créé avec succès.",
Expand Down Expand Up @@ -130,6 +131,8 @@
"Email Signup": "Inscription par courriel",
"field.optional": "Optionnelle",
"filterFooter.results": "Voir les résultats",
"filterList.showMore": "Voir plus",
"filterList.showLess": "Voir moins",
"filterModal.action": "Tout effacer",
"filterModal.headerTitle": "Filtres",
"filterSearch.name": "Entrez un {name}",
Expand Down Expand Up @@ -283,6 +286,7 @@
"productListing.loading": "Récupération du panier ...",
"productOptions.selectedLabel": "Choisie {label}:",
"productQuantity.label": "quantité de produit",
"productSort.sortByButton": "Trier par",
"productSort.sortButton": "Trier",
"quantity.buttonDecrement": "Diminuer la quantité",
"quantity.buttonIncrement": "Augmenter la quantité",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useEffect } from 'react';
import { act } from 'react-test-renderer';

import { createTestInstance } from '@magento/peregrine';
import { useFilterBlock } from '../useFilterBlock';

const log = jest.fn();

let handleClickProp = null;
let inputValues = {};

const Component = () => {
const talonProps = useFilterBlock(inputValues);

useEffect(() => {
log(talonProps);
handleClickProp = talonProps.handleClick;
}, [talonProps]);

return null;
};

const givenDefaultValues = () => {
inputValues = {
filterState: new Set(),
items: [],
initialOpen: false
};
};

const givenInitiallyOpen = () => {
inputValues = {
filterState: new Set(),
items: [],
initialOpen: true
};
};

const givenSelectedItems = () => {
const item = {
attribute_code: 'foo'
};

inputValues = {
filterState: new Set().add(item),
items: [item],
initialOpen: false
};
};

describe('#useFilterBlock', () => {
beforeEach(() => {
log.mockClear();
handleClickProp = null;
givenDefaultValues();
});

it('is closed by default', () => {
createTestInstance(<Component />);

expect(log).toHaveBeenCalledWith({
handleClick: expect.any(Function),
isExpanded: false
});
});

it('is open if passed initially open', () => {
givenInitiallyOpen();
createTestInstance(<Component />);

expect(log).toHaveBeenCalledWith({
handleClick: expect.any(Function),
isExpanded: true
});
});

it('is open if items are selected', () => {
givenSelectedItems();
createTestInstance(<Component />);

expect(log).toHaveBeenCalledWith({
handleClick: expect.any(Function),
isExpanded: true
});
});

it('can toggle visibility', () => {
createTestInstance(<Component />);

expect(typeof handleClickProp).toBe('function');

act(() => {
handleClickProp();
});

expect(log).toHaveBeenLastCalledWith({
handleClick: expect.any(Function),
isExpanded: true
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useEffect } from 'react';
import { act } from 'react-test-renderer';

import { createTestInstance } from '@magento/peregrine';
import { useFilterList } from '../useFilterList';

const log = jest.fn();

let handleClickProp = null;

const Component = () => {
const talonProps = useFilterList();

useEffect(() => {
log(talonProps);
handleClickProp = talonProps.handleListToggle;
}, [talonProps]);

return null;
};

describe('#useFilterList', () => {
beforeEach(() => {
log.mockClear();
handleClickProp = null;
});

it('is initially closed', () => {
createTestInstance(<Component />);

expect(log).toHaveBeenCalledWith({
handleListToggle: expect.any(Function),
isListExpanded: false
});
});

it('can toggle visibility', () => {
createTestInstance(<Component />);

expect(typeof handleClickProp).toBe('function');

act(() => {
handleClickProp();
});

expect(log).toHaveBeenLastCalledWith({
handleListToggle: expect.any(Function),
isListExpanded: true
});
});
});
1 change: 1 addition & 0 deletions packages/peregrine/lib/talons/FilterModal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { useFilterBlock } from './useFilterBlock';
export { useFilterFooter } from './useFilterFooter';
export { useFilterModal } from './useFilterModal';
export { useFilterState } from './useFilterState';
export { useFilterList } from './useFilterList';
18 changes: 15 additions & 3 deletions packages/peregrine/lib/talons/FilterModal/useFilterBlock.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { useCallback, useState } from 'react';
import { useCallback, useState, useEffect, useMemo } from 'react';

export const useFilterBlock = () => {
const [isExpanded, setExpanded] = useState(false);
export const useFilterBlock = props => {
const { filterState, items, initialOpen } = props;

const hasSelected = useMemo(() => {
return items.some(item => {
return filterState && filterState.has(item);
});
}, [filterState, items]);

const [isExpanded, setExpanded] = useState(hasSelected || initialOpen);

useEffect(() => {
setExpanded(hasSelected || initialOpen);
}, [hasSelected, initialOpen]);

const handleClick = useCallback(() => {
setExpanded(value => !value);
Expand Down
14 changes: 14 additions & 0 deletions packages/peregrine/lib/talons/FilterModal/useFilterList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useCallback, useState } from 'react';

export const useFilterList = () => {
const [isListExpanded, setExpanded] = useState(false);

const handleListToggle = useCallback(() => {
setExpanded(value => !value);
}, [setExpanded]);

return {
handleListToggle,
isListExpanded
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React, { useEffect } from 'react';
import { act } from 'react-test-renderer';
import { useHistory, useLocation } from 'react-router-dom';

import { createTestInstance } from '@magento/peregrine';
import { useFilterSidebar } from '../useFilterSidebar';

jest.mock('../../FilterModal/helpers', () => ({
getStateFromSearch: jest.fn(() => ({})),
getSearchFromState: jest.fn(() => 'searchFromState'),
stripHtml: jest.fn(() => 'strippedHtml')
}));

jest.mock('@magento/peregrine/lib/context/app', () => {
const api = {
closeDrawer: jest.fn()
};
const state = {
drawer: 'filter'
};
return {
useAppContext: jest.fn(() => [state, api])
};
});

jest.mock('../../FilterModal/useFilterState', () => {
const api = {
setItems: jest.fn()
};
const state = {};
return {
useFilterState: jest.fn(() => [state, api])
};
});

// Mock introspection to return all the filters from the test data
jest.mock('@apollo/client', () => {
const apolloClient = jest.requireActual('@apollo/client');
const introspectionData = {
__type: {
inputFields: [
{
name: 'price'
},
{
name: 'category_id'
},
{
name: 'foo'
}
]
}
};
return {
...apolloClient,
useQuery: jest.fn(() => ({ data: introspectionData, error: null }))
};
});

jest.mock('react-router-dom', () => ({
useHistory: jest.fn(() => ({ push: jest.fn() })),
useLocation: jest.fn(() => ({ pathname: '', search: '' }))
}));
const mockPush = jest.fn();
useHistory.mockImplementation(() => ({ push: mockPush }));

const defaultProps = {
filters: [
{
attribute_code: 'price',
label: 'Price',
options: [
{
label: '*-100',
value: '*_100'
}
]
},
{
attribute_code: 'category_id',
label: 'Category',
options: [
{
label: 'Bottoms',
value: '28'
},
{
label: 'Tops',
value: '19'
}
]
},
{
attribute_code: 'foo',
label: 'Foo',
options: [
{
label: 'Bar',
value: 'bar'
}
]
}
],
operations: {}
};
const log = jest.fn();

const Component = () => {
const talonProps = useFilterSidebar(defaultProps);

useEffect(() => {
log(talonProps);
}, [talonProps]);

return null;
};
describe('#useFilterSidebar', () => {
beforeEach(() => {
log.mockClear();
mockPush.mockClear();
});

it('returns expected shape', () => {
createTestInstance(<Component />);

expect(log).toHaveBeenCalledWith({
filterApi: expect.any(Object),
filterItems: expect.any(Object),
filterKeys: expect.any(Object),
filterNames: expect.any(Object),
filterState: expect.any(Object),
handleApply: expect.any(Function),
handleClose: expect.any(Function),
handleKeyDownActions: expect.any(Function),
handleOpen: expect.any(Function),
handleReset: expect.any(Function),
isApplying: expect.any(Boolean),
isOpen: expect.any(Boolean)
});
});

it('enables category_id filter on search page', () => {
useLocation.mockReturnValueOnce({
pathname: '/search.html'
});
createTestInstance(<Component />);
const { filterNames } = log.mock.calls[0][0];
expect(filterNames.get('category_id')).toBeTruthy();
});

it('only renders filters that are valid and enabled', () => {
createTestInstance(<Component />);
const { filterNames } = log.mock.calls[0][0];
expect(filterNames.get('foo')).toBeTruthy();
});

it('writes filter state to history when "isApplying"', () => {
createTestInstance(<Component />);
const { handleApply } = log.mock.calls[0][0];

act(() => {
handleApply();
});
expect(mockPush).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions packages/peregrine/lib/talons/FilterSidebar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useFilterSidebar } from './useFilterSidebar';
Loading