mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-01 00:13:37 +00:00
fix: let accessibility reset bring back the organisation's defaults and cover every option
This commit is contained in:
@@ -99,6 +99,29 @@ describe('reset', () => {
|
||||
// A shared everyday preference is not an accessibility setting to reset.
|
||||
expect(settings().disableThreading).toBe(true);
|
||||
});
|
||||
|
||||
it("gives back the organisation's defaults instead of writing Bulwark's as the user's choice", () => {
|
||||
usePolicyStore.setState({ policy: { ...DEFAULT_POLICY, defaults: { appFont: 'atkinson', fontSize: 'large' } }, loaded: true });
|
||||
settings().applyAdminPolicy();
|
||||
settings().updateSetting('appFont', 'lexend');
|
||||
settings().updateSetting('fontSize', 'huge');
|
||||
render(<AccessibilitySettings />);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'settings.accessibility.reset.button' }));
|
||||
expect(settings().appFont).toBe('atkinson');
|
||||
expect(settings().fontSize).toBe('large');
|
||||
// Nothing is left marked as chosen, so a later change of the default reaches the user.
|
||||
expect(settings().explicitSettings).not.toContain('appFont');
|
||||
expect(settings().explicitSettings).not.toContain('fontSize');
|
||||
});
|
||||
|
||||
it('also resets the plain text font and the send confirmation', () => {
|
||||
settings().updateSetting('plainTextFont', 'mono');
|
||||
settings().updateSetting('sendConfirmation', true);
|
||||
render(<AccessibilitySettings />);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'settings.accessibility.reset.button' }));
|
||||
expect(settings().plainTextFont).toBe('sans');
|
||||
expect(settings().sendConfirmation).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toast duration', () => {
|
||||
|
||||
@@ -14,7 +14,6 @@ import { voicesForLocale } from '@/lib/read-aloud';
|
||||
import { useSpeechVoices } from '@/hooks/use-read-aloud';
|
||||
import { NOTIFICATION_SOUNDS, playNotificationSound, type NotificationSoundChoice } from '@/lib/notification-sound';
|
||||
import {
|
||||
ACCESSIBILITY_DEFAULTS,
|
||||
ACCESSIBILITY_KEYS,
|
||||
FONT_SIZES,
|
||||
MAIL_MIN_FONT_SIZES,
|
||||
@@ -37,6 +36,9 @@ import type { Density, SendDelaySeconds } from '@/stores/settings-store';
|
||||
|
||||
type SettingKey = Parameters<ReturnType<typeof useSettingsStore.getState>['updateSetting']>[0];
|
||||
|
||||
/** What Reset gives back: the settings only this page has. */
|
||||
const RESET_KEYS: SettingKey[] = [...ACCESSIBILITY_KEYS, 'fontSize', 'plainTextFont', 'sendConfirmation'];
|
||||
|
||||
/** Opens another settings page (and section) the way deep links do. */
|
||||
function goToSettings(tab: string, section?: string) {
|
||||
window.dispatchEvent(new CustomEvent('settings-tab-change', { detail: section ? { tab, section } : tab }));
|
||||
@@ -64,7 +66,7 @@ export function AccessibilitySettings() {
|
||||
const tNotifications = useTranslations('settings.notifications');
|
||||
const tCalendar = useTranslations('calendar.settings');
|
||||
const settings = useSettingsStore();
|
||||
const { updateSetting } = settings;
|
||||
const { updateSetting, resetSettings } = settings;
|
||||
const { isSettingLocked, isSettingHidden, getRestriction } = usePolicyStore();
|
||||
const faviconsAllowed = !useNoThirdPartyTraffic();
|
||||
const [applied, setApplied] = useState<{ id: AccessibilityPresetId; changes: PresetChange[] } | null>(null);
|
||||
@@ -91,12 +93,10 @@ export function AccessibilitySettings() {
|
||||
setApplied(null);
|
||||
};
|
||||
|
||||
// Forgets the user's own values, so the organisation's defaults (or
|
||||
// Bulwark's) apply again. Shared everyday settings shown here too stay.
|
||||
const resetAll = () => {
|
||||
for (const key of ACCESSIBILITY_KEYS) {
|
||||
if (isSettingLocked(key)) continue;
|
||||
set(key, ACCESSIBILITY_DEFAULTS[key]);
|
||||
}
|
||||
set('fontSize', 'medium');
|
||||
resetSettings(RESET_KEYS.filter((key) => !isSettingLocked(key)));
|
||||
setApplied(null);
|
||||
setResetDone(true);
|
||||
};
|
||||
|
||||
@@ -804,6 +804,12 @@ interface SettingsState extends AccessibilitySettings {
|
||||
opts?: { internal?: boolean }
|
||||
) => void;
|
||||
resetToDefaults: () => void;
|
||||
/**
|
||||
* Forgets the user's own value for each of `keys`: the setting loses its
|
||||
* "chosen by the user" mark and takes the admin default (policy.defaults)
|
||||
* again, or the built-in one when the admin set none.
|
||||
*/
|
||||
resetSettings: (keys: readonly string[]) => void;
|
||||
/**
|
||||
* Applies the admin policy (#1248): `policy.defaults` to every setting the
|
||||
* user has not chosen, and a locked restriction's value to its setting
|
||||
@@ -1495,6 +1501,26 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
void settingsHooks.onSettingsReset.emit({});
|
||||
},
|
||||
|
||||
resetSettings: (keys) => {
|
||||
const own = keys.filter((key) => hasOwn(DEFAULT_SETTINGS, key) && key !== 'explicitSettings' && !BOOKKEEPING_SETTING_KEYS.has(key));
|
||||
if (own.length === 0) return;
|
||||
const before = get() as unknown as Record<string, unknown>;
|
||||
const previous = Object.fromEntries(own.map((key) => [key, before[key]]));
|
||||
const dropped = new Set(own);
|
||||
set({
|
||||
...Object.fromEntries(own.map((key) => [key, builtInDefault(key)])),
|
||||
explicitSettings: get().explicitSettings.filter((key) => !dropped.has(key)),
|
||||
} as Partial<SettingsState>);
|
||||
applyAdminPolicyPatch();
|
||||
applyVisualSettings();
|
||||
const after = get() as unknown as Record<string, unknown>;
|
||||
for (const key of own) {
|
||||
if (after[key] !== previous[key]) {
|
||||
void settingsHooks.onSettingChange.emit({ key, value: after[key], previous: previous[key] });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
exportSettings: (opts) => {
|
||||
const json = buildExport(opts);
|
||||
if (!opts?.forSync) {
|
||||
|
||||
Reference in New Issue
Block a user