Skip to content

Commit e35b26e

Browse files
authored
Merge pull request #748 from IT-Academy-BCN/feature/136-style-sort-select
2/2 feat(sort-select): 136 style sort select from ITA Challanges
2 parents 1f61b8a + 206004d commit e35b26e

File tree

11 files changed

+115
-32
lines changed

11 files changed

+115
-32
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
44
and this project adheres to
55
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
### [ita-challenges-frontend-3.24.0-RELEASE] - 2026-02-11
7+
### [ita-challenges-frontend-3.25.0-RELEASE] - 2026-02-16
8+
### Added
9+
- Custom styling for `SortSelectComponent` matching figma.
10+
- Accessibility support: Keyboard navigation, ARIA labels, and focus indicators.
11+
- Responsive layout with touch-optimized targets.
12+
13+
### [ita-challenges-frontend-3.24.0-RELEASE] - 2026-02-16
814
### Added
915
- `SortSelectComponent` (`src/app/modules/starter/components/sort-select/`) to modularize sort functionality.
1016
- Explicit "Ascending" and "Descending" selection options in the dropdown.

conf/.env.CI.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
MICROSERVICE_DEPLOY=ita-challenges-frontend
2-
MICROSERVICE_VERSION=3.24.0-RELEASE
2+
MICROSERVICE_VERSION=3.25.0-RELEASE

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ita-challenges-frontend",
3-
"version": "3.24.0-RELEASE",
4-
"scripts": {
3+
"version": "3.25.0-RELEASE",
4+
"scripts": {
55
"ng": "ng",
66
"start": "ng serve --proxy-config proxy.conf.dev.json",
77
"build": "ng build --configuration production && cp -r node_modules/tinymce dist/ita-challenges-frontend/browser/assets/tinymce",

src/app/modules/starter/components/challenge-list-filters/challenge-list-filters.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
(sortSelected)="changeSort($event)"
88
(orderSelected)="changeOrder($event)">
99
</app-sort-select>
10-
1110
<div>
1211
<app-challenge-filters-trigger
1312
[initialFilters]="initialFilters"

src/app/modules/starter/components/challenge-list-filters/challenge-list-filters.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ describe('ChallengeListFiltersComponent', () => {
2424
const element: HTMLElement = fixture.nativeElement
2525
expect(element.querySelector('#challenge-list-filters')).toBeTruthy()
2626
})
27-
})
27+
})

src/app/modules/starter/components/challenge-list-filters/challenge-list-filters.component.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
1-
21
import { Component, EventEmitter, Input, Output } from '@angular/core'
32
import { type FilterChallenge } from 'src/app/models/filter-challenge.model'
43

54
type ModalFilters = Pick<FilterChallenge, 'levels' | 'tags' | 'progress'>
65

7-
86
@Component({
97
selector: 'app-challenge-list-filters',
108
templateUrl: './challenge-list-filters.component.html',
119
styleUrls: ['./challenge-list-filters.component.scss']
1210
})
1311
export class ChallengeListFiltersComponent {
14-
1512
@Input() initialFilters: FilterChallenge = { languages: [], levels: [], progress: [], tags: [] }
1613
@Output() filtersApplied = new EventEmitter<ModalFilters>()
1714
@Output() sortSelected = new EventEmitter<string>()
1815
@Output() orderSelected = new EventEmitter<boolean>()
1916
sortBy: string = 'popularity'
2017
isAscending: boolean = false
2118

22-
protected onModalFiltersApplied(filters: ModalFilters): void {
19+
protected onModalFiltersApplied (filters: ModalFilters): void {
2320
this.filtersApplied.emit(filters)
2421
}
2522

src/app/modules/starter/components/sort-select/sort-select.component.html

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="dropdown">
2-
<button class="btn btn-outline-secondary dropdown-toggle d-flex align-items-center gap-2" type="button" data-bs-toggle="dropdown" aria-expanded="false">
3-
<img src="assets/img/icon/sort-selected.svg" [style.transform]="isAscending ? 'rotate(180deg)' : ''" alt="sort">
2+
<button class="btn sort-trigger dropdown-toggle d-flex align-items-center gap-2" type="button" data-bs-toggle="dropdown" aria-expanded="false" [attr.aria-label]="'modules.starter.main.section3.sort' | translate">
3+
<img src="assets/img/icon/order-direction.svg" [style.transform]="isAscending ? 'rotate(180deg)' : ''" alt="" aria-hidden="true" class="sort-icon">
44
{{ currentSortLabel | translate }}
55
</button>
66
<ul class="dropdown-menu dropdown-menu-end p-0">
@@ -10,15 +10,17 @@
1010
<button class="dropdown-item d-flex align-items-center gap-2"
1111
[class.active]="sortBy === 'popularity'"
1212
(click)="selectSort('popularity')">
13-
<img src="assets/img/icon/times-performed.svg" alt="popularity" class="icon">
13+
<img src="assets/img/icon/tick-pink.svg" alt="" aria-hidden="true" class="tick-icon" [style.visibility]="sortBy === 'popularity' ? 'visible' : 'hidden'">
14+
<img src="assets/img/icon/times-performed.svg" alt="" aria-hidden="true" class="icon">
1415
{{ 'modules.starter.main.section3.popularity' | translate }}
1516
</button>
1617
</li>
1718
<li>
1819
<button class="dropdown-item d-flex align-items-center gap-2"
1920
[class.active]="sortBy === 'creation_date'"
2021
(click)="selectSort('creation_date')">
21-
<img src="assets/img/icon/date.svg" alt="date" class="icon">
22+
<img src="assets/img/icon/tick-pink.svg" alt="" aria-hidden="true" class="tick-icon" [style.visibility]="sortBy === 'creation_date' ? 'visible' : 'hidden'">
23+
<img src="assets/img/icon/date.svg" alt="" aria-hidden="true" class="icon">
2224
{{ 'modules.starter.main.section3.date' | translate }}
2325
</button>
2426
</li>
@@ -27,7 +29,8 @@
2729
<button class="dropdown-item d-flex align-items-center gap-2"
2830
[class.active]="sortBy === 'likes'"
2931
(click)="selectSort('likes')">
30-
<img src="assets/img/icon/heart.svg" alt="likes" class="icon">
32+
<img src="assets/img/icon/tick-pink.svg" alt="" aria-hidden="true" class="tick-icon" [style.visibility]="sortBy === 'likes' ? 'visible' : 'hidden'">
33+
<img src="assets/img/icon/heart.svg" alt="" aria-hidden="true" class="icon">
3134
{{ 'modules.starter.main.section3.likes' | translate }}
3235
</button>
3336
</li>
@@ -37,7 +40,8 @@
3740
<button class="dropdown-item d-flex align-items-center gap-2"
3841
[class.active]="sortBy === 'difficulty'"
3942
(click)="selectSort('difficulty')">
40-
<img src="assets/img/icon/difficulty-off-gray.svg" alt="difficulty" class="icon">
43+
<img src="assets/img/icon/tick-pink.svg" alt="" aria-hidden="true" class="tick-icon" [style.visibility]="sortBy === 'difficulty' ? 'visible' : 'hidden'">
44+
<img src="assets/img/icon/difficulty-off-gray.svg" alt="" aria-hidden="true" class="icon">
4145
{{ 'modules.starter.main.section3.difficulty' | translate }}
4246
</button>
4347
</li>
@@ -46,23 +50,23 @@
4650

4751
<!-- Order -->
4852
<li>
49-
<button class="dropdown-item d-flex align-items-center justify-content-between"
53+
<button class="dropdown-item d-flex align-items-center gap-2"
5054
(click)="selectOrder(true)">
51-
<img *ngIf="isAscending" src="assets/img/icon/tick-pink.svg" alt="selected">
55+
<img src="assets/img/icon/tick-pink.svg" alt="" aria-hidden="true" class="tick-icon" [style.visibility]="isAscending ? 'visible' : 'hidden'">
5256
<div class="d-flex align-items-center gap-2">
53-
<img src="assets/img/icon/sort-selected.svg" style="transform: rotate(180deg);" alt="asc" class="icon">
57+
<img src="assets/img/icon/order-direction.svg" style="transform: rotate(180deg);" alt="" aria-hidden="true" class="icon">
5458
{{ 'modules.starter.main.section3.ascending' | translate }}
5559
</div>
5660
</button>
5761
</li>
5862
<li>
59-
<button class="dropdown-item d-flex align-items-center justify-content-between"
60-
(click)="selectOrder(false)"> <img *ngIf="!isAscending" src="assets/img/icon/tick-pink.svg" alt="selected">
63+
<button class="dropdown-item d-flex align-items-center gap-2"
64+
(click)="selectOrder(false)">
65+
<img src="assets/img/icon/tick-pink.svg" alt="" aria-hidden="true" class="tick-icon" [style.visibility]="!isAscending ? 'visible' : 'hidden'">
6166
<div class="d-flex align-items-center gap-2">
62-
<img src="assets/img/icon/sort-selected.svg" alt="desc" class="icon">
67+
<img src="assets/img/icon/order-direction.svg" alt="" aria-hidden="true" class="icon">
6368
{{ 'modules.starter.main.section3.descending' | translate }}
6469
</div>
65-
6670
</button>
6771
</li>
6872
</ul>
Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,77 @@
1-
.dropdown-item {
2-
cursor: pointer;
1+
@import 'src/styles/colors';
32

4-
.icon {
5-
width: 18px;
6-
height: 18px;
3+
4+
.dropdown {
5+
.sort-trigger {
6+
background-color: $black-2;
7+
color: $white;
8+
border: none;
9+
box-shadow: inset 0 0 0 1px $gray-2;
10+
padding: 10px 16px;
11+
border-radius: .7rem;
12+
transition: all 0.2s ease-in-out;
13+
14+
&:hover, &:focus, &:active, &.show {
15+
background-color: lighten($black-2, 10%);
16+
box-shadow: inset 0 0 0 1px $gray-2 !important;
17+
outline: none !important;
18+
color: $white;
19+
}
20+
21+
&:focus-visible {
22+
box-shadow: 0 0 0 2px $white, inset 0 0 0 1px $gray-2 !important;
23+
}
24+
25+
.sort-icon {
26+
filter: invert(1);
27+
}
28+
29+
30+
}
31+
32+
.dropdown-menu {
33+
padding: 0;
34+
border: 1px solid $gray-5;
35+
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
36+
border-radius: .7rem;
37+
overflow: hidden;
38+
39+
margin-top: 8px;
40+
z-index: 1000;
41+
}
42+
43+
.dropdown-item {
44+
cursor: pointer;
45+
padding: 12px 16px;
46+
transition: background-color 0.2s ease;
47+
color: $black-2;
48+
49+
.tick-icon {
50+
width: 18px;
51+
height: 18px;
52+
flex-shrink: 0;
53+
transition: filter 0.2s ease;
54+
}
55+
56+
.icon {
57+
width: 18px;
58+
height: 18px;
59+
transition: filter 0.2s ease;
60+
}
61+
62+
&.active {
63+
background-color: transparent;
64+
color: $black-2;
65+
font-weight: normal;
66+
}
67+
68+
&:hover, &:active, &:focus {
69+
background-color: $primary;
70+
color: $white !important;
71+
72+
.icon, .tick-icon {
73+
filter: brightness(0) invert(1);
74+
}
75+
}
776
}
877
}

src/app/modules/starter/components/sort-select/sort-select.component.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ describe('SortSelectComponent', () => {
5656
expect(activeBtn).toBeTruthy()
5757
})
5858

59-
it('should display selected icon for the current order', () => {
59+
it('should display visible tick for the current order', () => {
6060
component.isAscending = true
6161
fixture.detectChanges()
62-
const selectedIcons = fixture.debugElement.queryAll(By.css('img[alt="selected"]'))
63-
expect(selectedIcons.length).toBe(1)
62+
const items = fixture.debugElement.queryAll(By.css('.dropdown-item'))
63+
const ascendingBtn = items[4]
64+
const tick = ascendingBtn.query(By.css('.tick-icon'))
65+
66+
expect(tick.styles['visibility']).toBe('visible')
6467
})
6568
})

src/app/modules/starter/components/starter/starter.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ app-starter-filters p {
4848

4949
#challenges-container {
5050
height: 100%;
51+
overflow: visible;
5152
width: 100%;
52-
overflow: hidden;
5353
display: flex;
5454
flex-direction: column;
5555
padding-top: 2rem;

0 commit comments

Comments
 (0)