Skip to content

Commit

Permalink
Implements profile theme selection with KeyPadMenu (#160925)
Browse files Browse the repository at this point in the history
Closes #155941

## Summary

This PR changes the implementation of the theme selection component in
the user profile screen from EuiButtonGroup to EuiKeyPadMenu. This
better matches the original design.

### Previous Implementation
<img width="1090" alt="Screenshot 2023-06-28 at 1 49 22 PM"
src="https://github.com/elastic/kibana/assets/103939324/59a184cf-655b-423a-9ba8-16450166fb4c">


### Tests
-
`x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx`

### Manual Testing
- Start Elasricsearch, and start Kibana with an empty
kibana.yml/kibana.dev.yml
- Navigate to the _Edit profile_ screen via the profile/avatar button in
the top right portion of the uI

<img width="269" alt="Screenshot 2023-06-29 at 11 47 42 AM"
src="https://github.com/elastic/kibana/assets/103939324/52f5288f-0623-4658-a7a2-2ef85164cadc">

- Verify the theme settings appear as a KeyPadMenu and function as
expected

<img width="1079" alt="Screenshot 2023-06-28 at 2 38 44 PM"
src="https://github.com/elastic/kibana/assets/103939324/a3745b8b-f908-4cd0-bcf6-2bf878578499">

- Modify the kibana.yml (or kibana.dev.yml) with the line
`uiSettings.overrides.theme:darkMode: true`
- Refresh Kibana and verify that the dark theme is rendered and the
theme settings are disabled and includes a lock icon tip explaining why
the mode setting is locked

<img width="1051" alt="Screenshot 2023-06-30 at 12 24 12 PM"
src="https://github.com/elastic/kibana/assets/103939324/5968e1e4-88ae-43a6-8fea-0df7cf2db25b">

- Modify the kibana.yml (or kibana.dev.yml) with the line
`uiSettings.overrides.theme:darkMode: false`
- Refresh Kibana and verify that the light theme is rendered and the
theme settings are disabled and includes a lock icon tip explaining why
the mode setting is locked
  • Loading branch information
jeramysoucy authored Jul 3, 2023
1 parent d7454d4 commit a81287f
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,20 @@ describe('useUserProfileForm', () => {
<UserProfile user={nonCloudUser} data={data} />
</Providers>
);
const darkModeButton = testWrapper.find('EuiButtonGroup[data-test-subj="darkModeButton"]');
expect(darkModeButton).toBeTruthy();
expect(darkModeButton.getDOMNode()).not.toBeDisabled();

const overrideMsg = testWrapper.find('EuiText[data-test-subj="themeOverrideMessage"]');
expect(overrideMsg).toHaveLength(0);

const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]');
expect(themeMenu).toHaveLength(1);

const themeOptions = themeMenu.find('EuiKeyPadMenuItem');
expect(themeOptions).toHaveLength(3);
themeOptions.forEach((option) => {
expect(option.getDOMNode().classList.contains('euiKeyPadMenuItem-isDisabled')).toEqual(
false
);
});
});

it('should not display if the User is a cloud user', () => {
Expand All @@ -281,7 +292,7 @@ describe('useUserProfileForm', () => {
</Providers>
);

expect(testWrapper.exists('EuiButtonGroup[data-test-subj="darkModeButton"]')).toBeFalsy();
expect(testWrapper.exists('EuiButtonGroup[data-test-subj="themeMenu"]')).toBeFalsy();
});

it('should add special toast after submitting form successfully since darkMode requires a refresh', async () => {
Expand Down Expand Up @@ -314,8 +325,8 @@ describe('useUserProfileForm', () => {
const data: UserProfileData = {};

const nonCloudUser = mockAuthenticatedUser({ elastic_cloud_user: false });
coreStart.settings.client.get.mockReturnValueOnce(true);
coreStart.settings.client.isOverridden.mockReturnValueOnce(true);
coreStart.settings.client.get.mockReturnValue(true);
coreStart.settings.client.isOverridden.mockReturnValue(true);

const testWrapper = mount(
<Providers
Expand All @@ -332,17 +343,27 @@ describe('useUserProfileForm', () => {
</Providers>
);

const darkModeButton = testWrapper.find('EuiButtonGroup[data-test-subj="darkModeButton"]');
expect(darkModeButton).toBeTruthy();
expect(darkModeButton.getDOMNode()).toHaveProperty('disabled');
const overrideMsg = testWrapper.find('EuiIconTip[data-test-subj="themeOverrideTooltip"]');
expect(overrideMsg).toHaveLength(1);

const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]');
expect(themeMenu).toHaveLength(1);

const themeOptions = themeMenu.find('EuiKeyPadMenuItem');
expect(themeOptions).toHaveLength(3);
themeOptions.forEach((option) => {
expect(option.getDOMNode().classList.contains('euiKeyPadMenuItem-isDisabled')).toEqual(
true
);
});
});

it('should be disabled if the theme has been set to `darkMode: false` in the config', () => {
const data: UserProfileData = {};

const nonCloudUser = mockAuthenticatedUser({ elastic_cloud_user: false });
coreStart.settings.client.get.mockReturnValueOnce(false);
coreStart.settings.client.isOverridden.mockReturnValueOnce(true);
coreStart.settings.client.get.mockReturnValue(false);
coreStart.settings.client.isOverridden.mockReturnValue(true);

const testWrapper = mount(
<Providers
Expand All @@ -359,9 +380,19 @@ describe('useUserProfileForm', () => {
</Providers>
);

const darkModeButton = testWrapper.find('EuiButtonGroup[data-test-subj="darkModeButton"]');
expect(darkModeButton).toBeTruthy();
expect(darkModeButton.getDOMNode()).toHaveProperty('disabled');
const overrideMsg = testWrapper.find('EuiIconTip[data-test-subj="themeOverrideTooltip"]');
expect(overrideMsg).toHaveLength(1);

const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]');
expect(themeMenu).toHaveLength(1);

const themeOptions = themeMenu.find('EuiKeyPadMenuItem');
expect(themeOptions).toHaveLength(3);
themeOptions.forEach((option) => {
expect(option.getDOMNode().classList.contains('euiKeyPadMenuItem-isDisabled')).toEqual(
true
);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
EuiFormRow,
EuiIcon,
EuiIconTip,
EuiKeyPadMenu,
EuiKeyPadMenuItem,
EuiPageTemplate_Deprecated as EuiPageTemplate,
EuiSpacer,
EuiText,
Expand Down Expand Up @@ -165,6 +167,27 @@ function UserSettingsEditor({
}
}

interface ThemeKeyPadItem {
id: string;
label: string;
icon: string;
}

const themeKeyPadMenuItem = ({ id, label, icon }: ThemeKeyPadItem) => {
return (
<EuiKeyPadMenuItem
name={id}
label={label}
checkable="single"
isSelected={idSelected === id}
isDisabled={isThemeOverridden}
onChange={() => formik.setFieldValue('data.userSettings.darkMode', id)}
>
<EuiIcon type={icon} size="l" />
</EuiKeyPadMenuItem>
);
};

return (
<EuiDescribedFormGroup
fullWidth
Expand All @@ -186,62 +209,56 @@ function UserSettingsEditor({
>
<FormRow
name="data.userSettings.darkMode"
helpText={renderHelpText(isThemeOverridden)}
label={
<FormLabel for="data.userSettings.darkMode">
<FormattedMessage
id="xpack.security.accountManagement.userProfile.userSettings.theme"
defaultMessage="Mode"
/>
</FormLabel>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<FormLabel for="data.userSettings.darkMode">
<FormattedMessage
id="xpack.security.accountManagement.userProfile.userSettings.theme"
defaultMessage="Mode"
/>
</FormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>{renderHelpText(isThemeOverridden)}</EuiFlexItem>
</EuiFlexGroup>
}
fullWidth
>
<EuiButtonGroup
legend={i18n.translate(
<EuiKeyPadMenu
aria-label={i18n.translate(
'xpack.security.accountManagement.userProfile.userSettings.themeGroupDescription',
{
defaultMessage: 'Elastic theme',
}
)}
buttonSize="m"
data-test-subj="darkModeButton"
idSelected={idSelected}
isDisabled={isThemeOverridden}
options={[
{
id: '',
label: (
<FormattedMessage
id="xpack.security.accountManagement.userProfile.defaultModeButton"
defaultMessage="Space default"
/>
),
},
{
id: 'light',
label: (
<FormattedMessage
id="xpack.security.accountManagement.userProfile.lightModeButton"
defaultMessage="Light"
/>
),
iconType: 'sun',
},
{
id: 'dark',
label: (
<FormattedMessage
id="xpack.security.accountManagement.userProfile.darkModeButton"
defaultMessage="Dark"
/>
),
iconType: 'moon',
},
]}
onChange={(id: string) => formik.setFieldValue('data.userSettings.darkMode', id)}
isFullWidth
/>
data-test-subj="themeMenu"
checkable={true}
>
{themeKeyPadMenuItem({
id: '',
label: i18n.translate(
'xpack.security.accountManagement.userProfile.defaultModeButton',
{
defaultMessage: 'Space default',
}
),
icon: 'spaces',
})}
{themeKeyPadMenuItem({
id: 'light',
label: i18n.translate('xpack.security.accountManagement.userProfile.lightModeButton', {
defaultMessage: 'Light',
}),
icon: 'sun',
})}
{themeKeyPadMenuItem({
id: 'dark',
label: i18n.translate('xpack.security.accountManagement.userProfile.darkModeButton', {
defaultMessage: 'Dark',
}),
icon: 'moon',
})}
</EuiKeyPadMenu>
</FormRow>
</EuiDescribedFormGroup>
);
Expand Down Expand Up @@ -912,12 +929,23 @@ export const SaveChangesBottomBar: FunctionComponent = () => {
function renderHelpText(isOverridden: boolean) {
if (isOverridden) {
return (
<EuiText size="xs">
<FormattedMessage
id="xpack.security.accountManagement.userProfile.overriddenMessage"
defaultMessage="This setting is overridden by the Kibana server and can not be changed."
/>
</EuiText>
<EuiIconTip
data-test-subj="themeOverrideTooltip"
aria-label={i18n.translate(
'xpack.security.accountManagement.userProfile.themeModeLockedLabel',
{
defaultMessage: 'Theme mode locked',
}
)}
size="s"
type="lock"
content={
<FormattedMessage
id="xpack.security.accountManagement.userProfile.overriddenMessage"
defaultMessage="This setting is overridden by the Kibana server and can not be changed."
/>
}
/>
);
}
}
Expand Down

0 comments on commit a81287f

Please sign in to comment.