-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
implement the ability to filter and sort people in the table #516
base: master
Are you sure you want to change the base?
Conversation
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.
Looks great 👍
Left some suggestions, check them
src/api.ts
Outdated
export const NOT_SET_VALUE = '-'; | ||
|
||
export enum ColumnNames { | ||
Name = 'Name', | ||
Sex = 'Sex', | ||
Born = 'Born', | ||
Died = 'Died', | ||
} | ||
|
||
export enum PersonSex { | ||
All = '', | ||
Male = 'm', | ||
Female = 'f', | ||
} |
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.
Move constants to the constants file/folder and enums/types to the types folder
src/api.ts
Outdated
export type FilterType = { | ||
query: string | ''; | ||
centuries: string[]; | ||
sex: string | ''; | ||
}; |
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.
export type FilterType = { | |
query: string | ''; | |
centuries: string[]; | |
sex: string | ''; | |
}; | |
export type FilterType = { | |
query: string; | |
centuries: string[]; | |
sex: string; | |
}; |
An empty string is still a string
src/api.ts
Outdated
function getParent(people: Person[], parentName: string) { | ||
return people.find(({ name }) => name === parentName); | ||
} | ||
|
||
export function addParent(people: Person[]) { | ||
return people.map(person => { |
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.
Move all helpers to the helpers or utils folder.
src/api.ts
Outdated
let mother; | ||
let father; | ||
|
||
if (person.motherName) { | ||
mother = getParent(people, person.motherName); | ||
} | ||
|
||
if (person.fatherName) { | ||
father = getParent(people, person.fatherName); | ||
} | ||
|
||
return { | ||
...person, | ||
mother, | ||
father, | ||
}; |
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.
let mother; | |
let father; | |
if (person.motherName) { | |
mother = getParent(people, person.motherName); | |
} | |
if (person.fatherName) { | |
father = getParent(people, person.fatherName); | |
} | |
return { | |
...person, | |
mother, | |
father, | |
}; | |
const { motherName, fatherName } = person; | |
return { | |
...person, | |
mother: motherName ? getParent(people, motherName) : motherName, | |
father: getParent(people, fatherName), // or like this, and if no fatherName just return from the getParent function | |
}; |
src/api.ts
Outdated
switch (sortParam) { | ||
case ('name'): { |
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.
sortParam
should be enum
src/api.ts
Outdated
case ('born'): { | ||
return a[sortParam] - (b[sortParam]); | ||
} | ||
|
||
case ('died'): { | ||
return a[sortParam] - (b[sortParam]); | ||
} |
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.
case ('born'): { | |
return a[sortParam] - (b[sortParam]); | |
} | |
case ('died'): { | |
return a[sortParam] - (b[sortParam]); | |
} | |
case (SortParam.born): | |
case (SortParam.died): { | |
return a[sortParam] - (b[sortParam]); | |
} |
You can combine cases if they do a similar job
src/api.ts
Outdated
return people | ||
.filter(person => { | ||
if (filterOption.sex) { | ||
return person.sex === filterOption.sex; | ||
} | ||
|
||
return person; | ||
}) | ||
.filter(person => { |
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.
You iterate through an array of people every time even some of filterOption
is not set
if (order === 'desc') { | ||
filteredPeople.reverse(); | ||
} |
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.
'desc' looks like a magic number, and reverse
mutate your array
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.
but I used reverse to the array copy, not to the original array. Is it a problem in this case?
enum Sex { | ||
FEMALE = 'f', | ||
MALE = 'm', | ||
} |
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.
enums should be stored in types folder/file
…r function, create vars for 'magic values'
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.
src/Root.tsx
Outdated
<Route path="people" element={<PeoplePage />} /> | ||
<Route path="people/:slug?" element={<PeoplePage />} /> |
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.
<Route path="people" element={<PeoplePage />} /> | |
<Route path="people/:slug?" element={<PeoplePage />} /> | |
<Route path="people"> | |
<Route path=":slug?" element={<PeoplePage />} /> | |
</Route> |
src/api.ts
Outdated
}); | ||
} | ||
|
||
export function sortPeople(people: Person[], sortParam: SortParam | string) { |
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.
export function sortPeople(people: Person[], sortParam: SortParam | string) { | |
export function getSortedPeople(people: Person[], sortParam: SortParam | string) { |
src/api.ts
Outdated
return [...people]; | ||
} | ||
|
||
export function filterPeople( |
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.
export function filterPeople( | |
export function getFilteredPeople( |
src/api.ts
Outdated
.filter(person => person.name.includes(filterOption.query) | ||
|| person.motherName?.includes(filterOption.query) | ||
|| person.fatherName?.includes(filterOption.query)); |
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.
DRY
{lowerCasedValue !== sort && (<i className="fas fa-sort" />)} | ||
{lowerCasedValue === sort && !order && ( | ||
<i className="fas fa-sort-up" /> | ||
)} | ||
{order && lowerCasedValue === sort && ( |
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.
DRY, create variable for repeatable part of code
if (!event.target.value) { | ||
setSearchParams(getSearchWith(searchParams, | ||
{ query: null })); | ||
} else { | ||
setSearchParams(getSearchWith(searchParams, | ||
{ query: event.target.value })); | ||
} |
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.
if (!event.target.value) { | |
setSearchParams(getSearchWith(searchParams, | |
{ query: null })); | |
} else { | |
setSearchParams(getSearchWith(searchParams, | |
{ query: event.target.value })); | |
} | |
setSearchParams( | |
getSearchWith(searchParams, { | |
query: event.target.value || null | |
}) | |
); |
<a | ||
className="button is-link is-outlined is-fullwidth" | ||
href="#/people" | ||
> | ||
Reset all filters | ||
</a> |
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.
Use SearchLink
here
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.
Good job, but pay attention to the comments.
const getSortParams = (sortOption: string) => { | ||
if (sortOption !== sort) { | ||
return { sort: sortOption, order: null }; | ||
} | ||
|
||
if (sort === sortOption && !order) { | ||
return { sort: sortOption, order: 'desc' }; | ||
} | ||
|
||
return { sort: null, order: null }; | ||
}; |
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.
It is better to define this function outside the component so that it is not created every time a new render is performed.
export function addParent(people: Person[]) { | ||
return people.map(person => { | ||
const { motherName, fatherName } = person; | ||
|
||
return { | ||
...person, | ||
mother: motherName ? getParent(people, motherName) : undefined, | ||
father: fatherName ? getParent(people, fatherName) : undefined, | ||
}; | ||
}); | ||
} | ||
|
||
export function getSortedPeople( | ||
people: Person[], | ||
sortParam: SortParam | string, | ||
) { | ||
if (sortParam) { | ||
return [...people].sort((a, b) => { | ||
switch (sortParam) { | ||
case (SortParam.Name): | ||
case (SortParam.Sex): { | ||
return a[sortParam].localeCompare(b[sortParam]); | ||
} | ||
|
||
case (SortParam.Born): | ||
case (SortParam.Died): { | ||
return a[sortParam] - (b[sortParam]); | ||
} | ||
|
||
default: { | ||
return 0; | ||
} | ||
} | ||
}); | ||
} | ||
|
||
return [...people]; | ||
} | ||
|
||
const getFilteredPeopleHelper = ( | ||
name: string | null, | ||
query: string, | ||
) => { | ||
return name?.toLowerCase().includes(query); | ||
}; | ||
|
||
export function getFilteredPeople( | ||
filterOption: FilterType, | ||
people: Person[], | ||
) { | ||
let filteredPeople = people; | ||
|
||
if (filterOption.sex) { | ||
filteredPeople = filteredPeople | ||
.filter(person => person.sex === filterOption.sex); | ||
} | ||
|
||
if (filterOption.centuries.length) { | ||
filteredPeople = filteredPeople.filter(person => { | ||
const personBirthCentury = Math.ceil(person.born / YEARS_IN_CENTURY); | ||
|
||
return filterOption.centuries.includes(personBirthCentury.toString()); | ||
}); | ||
} | ||
|
||
if (filterOption.query) { | ||
filteredPeople = filteredPeople | ||
.filter(person => { | ||
const query = filterOption.query.toLowerCase(); | ||
|
||
return getFilteredPeopleHelper(person.name, query) | ||
|| getFilteredPeopleHelper(person.motherName, query) | ||
|| getFilteredPeopleHelper(person.fatherName, query); | ||
}); | ||
} | ||
|
||
return filteredPeople; | ||
} |
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.
These functions have nothing to do with the API, so they should be moved to a separate file
const getLinkClass = ({ isActive }: { isActive: boolean }) => cn( | ||
'navbar-item', | ||
{ 'has-background-grey-lighter': isActive }, | ||
); |
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.
It is better to define this function outside the component so that it is not created every time a new render is performed.
@@ -0,0 +1,10 @@ | |||
export const NOT_SET_VALUE = '-'; | |||
|
|||
export const YEARS_IN_CENTURY = 100; |
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.
export const YEARS_IN_CENTURY = 100; | |
export const CENTURY_DIVIDER = 100; |
|
||
export const YEARS_IN_CENTURY = 100; | ||
|
||
export const DESC_SORT = 'desc'; |
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.
export const DESC_SORT = 'desc'; | |
export const DESCENDING_SORT = 'desc'; |
DEMO LINK