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

CONSOLE-2391: Monitoring: Add i18n for Metrics and Dashboards pages #7266

Merged
merged 1 commit into from
Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
71 changes: 44 additions & 27 deletions frontend/public/components/monitoring/dashboards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as _ from 'lodash-es';
import { Dropdown, DropdownToggle, DropdownItem } from '@patternfly/react-core';
import * as React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { Map as ImmutableMap } from 'immutable';

Expand Down Expand Up @@ -70,6 +71,8 @@ const VariableDropdown: React.FC<VariableDropdownProps> = ({
onChange,
selectedKey,
}) => {
const { t } = useTranslation();

const [isOpen, toggleIsOpen, , setClosed] = useBoolean(false);

return (
Expand All @@ -79,7 +82,7 @@ const VariableDropdown: React.FC<VariableDropdownProps> = ({
<Dropdown
toggle={
<DropdownToggle className="monitoring-dashboards__dropdown-button" isDisabled={true}>
<RedExclamationCircleIcon /> Error loading options
<RedExclamationCircleIcon /> {t('monitoring~Error loading options')}
</DropdownToggle>
}
/>
Expand Down Expand Up @@ -201,29 +204,31 @@ const AllVariableDropdowns = connect(({ UI }: RootState) => ({
variables: UI.getIn(['monitoringDashboards', 'variables']),
}))(AllVariableDropdowns_);

const timespanOptions = {
'5m': '5 minutes',
'15m': '15 minutes',
'30m': '30 minutes',
'1h': '1 hour',
'2h': '2 hours',
'6h': '6 hours',
'12h': '12 hours',
'1d': '1 day',
'2d': '2 days',
'1w': '1 week',
'2w': '2 weeks',
};

const TimespanDropdown_: React.FC<TimespanDropdownProps> = ({ timespan, setTimespan }) => {
const { t } = useTranslation();

const onChange = React.useCallback((v: string) => setTimespan(parsePrometheusDuration(v)), [
setTimespan,
]);

const timespanOptions = {
'5m': t('monitoring~{{count}} minute', { count: 5 }),
'15m': t('monitoring~{{count}} minute', { count: 15 }),
'30m': t('monitoring~{{count}} minute', { count: 30 }),
'1h': t('monitoring~{{count}} hour', { count: 1 }),
'2h': t('monitoring~{{count}} hour', { count: 2 }),
'6h': t('monitoring~{{count}} hour', { count: 6 }),
'12h': t('monitoring~{{count}} hour', { count: 12 }),
'1d': t('monitoring~{{count}} day', { count: 1 }),
'2d': t('monitoring~{{count}} day', { count: 2 }),
'1w': t('monitoring~{{count}} week', { count: 1 }),
'2w': t('monitoring~{{count}} week', { count: 2 }),
};

return (
<VariableDropdown
items={timespanOptions}
label="Time Range"
label={t('monitoring~Time range')}
onChange={onChange}
selectedKey={formatPrometheusDuration(timespan)}
/>
Expand All @@ -239,12 +244,18 @@ export const TimespanDropdown = connect(
},
)(TimespanDropdown_);

const PollIntervalDropdown_ = ({ interval, setInterval }) => (
<div className="form-group monitoring-dashboards__dropdown-wrap">
<label className="monitoring-dashboards__dropdown-title">Refresh Interval</label>
<IntervalDropdown interval={interval} setInterval={setInterval} />
</div>
);
const PollIntervalDropdown_ = ({ interval, setInterval }) => {
const { t } = useTranslation();

return (
<div className="form-group monitoring-dashboards__dropdown-wrap">
<label className="monitoring-dashboards__dropdown-title">
{t('monitoring~Refresh interval')}
</label>
<IntervalDropdown interval={interval} setInterval={setInterval} />
</div>
);
};

export const PollIntervalDropdown = connect(
({ UI }: RootState) => ({
Expand Down Expand Up @@ -405,6 +416,8 @@ const MonitoringDashboardsPage_: React.FC<MonitoringDashboardsPageProps> = ({
match,
patchAllVariables,
}) => {
const { t } = useTranslation();

const [board, setBoard] = React.useState<string>();
const [boards, setBoards] = React.useState<Board[]>([]);
const [error, setError] = React.useState<string>();
Expand All @@ -428,7 +441,11 @@ const MonitoringDashboardsPage_: React.FC<MonitoringDashboardsPageProps> = ({
name: item.metadata.name,
};
} catch (e) {
setError(`Could not parse JSON data for dashboard "${item.metadata.name}"`);
setError(
t('monitoring~Could not parse JSON data for dashboard "{{dashboard}}"', {
dashboard: item.metadata.name,
}),
);
}
};

Expand All @@ -443,7 +460,7 @@ const MonitoringDashboardsPage_: React.FC<MonitoringDashboardsPageProps> = ({
setError(_.get(err, 'json.error', err.message));
}
});
}, [safeFetch, setLoaded]);
}, [safeFetch, setLoaded, t]);

const boardItems = React.useMemo(() => _.mapValues(_.mapKeys(boards, 'name'), 'data.title'), [
boards,
Expand Down Expand Up @@ -492,13 +509,13 @@ const MonitoringDashboardsPage_: React.FC<MonitoringDashboardsPageProps> = ({
return (
<>
<Helmet>
<title>Metrics Dashboards</title>
<title>{t('monitoring~Metrics dashboards')}</title>
</Helmet>
<div className="co-m-nav-title co-m-nav-title--detail">
<div className="monitoring-dashboards__header">
<h1 className="co-m-pane__heading">
<span>
Dashboards <GrafanaLink />
{t('monitoring~Dashboards')} <GrafanaLink />
</span>
</h1>
<div className="monitoring-dashboards__options">
Expand All @@ -510,7 +527,7 @@ const MonitoringDashboardsPage_: React.FC<MonitoringDashboardsPageProps> = ({
{!_.isEmpty(boardItems) && (
<VariableDropdown
items={boardItems}
label="Dashboard"
label={t('monitoring~Dashboard')}
onChange={changeBoard}
selectedKey={board}
/>
Expand Down
97 changes: 65 additions & 32 deletions frontend/public/components/monitoring/metrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,22 @@ const MetricsActionsMenu_: React.FC<MetricsActionsMenuProps> = ({
isAllExpanded,
setAllExpanded,
}) => {
const { t } = useTranslation();

const doDelete = () => {
deleteAll();
focusedQuery = undefined;
};

const actionsMenuActions = [
{ label: 'Add query', callback: addQuery },
{ label: t('monitoring~Add query'), callback: addQuery },
{
label: `${isAllExpanded ? 'Collapse' : 'Expand'} all query tables`,
label: isAllExpanded
? t('monitoring~Collapse all query tables')
: t('monitoring~Expand all query tables'),
callback: () => setAllExpanded(!isAllExpanded),
},
{ label: 'Delete all queries', callback: doDelete },
{ label: t('monitoring~Delete all queries'), callback: doDelete },
];

return (
Expand Down Expand Up @@ -208,6 +212,7 @@ const graphStateToProps = ({ UI }: RootState) => ({

const ToggleGraph_ = ({ hideGraphs, toggle }) => {
const { t } = useTranslation();

const icon = hideGraphs ? <ChartLineIcon /> : <CompressIcon />;

return (
Expand All @@ -226,6 +231,8 @@ export const ToggleGraph = connect(graphStateToProps, { toggle: UIActions.monito
);

const MetricsDropdown_: React.FC<MetricsDropdownProps> = ({ insertText, setMetrics }) => {
const { t } = useTranslation();

const [items, setItems] = React.useState<MetricsDropdownItems>();
const [error, setError] = React.useState<PrometheusAPIError>();

Expand Down Expand Up @@ -258,7 +265,7 @@ const MetricsDropdown_: React.FC<MetricsDropdownProps> = ({ insertText, setMetri
}
};

let title: React.ReactNode = 'Insert Metric at Cursor';
let title: React.ReactNode = t('monitoring~Insert metric at cursor');
if (error !== undefined) {
const message =
error?.response?.status === 403 ? 'Access restricted.' : 'Failed to load metrics list.';
Expand Down Expand Up @@ -396,6 +403,8 @@ const QueryInput_: React.FC<QueryInputProps> = ({
runQueries,
text = '',
}) => {
const { t } = useTranslation();

const [token, setToken] = React.useState('');

const inputRef = React.useRef(null);
Expand Down Expand Up @@ -487,24 +496,26 @@ const QueryInput_: React.FC<QueryInputProps> = ({
// Set the default textarea height to the number of lines in the query text
const rows = _.clamp((text.match(/\n/g) || []).length + 1, 2, 10);

const placeholder = t('monitoring~Expression (press Shift+Enter for newlines)');

return (
<div className="query-browser__query pf-c-dropdown">
<textarea
aria-label="Expression (press Shift+Enter for newlines)"
aria-label={placeholder}
autoFocus
className="pf-c-form-control query-browser__query-input"
onBlur={onBlur}
onChange={onChange}
onKeyDown={onKeyDown}
placeholder="Expression (press Shift+Enter for newlines)"
placeholder={placeholder}
ref={inputRef}
rows={rows}
spellCheck={false}
value={text}
/>
<Button
className="query-browser__clear-icon"
aria-label="Clear Query"
aria-label={t('monitoring~Clear query')}
onClick={onClear}
type="button"
variant="plain"
Expand Down Expand Up @@ -552,6 +563,8 @@ const QueryKebab_: React.FC<QueryKebabProps> = ({
series,
toggleIsEnabled,
}) => {
const { t } = useTranslation();

const toggleAllSeries = () => patchQuery({ disabledSeries: isDisabledSeriesEmpty ? series : [] });

const doDelete = () => {
Expand All @@ -562,12 +575,17 @@ const QueryKebab_: React.FC<QueryKebabProps> = ({
return (
<Kebab
options={[
{ label: `${isEnabled ? 'Disable' : 'Enable'} query`, callback: toggleIsEnabled },
{
label: `${isDisabledSeriesEmpty ? 'Hide' : 'Show'} all series`,
label: isEnabled ? t('monitoring~Disable query') : t('monitoring~Enable query'),
callback: toggleIsEnabled,
},
{
label: isDisabledSeriesEmpty
? t('monitoring~Hide all series')
: t('monitoring~Show all series'),
callback: toggleAllSeries,
},
{ label: 'Delete query', callback: doDelete },
{ label: t('monitoring~Delete query'), callback: doDelete },
]}
/>
);
Expand Down Expand Up @@ -633,13 +651,14 @@ const QueryTable_: React.FC<QueryTableProps> = ({
query,
series,
}) => {
const { t } = useTranslation();

const [data, setData] = React.useState<PrometheusData>();
const [error, setError] = React.useState<PrometheusAPIError>();
const [page, setPage] = React.useState(1);
const [perPage, setPerPage] = React.useState(50);
const [sortBy, setSortBy] = React.useState<ISortBy>();

const { t } = useTranslation();
const safeFetch = React.useCallback(useSafeFetch(), []);

const tick = () => {
Expand Down Expand Up @@ -809,8 +828,10 @@ const Query_: React.FC<QueryProps> = ({
patchQuery,
toggleIsEnabled,
}) => {
const { t } = useTranslation();

const switchKey = `${id}-${isEnabled}`;
const switchLabel = `${isEnabled ? 'Disable' : 'Enable'} query`;
const switchLabel = isEnabled ? t('monitoring~Disable query') : t('monitoring~Enable query');

const toggleIsExpanded = () => patchQuery({ isExpanded: !isExpanded });

Expand Down Expand Up @@ -850,6 +871,8 @@ const Query = connect(
)(Query_);

const QueryBrowserWrapper_: React.FC<QueryBrowserWrapperProps> = ({ patchQuery, queriesList }) => {
const { t } = useTranslation();

const queries = queriesList.toJS();

// Initialize queries from URL parameters
Expand Down Expand Up @@ -893,13 +916,13 @@ const QueryBrowserWrapper_: React.FC<QueryBrowserWrapperProps> = ({ patchQuery,
<EmptyState variant={EmptyStateVariant.full}>
<EmptyStateIcon icon={ChartLineIcon} />
<Title headingLevel="h2" size="md">
No Query Entered
{t('monitoring~No query entered')}
</Title>
<EmptyStateBody>
Enter a query in the box below to explore metrics for this cluster.
{t('monitoring~Enter a query in the box below to explore metrics for this cluster.')}
</EmptyStateBody>
<Button onClick={insertExampleQuery} variant="primary">
Insert Example Query
{t('monitoring~Insert example query')}
</Button>
</EmptyState>
</div>
Expand All @@ -916,23 +939,31 @@ const QueryBrowserWrapper = connect(
{ patchQuery: UIActions.queryBrowserPatchQuery },
)(QueryBrowserWrapper_);

const AddQueryButton_ = ({ addQuery }) => (
<Button
className="query-browser__inline-control"
onClick={addQuery}
type="button"
variant="secondary"
>
Add Query
</Button>
);
const AddQueryButton_ = ({ addQuery }) => {
const { t } = useTranslation();

return (
<Button
className="query-browser__inline-control"
onClick={addQuery}
type="button"
variant="secondary"
>
{t('monitoring~Add query')}
</Button>
);
};
const AddQueryButton = connect(null, { addQuery: UIActions.queryBrowserAddQuery })(AddQueryButton_);

const RunQueriesButton_ = ({ runQueries }) => (
<Button onClick={runQueries} type="submit" variant="primary">
Run Queries
</Button>
);
const RunQueriesButton_ = ({ runQueries }) => {
const { t } = useTranslation();

return (
<Button onClick={runQueries} type="submit" variant="primary">
{t('monitoring~Run queries')}
</Button>
);
};
const RunQueriesButton = connect(null, { runQueries: UIActions.queryBrowserRunQueries })(
RunQueriesButton_,
);
Expand All @@ -958,18 +989,20 @@ const PollIntervalDropdown = connect(
)(IntervalDropdown);

const QueryBrowserPage_: React.FC<QueryBrowserPageProps> = ({ deleteAll }) => {
const { t } = useTranslation();

// Clear queries on unmount
React.useEffect(() => deleteAll, [deleteAll]);

return (
<>
<Helmet>
<title>Metrics</title>
<title>{t('monitoring~Metrics')}</title>
</Helmet>
<div className="co-m-nav-title">
<h1 className="co-m-pane__heading">
<span>
Metrics
{t('monitoring~Metrics')}
<HeaderPrometheusLink />
</span>
<div className="co-actions">
Expand Down
Loading