Skip to content

Commit 36272b6

Browse files
committed
Implement toggle for high contrast mode
1 parent de398fe commit 36272b6

5 files changed

Lines changed: 71 additions & 7 deletions

File tree

src/platform/packages/shared/kbn-user-profile-components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type {
2525
export type {
2626
UserProfileData,
2727
UserSettingsData,
28+
ContrastModeValue,
2829
DarkModeValue,
2930
UserProfileAvatarData,
3031
} from './src/types';

src/platform/packages/shared/kbn-user-profile-components/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ export interface UserProfileAvatarData {
2929

3030
export type DarkModeValue = 'system' | 'dark' | 'light' | 'space_default';
3131

32+
export type ContrastModeValue = 'standard' | 'high';
33+
3234
/**
3335
* User settings stored in the data object of the User Profile
3436
*/
3537
export interface UserSettingsData {
3638
darkMode?: DarkModeValue;
39+
contrastMode?: ContrastModeValue;
3740
solutionNavOptOut?: boolean;
3841
}
3942

src/platform/packages/shared/react/kibana_context/root/eui_provider.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10+
import * as Rx from 'rxjs';
1011
import React, { FC, PropsWithChildren, useMemo } from 'react';
1112
import useObservable from 'react-use/lib/useObservable';
1213
import createCache from '@emotion/cache';
@@ -19,15 +20,22 @@ import {
1920
getThemeConfigByName,
2021
DEFAULT_THEME_CONFIG,
2122
} from '@kbn/react-kibana-context-common';
22-
import type { UserProfileService } from '@kbn/core-user-profile-browser';
2323
import type { ThemeServiceStart } from '@kbn/react-kibana-context-common';
2424

25+
interface IUserProfile {
26+
getUserProfile$: () => Rx.Observable<Record<string, unknown> | null>;
27+
}
28+
29+
interface UserSettings {
30+
contrastMode: 'high' | 'standard';
31+
}
32+
2533
/**
2634
* Props for the KibanaEuiProvider.
2735
*/
2836
export interface KibanaEuiProviderProps extends Pick<EuiProviderProps<{}>, 'modify' | 'colorMode'> {
2937
theme: ThemeServiceStart;
30-
userProfile?: Pick<UserProfileService, 'getUserProfile$'>; // TODO: use this to access a "high contrast mode" flag from user settings. Pass the flag to EuiProvider, when it is supported in EUI.
38+
userProfile?: IUserProfile;
3139
globalStyles?: boolean;
3240
}
3341

@@ -66,6 +74,7 @@ const cache = { default: emotionCache, global: globalCache, utility: utilitiesCa
6674
*/
6775
export const KibanaEuiProvider: FC<PropsWithChildren<KibanaEuiProviderProps>> = ({
6876
theme,
77+
userProfile,
6978
globalStyles: globalStylesProp,
7079
colorMode: colorModeProp,
7180
modify,
@@ -89,6 +98,12 @@ export const KibanaEuiProvider: FC<PropsWithChildren<KibanaEuiProviderProps>> =
8998
// colorMode provided by the `theme`.
9099
const colorMode = colorModeProp || themeColorMode;
91100

101+
const getUserProfile$ = userProfile?.getUserProfile$ ?? Rx.of;
102+
const userProfileData = useObservable(getUserProfile$());
103+
104+
const userSettings = userProfileData?.userSettings as UserSettings | undefined;
105+
const highContrastMode = userSettings?.contrastMode === 'high';
106+
92107
// This logic was drawn from the Core theme provider, and wasn't present (or even used)
93108
// elsewhere. Should be a passive addition to anyone using the older theme provider(s).
94109
const globalStyles = globalStylesProp === false ? false : undefined;
@@ -101,8 +116,8 @@ export const KibanaEuiProvider: FC<PropsWithChildren<KibanaEuiProviderProps>> =
101116
colorMode,
102117
globalStyles,
103118
utilityClasses: globalStyles,
119+
highContrastMode,
104120
theme: _theme,
105-
highContrastMode: false,
106121
}}
107122
>
108123
{children}

x-pack/platform/plugins/shared/security/public/account_management/user_profile/user_profile.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ describe('useUserProfileForm', () => {
7676
"initials": "fn",
7777
},
7878
"userSettings": Object {
79+
"contrastMode": "standard",
7980
"darkMode": "space_default",
8081
},
8182
},

x-pack/platform/plugins/shared/security/public/account_management/user_profile/user_profile.tsx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
EuiKeyPadMenuItem,
2525
EuiPopover,
2626
EuiSpacer,
27+
EuiSwitch,
2728
EuiText,
2829
EuiToolTip,
2930
useEuiTheme,
@@ -49,7 +50,11 @@ import {
4950
useFormChangesContext,
5051
} from '@kbn/security-form-components';
5152
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
52-
import type { DarkModeValue, UserProfileData } from '@kbn/user-profile-components';
53+
import type {
54+
ContrastModeValue,
55+
DarkModeValue,
56+
UserProfileData,
57+
} from '@kbn/user-profile-components';
5358
import { UserAvatar, useUpdateUserProfile } from '@kbn/user-profile-components';
5459

5560
import { createImageHandler, getRandomColor, VALID_HEX_COLOR } from './utils';
@@ -110,6 +115,7 @@ export interface UserProfileFormValues {
110115
};
111116
userSettings: {
112117
darkMode: DarkModeValue;
118+
contrastMode: ContrastModeValue;
113119
};
114120
};
115121
avatarType: 'initials' | 'image';
@@ -236,7 +242,7 @@ const UserSettingsEditor: FunctionComponent<UserSettingsEditorProps> = ({
236242
<FormLabel for="data.userSettings.darkMode">
237243
<FormattedMessage
238244
id="xpack.security.accountManagement.userProfile.userSettings.theme"
239-
defaultMessage="Mode"
245+
defaultMessage="Color mode"
240246
/>
241247
</FormLabel>
242248
),
@@ -318,6 +324,24 @@ const UserSettingsEditor: FunctionComponent<UserSettingsEditorProps> = ({
318324
</>
319325
);
320326

327+
const idSelectedContrast = formik.values.data.userSettings.contrastMode;
328+
const contrastModeMenu = () => {
329+
return (
330+
<EuiSwitch
331+
id="contrastMode"
332+
label={i18n.translate(
333+
'xpack.security.accountManagement.userProfile.highContrastModeButton',
334+
{ defaultMessage: 'High contrast' }
335+
)}
336+
checked={idSelectedContrast === 'high'}
337+
onChange={({ target }) => {
338+
const value = target.checked ? 'high' : 'standard';
339+
return formik.setFieldValue('data.userSettings.contrastMode', value);
340+
}}
341+
/>
342+
);
343+
};
344+
321345
return (
322346
<EuiDescribedFormGroup
323347
fullWidth
@@ -326,7 +350,7 @@ const UserSettingsEditor: FunctionComponent<UserSettingsEditorProps> = ({
326350
<h2>
327351
<FormattedMessage
328352
id="xpack.security.accountManagement.userProfile.userSettingsTitle"
329-
defaultMessage="Theme"
353+
defaultMessage="Appearance"
330354
/>
331355
</h2>
332356
}
@@ -343,6 +367,22 @@ const UserSettingsEditor: FunctionComponent<UserSettingsEditorProps> = ({
343367
{deprecatedWarning}
344368
</>
345369
</FormRow>
370+
371+
<FormRow
372+
name="data.userSettings.contrastMode"
373+
fullWidth
374+
label={
375+
<FormLabel for="data.userSettings.contrastMode">
376+
<FormattedMessage
377+
id="xpack.security.accountManagement.userProfile.userSettings.contrastMode"
378+
defaultMessage="Accessibility"
379+
/>
380+
</FormLabel>
381+
}
382+
hasChildLabel={false}
383+
>
384+
{contrastModeMenu()}
385+
</FormRow>
346386
</EuiDescribedFormGroup>
347387
);
348388
};
@@ -872,6 +912,7 @@ export function useUserProfileForm({ user, data }: UserProfileProps) {
872912
},
873913
userSettings: {
874914
darkMode: data.userSettings?.darkMode || 'space_default',
915+
contrastMode: data.userSettings?.contrastMode || 'standard',
875916
},
876917
}
877918
: undefined,
@@ -924,7 +965,10 @@ export function useUserProfileForm({ user, data }: UserProfileProps) {
924965
resetInitialValues(values);
925966

926967
let isRefreshRequired = false;
927-
if (initialValues.data?.userSettings.darkMode !== values.data?.userSettings.darkMode) {
968+
if (
969+
initialValues.data?.userSettings.darkMode !== values.data?.userSettings.darkMode ||
970+
initialValues.data?.userSettings.contrastMode !== values.data?.userSettings.contrastMode
971+
) {
928972
isRefreshRequired = true;
929973
}
930974
showSuccessNotification({ isRefreshRequired });

0 commit comments

Comments
 (0)