-
Notifications
You must be signed in to change notification settings - Fork 2
[CSL-3157] Sort Component #62
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import React from 'react'; | ||
| import { renderToString } from 'react-dom/server'; | ||
| import Sort from '../../src/components/Sort/Sort'; | ||
| import CioPlp from '../../src/components/CioPlp'; | ||
| import { DEMO_API_KEY } from '../../src/constants'; | ||
| import '@testing-library/jest-dom'; | ||
| import { transformSearchResponse } from '../../src/utils/transformers'; | ||
| import mockSearchResponse from '../local_examples/apiSearchResponse.json'; | ||
|
|
||
| describe('Testing Component on the server: Sort', () => { | ||
| beforeEach(() => { | ||
| // Mock console error to de-clutter the console for expected errors | ||
| const spy = jest.spyOn(console, 'error'); | ||
| spy.mockImplementation(() => {}); | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| jest.resetAllMocks(); // This will reset all mocks after each test | ||
| }); | ||
|
|
||
| const searchResponse = transformSearchResponse(mockSearchResponse); | ||
| const responseSortOptions = searchResponse.sortOptions; | ||
|
|
||
| it('Should throw error if used outside the CioPlp', () => { | ||
| expect(() => renderToString(<Sort searchOrBrowseResponse={searchResponse} />)).toThrow(); | ||
| }); | ||
|
|
||
| it('Should render sort options based on search or browse response', async () => { | ||
| const html = renderToString( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Sort searchOrBrowseResponse={searchResponse} /> | ||
| </CioPlp>, | ||
| ); | ||
|
|
||
| responseSortOptions.forEach((option) => { | ||
| expect(html).toContain(option.displayName); | ||
| }); | ||
| }); | ||
|
|
||
| it('Should render correctly with render props', () => { | ||
| const mockChildren = jest.fn().mockReturnValue(<div>Custom Sort</div>); | ||
|
|
||
| const sortProps = { | ||
| searchOrBrowseResponse: searchResponse, | ||
| children: mockChildren, | ||
| }; | ||
|
|
||
| const html = renderToString( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Sort {...sortProps} /> | ||
| </CioPlp>, | ||
| ); | ||
| expect(mockChildren).toHaveBeenCalled(); | ||
| expect(html).toContain('Custom Sort'); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| import React from 'react'; | ||
| import { render, waitFor, fireEvent } from '@testing-library/react'; | ||
| import Sort from '../../src/components/Sort/Sort'; | ||
| import CioPlp from '../../src/components/CioPlp'; | ||
| import { DEMO_API_KEY } from '../../src/constants'; | ||
| import '@testing-library/jest-dom'; | ||
| import { transformSearchResponse } from '../../src/utils/transformers'; | ||
| import mockSearchResponse from '../local_examples/apiSearchResponse.json'; | ||
|
|
||
| describe('Testing Component: Sort', () => { | ||
| let location; | ||
| const mockLocation = new URL('https://example.com'); | ||
|
|
||
| beforeEach(() => { | ||
| // Mock console error to de-clutter the console for expected errors | ||
| const spy = jest.spyOn(console, 'error'); | ||
| spy.mockImplementation(() => {}); | ||
|
|
||
| location = window.location; | ||
| delete window.location; | ||
| window.location = mockLocation; | ||
| mockLocation.href = 'https://example.com/'; | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| window.location = location; | ||
| jest.resetAllMocks(); // This will reset all mocks after each test | ||
| }); | ||
|
|
||
| const searchResponse = transformSearchResponse(mockSearchResponse); | ||
| const responseSortOptions = searchResponse.sortOptions; | ||
|
|
||
| it('Should throw error if used outside the CioPlp', () => { | ||
| expect(() => render(<Sort searchOrBrowseResponse={searchResponse} />)).toThrow(); | ||
| }); | ||
|
|
||
| it('Should render sort options based on search or browse response', async () => { | ||
| const { getByText } = render( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Sort searchOrBrowseResponse={searchResponse} /> | ||
| </CioPlp>, | ||
| ); | ||
|
|
||
| await waitFor(() => { | ||
| responseSortOptions.forEach((option) => { | ||
| expect(getByText(option.displayName)).toBeInTheDocument(); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('Should change selected sort option in component correctly', async () => { | ||
| const { container } = render( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Sort searchOrBrowseResponse={searchResponse} /> | ||
| </CioPlp>, | ||
| ); | ||
|
|
||
| const defaultSort = responseSortOptions.find((option) => option.status === 'selected'); | ||
| expect(container.querySelector('input:checked')).toBeDefined(); | ||
| expect(container.querySelector('input:checked').value).toBe(JSON.stringify(defaultSort)); | ||
|
|
||
| const newSortOption = responseSortOptions.find((option) => option.status !== 'selected'); | ||
| fireEvent.click( | ||
| container?.querySelector(`#${newSortOption.sortBy}-${newSortOption.sortOrder}`), | ||
| ); | ||
|
|
||
| expect(container.querySelector('input:checked')).toBeDefined(); | ||
| expect(container.querySelector('input:checked').value).toBe(JSON.stringify(newSortOption)); | ||
| }); | ||
|
|
||
| it('Should change selected sort option in url correctly', async () => { | ||
| const { container } = render( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Sort searchOrBrowseResponse={searchResponse} /> | ||
| </CioPlp>, | ||
| ); | ||
|
|
||
| const newSortOption = responseSortOptions.find((option) => option.status !== 'selected'); | ||
| fireEvent.click( | ||
| container?.querySelector(`#${newSortOption.sortBy}-${newSortOption.sortOrder}`), | ||
| ); | ||
|
|
||
| const urlObject = new URL(window.location.href); | ||
| expect(urlObject.searchParams.get('sortBy')).toEqual(newSortOption.sortBy); | ||
| expect(urlObject.searchParams.get('sortOrder')).toEqual(newSortOption.sortOrder); | ||
| }); | ||
|
|
||
| it('Should render correctly with render props', () => { | ||
| const mockChildren = jest.fn().mockReturnValue(<div>Custom Sort</div>); | ||
|
|
||
| const sortProps = { | ||
| searchOrBrowseResponse: searchResponse, | ||
| children: mockChildren, | ||
| }; | ||
|
|
||
| const { getByText } = render( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Sort {...sortProps} /> | ||
| </CioPlp>, | ||
| ); | ||
| expect(mockChildren).toHaveBeenCalled(); | ||
| expect(getByText('Custom Sort')).toBeInTheDocument(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import React, { useState } from 'react'; | ||
| import useSort from '../../hooks/useSort'; | ||
| import { | ||
| IncludeRenderProps, | ||
| PlpBrowseResponse, | ||
| PlpSearchResponse, | ||
| UseSortReturn, | ||
| } from '../../types'; | ||
|
|
||
| type SortProps = { | ||
| /** | ||
| * Default open state of dropdown | ||
| */ | ||
| isOpen?: boolean; | ||
| /** | ||
| * Used to build and render sort options dynamically | ||
| */ | ||
| searchOrBrowseResponse: PlpBrowseResponse | PlpSearchResponse; | ||
| }; | ||
| type SortWithRenderProps = IncludeRenderProps<SortProps, UseSortReturn>; | ||
|
|
||
| export default function Sort({ | ||
| isOpen: defaultOpen = true, | ||
| searchOrBrowseResponse, | ||
| children, | ||
| }: SortWithRenderProps) { | ||
| const [isOpen, setIsOpen] = useState(defaultOpen); | ||
| const { sortOptions, selectedSort, changeSelectedSort } = useSort(searchOrBrowseResponse); | ||
|
|
||
| const toggleCollapsible = () => { | ||
| setIsOpen(!isOpen); | ||
| }; | ||
|
|
||
| const handleOptionChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
| changeSelectedSort(JSON.parse(event.target.value)); | ||
| }; | ||
|
|
||
| return ( | ||
| <> | ||
| {typeof children === 'function' ? ( | ||
| children({ | ||
| sortOptions, | ||
| selectedSort, | ||
| changeSelectedSort, | ||
| }) | ||
| ) : ( | ||
| <div className='cio-plp-sort'> | ||
| <button type='button' className='collapsible' onClick={toggleCollapsible}> | ||
| Sort | ||
| <i className={`arrow ${isOpen ? 'arrow-up' : 'arrow-down'}`} /> | ||
| </button> | ||
| {isOpen && ( | ||
| <div className='collapsible-content'> | ||
| {sortOptions.map((option) => ( | ||
| <label | ||
| htmlFor={`${option.sortBy}-${option.sortOrder}`} | ||
| key={`${option.sortBy}-${option.sortOrder}`}> | ||
| <input | ||
| id={`${option.sortBy}-${option.sortOrder}`} | ||
| type='radio' | ||
| name={`${option.sortBy}-${option.sortOrder}`} | ||
| value={JSON.stringify(option)} | ||
| checked={ | ||
| selectedSort?.sortBy === option.sortBy && | ||
| selectedSort.sortOrder === option.sortOrder | ||
| } | ||
| onChange={handleOptionChange} | ||
| /> | ||
| <span>{option.displayName}</span> | ||
| </label> | ||
| ))} | ||
| </div> | ||
| )} | ||
| </div> | ||
| )} | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| Sort.defaultProps = { | ||
| isOpen: true, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| .cio-plp-sort { | ||
| background-color: #FFFFFF; | ||
| width: 100%; | ||
| box-sizing: border-box; | ||
| padding: 10px; | ||
| } | ||
|
|
||
| .cio-plp-sort .collapsible { | ||
| background-color: #FFFFFF; | ||
| border: none; | ||
| border-bottom: 1px solid #0000001A; | ||
| cursor: pointer; | ||
| padding: 10px; | ||
| width: 100%; | ||
| min-width: 180px; | ||
| text-align: left; | ||
| outline: none; | ||
| font-size: 18px; | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| } | ||
|
|
||
| .cio-plp-sort .collapsible-content { | ||
| padding: 10px; | ||
| background-color: #FFFFFF; | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| flex-direction: column; | ||
| } | ||
|
|
||
| .cio-plp-sort label { | ||
| cursor: pointer; | ||
| overflow: hidden; | ||
| width: 100%; | ||
| } | ||
|
|
||
| .cio-plp-sort input { | ||
| display: none; | ||
| } | ||
|
|
||
| .cio-plp-sort label span { | ||
| display: flex; | ||
| gap: 10px; | ||
| align-items: center; | ||
| padding: 10px; | ||
| transition: 0.25s ease; | ||
| } | ||
|
|
||
| /* Apply changes with a smooth and gradual transition */ | ||
| .cio-plp-sort label span:before { | ||
| display: flex; | ||
| flex-shrink: 0; | ||
| content: ""; | ||
| background-color: #FFFFFF; | ||
| width: 20px; | ||
| height: 20px; | ||
| border-radius: 50%; | ||
| transition: 0.25s ease; | ||
| border: 1px solid #00000033; | ||
| } | ||
|
|
||
| .cio-plp-sort input:checked+span:before { | ||
| box-shadow: inset 0 0 0 6px #000000; | ||
| } | ||
|
|
||
| .cio-plp-sort .arrow { | ||
| border: 1px solid #00000099; | ||
| border-width: 0 2px 2px 0; | ||
| display: inline-block; | ||
| padding: 4px; | ||
| } | ||
|
|
||
| .cio-plp-sort .arrow-up { | ||
| transform: rotate(-135deg); | ||
| -webkit-transform: rotate(-135deg); | ||
| margin-bottom: -6px; | ||
| } | ||
|
|
||
| .cio-plp-sort .arrow-down { | ||
| transform: rotate(45deg); | ||
| -webkit-transform: rotate(45deg); | ||
| margin-top: -6px; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The animation on these are so cool 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️