Skip to content

Commit 2dc6472

Browse files
authored
tdl-1909-[cra-rxjs]-feat: added sort and filters functionalities on profile page (#1918)
* feat: added sort and filters functionalities on profile page * fix: missing dependencies * fix: remove unused class * fix: code smell * fix: format and code smell * fix: code smell
1 parent a05332c commit 2dc6472

8 files changed

Lines changed: 203 additions & 12 deletions

File tree

cra-rxjs-styled-components/src/components/icons/CloseIcon.jsx renamed to cra-rxjs-styled-components/src/components/icons/CloseIcon.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export default function CloseIcon() {
99
data-view-component="true"
1010
height="1em"
1111
width="1em"
12+
fill="currentColor"
1213
>
1314
<path
1415
fillRule="evenodd"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { useRepoFilter } from '../../context/RepoFilterContext';
2+
import { defaultFilterType, defaultLanguage } from './data';
3+
import { CloseIcon } from '../icons';
4+
import {
5+
ClearButtonContainer,
6+
ClearButtonText,
7+
FilterTextContainer,
8+
FilterTextSmall,
9+
} from './RepoFilter.styles';
10+
11+
const modifyFilterTypeText = (filterText = 'test') => {
12+
if (filterText.endsWith('s')) {
13+
if (/forks/i.exec(filterText)) {
14+
filterText = filterText.replace('s', 'ed');
15+
} else {
16+
filterText = filterText.replace('s', '');
17+
}
18+
}
19+
return filterText;
20+
};
21+
22+
type FilterTextProps = {
23+
filteredRepoCount?: number;
24+
};
25+
26+
const FilterText = (props: FilterTextProps) => {
27+
const { filteredRepoCount } = props;
28+
const { filterType, search, language, sortBy, resetFilter } = useRepoFilter();
29+
30+
return (
31+
<FilterTextContainer>
32+
<div className="flex-grow">
33+
<FilterTextSmall>
34+
<strong>{filteredRepoCount}</strong>
35+
results for
36+
{filterType && filterType !== defaultFilterType && (
37+
<strong>{modifyFilterTypeText(filterType)}</strong>
38+
)}{' '}
39+
repositories
40+
{search && (
41+
<span>
42+
matching <strong> {search} </strong>
43+
</span>
44+
)}
45+
{language && language !== defaultLanguage && (
46+
<span>
47+
written in
48+
<strong> {language} </strong>
49+
</span>
50+
)}
51+
<span>
52+
sorted by
53+
<strong>{' ' + sortBy}</strong>
54+
</span>
55+
</FilterTextSmall>
56+
</div>
57+
<ClearButtonContainer onClick={resetFilter}>
58+
<ClearButtonText>
59+
<CloseIcon />
60+
</ClearButtonText>
61+
Clear filter
62+
</ClearButtonContainer>
63+
</FilterTextContainer>
64+
);
65+
};
66+
67+
export default FilterText;

cra-rxjs-styled-components/src/components/repo-filter/RepoFilter.styles.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,57 @@ export const RepoBtnText = styled.span`
5858
font-size: 14px;
5959
font-weight: 700;
6060
`;
61+
62+
export const FilterTextContainer = styled.div`
63+
display: flex;
64+
justify-content: space-between;
65+
align-items: center;
66+
border-bottom: 1px solid ${colors.gray300};
67+
padding: 16px 0;
68+
`;
69+
70+
export const FilterTextSmall = styled.small`
71+
font-size: 0.875rem;
72+
line-height: 1.25rem;
73+
text-transform: lowercase;
74+
display: flex;
75+
align-items: baseline;
76+
gap: 0.25rem;
77+
`;
78+
79+
export const ClearButtonContainer = styled.div`
80+
display: flex;
81+
align-items: center;
82+
justify-content: center;
83+
transition-property: color, background-color, border-color,
84+
text-decoration-color, fill, stroke;
85+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
86+
transition-duration: 150ms;
87+
transition-delay: 60ms;
88+
gap: 0.5rem;
89+
font-size: 0.875rem;
90+
line-height: 1.25rem;
91+
cursor: pointer;
92+
&:hover {
93+
color: ${colors.blue600};
94+
& > span {
95+
background-color: ${colors.blue600};
96+
}
97+
}
98+
`;
99+
100+
export const ClearButtonText = styled.span`
101+
display: flex;
102+
align-items: center;
103+
justify-content: center;
104+
border-radius: 0.375rem;
105+
color: #ffffff;
106+
background-color: ${colors.gray500};
107+
width: 16px;
108+
height: 16px;
109+
transition-property: color, background-color, border-color,
110+
text-decoration-color, fill, stroke;
111+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
112+
transition-duration: 150ms;
113+
transition-delay: 60ms;
114+
`;

cra-rxjs-styled-components/src/components/repo-filter/Repofilter.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { useEffect, useState } from 'react';
12
import { useRepoFilter } from '../../context/RepoFilterContext';
23
import FilterDropdown from '../filter-dropdown/FilterDropdown';
34
import { RepoBookIcon } from '../icons';
5+
import FilterText from './FilterText';
46
import {
57
Container,
68
FiltersWrapper,
@@ -9,7 +11,12 @@ import {
911
RepoFilterWrapper,
1012
} from './RepoFilter.styles';
1113
import SearchInput from './SearchInput';
12-
import { FILTER_TYPE_OPTIONS, SORT_OPTIONS } from './data';
14+
import {
15+
FILTER_TYPE_OPTIONS,
16+
SORT_OPTIONS,
17+
defaultFilterType,
18+
defaultLanguage,
19+
} from './data';
1320

1421
interface RepoFilterProps {
1522
languages?: string[];
@@ -25,8 +32,28 @@ export default function RepoFilter({
2532
const typeOptions = Object.values(FILTER_TYPE_OPTIONS);
2633
const sortOptions = Object.values(SORT_OPTIONS);
2734
const languageOptions = ['All', 'HTML', 'CSS', 'PHP'];
28-
const { filterType, setFilterType, language, setLanguage, sortBy, setSortBy } =
29-
useRepoFilter();
35+
const {
36+
filterType,
37+
setFilterType,
38+
language,
39+
setLanguage,
40+
sortBy,
41+
setSortBy,
42+
search,
43+
} = useRepoFilter();
44+
45+
const [isOnlySorted, setIsOnlySorted] = useState(true);
46+
47+
useEffect(() => {
48+
setIsOnlySorted(
49+
!!sortBy &&
50+
!(
51+
language !== defaultLanguage ||
52+
filterType !== defaultFilterType ||
53+
search
54+
)
55+
);
56+
}, [sortBy, language, filterType, search]);
3057

3158
return (
3259
<Container>
@@ -57,6 +84,7 @@ export default function RepoFilter({
5784
<RepoBtnText>{repoBtnText || 'New'}</RepoBtnText>
5885
</RepoBtn>
5986
</RepoFilterWrapper>
87+
{!isOnlySorted && <FilterText filteredRepoCount={filteredRepoCount} />}
6088
</Container>
6189
);
6290
}

cra-rxjs-styled-components/src/components/repo-filter/SearchInput.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export default function SearchInput() {
1515
id="search"
1616
placeholder="Find a repository..."
1717
value={search}
18+
autoFocus
1819
onInput={handleChange}
1920
/>
2021
);

cra-rxjs-styled-components/src/helpers/typeFilterFunction.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import { FILTER_TYPE_OPTIONS } from '@/components/repo-filter/data';
2-
import { Repository } from '@/interfaces/repositories.interfaces';
1+
import { FILTER_TYPE_OPTIONS } from '../components/repo-filter/data';
2+
import { Repository } from '../interfaces/repositories.interfaces';
33

4-
export const repoDataFilteredByType = (
5-
repos: Repository[],
6-
filterType: string
7-
): Repository[] => {
4+
export const repoDataFilteredByType = ({
5+
repos,
6+
filterType,
7+
}: {
8+
repos: Repository[];
9+
filterType: string;
10+
}): Repository[] => {
811
let response = repos.slice();
912
if (filterType === FILTER_TYPE_OPTIONS.forks) {
1013
response = repos.filter((repo: Repository) => repo.fork);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useRepoFilter } from '../../context/RepoFilterContext';
2+
import { repoDataFilteredByLanguage } from '../../helpers/languageFilterFunction';
3+
import { repoDataFilteredBySearch } from '../../helpers/searchFunction';
4+
import { sortedRepoData } from '../../helpers/sortRepoFunction';
5+
import { repoDataFilteredByType } from '../../helpers/typeFilterFunction';
6+
import { Repository } from '../../interfaces/repositories.interfaces';
7+
8+
const useRepoSortFilter = (repos: Repository[]): Repository[] => {
9+
let result = repos;
10+
const { sortBy, language, search, filterType } = useRepoFilter();
11+
12+
if (search) {
13+
result = repoDataFilteredBySearch({ repos: result, search });
14+
}
15+
16+
if (language) {
17+
result = repoDataFilteredByLanguage({ repos: result, language });
18+
}
19+
20+
if (filterType) {
21+
result = repoDataFilteredByType({ repos: result, filterType });
22+
}
23+
24+
if (sortBy) {
25+
result = sortedRepoData({ repos: result, sortBy });
26+
}
27+
28+
return result;
29+
};
30+
31+
export default useRepoSortFilter;

cra-rxjs-styled-components/src/routes/profile.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import LoadingRepoCard from '../components/repo-card/LoadingRepoCard';
1515
import TabNavigation from '../components/tab-nav/TabNav';
1616
import { tabs } from '../constants/data';
1717
import RepoFilter from '../components/repo-filter/Repofilter';
18+
import useRepoSortFilter from '../hooks/repositories/use-repo-sort-filter';
1819

1920
function Profile() {
2021
const context = useUser();
@@ -31,21 +32,26 @@ function Profile() {
3132
grid-area: content;
3233
`;
3334

35+
const sortAndFilteredRepositories = useRepoSortFilter(repositories);
36+
3437
return (
3538
<>
3639
<Header />
3740
<Layout>
3841
<UserProfileView />
3942
<ContentLayout>
40-
{!repositories.length ? (
43+
{!sortAndFilteredRepositories.length ? (
4144
<LoadingRepoCard />
4245
) : (
4346
<>
4447
<ProfileNav>
4548
<TabNavigation tabs={tabs} activeTab={tabs[0].title} />
4649
</ProfileNav>
47-
<RepoFilter languages={languages} />
48-
{repositories.map((repo) => (
50+
<RepoFilter
51+
languages={languages}
52+
filteredRepoCount={sortAndFilteredRepositories.length}
53+
/>
54+
{sortAndFilteredRepositories.map((repo) => (
4955
<RepoCard repo={repo} key={repo.id} star />
5056
))}
5157
<PaginateWrapper>

0 commit comments

Comments
 (0)