diff --git a/src/app/accessibility/accessibility-settings.service.spec.ts b/src/app/accessibility/accessibility-settings.service.spec.ts index d6f61840575..3d58c02d109 100644 --- a/src/app/accessibility/accessibility-settings.service.spec.ts +++ b/src/app/accessibility/accessibility-settings.service.spec.ts @@ -1,6 +1,5 @@ import { AccessibilitySettingsService, - AccessibilitySetting, AccessibilitySettings, ACCESSIBILITY_SETTINGS_METADATA_KEY, ACCESSIBILITY_COOKIE @@ -41,17 +40,6 @@ describe('accessibilitySettingsService', () => { ); }); - describe('getALlAccessibilitySettingsKeys', () => { - it('should return an array containing all accessibility setting names', () => { - const settingNames: AccessibilitySetting[] = [ - AccessibilitySetting.NotificationTimeOut, - AccessibilitySetting.LiveRegionTimeOut, - ]; - - expect(service.getAllAccessibilitySettingKeys()).toEqual(settingNames); - }); - }); - describe('get', () => { it('should return the setting if it is set', () => { const settings: AccessibilitySettings = { @@ -60,7 +48,7 @@ describe('accessibilitySettingsService', () => { service.getAll = jasmine.createSpy('getAll').and.returnValue(of(settings)); - service.get(AccessibilitySetting.NotificationTimeOut, 'default').subscribe(value => + service.get('notificationTimeOut', 'default').subscribe(value => expect(value).toEqual('1000') ); }); @@ -72,7 +60,7 @@ describe('accessibilitySettingsService', () => { service.getAll = jasmine.createSpy('getAll').and.returnValue(of(settings)); - service.get(AccessibilitySetting.LiveRegionTimeOut, 'default').subscribe(value => + service.get('liveRegionTimeOut', 'default').subscribe(value => expect(value).toEqual('default') ); }); @@ -82,7 +70,7 @@ describe('accessibilitySettingsService', () => { it('should return the setting as number if the value for the setting can be parsed to a number', () => { service.get = jasmine.createSpy('get').and.returnValue(of('1000')); - service.getAsNumber(AccessibilitySetting.NotificationTimeOut).subscribe(value => + service.getAsNumber('notificationTimeOut').subscribe(value => expect(value).toEqual(1000) ); }); @@ -90,7 +78,7 @@ describe('accessibilitySettingsService', () => { it('should return the default value if no value is set for the setting', () => { service.get = jasmine.createSpy('get').and.returnValue(of(null)); - service.getAsNumber(AccessibilitySetting.NotificationTimeOut, 123).subscribe(value => + service.getAsNumber('notificationTimeOut', 123).subscribe(value => expect(value).toEqual(123) ); }); @@ -98,7 +86,7 @@ describe('accessibilitySettingsService', () => { it('should return the default value if the value for the setting can not be parsed to a number', () => { service.get = jasmine.createSpy('get').and.returnValue(of('text')); - service.getAsNumber(AccessibilitySetting.NotificationTimeOut, 123).subscribe(value => + service.getAsNumber('notificationTimeOut', 123).subscribe(value => expect(value).toEqual(123) ); }); @@ -176,7 +164,7 @@ describe('accessibilitySettingsService', () => { it('should correctly update the chosen setting', () => { service.updateSettings = jasmine.createSpy('updateSettings'); - service.set(AccessibilitySetting.LiveRegionTimeOut, '500'); + service.set('liveRegionTimeOut', '500'); expect(service.updateSettings).toHaveBeenCalledWith({ liveRegionTimeOut: '500' }); }); }); @@ -307,7 +295,7 @@ describe('accessibilitySettingsService', () => { }); it('should set the settings in metadata', () => { - service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }).subscribe(); + service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }).subscribe(); expect(ePerson.setMetadata).toHaveBeenCalled(); }); @@ -318,19 +306,19 @@ describe('accessibilitySettingsService', () => { }); it('should create a patch with the metadata changes', () => { - service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }).subscribe(); + service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }).subscribe(); expect(ePersonService.createPatchFromCache).toHaveBeenCalled(); }); it('should send the patch request', () => { - service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }).subscribe(); + service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }).subscribe(); expect(ePersonService.patch).toHaveBeenCalled(); }); it('should emit true when the update succeeded', fakeAsync(() => { ePersonService.patch = jasmine.createSpy().and.returnValue(createSuccessfulRemoteDataObject$({})); - service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }) + service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }) .subscribe(value => { expect(value).toBeTrue(); }); @@ -341,7 +329,7 @@ describe('accessibilitySettingsService', () => { it('should emit false when the update failed', fakeAsync(() => { ePersonService.patch = jasmine.createSpy().and.returnValue(createFailedRemoteDataObject$()); - service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }) + service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }) .subscribe(value => { expect(value).toBeFalse(); }); @@ -357,7 +345,7 @@ describe('accessibilitySettingsService', () => { }); it('should store the settings in a cookie', () => { - service.setSettingsInCookie({ [AccessibilitySetting.LiveRegionTimeOut]: '500' }); + service.setSettingsInCookie({ ['liveRegionTimeOut']: '500' }); expect(cookieService.set).toHaveBeenCalled(); }); @@ -368,12 +356,4 @@ describe('accessibilitySettingsService', () => { }); }); - describe('getInputType', () => { - it('should correctly return the input type', () => { - expect(service.getInputType(AccessibilitySetting.NotificationTimeOut)).toEqual('number'); - expect(service.getInputType(AccessibilitySetting.LiveRegionTimeOut)).toEqual('number'); - expect(service.getInputType('unknownValue' as AccessibilitySetting)).toEqual('text'); - }); - }); - }); diff --git a/src/app/accessibility/accessibility-settings.service.stub.ts b/src/app/accessibility/accessibility-settings.service.stub.ts index b619a337de9..4ac965059e6 100644 --- a/src/app/accessibility/accessibility-settings.service.stub.ts +++ b/src/app/accessibility/accessibility-settings.service.stub.ts @@ -31,4 +31,10 @@ export class AccessibilitySettingsServiceStub { setSettingsInCookie = jasmine.createSpy('setSettingsInCookie'); getInputType = jasmine.createSpy('getInputType').and.returnValue('text'); + + convertFormValuesToStoredValues = jasmine.createSpy('convertFormValuesToStoredValues').and.returnValue({}); + + convertStoredValuesToFormValues = jasmine.createSpy('convertStoredValuesToFormValues').and.returnValue({}); + + getPlaceholder = jasmine.createSpy('getPlaceholder').and.returnValue('placeholder'); } diff --git a/src/app/accessibility/accessibility-settings.service.ts b/src/app/accessibility/accessibility-settings.service.ts index 4089fd03b17..813bccd4706 100644 --- a/src/app/accessibility/accessibility-settings.service.ts +++ b/src/app/accessibility/accessibility-settings.service.ts @@ -2,13 +2,14 @@ import { Injectable } from '@angular/core'; import { Observable, of, switchMap } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { CookieService } from '../core/services/cookie.service'; -import { hasValue, isNotEmpty, isNotEmptyOperator } from '../shared/empty.util'; +import { hasValue, isNotEmpty } from '../shared/empty.util'; import { AuthService } from '../core/auth/auth.service'; import { EPerson } from '../core/eperson/models/eperson.model'; import { EPersonDataService } from '../core/eperson/eperson-data.service'; import { getFirstCompletedRemoteData } from '../core/shared/operators'; import cloneDeep from 'lodash/cloneDeep'; import { environment } from '../../environments/environment'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; /** * Name of the cookie used to store the settings locally @@ -21,16 +22,30 @@ export const ACCESSIBILITY_COOKIE = 'dsAccessibilityCookie'; export const ACCESSIBILITY_SETTINGS_METADATA_KEY = 'dspace.accessibility.settings'; /** - * Enum containing all possible accessibility settings. - * When adding new settings, the {@link AccessibilitySettingsService#getInputType} method and the i18n keys for the - * accessibility settings page should be updated. + * Type containing all possible accessibility settings. + * When adding new settings, make sure to add the new setting to the accessibility-settings component form. + * The converter methods to convert from stored format to form format (and vice-versa) need to be updated as well. */ -export enum AccessibilitySetting { - NotificationTimeOut = 'notificationTimeOut', - LiveRegionTimeOut = 'liveRegionTimeOut', -} +export type AccessibilitySetting = 'notificationTimeOut' | 'liveRegionTimeOut'; -export type AccessibilitySettings = { [key in AccessibilitySetting]?: any }; +/** + * Type representing an object that contains accessibility settings values for all accessibility settings. + */ +export type FullAccessibilitySettings = { [key in AccessibilitySetting]: string }; + +/** + * Type representing an object that contains accessibility settings values for some accessibility settings. + */ +export type AccessibilitySettings = Partial; + +/** + * The accessibility settings object format used by the accessibility-settings component form. + */ +export interface AccessibilitySettingsFormValues { + notificationTimeOutEnabled: boolean, + notificationTimeOut: string, + liveRegionTimeOut: string, +} /** * Service handling the retrieval and configuration of accessibility settings. @@ -50,10 +65,6 @@ export class AccessibilitySettingsService { ) { } - getAllAccessibilitySettingKeys(): AccessibilitySetting[] { - return Object.entries(AccessibilitySetting).map(([_, val]) => val); - } - /** * Get the stored value for the provided {@link AccessibilitySetting}. If the value does not exist or if it is empty, * the provided defaultValue is emitted instead. @@ -192,8 +203,8 @@ export class AccessibilitySettingsService { return this.ePersonService.createPatchFromCache(user).pipe( take(1), - isNotEmptyOperator(), - switchMap(operations => this.ePersonService.patch(user, operations)), + switchMap(operations => + isNotEmpty(operations) ? this.ePersonService.patch(user, operations) : createSuccessfulRemoteDataObject$({})), getFirstCompletedRemoteData(), map(rd => rd.hasSucceeded), ); @@ -211,17 +222,75 @@ export class AccessibilitySettingsService { } /** - * Returns the input type that a form should use for the provided {@link AccessibilitySetting} + * Clears all settings in the cookie and attempts to clear settings in metadata. + * Emits true if settings in metadata were cleared and false otherwise. + */ + clearSettings(): Observable { + this.setSettingsInCookie({}); + return this.setSettingsInAuthenticatedUserMetadata({}); + } + + /** + * Retrieve the placeholder to be used for the provided AccessibilitySetting. + * Returns an empty string when no placeholder is specified for the provided setting. */ - getInputType(setting: AccessibilitySetting): string { + getPlaceholder(setting: AccessibilitySetting): string { switch (setting) { - case AccessibilitySetting.NotificationTimeOut: - return 'number'; - case AccessibilitySetting.LiveRegionTimeOut: - return 'number'; + case 'notificationTimeOut': + return millisecondsToSeconds(environment.notifications.timeOut.toString()); + case 'liveRegionTimeOut': + return millisecondsToSeconds(environment.liveRegion.messageTimeOutDurationMs.toString()); default: - return 'text'; + return ''; } } + /** + * Convert values in the provided accessibility settings object to values ready to be stored. + */ + convertFormValuesToStoredValues(settings: AccessibilitySettingsFormValues): FullAccessibilitySettings { + return { + notificationTimeOut: settings.notificationTimeOutEnabled ? + secondsToMilliseconds(settings.notificationTimeOut) : '0', + liveRegionTimeOut: secondsToMilliseconds(settings.liveRegionTimeOut), + }; + } + + /** + * Convert values in the provided accessibility settings object to values ready to show in the form. + */ + convertStoredValuesToFormValues(settings: AccessibilitySettings): AccessibilitySettingsFormValues { + return { + notificationTimeOutEnabled: parseFloat(settings.notificationTimeOut) !== 0, + notificationTimeOut: millisecondsToSeconds(settings.notificationTimeOut), + liveRegionTimeOut: millisecondsToSeconds(settings.liveRegionTimeOut), + }; + } + +} + +/** + * Converts a string representing seconds to a string representing milliseconds + * Returns null if the input could not be parsed to a float + */ +function secondsToMilliseconds(secondsStr: string): string { + const seconds = parseFloat(secondsStr); + if (isNaN(seconds)) { + return null; + } else { + return (seconds * 1000).toString(); + } +} + +/** + * Converts a string representing milliseconds to a string representing seconds + * Returns null if the input could not be parsed to a float + */ +function millisecondsToSeconds(millisecondsStr: string): string { + const milliseconds = parseFloat(millisecondsStr); + if (isNaN(milliseconds)) { + return null; + } else { + return (milliseconds / 1000).toString(); + } } diff --git a/src/app/info/accessibility-settings/accessibility-settings.component.html b/src/app/info/accessibility-settings/accessibility-settings.component.html index 6550c6a2880..cacfae61fce 100644 --- a/src/app/info/accessibility-settings/accessibility-settings.component.html +++ b/src/app/info/accessibility-settings/accessibility-settings.component.html @@ -2,25 +2,87 @@

{{ 'info.accessibility-settings.title' | translate }}

-
-