Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(settings): categorize user settings #793

Merged
merged 24 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4b503e2
fix(applayout): fix nested anchor warnings
tthvo Jan 6, 2023
ed01646
feat(settings): categorize settings
tthvo Jan 6, 2023
d87d7ef
test(target): apply eslint
tthvo Jan 6, 2023
5965c32
chore(settings): move language select to setting page
tthvo Jan 9, 2023
3e036a5
test(settings): add tests to setting view
tthvo Jan 9, 2023
df1a212
test(settings): add tests for auto-refresh setting
tthvo Jan 10, 2023
cee9fb1
chore(settings): apply eslint fixes
tthvo Jan 10, 2023
fcd744f
test(recordings): update snapshots
tthvo Jan 10, 2023
13b40fd
test(settings): add tests for deletion dialog control
tthvo Jan 10, 2023
d46def9
test(settings): add tests for feature level panel
tthvo Jan 10, 2023
ae058fc
test(settings): add tests for language select
tthvo Jan 10, 2023
a650631
test(settings): add tests for websocket debounce input
tthvo Jan 10, 2023
d0e60a0
test(settings): add tests for notification control
tthvo Jan 10, 2023
a613eb3
test(settings): add tests for automatic analysis config
tthvo Jan 10, 2023
7471d59
chore(eslint): apply eslint fixes
tthvo Jan 10, 2023
4d526db
chore(settings): remove unused classNames
tthvo Jan 10, 2023
b65b607
feat(settings): add link to language setting on user icon
tthvo Jan 10, 2023
440cdcb
feat(login): add language selector for login page
tthvo Jan 10, 2023
5345919
test(settings): wrap setting comp in router
tthvo Jan 10, 2023
63bf197
chore(eslint): apply eslint fixes
tthvo Jan 10, 2023
f4e81fe
!fixup(layout): should select language tab if on /settings route
tthvo Jan 10, 2023
0c14f63
fixup(settings): update group name
tthvo Jan 10, 2023
044eb08
fix(logout): redirect to / if on /settings
tthvo Jan 10, 2023
e7186ef
chore(prettier): apply prettier
tthvo Jan 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ module.exports = {
moduleNameMapper: {
'\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"@app/(.*)": '<rootDir>/src/app/$1'
"@app/(.*)": '<rootDir>/src/app/$1',
"@i18n/(.*)": '<rootDir>/src/i18n/$1',
},

// A preset that is used as a base for Jest's configuration
Expand Down
78 changes: 26 additions & 52 deletions src/app/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,40 +41,40 @@ import build from '@app/build.json';
import { NotificationCenter } from '@app/Notifications/NotificationCenter';
import { Notification, NotificationsContext } from '@app/Notifications/Notifications';
import { IAppRoute, navGroups, routes } from '@app/routes';
import { selectTab } from '@app/Settings/Settings';
import { DynamicFeatureFlag, FeatureFlag } from '@app/Shared/FeatureFlag/FeatureFlag';
import { SessionState } from '@app/Shared/Services/Login.service';
import { NotificationCategory } from '@app/Shared/Services/NotificationChannel.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { FeatureLevel } from '@app/Shared/Services/Settings.service';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import { openTabForUrl } from '@app/utils/utils';
import { localeReadable } from '@i18n/i18nextUtil';
import {
Alert,
AlertActionCloseButton,
AlertGroup,
AlertVariant,
AlertActionCloseButton,
Brand,
Button,
Dropdown,
DropdownGroup,
DropdownItem,
DropdownToggle,
Label,
Masthead,
MastheadBrand,
MastheadContent,
MastheadMain,
MastheadToggle,
Nav,
NavGroup,
NavItem,
NavList,
NotificationBadge,
Page,
PageSidebar,
SkipToContent,
Label,
Masthead,
MastheadBrand,
MastheadContent,
MastheadMain,
MastheadToggle,
PageToggleButton,
SkipToContent,
Toolbar,
ToolbarContent,
ToolbarGroup,
Expand All @@ -92,7 +92,6 @@ import {
} from '@patternfly/react-icons';
import * as _ from 'lodash';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Link, matchPath, NavLink, useHistory, useLocation } from 'react-router-dom';
import { map } from 'rxjs/operators';
import { AuthModal } from './AuthModal';
Expand All @@ -116,7 +115,6 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const [isNotificationDrawerExpanded, setNotificationDrawerExpanded] = React.useState(false);
const [showUserIcon, setShowUserIcon] = React.useState(false);
const [showUserInfoDropdown, setShowUserInfoDropdown] = React.useState(false);
const [showLanguageDropdown, setShowLanguageDropdown] = React.useState(false);
const [showHelpDropdown, setShowHelpDropdown] = React.useState(false);
const [username, setUsername] = React.useState('');
const [notifications, setNotifications] = React.useState([] as Notification[]);
Expand All @@ -125,8 +123,6 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const [errorNotificationsCount, setErrorNotificationsCount] = React.useState(0);
const location = useLocation();

const [_t, i18n] = useTranslation();

React.useEffect(() => {
addSubscription(
serviceContext.target.authFailure().subscribe(() => {
Expand Down Expand Up @@ -270,6 +266,14 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
addSubscription(serviceContext.login.setLoggedOut().subscribe());
}, [serviceContext.login, addSubscription]);

const handleLanguagePref = React.useCallback(() => {
if (routerHistory.location.pathname === '/settings') {
selectTab('Language & Region');
} else {
routerHistory.push('/settings', { preSelectedTab: 'Language & Region' });
}
}, [routerHistory]);

const handleUserInfoToggle = React.useCallback(() => setShowUserInfoDropdown((v) => !v), [setShowUserInfoDropdown]);

React.useEffect(() => {
Expand All @@ -278,11 +282,16 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {

const userInfoItems = React.useMemo(
() => [
<FeatureFlag level={FeatureLevel.BETA} key={'language-preferences-feature-flag'}>
<DropdownGroup key={'language-preferences'}>
<DropdownItem onClick={handleLanguagePref}>Language preference</DropdownItem>
</DropdownGroup>
</FeatureFlag>,
<DropdownGroup key={'log-out'}>
<DropdownItem onClick={handleLogout}>Log out</DropdownItem>
</DropdownGroup>,
],
[handleLogout]
[handleLogout, handleLanguagePref]
);

const UserInfoToggle = React.useMemo(
Expand Down Expand Up @@ -337,28 +346,6 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
),
[handleHelpToggle]
);
const handleLanguageToggle = React.useCallback(() => setShowLanguageDropdown((v) => !v), [setShowLanguageDropdown]);
tthvo marked this conversation as resolved.
Show resolved Hide resolved

const languageItems = React.useMemo(
() => [
<DropdownGroup key={'dropdown-en'}>
<DropdownItem onClick={() => i18n.changeLanguage('en')}>English</DropdownItem>
</DropdownGroup>,
<DropdownGroup key={'dropdown-zh'}>
<DropdownItem onClick={() => i18n.changeLanguage('zh')}>中文</DropdownItem>
</DropdownGroup>,
],
[i18n]
);

const LanguageToggle = React.useMemo(
() => (
<DropdownToggle onToggle={handleLanguageToggle} toggleIndicator={CaretDownIcon}>
{localeReadable(i18n.language)}
</DropdownToggle>
),
[i18n.language, handleLanguageToggle]
);

const levelBadge = React.useCallback((level: FeatureLevel) => {
return (
Expand All @@ -378,17 +365,6 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
<Toolbar isFullHeight isStatic>
<ToolbarContent>
<ToolbarGroup variant="icon-button-group" alignment={{ default: 'alignRight' }}>
<FeatureFlag level={FeatureLevel.BETA}>
<ToolbarItem>
<Dropdown
isPlain
onSelect={() => setShowLanguageDropdown(false)}
isOpen={showLanguageDropdown}
toggle={LanguageToggle}
dropdownItems={languageItems}
/>
</ToolbarItem>
</FeatureFlag>
<FeatureFlag strict level={FeatureLevel.DEVELOPMENT}>
<ToolbarItem>
<Button
Expand Down Expand Up @@ -435,6 +411,7 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
onSelect={() => setShowUserInfoDropdown(false)}
isOpen={showUserInfoDropdown}
toggle={UserInfoToggle}
position="right"
dropdownItems={userInfoItems}
/>
</ToolbarItem>
Expand All @@ -451,17 +428,13 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
handleSettingsButtonClick,
setShowHelpDropdown,
setShowUserInfoDropdown,
setShowLanguageDropdown,
showUserIcon,
showUserInfoDropdown,
showHelpDropdown,
showLanguageDropdown,
UserInfoToggle,
userInfoItems,
HelpToggle,
helpItems,
LanguageToggle,
languageItems,
]
);

Expand All @@ -480,11 +453,12 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
</PageToggleButton>
</MastheadToggle>
<MastheadMain>
<MastheadBrand>
<MastheadBrand component={'div'}>
<Link to="/">
<Brand alt="Cryostat" src={cryostatLogo} className="cryostat-logo" />
</Link>
</MastheadBrand>

<DynamicFeatureFlag levels={[FeatureLevel.DEVELOPMENT, FeatureLevel.BETA]} component={levelBadge} />
</MastheadMain>
<MastheadContent>{HeaderToolbar}</MastheadContent>
Expand Down
8 changes: 7 additions & 1 deletion src/app/BreadcrumbPage/BreadcrumbPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,16 @@ export const BreadcrumbPage: React.FC<BreadcrumbPageProps> = (props) => {
</Breadcrumb>
<Stack hasGutter={true}>
{React.Children.map(props.children, (child) => (
<StackItem isFilled={child && child['isFilled']}>{child}</StackItem>
<StackItem isFilled={isItemFilled(child)}>{child}</StackItem>
))}
</Stack>
</PageSection>
</>
);
};

export const isItemFilled = (item: React.ReactNode): boolean => {
if (!item) return false;
const toCheck = item['props'] ? item['props'] : item;
return toCheck['isFilled'] || toCheck['isFullHeight'];
};
64 changes: 50 additions & 14 deletions src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisConfigForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,18 +277,9 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
return '';
}, [templateName, templateType]);

if (errorMessage != '') {
return (
<ErrorView
title={'Error displaying recording configuration settings'}
message={errorMessage}
retry={isAuthFail(errorMessage) ? authRetry : undefined}
/>
);
}
return (
<Form isHorizontal={props.isSettingsForm}>
<FormSection title={props.isSettingsForm ? undefined : 'Profiling Recording Configuration'}>
const formContent = React.useMemo(
() => (
<>
<FormGroup
label="Template"
isRequired
Expand Down Expand Up @@ -407,7 +398,52 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
</HelperText>
)}
</ActionGroup>
</FormSection>
</Form>
</>
),
[
templateName,
templateType,
templates,
isLoading,
isSaveLoading,
selectedSpecifier,
maxSize,
maxSizeUnits,
maxAge,
maxAgeUnits,
isFormInvalid,
showHelperMessage,
createButtonLoadingProps,
handleMaxSizeChange,
handleMaxAgeChange,
handleMaxSizeUnitChange,
handleMaxAgeUnitChange,
handleSaveConfig,
handleSubmit,
handleTemplateChange,
props.isSettingsForm,
saveButtonLoadingProps,
]
);

if (errorMessage != '') {
return (
<ErrorView
title={'Error displaying recording configuration settings'}
message={errorMessage}
retry={isAuthFail(errorMessage) ? authRetry : undefined}
/>
);
}
return (
<>
{props.isSettingsForm ? (
formContent
) : (
<Form>
<FormSection title={'Profiling Recording Configuration'}>{formContent}</FormSection>
</Form>
)}
</>
);
};
2 changes: 1 addition & 1 deletion src/app/DurationPicker/DurationPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const DurationPicker: React.FC<DurationPickerProps> = (props) => {
isRequired
type="number"
id="duration-picker-period"
aria-describedby="recording-duration-helper"
aria-label="Duration Picker Period Input"
onChange={(v) => props.onPeriodChange(Number(v))}
isDisabled={!props.enabled}
min="0"
Expand Down
21 changes: 19 additions & 2 deletions src/app/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { Language } from '@app/Settings/Language';
import { FeatureFlag } from '@app/Shared/FeatureFlag/FeatureFlag';
import { AuthMethod } from '@app/Shared/Services/Login.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { FeatureLevel } from '@app/Shared/Services/Settings.service';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import { Card, CardBody, CardFooter, CardTitle, PageSection, Text } from '@patternfly/react-core';
import {
Card,
CardActions,
CardBody,
CardFooter,
CardHeader,
CardTitle,
PageSection,
Text,
} from '@patternfly/react-core';
import * as React from 'react';
import { NotificationsContext } from '../Notifications/Notifications';
import { BasicAuthDescriptionText, BasicAuthForm } from './BasicAuthForm';
Expand Down Expand Up @@ -100,7 +112,12 @@ export const Login: React.FC<LoginProps> = (_) => {
return (
<PageSection>
<Card>
<CardTitle>Login</CardTitle>
<CardHeader>
<CardTitle>Login</CardTitle>
<CardActions>
<FeatureFlag level={FeatureLevel.BETA}>{React.createElement(Language.content, null)}</FeatureFlag>
</CardActions>
</CardHeader>
<CardBody>{loginForm}</CardBody>
<CardFooter>{descriptionText}</CardFooter>
</Card>
Expand Down
1 change: 1 addition & 0 deletions src/app/Settings/AutoRefresh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,5 @@ export const AutoRefresh: UserSetting = {
description:
'Set the refresh period for content views. Views normally update dynamically via WebSocket notifications, so this should not be needed unless WebSockets are not working.',
content: Component,
category: 'Connectivity',
};
3 changes: 2 additions & 1 deletion src/app/Settings/AutomatedAnalysisConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const Component = () => {
<TargetSelect simple />
</StackItem>
<StackItem>
<Title headingLevel="h2" size="lg">
<Title headingLevel="h3" size="md">
Current configuration
</Title>
</StackItem>
Expand Down Expand Up @@ -108,4 +108,5 @@ export const AutomatedAnalysisConfig: UserSetting = {
description:
'Set the recording configuration for automated analysis recordings. You may want smaller or larger values for max-age and max-size depending on how recent you want events to be recorded from the analysis.',
content: Component,
category: 'Dashboard',
tthvo marked this conversation as resolved.
Show resolved Hide resolved
};
Loading