Internationalisation: More markup for translations (#103392)
* some more translations * more markup * more translations * fix tests
This commit is contained in:
+4
-172
@@ -654,19 +654,15 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellNG.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Table/TableNG/Filter/Filter.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Table/TableNG/Filter/FilterPopup.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Table/TableNG/Filter/utils.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@@ -685,8 +681,7 @@ exports[`better eslint`] = {
|
||||
"packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Table/TableNG/utils.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@@ -850,68 +845,12 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not re-export imported variable (\`updateConfigurationSubtitle\`)", "2"],
|
||||
[0, 0, 0, "Do not re-export imported variable (\`updateNavIndex\`)", "3"]
|
||||
],
|
||||
"public/app/core/components/AccessControl/AddPermission.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"]
|
||||
],
|
||||
"public/app/core/components/AccessControl/PermissionListItem.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/AccessControl/index.ts:5381": [
|
||||
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/News/NewsDrawer.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/News/NewsWrapper.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSelect.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/TopBar/ProfileButton.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/Breadcrumbs/Breadcrumbs.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/CloseButton/CloseButton.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/DynamicImports/SafeDynamicImport.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/core/components/FolderFilter/FolderFilter.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"]
|
||||
],
|
||||
"public/app/core/components/ForgottenPassword/ChangePassword.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"]
|
||||
],
|
||||
"public/app/core/components/ForgottenPassword/ForgottenPassword.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"]
|
||||
],
|
||||
"public/app/core/components/FormPrompt/FormPrompt.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/GraphNG/GraphNG.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
@@ -920,103 +859,11 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
],
|
||||
"public/app/core/components/Layers/LayerDragDropList.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"]
|
||||
],
|
||||
"public/app/core/components/Layers/LayerName.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/LocalStorageValueProvider/index.tsx:5381": [
|
||||
[0, 0, 0, "Do not re-export imported variable (\`./LocalStorageValueProvider\`)", "0"]
|
||||
],
|
||||
"public/app/core/components/Login/PasswordlessConfirmationForm.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/OptionsUI/color.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/OptionsUI/fieldColor.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/OptionsUI/units.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/Page/EditableTitle.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/PanelTypeFilter/PanelTypeFilter.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/PluginHelp/PluginHelp.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/QueryOperationRow/QueryOperationRowHeader.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/RolePicker/RoleMenuGroupOption.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/RolePicker/RoleMenuOption.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/RolePicker/RolePickerMenu.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/RolePicker/RolePickerSubMenu.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/RolePickerDrawer/RolePickerBadges.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/Select/OldFolderPicker.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/Select/OrgPicker.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/Select/ServiceAccountPicker.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/Select/SortPicker.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/core/components/Select/TeamPicker.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/Select/UserPicker.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/components/Signup/SignupPage.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "4"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "5"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "6"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "7"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "8"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "9"]
|
||||
],
|
||||
"public/app/core/components/Signup/VerifyEmail.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"]
|
||||
],
|
||||
"public/app/core/components/TagFilter/TagFilter.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/core/components/TagFilter/TagOption.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/core/components/TimeSeries/utils.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
@@ -1037,10 +884,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not re-export imported variable (\`profiler\`)", "6"],
|
||||
[0, 0, 0, "Do not re-export imported variable (\`updateLegendValues\`)", "7"]
|
||||
],
|
||||
"public/app/core/navigation/GrafanaRouteError.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/core/navigation/types.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
@@ -1136,10 +979,6 @@ exports[`better eslint`] = {
|
||||
"public/app/features/actions/ParamsEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/admin/AdminFeatureTogglesTable.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
||||
],
|
||||
"public/app/features/admin/UpgradePage.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
@@ -1148,13 +987,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"]
|
||||
],
|
||||
"public/app/features/admin/Users/OrgUsersTable.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
],
|
||||
"public/app/features/admin/Users/UsersTable.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/features/alerting/routes.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('Pin nav items', () => {
|
||||
bookmarkPinIcon.click({ force: true });
|
||||
|
||||
// Check if the Administration menu item is visible in the Bookmarks section
|
||||
cy.get('[aria-label="Expand section Bookmarks"]').click();
|
||||
cy.get('[aria-label="Expand section: Bookmarks"]').click();
|
||||
const bookmarks = cy.get('[href="/bookmarks"]').parentsUntil('li').siblings('ul');
|
||||
bookmarks.within(() => {
|
||||
cy.get('a').should('contain.text', 'Administration');
|
||||
@@ -46,7 +46,7 @@ describe('Pin nav items', () => {
|
||||
|
||||
// Check if the Bookmark section is visible and open it
|
||||
cy.get('[href="/bookmarks"]').should('be.visible');
|
||||
cy.get('[aria-label="Expand section Bookmarks"]').click();
|
||||
cy.get('[aria-label="Expand section: Bookmarks"]').click();
|
||||
|
||||
// Check if the Administration menu item is visible in the Bookmarks section
|
||||
const bookmarks = cy.get('[href="/bookmarks"]').parentsUntil('li').siblings('ul').children();
|
||||
|
||||
@@ -186,7 +186,7 @@ export function TableCellNG(props: TableCellNGProps) {
|
||||
{cellInspect && (
|
||||
<IconButton
|
||||
name="eye"
|
||||
tooltip="Inspect value"
|
||||
tooltip={t('grafana-ui.table.cell-inspect-tooltip', 'Inspect value')}
|
||||
onClick={() => {
|
||||
setContextMenuProps({
|
||||
value: String(value ?? ''),
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Field, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
|
||||
import { Button, ButtonSelect, ClickOutsideWrapper, FilterInput, Label, Stack } from '../../..';
|
||||
import { useStyles2, useTheme2 } from '../../../../themes';
|
||||
import { Trans } from '../../../../utils/i18n';
|
||||
import { t, Trans } from '../../../../utils/i18n';
|
||||
import { FilterType } from '../types';
|
||||
|
||||
import { FilterList } from './FilterList';
|
||||
@@ -118,7 +118,11 @@ export const FilterPopup = ({
|
||||
<div className={styles.listDivider} />
|
||||
|
||||
<Stack gap={1}>
|
||||
<FilterInput placeholder="Filter values" onChange={setSearchFilter} value={searchFilter} />
|
||||
<FilterInput
|
||||
placeholder={t('grafana-ui.table.filter-popup-input-placeholder', 'Filter values')}
|
||||
onChange={setSearchFilter}
|
||||
value={searchFilter}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
style={{ color: matchCase ? theme.colors.text.link : theme.colors.text.disabled }}
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
import { TableCellDisplayMode } from '@grafana/schema';
|
||||
|
||||
import { useStyles2, useTheme2 } from '../../../themes';
|
||||
import { Trans } from '../../../utils/i18n';
|
||||
import { t, Trans } from '../../../utils/i18n';
|
||||
import { ContextMenu } from '../../ContextMenu/ContextMenu';
|
||||
import { MenuItem } from '../../Menu/MenuItem';
|
||||
import { Pagination } from '../../Pagination/Pagination';
|
||||
@@ -423,7 +423,7 @@ export function TableNG(props: TableNGProps) {
|
||||
return (
|
||||
<>
|
||||
<MenuItem
|
||||
label="Inspect value"
|
||||
label={t('grafana-ui.table.inspect-menu-label', 'Inspect value')}
|
||||
onClick={() => {
|
||||
setIsInspecting(true);
|
||||
}}
|
||||
|
||||
@@ -67,7 +67,10 @@ export const AddPermission = ({
|
||||
(PermissionTarget.BuiltInRole && OrgRole.hasOwnProperty(builtInRole));
|
||||
|
||||
return (
|
||||
<div className="cta-form" aria-label="Permissions slider">
|
||||
<div
|
||||
className="cta-form"
|
||||
aria-label={t('access-control.add-permission.permissions-aria-label', 'Permissions slider')}
|
||||
>
|
||||
<CloseButton onClick={onCancel} />
|
||||
<h5>{title}</h5>
|
||||
|
||||
@@ -80,7 +83,7 @@ export const AddPermission = ({
|
||||
>
|
||||
<Stack gap={1} direction="row">
|
||||
<Select
|
||||
aria-label="Role to add new permission to"
|
||||
aria-label={t('access-control.add-permission.role-select-aria-label', 'Role to add new permission to')}
|
||||
value={target}
|
||||
options={targetOptions}
|
||||
onChange={(v) => setPermissionTarget(v.value!)}
|
||||
@@ -98,7 +101,7 @@ export const AddPermission = ({
|
||||
|
||||
{target === PermissionTarget.BuiltInRole && (
|
||||
<Select
|
||||
aria-label={'Built-in role picker'}
|
||||
aria-label={t('access-control.add-permission.built-in-aria-label', 'Built-in role picker')}
|
||||
options={Object.values(OrgRole)
|
||||
.filter((r) => r !== OrgRole.None)
|
||||
.map((r) => ({ value: r, label: r }))}
|
||||
@@ -108,7 +111,7 @@ export const AddPermission = ({
|
||||
)}
|
||||
|
||||
<Select
|
||||
aria-label="Permission Level"
|
||||
aria-label={t('access-control.add-permission.level-aria-label', 'Permission level')}
|
||||
width="auto"
|
||||
value={permissions.find((p) => p === permission)}
|
||||
options={permissions.map((p) => ({ label: p, value: p }))}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Box, Button, Icon, Select, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { ResourcePermission } from './types';
|
||||
|
||||
@@ -62,11 +62,21 @@ export const PermissionListItem = ({ item, permissionLevels, canSet, onRemove, o
|
||||
variant="destructive"
|
||||
disabled={!canSet}
|
||||
onClick={() => onRemove(item)}
|
||||
aria-label={`Remove permission for ${getName(item)}`}
|
||||
aria-label={t(
|
||||
'access-control.permission-list-item.remove-aria-label',
|
||||
'Remove permission for {{identifier}}',
|
||||
{
|
||||
identifier: getName(item),
|
||||
}
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Tooltip content={item.isInherited ? 'Inherited Permission' : 'Provisioned Permission'}>
|
||||
<Button size="sm" icon="lock" aria-label="Locked permission indicator" />
|
||||
<Button
|
||||
size="sm"
|
||||
icon="lock"
|
||||
aria-label={t('access-control.permission-list-item.locked-aria-label', 'Locked permission indicator')}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</td>
|
||||
|
||||
@@ -48,16 +48,16 @@ describe('MegaMenu', () => {
|
||||
|
||||
it('should render children', async () => {
|
||||
setup();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Expand section Section name' }));
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Expand section: Section name' }));
|
||||
expect(await screen.findByRole('link', { name: 'Child1' })).toBeInTheDocument();
|
||||
expect(await screen.findByRole('link', { name: 'Child2' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render grandchildren', async () => {
|
||||
setup();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Expand section Section name' }));
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Expand section: Section name' }));
|
||||
expect(await screen.findByRole('link', { name: 'Child1' })).toBeInTheDocument();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Expand section Child1' }));
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Expand section: Child1' }));
|
||||
expect(await screen.findByRole('link', { name: 'Grandchild1' })).toBeInTheDocument();
|
||||
expect(await screen.findByRole('link', { name: 'Child2' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { FeatureState, GrafanaTheme2, NavModelItem, toIconName } from '@grafana/
|
||||
import { useStyles2, Text, IconButton, Icon, Stack, FeatureBadge } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
|
||||
import { t } from '../../../internationalization';
|
||||
import { Indent } from '../../Indent/Indent';
|
||||
|
||||
import { FeatureHighlight } from './FeatureHighlight';
|
||||
@@ -114,7 +115,15 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
|
||||
<div className={styles.collapseButtonWrapper}>
|
||||
{showExpandButton && (
|
||||
<IconButton
|
||||
aria-label={`${sectionExpanded ? 'Collapse' : 'Expand'} section ${link.text}`}
|
||||
aria-label={
|
||||
sectionExpanded
|
||||
? t('navigation.megamenu-item.collapse-aria-label', 'Collapse section: {{sectionName}}', {
|
||||
sectionName: link.text,
|
||||
})
|
||||
: t('navigation.megamenu-item.expand-aria-label', 'Expand section: {{sectionName}}', {
|
||||
sectionName: link.text,
|
||||
})
|
||||
}
|
||||
className={styles.collapseButton}
|
||||
onClick={() => setSectionExpanded(!sectionExpanded)}
|
||||
name={getIconName(Boolean(sectionExpanded))}
|
||||
|
||||
@@ -25,7 +25,7 @@ export function NewsContainer({ onClose }: NewsContainerProps) {
|
||||
href="https://grafana.com/blog/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title="Go to Grafana labs blog"
|
||||
title={t('news.link-title', 'Go to Grafana labs blog')}
|
||||
className={styles.grot}
|
||||
>
|
||||
<img src="public/img/grot-news.svg" alt="Grot reading news" />
|
||||
@@ -36,7 +36,7 @@ export function NewsContainer({ onClose }: NewsContainerProps) {
|
||||
variant="secondary"
|
||||
onClick={onClose}
|
||||
data-testid={selectors.components.Drawer.General.close}
|
||||
tooltip={t(`news.drawer.close`, 'Close Drawer')}
|
||||
tooltip={t(`news.drawer.close`, 'Close drawer')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,8 @@ import { useStyles2 } from '@grafana/ui';
|
||||
import { News } from 'app/plugins/panel/news/component/News';
|
||||
import { useNewsFeed } from 'app/plugins/panel/news/useNewsFeed';
|
||||
|
||||
import { t } from '../../../internationalization';
|
||||
|
||||
interface NewsWrapperProps {
|
||||
feedUrl: string;
|
||||
}
|
||||
@@ -42,7 +44,12 @@ export function NewsWrapper({ feedUrl }: NewsWrapperProps) {
|
||||
</>
|
||||
)}
|
||||
<div className={styles.grot}>
|
||||
<a href="https://grafana.com/blog/" target="_blank" rel="noreferrer" title="Go to Grafana labs blog">
|
||||
<a
|
||||
href="https://grafana.com/blog/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title={t('news.link-title', 'Go to Grafana labs blog')}
|
||||
>
|
||||
<img src="public/img/grot-news.svg" alt="Grot reading news" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,8 @@ import { Icon, Select, useStyles2 } from '@grafana/ui';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { UserOrg } from 'app/types';
|
||||
|
||||
import { t } from '../../../internationalization';
|
||||
|
||||
import { OrganizationBaseProps } from './types';
|
||||
|
||||
export function OrganizationSelect({ orgs, onSelectChange }: OrganizationBaseProps) {
|
||||
@@ -32,7 +34,7 @@ export function OrganizationSelect({ orgs, onSelectChange }: OrganizationBasePro
|
||||
|
||||
return (
|
||||
<Select<UserOrg>
|
||||
aria-label="Change organization"
|
||||
aria-label={t('navigation.org-switcher.aria-label', 'Change organization')}
|
||||
width={'auto'}
|
||||
value={value}
|
||||
prefix={<Icon className="prefix-icon" name="building" />}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Menu, Dropdown, useStyles2, useTheme2, ToolbarButton } from '@grafana/u
|
||||
import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange';
|
||||
import { useSelector } from 'app/types';
|
||||
|
||||
import { t } from '../../../internationalization';
|
||||
import { NavToolbarSeparator } from '../NavToolbar/NavToolbarSeparator';
|
||||
|
||||
import { findCreateActions } from './utils';
|
||||
@@ -53,7 +54,7 @@ export const QuickAdd = ({}: Props) => {
|
||||
iconOnly
|
||||
icon={isSmallScreen ? 'plus-circle' : 'plus'}
|
||||
isOpen={isSmallScreen ? undefined : isOpen}
|
||||
aria-label="New"
|
||||
aria-label={t('navigation.quick-add.aria-label', 'New')}
|
||||
/>
|
||||
</Dropdown>
|
||||
<NavToolbarSeparator className={styles.separator} />
|
||||
|
||||
@@ -61,7 +61,7 @@ export function ProfileButton({ profileNode }: Props) {
|
||||
className={styles.profileButton}
|
||||
imgSrc={contextSrv.user.gravatarUrl}
|
||||
imgAlt="User avatar"
|
||||
aria-label="Profile"
|
||||
aria-label={t('navigation.profile.aria-label', 'Profile')}
|
||||
/>
|
||||
</Dropdown>
|
||||
{showNewsDrawer && <NewsContainer onClose={onToggleShowNewsDrawer} />}
|
||||
|
||||
@@ -78,14 +78,14 @@ export const SingleTopBar = memo(function SingleTopBar({
|
||||
<QuickAdd />
|
||||
{enrichedHelpNode && (
|
||||
<Dropdown overlay={() => <TopNavBarMenu node={enrichedHelpNode} />} placement="bottom-end">
|
||||
<ToolbarButton iconOnly icon="question-circle" aria-label="Help" />
|
||||
<ToolbarButton iconOnly icon="question-circle" aria-label={t('navigation.help.aria-label', 'Help')} />
|
||||
</Dropdown>
|
||||
)}
|
||||
<ToolbarButton
|
||||
icon="monitor"
|
||||
className={styles.kioskToggle}
|
||||
onClick={onToggleKioskMode}
|
||||
tooltip="Enable kiosk mode"
|
||||
tooltip={t('navigation.kiosk.tooltip', 'Enable kiosk mode')}
|
||||
/>
|
||||
{!contextSrv.user.isSignedIn && <SignInLink />}
|
||||
{config.featureToggles.inviteUserExperimental && <InviteUserButton />}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { css } from '@emotion/css';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { BreadcrumbItem } from './BreadcrumbItem';
|
||||
import { Breadcrumb } from './types';
|
||||
|
||||
@@ -15,7 +17,7 @@ export function Breadcrumbs({ breadcrumbs, className }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<nav aria-label="Breadcrumbs" className={className}>
|
||||
<nav aria-label={t('navigation.breadcrumbs.aria-label', 'Breadcrumbs')} className={className}>
|
||||
<ol className={styles.breadcrumbs}>
|
||||
{breadcrumbs.map((breadcrumb, index) => (
|
||||
<BreadcrumbItem
|
||||
|
||||
@@ -4,6 +4,8 @@ import * as React from 'react';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { IconButton, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
'aria-label'?: string;
|
||||
@@ -19,7 +21,7 @@ export const CloseButton = ({ onClick, 'aria-label': ariaLabel, style }: Props)
|
||||
name="times"
|
||||
onClick={onClick}
|
||||
style={style}
|
||||
tooltip="Close"
|
||||
tooltip={t('close-button.tooltip', 'Close')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { AsyncMultiSelect, Icon, Button, useStyles2 } from '@grafana/ui';
|
||||
import { config } from 'app/core/config';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { getGrafanaSearcher } from 'app/features/search/service/searcher';
|
||||
import { FolderInfo, PermissionLevelString } from 'app/types';
|
||||
@@ -34,14 +34,7 @@ export function FolderFilter({ onChange, maxMenuHeight }: FolderFilterProps): JS
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{value.length > 0 && (
|
||||
<Button
|
||||
size="xs"
|
||||
icon="trash-alt"
|
||||
fill="text"
|
||||
className={styles.clear}
|
||||
onClick={() => onChange([])}
|
||||
aria-label="Clear folders"
|
||||
>
|
||||
<Button size="xs" icon="trash-alt" fill="text" className={styles.clear} onClick={() => onChange([])}>
|
||||
<Trans i18nKey="folder-filter.clear-folder-button">Clear folders</Trans>
|
||||
</Button>
|
||||
)}
|
||||
@@ -51,10 +44,10 @@ export function FolderFilter({ onChange, maxMenuHeight }: FolderFilterProps): JS
|
||||
isLoading={loading}
|
||||
loadOptions={debouncedLoadOptions}
|
||||
maxMenuHeight={maxMenuHeight}
|
||||
placeholder="Filter by folder"
|
||||
placeholder={t('folder-filter.select-placeholder', 'Filter by folder')}
|
||||
noOptionsMessage="No folders found"
|
||||
prefix={<Icon name="filter" />}
|
||||
aria-label="Folder filter"
|
||||
aria-label={t('folder-filter.select-aria-label', 'Folder filter')}
|
||||
defaultOptions
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Tooltip, Field, Button, Alert, useStyles2, Stack } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { getStyles } from '../Login/LoginForm';
|
||||
import { PasswordField } from '../PasswordField/PasswordField';
|
||||
@@ -50,9 +50,19 @@ export const ChangePassword = ({ onSubmit, onSkip, showDefaultPasswordWarning }:
|
||||
return (
|
||||
<form onSubmit={handleSubmit(submit)}>
|
||||
{showDefaultPasswordWarning && (
|
||||
<Alert severity="info" title="Continuing to use the default password exposes you to security risks." />
|
||||
<Alert
|
||||
severity="info"
|
||||
title={t(
|
||||
'forgot-password.change-password.default-password-alert',
|
||||
'Continuing to use the default password exposes you to security risks.'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Field label="New password" invalid={!!errors.newPassword} error={errors?.newPassword?.message}>
|
||||
<Field
|
||||
label={t('forgot-password.change-password.new-password-label', 'New password')}
|
||||
invalid={!!errors.newPassword}
|
||||
error={errors?.newPassword?.message}
|
||||
>
|
||||
<PasswordField
|
||||
onFocus={() => setDisplayValidationLabels(true)}
|
||||
{...register('newPassword', {
|
||||
@@ -72,7 +82,11 @@ export const ChangePassword = ({ onSubmit, onSkip, showDefaultPasswordWarning }:
|
||||
strongPasswordValidations={strongPasswordValidations}
|
||||
/>
|
||||
)}
|
||||
<Field label="Confirm new password" invalid={!!errors.confirmNew} error={errors?.confirmNew?.message}>
|
||||
<Field
|
||||
label={t('forgot-password.change-password.confirm-label', 'Confirm new password')}
|
||||
invalid={!!errors.confirmNew}
|
||||
error={errors?.confirmNew?.message}
|
||||
>
|
||||
<PasswordField
|
||||
{...register('confirmNew', {
|
||||
required: 'Confirmed Password is required',
|
||||
|
||||
@@ -6,7 +6,7 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { Field, Input, Button, Legend, Container, useStyles2, LinkButton, Stack } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
interface EmailDTO {
|
||||
userOrEmail: string;
|
||||
@@ -59,14 +59,17 @@ export const ForgottenPassword = () => {
|
||||
<Trans i18nKey="forgot-password.reset-password-header">Reset password</Trans>
|
||||
</Legend>
|
||||
<Field
|
||||
label="User"
|
||||
description="Enter your information to get a reset link sent to you"
|
||||
label={t('forgot-password.user-field-label', 'User')}
|
||||
description={t(
|
||||
'forgot-password.user-field-description',
|
||||
'Enter your information to get a reset link sent to you'
|
||||
)}
|
||||
invalid={!!errors.userOrEmail}
|
||||
error={errors?.userOrEmail?.message}
|
||||
>
|
||||
<Input
|
||||
id="user-input"
|
||||
placeholder="Email or username"
|
||||
placeholder={t('forgot-password.user-field-placeholder', 'Email or username')}
|
||||
{...register('userOrEmail', { required: 'Email or username is required' })}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
|
||||
import { Navigate } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { Button, Modal } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { Prompt } from './Prompt';
|
||||
|
||||
@@ -99,7 +99,7 @@ const UnsavedChangesModal = ({ onDiscard, onBackToForm, isOpen }: UnsavedChanges
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
title="Leave page?"
|
||||
title={t('form-prompt.title', 'Leave page?')}
|
||||
onDismiss={onBackToForm}
|
||||
icon="exclamation-triangle"
|
||||
className={css({ width: '500px' })}
|
||||
|
||||
@@ -38,13 +38,13 @@ describe('LayerDragDropList', () => {
|
||||
it('renders draggable icon', () => {
|
||||
renderScenario({});
|
||||
|
||||
expect(screen.getAllByLabelText('Drag and drop icon').length).toEqual(2);
|
||||
expect(screen.getAllByLabelText('Drag and drop to reorder').length).toEqual(2);
|
||||
});
|
||||
|
||||
it('does not render draggable icon', () => {
|
||||
renderScenario({ excludeBaseLayer: true });
|
||||
|
||||
expect(screen.queryAllByLabelText('Drag and drop icon').length).toEqual(0);
|
||||
expect(screen.queryAllByLabelText('Drag and drop to reorder').length).toEqual(0);
|
||||
});
|
||||
|
||||
function renderScenario(overrides: Partial<LayerDragDropListProps<testLayer>>) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Icon, IconButton, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { LayerName } from './LayerName';
|
||||
import { LayerElement } from './types';
|
||||
|
||||
@@ -81,7 +83,7 @@ export const LayerDragDropList = <T extends LayerElement>({
|
||||
{onDuplicate ? (
|
||||
<IconButton
|
||||
name="copy"
|
||||
tooltip="Duplicate"
|
||||
tooltip={t('layers.layer-drag-drop-list.duplicate-tooltip', 'Duplicate')}
|
||||
className={style.actionIcon}
|
||||
onClick={() => onDuplicate(element)}
|
||||
/>
|
||||
@@ -89,7 +91,7 @@ export const LayerDragDropList = <T extends LayerElement>({
|
||||
|
||||
<IconButton
|
||||
name="trash-alt"
|
||||
tooltip="Remove"
|
||||
tooltip={t('layers.layer-drag-drop-list.remove-tooltip', 'Remove')}
|
||||
className={cx(style.actionIcon, style.dragIcon)}
|
||||
onClick={() => onDelete(element)}
|
||||
/>
|
||||
@@ -97,8 +99,10 @@ export const LayerDragDropList = <T extends LayerElement>({
|
||||
)}
|
||||
{layers.length > shouldRenderDragIconLengthThreshold && (
|
||||
<Icon
|
||||
aria-label="Drag and drop icon"
|
||||
title="Drag and drop to reorder"
|
||||
aria-label={t(
|
||||
'layers.layer-drag-drop-list.draggable-aria-label',
|
||||
'Drag and drop to reorder'
|
||||
)}
|
||||
name="draggabledots"
|
||||
size="lg"
|
||||
className={style.dragIcon}
|
||||
|
||||
@@ -5,6 +5,8 @@ import * as React from 'react';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Icon, Input, FieldValidationMessage, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface LayerNameProps {
|
||||
name: string;
|
||||
onChange: (v: string) => void;
|
||||
@@ -73,7 +75,7 @@ export const LayerName = ({ name, onChange, verifyLayerNameUniqueness, overrideS
|
||||
{!isEditing && (
|
||||
<button
|
||||
className={styles.layerNameWrapper}
|
||||
title="Edit layer name"
|
||||
title={t('layers.layer-name.edit-layer-title', 'Edit layer name')}
|
||||
onClick={onEditLayer}
|
||||
data-testid="layer-name-div"
|
||||
>
|
||||
|
||||
@@ -80,13 +80,17 @@ export const PasswordlessConfirmation = ({ onSubmit, isLoggingIn }: Props) => {
|
||||
</Field>
|
||||
{signup && (
|
||||
<>
|
||||
<Field label={'Username'} invalid={!!errors.code} error={errors.code?.message} hidden={true}>
|
||||
<Field
|
||||
label={t('login.form.passwordless-username-label', 'Username')}
|
||||
invalid={!!errors.code}
|
||||
error={errors.code?.message}
|
||||
hidden={true}
|
||||
>
|
||||
<Input
|
||||
{...register('username')}
|
||||
id={usernameId}
|
||||
autoFocus
|
||||
autoCapitalize="none"
|
||||
placeholder={'username'}
|
||||
data-testid={selectors.pages.PasswordlessLogin.email}
|
||||
hidden={true}
|
||||
/>
|
||||
@@ -97,7 +101,6 @@ export const PasswordlessConfirmation = ({ onSubmit, isLoggingIn }: Props) => {
|
||||
id={nameId}
|
||||
autoFocus
|
||||
autoCapitalize="none"
|
||||
placeholder={t('login.form.name-placeholder', 'name')}
|
||||
data-testid={selectors.pages.PasswordlessLogin.email}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
@@ -4,6 +4,8 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useTheme2, useStyles2, ColorPicker, IconButton } from '@grafana/ui';
|
||||
import { ColorSwatch } from '@grafana/ui/internal';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface ColorValueEditorSettings {
|
||||
placeholder?: string;
|
||||
/** defaults to true */
|
||||
@@ -57,7 +59,11 @@ export const ColorValueEditor = ({ value, settings, onChange, details }: Props)
|
||||
</span>
|
||||
)}
|
||||
{settings?.isClearable && value && (
|
||||
<IconButton name="times" onClick={() => onChange(undefined)} tooltip="Clear settings" />
|
||||
<IconButton
|
||||
name="times"
|
||||
onClick={() => onChange(undefined)}
|
||||
tooltip={t('options-ui.color.clear-tooltip', 'Clear settings')}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { useStyles2, useTheme2, Field, RadioButtonGroup, Select } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { ColorValueEditor } from './color';
|
||||
|
||||
type Props = StandardEditorProps<FieldColor | undefined, FieldColorConfigSettings>;
|
||||
@@ -98,7 +100,7 @@ export const FieldColorEditor = ({ value, onChange, item, id }: Props) => {
|
||||
<div style={{ marginBottom: theme.spacing(2) }}>
|
||||
<Select minMenuHeight={200} options={options} value={mode} onChange={onModeChange} inputId={id} />
|
||||
</div>
|
||||
<Field label="Color series by">
|
||||
<Field label={t('options-ui.field-color.color-by-label', 'Color series by')}>
|
||||
<RadioButtonGroup value={value?.seriesBy ?? 'last'} options={seriesModes} onChange={onSeriesModeChange} />
|
||||
</Field>
|
||||
</>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { css } from '@emotion/css';
|
||||
import { StandardEditorProps, GrafanaTheme2, UnitFieldConfigSettings } from '@grafana/data';
|
||||
import { IconButton, UnitPicker, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
type Props = StandardEditorProps<string, UnitFieldConfigSettings>;
|
||||
|
||||
export function UnitValueEditor({ value, onChange, item }: Props) {
|
||||
@@ -13,7 +15,11 @@ export function UnitValueEditor({ value, onChange, item }: Props) {
|
||||
<span className={styles.first}>
|
||||
<UnitPicker value={value} onChange={onChange} />
|
||||
</span>
|
||||
<IconButton name="times" onClick={() => onChange(undefined)} tooltip="Clear unit selection" />
|
||||
<IconButton
|
||||
name="times"
|
||||
onClick={() => onChange(undefined)}
|
||||
tooltip={t('options-ui.units.clear-tooltip', 'Clear unit selection')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { isFetchError } from '@grafana/runtime';
|
||||
import { Field, IconButton, Input, useStyles2, Text } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface Props {
|
||||
value: string;
|
||||
onEdit: (newValue: string) => Promise<void>;
|
||||
@@ -64,7 +66,12 @@ export const EditableTitle = ({ value, onEdit }: Props) => {
|
||||
<Text element="h1" truncate>
|
||||
{localValue}
|
||||
</Text>
|
||||
<IconButton name="pen" size="lg" tooltip="Edit title" onClick={() => setIsEditing(true)} />
|
||||
<IconButton
|
||||
name="pen"
|
||||
size="lg"
|
||||
tooltip={t('page.editable-title.edit-tooltip', 'Edit title')}
|
||||
onClick={() => setIsEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, PanelPluginMeta, SelectableValue } from '@grafana/data';
|
||||
import { Icon, Button, MultiSelect, useStyles2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { getAllPanelPluginMeta } from 'app/features/panel/state/util';
|
||||
|
||||
export interface Props {
|
||||
@@ -35,8 +35,8 @@ export const PanelTypeFilter = ({ onChange: propsOnChange, maxMenuHeight }: Prop
|
||||
defaultOptions: true,
|
||||
getOptionLabel: (i: SelectableValue<PanelPluginMeta>) => i.label,
|
||||
getOptionValue: (i: SelectableValue<PanelPluginMeta>) => i.value,
|
||||
noOptionsMessage: 'No Panel types found',
|
||||
placeholder: 'Filter by type',
|
||||
noOptionsMessage: t('panel-type-filter.select-no-options', 'No panel types found'),
|
||||
placeholder: t('panel-type-filter.select-placeholder', 'Filter by type'),
|
||||
maxMenuHeight,
|
||||
options,
|
||||
value,
|
||||
@@ -46,18 +46,15 @@ export const PanelTypeFilter = ({ onChange: propsOnChange, maxMenuHeight }: Prop
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{value.length > 0 && (
|
||||
<Button
|
||||
size="xs"
|
||||
icon="trash-alt"
|
||||
fill="text"
|
||||
className={styles.clear}
|
||||
onClick={() => onChange([])}
|
||||
aria-label="Clear types"
|
||||
>
|
||||
<Button size="xs" icon="trash-alt" fill="text" className={styles.clear} onClick={() => onChange([])}>
|
||||
<Trans i18nKey="panel-type-filter.clear-button">Clear types</Trans>
|
||||
</Button>
|
||||
)}
|
||||
<MultiSelect<PanelPluginMeta> {...selectOptions} prefix={<Icon name="filter" />} aria-label="Panel Type filter" />
|
||||
<MultiSelect<PanelPluginMeta>
|
||||
{...selectOptions}
|
||||
prefix={<Icon name="filter" />}
|
||||
aria-label={t('panel-type-filter.select-aria-label', 'Panel type filter')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAsync } from 'react-use';
|
||||
import { renderMarkdown } from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { LoadingPlaceholder } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
interface Props {
|
||||
pluginId: string;
|
||||
@@ -17,7 +17,7 @@ export function PluginHelp({ pluginId }: Props) {
|
||||
const renderedMarkdown = renderMarkdown(value);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingPlaceholder text="Loading help..." />;
|
||||
return <LoadingPlaceholder text={t('plugins.plugin-help.loading', 'Loading help...')} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -71,7 +71,7 @@ export const QueryOperationRowHeader = ({
|
||||
// disabling the a11y rules here as the IconButton above handles keyboard interactions
|
||||
// this is just to provide a better experience for mouse users
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div className={styles.titleWrapper} onClick={onRowToggle} aria-label="Query operation row title">
|
||||
<div className={styles.titleWrapper} onClick={onRowToggle}>
|
||||
<div className={cx(styles.title, disabled && styles.disabled)}>{title}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,8 @@ import * as React from 'react';
|
||||
import { Checkbox, Portal, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { getSelectStyles } from '@grafana/ui/internal';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { getStyles } from './styles';
|
||||
|
||||
interface RoleMenuGroupsOptionProps {
|
||||
@@ -84,7 +86,12 @@ export const RoleMenuGroupOption = memo(
|
||||
<div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
{/* TODO: fix keyboard a11y */}
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
||||
<div ref={ref} className={wrapperClassName} aria-label="Role picker option" onClick={onClickInternal}>
|
||||
<div
|
||||
ref={ref}
|
||||
className={wrapperClassName}
|
||||
aria-label={t('role-picker.menu-group-option-aria-label', 'Role picker option')}
|
||||
onClick={onClickInternal}
|
||||
>
|
||||
<Checkbox
|
||||
value={isSelected}
|
||||
className={cx(customStyles.menuOptionCheckbox, {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Checkbox, Icon, Tooltip, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { getSelectStyles } from '@grafana/ui/internal';
|
||||
import { Role } from 'app/types';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { getStyles } from './styles';
|
||||
|
||||
interface RoleMenuOptionProps {
|
||||
@@ -49,7 +51,12 @@ export const RoleMenuOption = forwardRef<HTMLDivElement, React.PropsWithChildren
|
||||
return (
|
||||
// TODO: fix keyboard a11y
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div ref={ref} className={wrapperClassName} aria-label="Role picker option" onClick={onChangeInternal}>
|
||||
<div
|
||||
ref={ref}
|
||||
className={wrapperClassName}
|
||||
aria-label={t('role-picker.menu-option-aria-label', 'Role picker option')}
|
||||
onClick={onChangeInternal}
|
||||
>
|
||||
<Checkbox
|
||||
value={isSelected}
|
||||
className={customStyles.menuOptionCheckbox}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { Button, ScrollContainer, Stack, TextLink, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { getSelectStyles } from '@grafana/ui/internal';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { OrgRole, Role } from 'app/types';
|
||||
|
||||
import { BuiltinRoleSelector } from './BuiltinRoleSelector';
|
||||
@@ -237,7 +237,7 @@ export const RolePickerMenu = ({
|
||||
})
|
||||
)}
|
||||
>
|
||||
<div className={customStyles.menu} aria-label="Role picker menu">
|
||||
<div className={customStyles.menu} aria-label={t('role-picker.menu-aria-label', 'Role picker menu')}>
|
||||
<ScrollContainer
|
||||
maxHeight={`${MENU_MAX_HEIGHT}px`}
|
||||
// NOTE: this is a way to force hiding of the scrollbar
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cx } from '@emotion/css';
|
||||
|
||||
import { Button, ScrollContainer, Stack, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { getSelectStyles } from '@grafana/ui/internal';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { Role } from 'app/types';
|
||||
|
||||
import { RoleMenuOption } from './RoleMenuOption';
|
||||
@@ -40,7 +40,7 @@ export const RolePickerSubMenu = ({
|
||||
return (
|
||||
<div
|
||||
className={cx(customStyles.subMenu, { [customStyles.subMenuLeft]: showOnLeft })}
|
||||
aria-label="Role picker submenu"
|
||||
aria-label={t('role-picker.sub-menu-aria-label', 'Role picker submenu')}
|
||||
>
|
||||
<ScrollContainer maxHeight={`${MENU_MAX_HEIGHT}px`}>
|
||||
<div className={styles.optionBody}>
|
||||
|
||||
@@ -6,6 +6,8 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2, Badge, Stack } from '@grafana/ui';
|
||||
import { OrgUser } from 'app/types';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { RolePickerDrawer } from './RolePickerDrawer';
|
||||
|
||||
export interface Props {
|
||||
@@ -39,7 +41,12 @@ export const RolePickerBadges = ({ disabled, user }: Props) => {
|
||||
<Stack gap={1}>
|
||||
<Badge className={badgeStyle} color="blue" onClick={drawerControl} text={watch('role')} />
|
||||
{user.roles && user.roles.length > 0 && (
|
||||
<Badge className={badgeStyle} color="blue" onClick={drawerControl} text={`+${user.roles.length}`} />
|
||||
<Badge
|
||||
className={badgeStyle}
|
||||
color="blue"
|
||||
onClick={drawerControl}
|
||||
text={t('role-picker-drawer.user-count', '+{{numUsers}}', { numUsers: user.roles.length })}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
{isDrawerOpen && (
|
||||
|
||||
@@ -332,7 +332,7 @@ export function OldFolderPicker(props: Props) {
|
||||
value={newFolderValue}
|
||||
onChange={onNewFolderChange}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Press enter to confirm new folder."
|
||||
placeholder={t('folder-picker.input-placeholder', 'Press enter to confirm new folder')}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -6,6 +6,8 @@ import { getBackendSrv } from '@grafana/runtime';
|
||||
import { AsyncSelect } from '@grafana/ui';
|
||||
import { Organization, UserOrg } from 'app/types';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export type OrgSelectItem = SelectableValue<Organization>;
|
||||
|
||||
export interface Props {
|
||||
@@ -65,7 +67,7 @@ export function OrgPicker({ onSelected, className, inputId, autoFocus, excludeOr
|
||||
setSelected(item);
|
||||
}}
|
||||
value={selected}
|
||||
placeholder="Select organization"
|
||||
placeholder={t('org-picker.select-placeholder', 'Select organization')}
|
||||
noOptionsMessage="No organizations found"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -7,6 +7,8 @@ import { getBackendSrv } from '@grafana/runtime';
|
||||
import { AsyncSelect } from '@grafana/ui';
|
||||
import { ServiceAccountDTO, ServiceAccountsState } from 'app/types';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface Props {
|
||||
onSelected: (user: SelectableValue<ServiceAccountDTO>) => void;
|
||||
className?: string;
|
||||
@@ -65,9 +67,9 @@ export class ServiceAccountPicker extends Component<Props, State> {
|
||||
defaultOptions={true}
|
||||
loadOptions={this.search}
|
||||
onChange={onSelected}
|
||||
placeholder="Start typing to search for service accounts"
|
||||
placeholder={t('service-account-picker.select-placeholder', 'Start typing to search for service accounts')}
|
||||
noOptionsMessage="No service accounts found"
|
||||
aria-label="Service Account picker"
|
||||
aria-label={t('service-account-picker.select-aria-label', 'Service account picker')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Icon, Select } from '@grafana/ui';
|
||||
import { DEFAULT_SORT } from 'app/features/search/constants';
|
||||
import { getGrafanaSearcher } from 'app/features/search/service/searcher';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface Props {
|
||||
onChange: (sortValue: SelectableValue) => void;
|
||||
value?: string;
|
||||
@@ -40,7 +42,7 @@ export function SortPicker({ onChange, value, placeholder, filter, getSortOption
|
||||
onChange={onChange}
|
||||
value={options.value?.find((opt) => opt.value === value) ?? null}
|
||||
options={options.value}
|
||||
aria-label="Sort"
|
||||
aria-label={t('sort-picker.select-aria-label', 'Sort')}
|
||||
placeholder={placeholder ?? `Sort (Default ${DEFAULT_SORT.label})`}
|
||||
prefix={<Icon name={isDesc ? 'sort-amount-down' : 'sort-amount-up'} />}
|
||||
isClearable={isClearable}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { getBackendSrv } from '@grafana/runtime';
|
||||
import { AsyncSelect } from '@grafana/ui';
|
||||
import { Team } from 'app/types';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface Props {
|
||||
onSelected: (team: SelectableValue<Team>) => void;
|
||||
className?: string;
|
||||
@@ -82,9 +84,9 @@ export class TeamPicker extends Component<Props, State> {
|
||||
value={value}
|
||||
onChange={onSelected}
|
||||
className={className}
|
||||
placeholder="Select a team"
|
||||
placeholder={t('team-picker.select-placeholder', 'Select a team')}
|
||||
noOptionsMessage="No teams found"
|
||||
aria-label="Team picker"
|
||||
aria-label={t('team-picker.select-aria-label', 'Team picker')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,6 +7,8 @@ import { getBackendSrv } from '@grafana/runtime';
|
||||
import { AsyncSelect } from '@grafana/ui';
|
||||
import { OrgUser } from 'app/types';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
export interface Props {
|
||||
onSelected: (user: SelectableValue<OrgUser>) => void;
|
||||
className?: string;
|
||||
@@ -65,9 +67,9 @@ export class UserPicker extends Component<Props, State> {
|
||||
defaultOptions={true}
|
||||
loadOptions={this.search}
|
||||
onChange={onSelected}
|
||||
placeholder="Start typing to search for user"
|
||||
placeholder={t('user-picker.select-placeholder', 'Start typing to search for user')}
|
||||
noOptionsMessage="No users found"
|
||||
aria-label="User picker"
|
||||
aria-label={t('user-picker.select-aria-label', 'User picker')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getBackendSrv } from '@grafana/runtime';
|
||||
import { Field, Input, Button, LinkButton, Stack } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
import { w3cStandardEmailValidator } from 'app/features/admin/utils';
|
||||
|
||||
@@ -67,10 +67,14 @@ export const SignupPage = ({ queryParams }: Props) => {
|
||||
<LoginLayout>
|
||||
<InnerBox>
|
||||
<form onSubmit={handleSubmit(onSubmit)} style={{ width: '100%' }}>
|
||||
<Field label="Your name">
|
||||
<Input id="user-name" {...register('name')} placeholder="(optional)" />
|
||||
<Field label={t('sign-up.user-name-label', 'Your name')}>
|
||||
<Input
|
||||
id="user-name"
|
||||
{...register('name')}
|
||||
placeholder={t('sign-up.user-name-placeholder', '(optional)')}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Email" invalid={!!errors.email} error={errors.email?.message}>
|
||||
<Field label={t('sign-up.email-label', 'Email')} invalid={!!errors.email} error={errors.email?.message}>
|
||||
<Input
|
||||
id="email"
|
||||
{...register('email', {
|
||||
@@ -81,20 +85,23 @@ export const SignupPage = ({ queryParams }: Props) => {
|
||||
},
|
||||
})}
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
/>
|
||||
</Field>
|
||||
{!getConfig().autoAssignOrg && (
|
||||
<Field label="Org. name">
|
||||
<Input id="org-name" {...register('orgName')} placeholder="Org. name" />
|
||||
<Field label={t('sign-up.org-name-label', 'Org. name')}>
|
||||
<Input id="org-name" {...register('orgName')} />
|
||||
</Field>
|
||||
)}
|
||||
{getConfig().verifyEmailEnabled && (
|
||||
<Field label="Email verification code (sent to your email)">
|
||||
<Input id="verification-code" {...register('code')} placeholder="Code" />
|
||||
<Field label={t('sign-up.verification-code-label', 'Email verification code (sent to your email)')}>
|
||||
<Input id="verification-code" {...register('code')} />
|
||||
</Field>
|
||||
)}
|
||||
<Field label="Password" invalid={!!errors.password} error={errors?.password?.message}>
|
||||
<Field
|
||||
label={t('sign-up.password-label', 'Password')}
|
||||
invalid={!!errors.password}
|
||||
error={errors?.password?.message}
|
||||
>
|
||||
<PasswordField
|
||||
id="new-password"
|
||||
autoFocus
|
||||
@@ -102,7 +109,11 @@ export const SignupPage = ({ queryParams }: Props) => {
|
||||
{...register('password', { required: 'Password is required' })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Confirm password" invalid={!!errors.confirm} error={errors?.confirm?.message}>
|
||||
<Field
|
||||
label={t('sign-up.confirm-password-label', 'Confirm password')}
|
||||
invalid={!!errors.confirm}
|
||||
error={errors?.confirm?.message}
|
||||
>
|
||||
<PasswordField
|
||||
id="confirm-new-password"
|
||||
autoComplete="new-password"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getBackendSrv } from '@grafana/runtime';
|
||||
import { Field, Input, Button, Legend, Container, LinkButton, Stack } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { w3cStandardEmailValidator } from 'app/features/admin/utils';
|
||||
|
||||
interface EmailDTO {
|
||||
@@ -55,8 +55,11 @@ export const VerifyEmail = () => {
|
||||
<Trans i18nKey="sign-up.verify.header">Verify email</Trans>
|
||||
</Legend>
|
||||
<Field
|
||||
label="Email"
|
||||
description="Enter your email address to get a verification link sent to you"
|
||||
label={t('sign-up.verify.email-label', 'Email')}
|
||||
description={t(
|
||||
'sign-up.verify.email-description',
|
||||
'Enter your email address to get a verification link sent to you'
|
||||
)}
|
||||
invalid={!!errors.email}
|
||||
error={errors.email?.message}
|
||||
>
|
||||
@@ -69,7 +72,6 @@ export const VerifyEmail = () => {
|
||||
message: 'Email is invalid',
|
||||
},
|
||||
})}
|
||||
placeholder="Email"
|
||||
/>
|
||||
</Field>
|
||||
<Stack>
|
||||
|
||||
@@ -159,7 +159,12 @@ export const TagFilter = ({
|
||||
<Trans i18nKey="tag-filter.clear-button">Clear tags</Trans>
|
||||
</button>
|
||||
)}
|
||||
<MultiSelect key={selectKey} {...selectOptions} prefix={<Icon name="tag-alt" />} aria-label="Tag filter" />
|
||||
<MultiSelect
|
||||
key={selectKey}
|
||||
{...selectOptions}
|
||||
prefix={<Icon name="tag-alt" />}
|
||||
aria-label={t('tag-filter.select-aria-label', 'Tag filter')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@ import { OptionProps } from 'react-select';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../internationalization';
|
||||
|
||||
import { TagBadge } from './TagBadge';
|
||||
|
||||
export interface TagSelectOption {
|
||||
@@ -16,7 +18,11 @@ export const TagOption = ({ data, className, label, isFocused, innerProps }: Opt
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.option, isFocused && styles.optionFocused)} aria-label="Tag option" {...innerProps}>
|
||||
<div
|
||||
className={cx(styles.option, isFocused && styles.optionFocused)}
|
||||
aria-label={t('tag-filter.tag-option-label', 'Tag option')}
|
||||
{...innerProps}
|
||||
>
|
||||
<div className={cx(styles.optionInner, className)}>
|
||||
{typeof label === 'string' ? <TagBadge label={label} removeIcon={false} count={data.count ?? 0} /> : label}
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { GrafanaTheme2, locationUtil, PageLayoutType } from '@grafana/data';
|
||||
import { Button, ErrorWithStack, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { Page } from '../components/Page/Page';
|
||||
import { Trans } from '../internationalization';
|
||||
import { t, Trans } from '../internationalization';
|
||||
|
||||
interface Props {
|
||||
error: Error | null;
|
||||
@@ -45,11 +45,15 @@ export function GrafanaRouteError({ error, errorInfo }: Props) {
|
||||
<Button size="md" variant="secondary" icon="repeat" onClick={() => window.location.reload()}>
|
||||
<Trans i18nKey="route-error.reload-button">Reload</Trans>
|
||||
</Button>
|
||||
<ErrorWithStack title={'Error details'} error={error} errorInfo={errorInfo} />
|
||||
<ErrorWithStack title={t('route-error.error-title', 'Error details')} error={error} errorInfo={errorInfo} />
|
||||
</div>
|
||||
)}
|
||||
{!isChunkLoadingError && (
|
||||
<ErrorWithStack title={'An unexpected error happened'} error={error} errorInfo={errorInfo} />
|
||||
<ErrorWithStack
|
||||
title={t('route-error.error-unexpected-title', 'An unexpected error happened')}
|
||||
error={error}
|
||||
errorInfo={errorInfo}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
@@ -166,10 +166,16 @@ export function AdminFeatureTogglesTable({ featureToggles, allowEditing, onUpdat
|
||||
body={
|
||||
<div>
|
||||
<p>
|
||||
Some features are stable (GA) and enabled by default, whereas some are currently in their preliminary
|
||||
Beta phase, available for early adoption.
|
||||
<Trans i18nKey="admin.admin-feature-toggles-table.confirm-modal-body-1">
|
||||
Some features are stable (GA) and enabled by default, whereas some are currently in their
|
||||
preliminary Beta phase, available for early adoption.
|
||||
</Trans>
|
||||
</p>
|
||||
<p>
|
||||
<Trans i18nKey="admin.admin-feature-toggles-table.confirm-modal-body-2">
|
||||
We advise understanding the implications of each feature change before making modifications.
|
||||
</Trans>
|
||||
</p>
|
||||
<p>We advise understanding the implications of each feature change before making modifications.</p>
|
||||
</div>
|
||||
}
|
||||
confirmText="Save changes"
|
||||
|
||||
@@ -241,7 +241,9 @@ export const OrgUsersTable = ({
|
||||
setUserToRemove(original);
|
||||
}}
|
||||
icon="times"
|
||||
aria-label={`Delete user ${original.name}`}
|
||||
aria-label={t('admin.org-users-table.delete-aria-label', 'Delete user: {{name}}', {
|
||||
name: original.name,
|
||||
})}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -174,8 +174,8 @@ export const UsersTable = ({
|
||||
size="sm"
|
||||
icon="pen"
|
||||
href={`admin/users/edit/${original.uid}`}
|
||||
aria-label={`Edit user ${original.name}`}
|
||||
tooltip={'Edit user'}
|
||||
aria-label={t('admin.users-table.edit-aria-label', 'Edit user: {{name}}', { name: original.name })}
|
||||
tooltip={t('admin.users-table.edit-tooltip', 'Edit user')}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
"_comment": "The code is the source of truth for English phrases. They should be updated in the components directly, and additional plurals specified in this file.",
|
||||
"access-control": {
|
||||
"add-permission": {
|
||||
"built-in-aria-label": "Built-in role picker",
|
||||
"level-aria-label": "Permission level",
|
||||
"permissions-aria-label": "Permissions slider",
|
||||
"role-label": "Role",
|
||||
"role-select-aria-label": "Role to add new permission to",
|
||||
"serviceaccount-label": "Service Account",
|
||||
"team-label": "Team",
|
||||
"title": "Add permission for",
|
||||
@@ -15,7 +19,9 @@
|
||||
"permission": "Permission"
|
||||
},
|
||||
"permission-list-item": {
|
||||
"inherited": "Inherited from folder"
|
||||
"inherited": "Inherited from folder",
|
||||
"locked-aria-label": "Locked permission indicator",
|
||||
"remove-aria-label": "Remove permission for {{identifier}}"
|
||||
},
|
||||
"permissions": {
|
||||
"add-label": "Add a permission",
|
||||
@@ -60,6 +66,8 @@
|
||||
}
|
||||
},
|
||||
"admin-feature-toggles-table": {
|
||||
"confirm-modal-body-1": "Some features are stable (GA) and enabled by default, whereas some are currently in their preliminary Beta phase, available for early adoption.",
|
||||
"confirm-modal-body-2": "We advise understanding the implications of each feature change before making modifications.",
|
||||
"get-stage-cell": {
|
||||
"beta": "Beta",
|
||||
"deprecated": "Deprecated",
|
||||
@@ -167,6 +175,7 @@
|
||||
"columns": {
|
||||
"aria-label-role": "Role"
|
||||
},
|
||||
"delete-aria-label": "Delete user: {{name}}",
|
||||
"title-delete": "Delete"
|
||||
},
|
||||
"orgs": {
|
||||
@@ -267,6 +276,8 @@
|
||||
"columns": {
|
||||
"title-edit-user": "Edit user"
|
||||
},
|
||||
"edit-aria-label": "Edit user: {{name}}",
|
||||
"edit-tooltip": "Edit user",
|
||||
"last-seen-never": "Never",
|
||||
"no-licensed-roles": "Not assigned"
|
||||
}
|
||||
@@ -2257,6 +2268,9 @@
|
||||
"success": "Copied"
|
||||
}
|
||||
},
|
||||
"close-button": {
|
||||
"tooltip": "Close"
|
||||
},
|
||||
"combobox": {
|
||||
"async": {
|
||||
"error": "An error occurred while loading options."
|
||||
@@ -4295,10 +4309,13 @@
|
||||
}
|
||||
},
|
||||
"folder-filter": {
|
||||
"clear-folder-button": "Clear folders"
|
||||
"clear-folder-button": "Clear folders",
|
||||
"select-aria-label": "Folder filter",
|
||||
"select-placeholder": "Filter by folder"
|
||||
},
|
||||
"folder-picker": {
|
||||
"create-instructions": "Press enter to create the new folder.",
|
||||
"input-placeholder": "Press enter to confirm new folder",
|
||||
"loading": "Loading folders..."
|
||||
},
|
||||
"folder-repo": {
|
||||
@@ -4307,18 +4324,25 @@
|
||||
"forgot-password": {
|
||||
"back-button": "Back to login",
|
||||
"change-password": {
|
||||
"confirm-label": "Confirm new password",
|
||||
"default-password-alert": "Continuing to use the default password exposes you to security risks.",
|
||||
"new-password-label": "New password",
|
||||
"skip-button": "Skip",
|
||||
"submit-button": "Submit"
|
||||
},
|
||||
"contact-admin": "Did you forget your username or email? Contact your Grafana administrator.",
|
||||
"email-sent": "An email with a reset link has been sent to the email address. You should receive it shortly.",
|
||||
"reset-password-header": "Reset password",
|
||||
"send-email-button": "Send reset email"
|
||||
"send-email-button": "Send reset email",
|
||||
"user-field-description": "Enter your information to get a reset link sent to you",
|
||||
"user-field-label": "User",
|
||||
"user-field-placeholder": "Email or username"
|
||||
},
|
||||
"form-prompt": {
|
||||
"continue-button": "Continue editing",
|
||||
"description": "Changes that you made may not be saved.",
|
||||
"discard-button": "Discard unsaved changes"
|
||||
"discard-button": "Discard unsaved changes",
|
||||
"title": "Leave page?"
|
||||
},
|
||||
"frontendtests": {
|
||||
"test-key": "test-key not found"
|
||||
@@ -4588,6 +4612,7 @@
|
||||
"cell-filter-on": "Filter for value",
|
||||
"cell-filter-out": "Filter out value",
|
||||
"cell-inspect": "Inspect value",
|
||||
"cell-inspect-tooltip": "Inspect value",
|
||||
"copy": "Copy to Clipboard",
|
||||
"count": "Count",
|
||||
"csv-counts": "Rows:{{rows}}, Columns:{{columns}} <5></5>",
|
||||
@@ -4597,8 +4622,10 @@
|
||||
"filter-popup-cancel": "Cancel",
|
||||
"filter-popup-clear": "Clear filter",
|
||||
"filter-popup-heading": "Filter by values:",
|
||||
"filter-popup-input-placeholder": "Filter values",
|
||||
"filter-popup-match-case": "Match case",
|
||||
"inspect-drawer-title": "Inspect value",
|
||||
"inspect-menu-label": "Inspect value",
|
||||
"no-values-label": "No values",
|
||||
"pagination-summary": "{{itemsRangeStart}} - {{displayedEnd}} of {{numRows}} rows"
|
||||
},
|
||||
@@ -4741,6 +4768,16 @@
|
||||
"sign-up": "Sign up"
|
||||
}
|
||||
},
|
||||
"layers": {
|
||||
"layer-drag-drop-list": {
|
||||
"draggable-aria-label": "Drag and drop to reorder",
|
||||
"duplicate-tooltip": "Duplicate",
|
||||
"remove-tooltip": "Remove"
|
||||
},
|
||||
"layer-name": {
|
||||
"edit-layer-title": "Edit layer name"
|
||||
}
|
||||
},
|
||||
"ldap-drawer": {
|
||||
"attributes-section": {
|
||||
"description": "Specify the LDAP attributes that map to the user's given name, surname, and email address, ensuring the application correctly retrieves and displays user information.",
|
||||
@@ -4999,10 +5036,10 @@
|
||||
"email-placeholder": "email",
|
||||
"email-required": "Email is required",
|
||||
"name-label": "Name",
|
||||
"name-placeholder": "name",
|
||||
"password-label": "Password",
|
||||
"password-placeholder": "password",
|
||||
"password-required": "Password is required",
|
||||
"passwordless-username-label": "Username",
|
||||
"submit-label": "Log in",
|
||||
"submit-loading-label": "Logging in...",
|
||||
"username-label": "Email or username",
|
||||
@@ -5740,6 +5777,12 @@
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"breadcrumbs": {
|
||||
"aria-label": "Breadcrumbs"
|
||||
},
|
||||
"help": {
|
||||
"aria-label": "Help"
|
||||
},
|
||||
"invite-user": {
|
||||
"invite-button": "Invite",
|
||||
"invite-tooltip": "Invite new member"
|
||||
@@ -5749,6 +5792,7 @@
|
||||
"remove-bookmark": "Remove from Bookmarks"
|
||||
},
|
||||
"kiosk": {
|
||||
"tooltip": "Enable kiosk mode",
|
||||
"tv-alert": "Press ESC to exit kiosk mode"
|
||||
},
|
||||
"megamenu": {
|
||||
@@ -5758,12 +5802,26 @@
|
||||
"open": "Open menu",
|
||||
"undock": "Undock menu"
|
||||
},
|
||||
"megamenu-item": {
|
||||
"collapse-aria-label": "Collapse section: {{sectionName}}",
|
||||
"expand-aria-label": "Expand section: {{sectionName}}"
|
||||
},
|
||||
"org-switcher": {
|
||||
"aria-label": "Change organization"
|
||||
},
|
||||
"profile": {
|
||||
"aria-label": "Profile"
|
||||
},
|
||||
"quick-add": {
|
||||
"aria-label": "New"
|
||||
},
|
||||
"rss-button": "Latest from the blog"
|
||||
},
|
||||
"news": {
|
||||
"drawer": {
|
||||
"close": "Close Drawer"
|
||||
"close": "Close drawer"
|
||||
},
|
||||
"link-title": "Go to Grafana labs blog",
|
||||
"title": "Latest from the blog"
|
||||
},
|
||||
"notifications": {
|
||||
@@ -5788,6 +5846,17 @@
|
||||
"error": "Login provider denied login request"
|
||||
}
|
||||
},
|
||||
"options-ui": {
|
||||
"color": {
|
||||
"clear-tooltip": "Clear settings"
|
||||
},
|
||||
"field-color": {
|
||||
"color-by-label": "Color series by"
|
||||
},
|
||||
"units": {
|
||||
"clear-tooltip": "Clear unit selection"
|
||||
}
|
||||
},
|
||||
"org": {
|
||||
"new-org-page": {
|
||||
"create": "Create",
|
||||
@@ -5809,6 +5878,14 @@
|
||||
"submit": "Submit"
|
||||
}
|
||||
},
|
||||
"org-picker": {
|
||||
"select-placeholder": "Select organization"
|
||||
},
|
||||
"page": {
|
||||
"editable-title": {
|
||||
"edit-tooltip": "Edit title"
|
||||
}
|
||||
},
|
||||
"panel": {
|
||||
"header-menu": {
|
||||
"copy": "Copy",
|
||||
@@ -5856,7 +5933,10 @@
|
||||
"unsupported-layout": "Unsupported layout"
|
||||
},
|
||||
"panel-type-filter": {
|
||||
"clear-button": "Clear types"
|
||||
"clear-button": "Clear types",
|
||||
"select-aria-label": "Panel type filter",
|
||||
"select-no-options": "No panel types found",
|
||||
"select-placeholder": "Filter by type"
|
||||
},
|
||||
"playlist": {
|
||||
"start-modal": {
|
||||
@@ -6065,6 +6145,7 @@
|
||||
},
|
||||
"plugin-help": {
|
||||
"error": "An error occurred when loading help.",
|
||||
"loading": "Loading help...",
|
||||
"not-found": "No query help could be found."
|
||||
},
|
||||
"plugin-installed-badge": {
|
||||
@@ -6822,9 +6903,13 @@
|
||||
"clear-button": "Clear all",
|
||||
"tooltip": "You can now select the \"No basic role\" option and add permissions to your custom needs. You can find more information in <1>our documentation</1>."
|
||||
},
|
||||
"menu-aria-label": "Role picker menu",
|
||||
"menu-group-option-aria-label": "Role picker option",
|
||||
"menu-option-aria-label": "Role picker option",
|
||||
"sub-menu": {
|
||||
"clear-button": "Clear"
|
||||
},
|
||||
"sub-menu-aria-label": "Role picker submenu",
|
||||
"title": {
|
||||
"description": "Assign roles to users to ensure granular control over access to Grafana‘s features and resources. Find out more in our <2>documentation</2>."
|
||||
}
|
||||
@@ -6832,10 +6917,13 @@
|
||||
"role-picker-drawer": {
|
||||
"basic-roles": {
|
||||
"label": "Basic Roles"
|
||||
}
|
||||
},
|
||||
"user-count": "+{{numUsers}}"
|
||||
},
|
||||
"route-error": {
|
||||
"description": "Grafana has likely been updated. Please try reloading the page.",
|
||||
"error-title": "Error details",
|
||||
"error-unexpected-title": "An unexpected error happened",
|
||||
"reload-button": "Reload",
|
||||
"title": "Unable to find application file"
|
||||
},
|
||||
@@ -6952,6 +7040,10 @@
|
||||
"label": "Role"
|
||||
}
|
||||
},
|
||||
"service-account-picker": {
|
||||
"select-aria-label": "Service account picker",
|
||||
"select-placeholder": "Start typing to search for service accounts"
|
||||
},
|
||||
"service-accounts": {
|
||||
"empty-state": {
|
||||
"button-title": "Add service account",
|
||||
@@ -7220,10 +7312,19 @@
|
||||
},
|
||||
"sign-up": {
|
||||
"back-button": "Back to login",
|
||||
"confirm-password-label": "Confirm password",
|
||||
"email-label": "Email",
|
||||
"org-name-label": "Org. name",
|
||||
"password-label": "Password",
|
||||
"submit-button": "Submit",
|
||||
"user-name-label": "Your name",
|
||||
"user-name-placeholder": "(optional)",
|
||||
"verification-code-label": "Email verification code (sent to your email)",
|
||||
"verify": {
|
||||
"back-button": "Back to login",
|
||||
"complete-button": "Complete signup",
|
||||
"email-description": "Enter your email address to get a verification link sent to you",
|
||||
"email-label": "Email",
|
||||
"header": "Verify email",
|
||||
"info": "An email with a verification link has been sent to the email address. You should receive it shortly.",
|
||||
"send-button": "Send verification email"
|
||||
@@ -7281,6 +7382,9 @@
|
||||
"url-column-header": "Snapshot url",
|
||||
"view-button": "View"
|
||||
},
|
||||
"sort-picker": {
|
||||
"select-aria-label": "Sort"
|
||||
},
|
||||
"support-bundles": {
|
||||
"new-bundle-button": {
|
||||
"new-support-bundle": "New support bundle"
|
||||
@@ -7308,7 +7412,13 @@
|
||||
"clear-button": "Clear tags",
|
||||
"loading": "Loading...",
|
||||
"no-tags": "No tags found",
|
||||
"placeholder": "Filter by tag"
|
||||
"placeholder": "Filter by tag",
|
||||
"select-aria-label": "Tag filter",
|
||||
"tag-option-label": "Tag option"
|
||||
},
|
||||
"team-picker": {
|
||||
"select-aria-label": "Team picker",
|
||||
"select-placeholder": "Select a team"
|
||||
},
|
||||
"teams": {
|
||||
"create-team": {
|
||||
@@ -7763,6 +7873,10 @@
|
||||
"select-org-button": "Select organisation",
|
||||
"title": "Organizations"
|
||||
},
|
||||
"user-picker": {
|
||||
"select-aria-label": "User picker",
|
||||
"select-placeholder": "Start typing to search for user"
|
||||
},
|
||||
"user-profile": {
|
||||
"fields": {
|
||||
"email-error": "Email is required",
|
||||
|
||||
Reference in New Issue
Block a user