Skip to content

Commit d33743b

Browse files
authored
[ENG-8901] Move terms of service consent banner (#438)
* feat(eng-8901-6): update the tos banner * feat(eng-8901-6): tests for consent banner
1 parent 5c1258c commit d33743b

File tree

12 files changed

+95
-89
lines changed

12 files changed

+95
-89
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<osf-scheduled-banner></osf-scheduled-banner>
22
<osf-maintenance-banner></osf-maintenance-banner>
33
<osf-cookie-consent-banner></osf-cookie-consent-banner>
4+
<osf-tos-consent-banner></osf-tos-consent-banner>

src/app/core/components/osf-banners/osf-banner.component.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
55

66
import { CookieConsentBannerComponent } from './cookie-consent-banner/cookie-consent-banner.component';
77
import { ScheduledBannerComponent } from './scheduled-banner/scheduled-banner.component';
8+
import { TosConsentBannerComponent } from './tos-consent-banner/tos-consent-banner.component';
89
import { OSFBannerComponent } from './osf-banner.component';
910

1011
import { OSFTestingModule } from '@testing/osf.testing.module';
@@ -23,6 +24,7 @@ describe('Component: OSF Banner', () => {
2324
MockComponentWithSignal('osf-maintenance-banner'),
2425
MockComponent(ScheduledBannerComponent),
2526
MockComponent(CookieConsentBannerComponent),
27+
MockComponent(TosConsentBannerComponent),
2628
],
2729
}).compileComponents();
2830

src/app/core/components/osf-banners/osf-banner.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
33
import { CookieConsentBannerComponent } from './cookie-consent-banner/cookie-consent-banner.component';
44
import { MaintenanceBannerComponent } from './maintenance-banner/maintenance-banner.component';
55
import { ScheduledBannerComponent } from './scheduled-banner/scheduled-banner.component';
6+
import { TosConsentBannerComponent } from './tos-consent-banner/tos-consent-banner.component';
67

78
/**
89
* Wrapper component responsible for rendering all global or conditional banners.
@@ -21,7 +22,12 @@ import { ScheduledBannerComponent } from './scheduled-banner/scheduled-banner.co
2122
*/
2223
@Component({
2324
selector: 'osf-banner-component',
24-
imports: [MaintenanceBannerComponent, ScheduledBannerComponent, CookieConsentBannerComponent],
25+
imports: [
26+
MaintenanceBannerComponent,
27+
ScheduledBannerComponent,
28+
CookieConsentBannerComponent,
29+
TosConsentBannerComponent,
30+
],
2531
templateUrl: './osf-banner.component.html',
2632
changeDetection: ChangeDetectionStrategy.OnPush,
2733
})

src/app/features/home/components/tos-consent-banner/tos-consent-banner.component.html renamed to src/app/core/components/osf-banners/tos-consent-banner/tos-consent-banner.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@if (!acceptedTermsOfServiceChange()) {
2-
<div class="p-4 bg-white">
2+
<div class="p-3">
33
<p-message styleClass="w-full" severity="warn">
44
<ng-template #container>
55
<div class="flex flex-column w-full">

src/app/features/home/components/tos-consent-banner/tos-consent-banner.component.spec.ts renamed to src/app/core/components/osf-banners/tos-consent-banner/tos-consent-banner.component.spec.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { Store } from '@ngxs/store';
22

33
import { MockComponent } from 'ng-mocks';
44

5-
import { of } from 'rxjs';
6-
5+
import { signal } from '@angular/core';
76
import { ComponentFixture, TestBed } from '@angular/core/testing';
87
import { By } from '@angular/platform-browser';
98

@@ -15,32 +14,26 @@ import { IconComponent } from '@shared/components';
1514
import { TosConsentBannerComponent } from './tos-consent-banner.component';
1615

1716
import { TranslationServiceMock } from '@testing/mocks/translation.service.mock';
18-
import { OSFTestingModule, OSFTestingStoreModule } from '@testing/osf.testing.module';
17+
import { OSFTestingStoreModule } from '@testing/osf.testing.module';
1918
import { provideMockStore } from '@testing/providers/store-provider.mock';
20-
import { ToastServiceMockBuilder } from '@testing/providers/toast-provider.mock';
2119

22-
describe('TosConsentBannerComponent', () => {
23-
let component: TosConsentBannerComponent;
20+
describe('Component: Tos Consent Banner', () => {
2421
let fixture: ComponentFixture<TosConsentBannerComponent>;
25-
let store: jest.Mocked<Store>;
26-
let toastServiceMock: ReturnType<ToastServiceMockBuilder['build']>;
22+
let store: Store;
2723

2824
beforeEach(async () => {
2925
await TestBed.configureTestingModule({
30-
imports: [TosConsentBannerComponent, OSFTestingStoreModule, OSFTestingModule, MockComponent(IconComponent)],
26+
imports: [OSFTestingStoreModule, TosConsentBannerComponent, MockComponent(IconComponent)],
3127
providers: [
3228
provideMockStore({
33-
signals: [{ selector: UserSelectors.getCurrentUser, value: MOCK_USER }],
29+
signals: [{ selector: UserSelectors.getCurrentUser, value: signal(MOCK_USER) }],
3430
}),
3531
TranslationServiceMock,
3632
],
3733
}).compileComponents();
3834

3935
fixture = TestBed.createComponent(TosConsentBannerComponent);
40-
store = TestBed.inject(Store) as jest.Mocked<Store>;
41-
component = fixture.componentInstance;
42-
store.dispatch = jest.fn().mockReturnValue(of(undefined));
43-
toastServiceMock = ToastServiceMockBuilder.create().build();
36+
store = TestBed.inject(Store);
4437
fixture.detectChanges();
4538
});
4639

@@ -58,6 +51,7 @@ describe('TosConsentBannerComponent', () => {
5851
});
5952

6053
it('should dispatch AcceptTermsOfServiceByUser action when "Continue" is clicked', () => {
54+
jest.spyOn(store, 'dispatch');
6155
const checkboxInput = fixture.debugElement.query(By.css('p-checkbox input')).nativeElement;
6256
checkboxInput.click();
6357
fixture.detectChanges();
@@ -67,15 +61,5 @@ describe('TosConsentBannerComponent', () => {
6761
fixture.detectChanges();
6862

6963
expect(store.dispatch).toHaveBeenCalledWith(new AcceptTermsOfServiceByUser());
70-
expect(toastServiceMock.showError).not.toHaveBeenCalled();
71-
});
72-
73-
it('should show toast banner if acceptedTermsOfService is false and "Continue" is clicked', () => {
74-
component.acceptedTermsOfService.set(false);
75-
const continueButton = fixture.debugElement.query(By.css('p-button button')).nativeElement;
76-
continueButton.disabled = false;
77-
continueButton.click();
78-
fixture.detectChanges();
79-
expect(component.errorMessage).toEqual('toast.tos-consent.error-message');
8064
});
8165
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createDispatchMap, select } from '@ngxs/store';
2+
3+
import { TranslatePipe } from '@ngx-translate/core';
4+
5+
import { Button } from 'primeng/button';
6+
import { Checkbox } from 'primeng/checkbox';
7+
import { Message } from 'primeng/message';
8+
9+
import { Component, computed, signal } from '@angular/core';
10+
import { FormsModule } from '@angular/forms';
11+
import { RouterLink } from '@angular/router';
12+
13+
import { AcceptTermsOfServiceByUser, UserSelectors } from '@core/store/user';
14+
import { IconComponent } from '@osf/shared/components';
15+
16+
/**
17+
* TosConsentBannerComponent displays a Terms of Service (ToS) consent banner for users who haven't accepted yet.
18+
* It includes a checkbox, error handling, and dispatches an action to accept the ToS upon confirmation.
19+
*
20+
* This component integrates:
21+
* - PrimeNG UI elements (`Checkbox`, `Button`, `Message`)
22+
* - i18n translation support
23+
* - NGXS store selectors and actions
24+
* - Signal-based reactivity and computed values
25+
* - Toast notifications for user feedback
26+
*
27+
* @component
28+
* @example
29+
* <osf-tos-consent-banner></osf-tos-consent-banner>
30+
*/
31+
@Component({
32+
selector: 'osf-tos-consent-banner',
33+
imports: [FormsModule, Checkbox, Button, Message, TranslatePipe, IconComponent, RouterLink],
34+
templateUrl: './tos-consent-banner.component.html',
35+
})
36+
export class TosConsentBannerComponent {
37+
/**
38+
* NGXS dispatch map for the AcceptTermsOfServiceByUser action.
39+
*/
40+
readonly actions = createDispatchMap({ acceptTermsOfServiceByUser: AcceptTermsOfServiceByUser });
41+
42+
/**
43+
* Signal of the current user from NGXS UserSelectors.
44+
*/
45+
readonly currentUser = select(UserSelectors.getCurrentUser);
46+
47+
/**
48+
* Local signal tracking whether the user has accepted the Terms of Service via checkbox.
49+
*/
50+
acceptedTermsOfService = signal(false);
51+
52+
/**
53+
* Computed signal indicating whether the user has already accepted the Terms of Service.
54+
*/
55+
readonly acceptedTermsOfServiceChange = computed(() => {
56+
const user = this.currentUser();
57+
return user?.acceptedTermsOfService ?? false;
58+
});
59+
60+
/**
61+
* Triggered when the user clicks the Continue button.
62+
* - Shows an error toast if checkbox is not checked.
63+
* - Dispatches `AcceptTermsOfServiceByUser` action otherwise.
64+
*/
65+
onContinue() {
66+
this.actions.acceptTermsOfServiceByUser();
67+
}
68+
}

src/app/features/analytics/analytics.component.spec.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { MockComponents, MockProvider } from 'ng-mocks';
22

33
import { of } from 'rxjs';
44

5+
import { signal } from '@angular/core';
56
import { ComponentFixture, TestBed } from '@angular/core/testing';
67
import { ActivatedRoute, Router } from '@angular/router';
78

@@ -24,7 +25,7 @@ import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.moc
2425
import { RouterMockBuilder } from '@testing/providers/router-provider.mock';
2526
import { provideMockStore } from '@testing/providers/store-provider.mock';
2627

27-
describe.skip('AnalyticsComponent', () => {
28+
describe('Component: Analytics', () => {
2829
let component: AnalyticsComponent;
2930
let fixture: ComponentFixture<AnalyticsComponent>;
3031
let routerMock: ReturnType<RouterMockBuilder['build']>;
@@ -69,11 +70,11 @@ describe.skip('AnalyticsComponent', () => {
6970
{ selector: AnalyticsSelectors.isMetricsError, value: false },
7071
],
7172
signals: [
72-
{ selector: metricsSelector, value: metrics },
73-
{ selector: relatedCountsSelector, value: relatedCounts },
74-
{ selector: AnalyticsSelectors.isMetricsLoading, value: false },
75-
{ selector: AnalyticsSelectors.isRelatedCountsLoading, value: false },
76-
{ selector: AnalyticsSelectors.isMetricsError, value: false },
73+
{ selector: metricsSelector, value: signal(metrics) },
74+
{ selector: relatedCountsSelector, value: signal(relatedCounts) },
75+
{ selector: AnalyticsSelectors.isMetricsLoading, value: signal(false) },
76+
{ selector: AnalyticsSelectors.isRelatedCountsLoading, value: signal(false) },
77+
{ selector: AnalyticsSelectors.isMetricsError, value: signal(false) },
7778
],
7879
}),
7980
{ provide: IS_WEB, useValue: of(true) },
@@ -86,11 +87,6 @@ describe.skip('AnalyticsComponent', () => {
8687
component = fixture.componentInstance;
8788
});
8889

89-
it('should create', () => {
90-
fixture.detectChanges();
91-
expect(component).toBeTruthy();
92-
});
93-
9490
it('should set selectedRange via onRangeChange', () => {
9591
fixture.detectChanges();
9692
component.onRangeChange('month');
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { TosConsentBannerComponent } from './tos-consent-banner/tos-consent-banner.component';
1+
export { TosConsentBannerComponent } from '../../../core/components/osf-banners/tos-consent-banner/tos-consent-banner.component';

src/app/features/home/components/tos-consent-banner/tos-consent-banner.component.scss

Whitespace-only changes.

src/app/features/home/components/tos-consent-banner/tos-consent-banner.component.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)