mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 07:26:03 -03:30
Updates Lingui
This commit is contained in:
@@ -93,7 +93,8 @@
|
|||||||
"RunOnRadio",
|
"RunOnRadio",
|
||||||
"NodeTypeLetter",
|
"NodeTypeLetter",
|
||||||
"SelectableItem",
|
"SelectableItem",
|
||||||
"Dash"
|
"Dash",
|
||||||
|
"Plural"
|
||||||
],
|
],
|
||||||
"ignoreCallee": ["describe"]
|
"ignoreCallee": ["describe"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
{
|
{"catalogs":[{
|
||||||
"localeDir": "src/locales/",
|
"path": "<rootDir>/locales/{locale}/messages",
|
||||||
"srcPathDirs": ["src/"],
|
"include": ["<rootDir>"],
|
||||||
"format": "po",
|
"exclude": ["**/node_modules/**"]
|
||||||
"sourceLocale": "en"
|
}],
|
||||||
|
"compileNamespace": "cjs",
|
||||||
|
"extractBabelOptions": {},
|
||||||
|
"compilerBabelOptions": {},
|
||||||
|
"fallbackLocales": { default: "en"},
|
||||||
|
"format": "po",
|
||||||
|
"locales": ["en","es","fr","nl","zh","ja", "zu"],
|
||||||
|
"orderBy": "messageId",
|
||||||
|
"pseudoLocale": "zu",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"runtimeConfigModule": ["@lingui/core", "i18n"],
|
||||||
|
"sourceLocale": "en",
|
||||||
}
|
}
|
||||||
|
|||||||
1261
awx/ui_next/package-lock.json
generated
1261
awx/ui_next/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
|||||||
"node": "14.x"
|
"node": "14.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lingui/react": "^2.9.1",
|
"@lingui/react": "^3.7.1",
|
||||||
"@patternfly/patternfly": "^4.80.3",
|
"@patternfly/patternfly": "^4.80.3",
|
||||||
"@patternfly/react-core": "^4.90.2",
|
"@patternfly/react-core": "^4.90.2",
|
||||||
"@patternfly/react-icons": "4.7.22",
|
"@patternfly/react-icons": "4.7.22",
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
"ace-builds": "^1.4.12",
|
"ace-builds": "^1.4.12",
|
||||||
"ansi-to-html": "^0.6.11",
|
"ansi-to-html": "^0.6.11",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
"babel-plugin-macros": "^3.0.1",
|
||||||
|
"codemirror": "^5.47.0",
|
||||||
"d3": "^5.12.0",
|
"d3": "^5.12.0",
|
||||||
"dagre": "^0.8.4",
|
"dagre": "^0.8.4",
|
||||||
"formik": "^2.1.2",
|
"formik": "^2.1.2",
|
||||||
@@ -33,8 +35,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/polyfill": "^7.8.7",
|
"@babel/polyfill": "^7.8.7",
|
||||||
"@cypress/instrument-cra": "^1.4.0",
|
"@cypress/instrument-cra": "^1.4.0",
|
||||||
"@lingui/cli": "^2.9.2",
|
"@lingui/cli": "^3.7.1",
|
||||||
"@lingui/macro": "^2.9.1",
|
"@lingui/macro": "^3.7.1",
|
||||||
"@nteract/mockument": "^1.0.4",
|
"@nteract/mockument": "^1.0.4",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"enzyme": "^3.10.0",
|
"enzyme": "^3.10.0",
|
||||||
|
|||||||
@@ -7,18 +7,24 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Redirect,
|
Redirect,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import { I18n, I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
import { Card, PageSection } from '@patternfly/react-core';
|
import { i18n } from '@lingui/core';
|
||||||
|
|
||||||
import { ConfigProvider, useAuthorizedPath } from './contexts/Config';
|
import { en, es, fr, nl, zh, ja, zu } from 'make-plural/plurals';
|
||||||
import AppContainer from './components/AppContainer';
|
import AppContainer from './components/AppContainer';
|
||||||
import Background from './components/Background';
|
import Background from './components/Background';
|
||||||
import NotFound from './screens/NotFound';
|
import NotFound from './screens/NotFound';
|
||||||
import Login from './screens/Login';
|
import Login from './screens/Login';
|
||||||
|
|
||||||
import ja from './locales/ja/messages';
|
import japanese from './locales/ja/messages';
|
||||||
import en from './locales/en/messages';
|
import english from './locales/en/messages';
|
||||||
|
import zulu from './locales/zu/messages';
|
||||||
|
import french from './locales/fr/messages';
|
||||||
|
import dutch from './locales/nl/messages';
|
||||||
|
import chinese from './locales/zh/messages';
|
||||||
|
import spanish from './locales/es/messages';
|
||||||
import { isAuthenticated } from './util/auth';
|
import { isAuthenticated } from './util/auth';
|
||||||
|
|
||||||
import { getLanguageWithoutRegionCode } from './util/language';
|
import { getLanguageWithoutRegionCode } from './util/language';
|
||||||
|
|
||||||
import getRouteConfig from './routeConfig';
|
import getRouteConfig from './routeConfig';
|
||||||
@@ -74,7 +80,15 @@ const ProtectedRoute = ({ children, ...rest }) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const catalogs = { en, ja };
|
const catalogs = {
|
||||||
|
en: english,
|
||||||
|
ja: japanese,
|
||||||
|
zu: zulu,
|
||||||
|
fr: french,
|
||||||
|
es: spanish,
|
||||||
|
zh: chinese,
|
||||||
|
nl: dutch,
|
||||||
|
};
|
||||||
let language = getLanguageWithoutRegionCode(navigator);
|
let language = getLanguageWithoutRegionCode(navigator);
|
||||||
if (!Object.keys(catalogs).includes(language)) {
|
if (!Object.keys(catalogs).includes(language)) {
|
||||||
// If there isn't a string catalog available for the browser's
|
// If there isn't a string catalog available for the browser's
|
||||||
@@ -83,32 +97,56 @@ function App() {
|
|||||||
}
|
}
|
||||||
const { hash, search, pathname } = useLocation();
|
const { hash, search, pathname } = useLocation();
|
||||||
|
|
||||||
|
i18n.loadLocaleData('en', { plurals: en });
|
||||||
|
i18n.loadLocaleData('es', { plurals: es });
|
||||||
|
i18n.loadLocaleData('fr', { plurals: fr });
|
||||||
|
i18n.loadLocaleData('nl', { plurals: nl });
|
||||||
|
i18n.loadLocaleData('zh', { plurals: zh });
|
||||||
|
i18n.loadLocaleData('ja', { plurals: ja });
|
||||||
|
i18n.loadLocaleData('zu', { plurals: zu });
|
||||||
|
i18n.load({
|
||||||
|
en: english.messages,
|
||||||
|
ja: japanese.messages,
|
||||||
|
zu: zulu.messages,
|
||||||
|
fr: french.messages,
|
||||||
|
nl: dutch.messages,
|
||||||
|
zh: chinese.messages,
|
||||||
|
es: spanish.messages,
|
||||||
|
});
|
||||||
|
i18n.activate(language);
|
||||||
return (
|
return (
|
||||||
<I18nProvider language={language} catalogs={catalogs}>
|
<I18nProvider i18n={i18n} catalogs={catalogs}>
|
||||||
<I18n>
|
<Background>
|
||||||
{({ i18n }) => (
|
<Switch>
|
||||||
<Background>
|
<Route exact strict path="/*/">
|
||||||
<Switch>
|
<Redirect to={`${pathname.slice(0, -1)}${search}${hash}`} />
|
||||||
<Route exact strict path="/*/">
|
</Route>
|
||||||
<Redirect to={`${pathname.slice(0, -1)}${search}${hash}`} />
|
<Route path="/login">
|
||||||
</Route>
|
<Login isAuthenticated={isAuthenticated} />
|
||||||
<Route path="/login">
|
</Route>
|
||||||
<Login isAuthenticated={isAuthenticated} />
|
<Route exact path="/">
|
||||||
</Route>
|
<Redirect to="/home" />
|
||||||
<Route exact path="/">
|
</Route>
|
||||||
<Redirect to="/home" />
|
<ProtectedRoute>
|
||||||
</Route>
|
<AppContainer navRouteConfig={getRouteConfig(i18n)}>
|
||||||
<ProtectedRoute>
|
<Switch>
|
||||||
<ConfigProvider>
|
{getRouteConfig(i18n)
|
||||||
<AppContainer navRouteConfig={getRouteConfig(i18n)}>
|
.flatMap(({ routes }) => routes)
|
||||||
<AuthorizedRoutes routeConfig={getRouteConfig(i18n)} />
|
.map(({ path, screen: Screen }) => (
|
||||||
</AppContainer>
|
<ProtectedRoute key={path} path={path}>
|
||||||
</ConfigProvider>
|
<Screen match={match} />
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
</Switch>
|
))
|
||||||
</Background>
|
.concat(
|
||||||
)}
|
<ProtectedRoute key="not-found" path="*">
|
||||||
</I18n>
|
<NotFound />
|
||||||
|
</ProtectedRoute>
|
||||||
|
)}
|
||||||
|
</Switch>
|
||||||
|
</AppContainer>
|
||||||
|
</ProtectedRoute>
|
||||||
|
</Switch>
|
||||||
|
</Background>
|
||||||
</I18nProvider>
|
</I18nProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import {
|
import {
|
||||||
mountWithContexts,
|
mountWithContexts,
|
||||||
|
shallowWithContexts,
|
||||||
waitForElement,
|
waitForElement,
|
||||||
} from '../../../testUtils/enzymeHelpers';
|
} from '../../../testUtils/enzymeHelpers';
|
||||||
import { sleep } from '../../../testUtils/testUtils';
|
import { sleep } from '../../../testUtils/testUtils';
|
||||||
@@ -27,17 +27,19 @@ describe('<SelectResourceStep />', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
});
|
});
|
||||||
test('initially renders without crashing', () => {
|
test('initially renders without crashing', async () => {
|
||||||
shallow(
|
act(() => {
|
||||||
<SelectResourceStep
|
shallowWithContexts(
|
||||||
searchColumns={searchColumns}
|
<SelectResourceStep
|
||||||
sortColumns={sortColumns}
|
searchColumns={searchColumns}
|
||||||
displayKey="username"
|
sortColumns={sortColumns}
|
||||||
onRowClick={() => {}}
|
displayKey="username"
|
||||||
fetchItems={() => {}}
|
onRowClick={() => {}}
|
||||||
fetchOptions={() => {}}
|
fetchItems={() => {}}
|
||||||
/>
|
fetchOptions={() => {}}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fetches resources on mount and adds items to list', async () => {
|
test('fetches resources on mount and adds items to list', async () => {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
import {
|
||||||
|
mountWithContexts,
|
||||||
|
shallowWithContexts,
|
||||||
|
} from '../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import SelectRoleStep from './SelectRoleStep';
|
import SelectRoleStep from './SelectRoleStep';
|
||||||
|
|
||||||
describe('<SelectRoleStep />', () => {
|
describe('<SelectRoleStep />', () => {
|
||||||
@@ -31,7 +35,7 @@ describe('<SelectRoleStep />', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
test('initially renders without crashing', () => {
|
test('initially renders without crashing', () => {
|
||||||
wrapper = shallow(
|
wrapper = shallowWithContexts(
|
||||||
<SelectRoleStep
|
<SelectRoleStep
|
||||||
roles={roles}
|
roles={roles}
|
||||||
selectedResourceRows={selectedResourceRows}
|
selectedResourceRows={selectedResourceRows}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t, Plural } from '@lingui/macro';
|
||||||
import { arrayOf, func } from 'prop-types';
|
import { arrayOf, func } from 'prop-types';
|
||||||
import { Button, DropdownItem, Tooltip } from '@patternfly/react-core';
|
import { Button, DropdownItem, Tooltip } from '@patternfly/react-core';
|
||||||
import { KebabifiedContext } from '../../contexts/Kebabified';
|
import { KebabifiedContext } from '../../contexts/Kebabified';
|
||||||
@@ -22,7 +22,6 @@ function JobListCancelButton({ i18n, jobsToCancel, onCancel }) {
|
|||||||
const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext);
|
const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const numJobsToCancel = jobsToCancel.length;
|
const numJobsToCancel = jobsToCancel.length;
|
||||||
const zeroOrOneJobSelected = numJobsToCancel < 2;
|
|
||||||
|
|
||||||
const handleCancelJob = () => {
|
const handleCancelJob = () => {
|
||||||
onCancel();
|
onCancel();
|
||||||
@@ -54,35 +53,32 @@ function JobListCancelButton({ i18n, jobsToCancel, onCancel }) {
|
|||||||
<div>
|
<div>
|
||||||
{cannotCancelPermissions.length > 0 && (
|
{cannotCancelPermissions.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
{i18n._(
|
<Plural
|
||||||
'{numJobsUnableToCancel, plural, one {You do not have permission to cancel the following job:} other {You do not have permission to cancel the following jobs:}}',
|
value={cannotCancelPermissions.length}
|
||||||
{
|
one="You do not have permission to cancel the following job:"
|
||||||
numJobsUnableToCancel: cannotCancelPermissions.length,
|
other="You do not have permission to cancel the following jobs:"
|
||||||
}
|
/>
|
||||||
)}
|
|
||||||
{' '.concat(cannotCancelPermissions.join(', '))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{cannotCancelNotRunning.length > 0 && (
|
{cannotCancelNotRunning.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
{i18n._(
|
<Plural
|
||||||
'{numJobsUnableToCancel, plural, one {You cannot cancel the following job because it is not running:} other {You cannot cancel the following jobs because they are not running:}}',
|
value={cannotCancelNotRunning.length}
|
||||||
{
|
one="You cannot cancel the following job because it is not running"
|
||||||
numJobsUnableToCancel: cannotCancelNotRunning.length,
|
other="You cannot cancel the following jobs because they are not running"
|
||||||
}
|
/>
|
||||||
)}
|
|
||||||
{' '.concat(cannotCancelNotRunning.join(', '))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (numJobsToCancel > 0) {
|
if (numJobsToCancel > 0) {
|
||||||
return i18n._(
|
return (
|
||||||
'{numJobsToCancel, plural, one {Cancel selected job} other {Cancel selected jobs}}',
|
<Plural
|
||||||
{
|
value={numJobsToCancel}
|
||||||
numJobsToCancel,
|
one={i18n._(t`Cancel selected job`)}
|
||||||
}
|
other={i18n._(t`Cancel selected jobs`)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return i18n._(t`Select a job to cancel`);
|
return i18n._(t`Select a job to cancel`);
|
||||||
@@ -92,12 +88,8 @@ function JobListCancelButton({ i18n, jobsToCancel, onCancel }) {
|
|||||||
jobsToCancel.length === 0 ||
|
jobsToCancel.length === 0 ||
|
||||||
jobsToCancel.some(cannotCancelBecausePermissions) ||
|
jobsToCancel.some(cannotCancelBecausePermissions) ||
|
||||||
jobsToCancel.some(cannotCancelBecauseNotRunning);
|
jobsToCancel.some(cannotCancelBecauseNotRunning);
|
||||||
|
const cancelJobText = (
|
||||||
const cancelJobText = i18n._(
|
<Plural value={numJobsToCancel} one="Cancel job" other="Cancel jobs" />
|
||||||
'{zeroOrOneJobSelected, plural, one {Cancel job} other {Cancel jobs}}',
|
|
||||||
{
|
|
||||||
zeroOrOneJobSelected,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -156,12 +148,11 @@ function JobListCancelButton({ i18n, jobsToCancel, onCancel }) {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{i18n._(
|
<Plural
|
||||||
'{numJobsToCancel, plural, one {This action will cancel the following job:} other {This action will cancel the following jobs:}}',
|
value={numJobsToCancel}
|
||||||
{
|
one="This action will cancel the following job:"
|
||||||
numJobsToCancel,
|
other="This action will cancel the following jobs:"
|
||||||
}
|
/>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{jobsToCancel.map(job => (
|
{jobsToCancel.map(job => (
|
||||||
<span key={job.id}>
|
<span key={job.id}>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ import {
|
|||||||
Pagination as PFPagination,
|
Pagination as PFPagination,
|
||||||
DropdownDirection,
|
DropdownDirection,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { I18n } from '@lingui/react';
|
import { i18n } from '@lingui/core';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
const AWXPagination = styled(PFPagination)`
|
const AWXPagination = styled(PFPagination)`
|
||||||
@@ -19,26 +19,22 @@ const AWXPagination = styled(PFPagination)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default props => (
|
export default props => (
|
||||||
<I18n>
|
<AWXPagination
|
||||||
{({ i18n }) => (
|
titles={{
|
||||||
<AWXPagination
|
items: i18n._(t`items`),
|
||||||
titles={{
|
page: i18n._(t`page`),
|
||||||
items: i18n._(t`items`),
|
pages: i18n._(t`pages`),
|
||||||
page: i18n._(t`page`),
|
itemsPerPage: i18n._(t`Items per page`),
|
||||||
pages: i18n._(t`pages`),
|
perPageSuffix: i18n._(t`per page`),
|
||||||
itemsPerPage: i18n._(t`Items per page`),
|
toFirstPage: i18n._(t`Go to first page`),
|
||||||
perPageSuffix: i18n._(t`per page`),
|
toPreviousPage: i18n._(t`Go to previous page`),
|
||||||
toFirstPage: i18n._(t`Go to first page`),
|
toLastPage: i18n._(t`Go to last page`),
|
||||||
toPreviousPage: i18n._(t`Go to previous page`),
|
toNextPage: i18n._(t`Go to next page`),
|
||||||
toLastPage: i18n._(t`Go to last page`),
|
optionsToggle: i18n._(t`Select`),
|
||||||
toNextPage: i18n._(t`Go to next page`),
|
currPage: i18n._(t`Current page`),
|
||||||
optionsToggle: i18n._(t`Select`),
|
paginationTitle: i18n._(t`Pagination`),
|
||||||
currPage: i18n._(t`Current page`),
|
}}
|
||||||
paginationTitle: i18n._(t`Pagination`),
|
dropDirection={DropdownDirection.up}
|
||||||
}}
|
{...props}
|
||||||
dropDirection={DropdownDirection.up}
|
/>
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</I18n>
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ function DeleteRoleConfirmationModal({
|
|||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
const isTeamRole = () => {
|
const isTeamRole = () => {
|
||||||
return typeof role.team_id !== 'undefined';
|
return typeof role.team_id !== 'undefined'
|
||||||
|
? i18n._(t`Team`)
|
||||||
|
: i18n._(t`User`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const title = i18n._(
|
const title = i18n._(t`Remove ${isTeamRole()} Access`);
|
||||||
t`Remove ${isTeamRole() ? i18n._(t`Team`) : i18n._(t`User`)} Access`
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
variant="danger"
|
variant="danger"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ import React from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useField } from 'formik';
|
import { useField } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t, Trans } from '@lingui/macro';
|
import { t, Trans, Plural } from '@lingui/macro';
|
||||||
import { RRule } from 'rrule';
|
import { RRule } from 'rrule';
|
||||||
import {
|
import {
|
||||||
Checkbox as _Checkbox,
|
Checkbox as _Checkbox,
|
||||||
@@ -185,29 +185,17 @@ const FrequencyDetailSubform = ({ i18n }) => {
|
|||||||
|
|
||||||
switch (frequency.value) {
|
switch (frequency.value) {
|
||||||
case 'minute':
|
case 'minute':
|
||||||
return i18n._('{intervalValue, plural, one {minute} other {minutes}}', {
|
return <Plural value={intervalValue} one="minute" other="minutes" />;
|
||||||
intervalValue,
|
|
||||||
});
|
|
||||||
case 'hour':
|
case 'hour':
|
||||||
return i18n._('{intervalValue, plural, one {hour} other {hours}}', {
|
return <Plural value={intervalValue} one="hour" other="hours" />;
|
||||||
intervalValue,
|
|
||||||
});
|
|
||||||
case 'day':
|
case 'day':
|
||||||
return i18n._('{intervalValue, plural, one {day} other {days}}', {
|
return <Plural value={intervalValue} one="day" other="days" />;
|
||||||
intervalValue,
|
|
||||||
});
|
|
||||||
case 'week':
|
case 'week':
|
||||||
return i18n._('{intervalValue, plural, one {week} other {weeks}}', {
|
return <Plural value={intervalValue} one="week" other="weeks" />;
|
||||||
intervalValue,
|
|
||||||
});
|
|
||||||
case 'month':
|
case 'month':
|
||||||
return i18n._('{intervalValue, plural, one {month} other {months}}', {
|
return <Plural value={intervalValue} one="month" other="months" />;
|
||||||
intervalValue,
|
|
||||||
});
|
|
||||||
case 'year':
|
case 'year':
|
||||||
return i18n._('{intervalValue, plural, one {year} other {years}}', {
|
return <Plural value={intervalValue} one="year" other="years" />;
|
||||||
intervalValue,
|
|
||||||
});
|
|
||||||
default:
|
default:
|
||||||
throw new Error(i18n._(t`Frequency did not match an expected value`));
|
throw new Error(i18n._(t`Frequency did not match an expected value`));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mount } from 'enzyme';
|
|
||||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
import {
|
||||||
|
mountWithContexts,
|
||||||
|
shallowWithContexts,
|
||||||
|
} from '../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import Sparkline from './Sparkline';
|
import Sparkline from './Sparkline';
|
||||||
|
|
||||||
describe('Sparkline', () => {
|
describe('Sparkline', () => {
|
||||||
test('renders the expected content', () => {
|
test('renders the expected content', () => {
|
||||||
const wrapper = mount(<Sparkline />);
|
const wrapper = shallowWithContexts(<Sparkline />);
|
||||||
expect(wrapper).toHaveLength(1);
|
expect(wrapper).toHaveLength(1);
|
||||||
});
|
});
|
||||||
test('renders an icon with tooltips and links for each job', () => {
|
test('renders an icon with tooltips and links for each job', () => {
|
||||||
|
|||||||
@@ -170,8 +170,7 @@ function TemplateList({ defaultParams, i18n }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { en } from 'make-plural/plurals';
|
||||||
|
import { I18nProvider } from '@lingui/react';
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||||
|
import english from '../../locales/en/messages';
|
||||||
import { WorkflowStateContext } from '../../contexts/Workflow';
|
import { WorkflowStateContext } from '../../contexts/Workflow';
|
||||||
import WorkflowStartNode from './WorkflowStartNode';
|
import WorkflowStartNode from './WorkflowStartNode';
|
||||||
|
|
||||||
@@ -10,16 +15,22 @@ const nodePositions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
|
i18n.load({ en: english });
|
||||||
|
i18n.activate('en');
|
||||||
|
|
||||||
describe('WorkflowStartNode', () => {
|
describe('WorkflowStartNode', () => {
|
||||||
test('mounts successfully', () => {
|
test('mounts successfully', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<svg>
|
<svg>
|
||||||
<WorkflowStateContext.Provider value={{ nodePositions }}>
|
<I18nProvider i18n={i18n}>
|
||||||
<WorkflowStartNode
|
<WorkflowStateContext.Provider value={{ nodePositions }}>
|
||||||
nodePositions={nodePositions}
|
<WorkflowStartNode
|
||||||
showActionTooltip={false}
|
nodePositions={nodePositions}
|
||||||
/>
|
showActionTooltip={false}
|
||||||
</WorkflowStateContext.Provider>
|
/>
|
||||||
|
</WorkflowStateContext.Provider>
|
||||||
|
</I18nProvider>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
expect(wrapper).toHaveLength(1);
|
expect(wrapper).toHaveLength(1);
|
||||||
@@ -27,9 +38,14 @@ describe('WorkflowStartNode', () => {
|
|||||||
test('tooltip shown on hover', () => {
|
test('tooltip shown on hover', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<svg>
|
<svg>
|
||||||
<WorkflowStateContext.Provider value={{ nodePositions }}>
|
<I18nProvider i18n={i18n}>
|
||||||
<WorkflowStartNode nodePositions={nodePositions} showActionTooltip />
|
<WorkflowStateContext.Provider value={{ nodePositions }}>
|
||||||
</WorkflowStateContext.Provider>
|
<WorkflowStartNode
|
||||||
|
nodePositions={nodePositions}
|
||||||
|
showActionTooltip
|
||||||
|
/>
|
||||||
|
</WorkflowStateContext.Provider>
|
||||||
|
</I18nProvider>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('WorkflowActionTooltip')).toHaveLength(0);
|
expect(wrapper.find('WorkflowActionTooltip')).toHaveLength(0);
|
||||||
|
|||||||
@@ -185,8 +185,7 @@ function CredentialDetail({ i18n, credential }) {
|
|||||||
}, [fetchDetails]);
|
}, [fetchDetails]);
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.credential(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.credential(
|
||||||
credential,
|
credential
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasContentLoading) {
|
if (hasContentLoading) {
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ function CredentialList({ i18n }) {
|
|||||||
const canAdd =
|
const canAdd =
|
||||||
actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
|
actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.credential(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.credential(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<PageSection>
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ function CredentialTypeList({ i18n }) {
|
|||||||
const canAdd = actions && actions.POST;
|
const canAdd = actions && actions.POST;
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.credentialType(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.credentialType(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
|
|||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.executionEnvironment(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.executionEnvironment(
|
||||||
executionEnvironment,
|
executionEnvironment
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
|||||||
@@ -106,8 +106,7 @@ function ExecutionEnvironmentList({ i18n }) {
|
|||||||
|
|
||||||
const canAdd = actions && actions.POST;
|
const canAdd = actions && actions.POST;
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.executionEnvironment(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.executionEnvironment(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ function ContainerGroupDetails({ instanceGroup, i18n }) {
|
|||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.instanceGroup(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.instanceGroup(
|
||||||
instanceGroup,
|
instanceGroup
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ function InstanceGroupDetails({ instanceGroup, i18n }) {
|
|||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.instanceGroup(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.instanceGroup(
|
||||||
instanceGroup,
|
instanceGroup
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
const verifyInstanceGroup = item => {
|
const verifyInstanceGroup = item => {
|
||||||
if (item.is_isolated) {
|
if (item.is_isolated) {
|
||||||
|
|||||||
@@ -187,8 +187,7 @@ function InstanceGroupList({ i18n }) {
|
|||||||
: `${match.url}/${item.id}/details`;
|
: `${match.url}/${item.id}/details`;
|
||||||
};
|
};
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.instanceGroup(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.instanceGroup(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ function InventoryDetail({ inventory, i18n }) {
|
|||||||
} = inventory.summary_fields;
|
} = inventory.summary_fields;
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.inventory(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.inventory(
|
||||||
inventory,
|
inventory
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@@ -219,10 +219,12 @@ describe('<InventoryGroupsList/> error handling', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')();
|
wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')();
|
||||||
});
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
await waitForElement(
|
await waitForElement(
|
||||||
wrapper,
|
wrapper,
|
||||||
'AlertModal[title="Delete Group?"]',
|
'AlertModal__Header',
|
||||||
el => el.props().isOpen === true
|
el => el.text() === 'Delete Group?'
|
||||||
);
|
);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
|
wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useCallback, useEffect } from 'react';
|
import React, { useState, useCallback, useEffect } from 'react';
|
||||||
import { useLocation, useRouteMatch, Link } from 'react-router-dom';
|
import { useLocation, useRouteMatch, Link } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t, Plural } from '@lingui/macro';
|
||||||
import { Card, PageSection, DropdownItem } from '@patternfly/react-core';
|
import { Card, PageSection, DropdownItem } from '@patternfly/react-core';
|
||||||
import { InventoriesAPI } from '../../../api';
|
import { InventoriesAPI } from '../../../api';
|
||||||
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
||||||
@@ -129,8 +129,7 @@ function InventoryList({ i18n }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.inventory(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.inventory(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const addInventory = i18n._(t`Add inventory`);
|
const addInventory = i18n._(t`Add inventory`);
|
||||||
@@ -219,15 +218,14 @@ function InventoryList({ i18n }) {
|
|||||||
onDelete={handleInventoryDelete}
|
onDelete={handleInventoryDelete}
|
||||||
itemsToDelete={selected}
|
itemsToDelete={selected}
|
||||||
pluralizedItemName={i18n._(t`Inventories`)}
|
pluralizedItemName={i18n._(t`Inventories`)}
|
||||||
warningMessage={i18n._(
|
|
||||||
'{numItemsToDelete, plural, one {The inventory will be in a pending status until the final delete is processed.} other {The inventories will be in a pending status until the final delete is processed.}}',
|
|
||||||
{ numItemsToDelete: selected.length }
|
|
||||||
)}
|
|
||||||
deleteMessage={i18n._(
|
|
||||||
'{numItemsToDelete, plural, one {This inventory is currently being used by other resources. Are you sure you want to delete it?} other {Deleting these inventories could impact other resources that rely on them. Are you sure you want to delete anyway?}}',
|
|
||||||
{ numItemsToDelete: selected.length }
|
|
||||||
)}
|
|
||||||
deleteDetailsRequests={deleteDetailsRequests}
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
|
warningMessage={
|
||||||
|
<Plural
|
||||||
|
value={selected.length}
|
||||||
|
one="The inventory will be in a pending status until the final delete is processed."
|
||||||
|
other="The inventories will be in a pending status until the final delete is processed."
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ function InventorySourceDetail({ inventorySource, i18n }) {
|
|||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.inventorySource(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.inventorySource(
|
||||||
inventorySource.inventory,
|
inventorySource.inventory,
|
||||||
i18n,
|
|
||||||
inventorySource
|
inventorySource
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,6 @@ function InventorySourceList({ i18n }) {
|
|||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.inventorySource(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.inventorySource(
|
||||||
id,
|
id,
|
||||||
i18n,
|
|
||||||
selected[0]
|
selected[0]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'styled-components/macro';
|
|||||||
import React, { useState, useContext, useEffect } from 'react';
|
import React, { useState, useContext, useEffect } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { func, bool, arrayOf } from 'prop-types';
|
import { func, bool, arrayOf } from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t, Plural } from '@lingui/macro';
|
||||||
import { Button, Radio, DropdownItem } from '@patternfly/react-core';
|
import { Button, Radio, DropdownItem } from '@patternfly/react-core';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { KebabifiedContext } from '../../../contexts/Kebabified';
|
import { KebabifiedContext } from '../../../contexts/Kebabified';
|
||||||
@@ -18,12 +18,8 @@ const ListItem = styled.li`
|
|||||||
color: var(--pf-global--danger-color--100);
|
color: var(--pf-global--danger-color--100);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InventoryGroupsDeleteModal = ({
|
const InventoryGroupsDeleteModal = ({ onAfterDelete, isDisabled, groups }) => {
|
||||||
onAfterDelete,
|
const { i18n } = useLingui();
|
||||||
isDisabled,
|
|
||||||
groups,
|
|
||||||
i18n,
|
|
||||||
}) => {
|
|
||||||
const [radioOption, setRadioOption] = useState(null);
|
const [radioOption, setRadioOption] = useState(null);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
@@ -87,9 +83,11 @@ const InventoryGroupsDeleteModal = ({
|
|||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
variant="danger"
|
variant="danger"
|
||||||
title={
|
title={
|
||||||
groups.length > 1
|
<Plural
|
||||||
? i18n._(t`Delete Groups?`)
|
value={groups.length}
|
||||||
: i18n._(t`Delete Group?`)
|
one="Delete Group?"
|
||||||
|
other="Delete Groups?"
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
actions={[
|
actions={[
|
||||||
@@ -112,11 +110,12 @@ const InventoryGroupsDeleteModal = ({
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{i18n._(
|
<Plural
|
||||||
t`Are you sure you want to delete the ${
|
value={groups.length}
|
||||||
groups.length > 1 ? i18n._(t`groups`) : i18n._(t`group`)
|
one="Are you sure you want delete the group below?"
|
||||||
} below?`
|
other="Are you sure you want delete the groups below?"
|
||||||
)}
|
/>
|
||||||
|
|
||||||
<div css="padding: 24px 0;">
|
<div css="padding: 24px 0;">
|
||||||
{groups.map(group => {
|
{groups.map(group => {
|
||||||
return <ListItem key={group.id}>{group.name}</ListItem>;
|
return <ListItem key={group.id}>{group.name}</ListItem>;
|
||||||
@@ -167,4 +166,4 @@ InventoryGroupsDeleteModal.defaultProps = {
|
|||||||
groups: [],
|
groups: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withI18n()(InventoryGroupsDeleteModal);
|
export default InventoryGroupsDeleteModal;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useHistory, useLocation, withRouter } from 'react-router-dom';
|
import { useHistory, useLocation, withRouter } from 'react-router-dom';
|
||||||
import { I18n } from '@lingui/react';
|
import { i18n } from '@lingui/core';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import {
|
import {
|
||||||
@@ -625,7 +625,7 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
|||||||
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
|
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSearchComponent = i18n => (
|
const renderSearchComponent = () => (
|
||||||
<Search
|
<Search
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
columns={[
|
columns={[
|
||||||
@@ -688,176 +688,157 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<I18n>
|
<>
|
||||||
{({ i18n }) => (
|
<CardBody>
|
||||||
<>
|
{isHostModalOpen && (
|
||||||
<CardBody>
|
<HostEventModal
|
||||||
{isHostModalOpen && (
|
onClose={handleHostModalClose}
|
||||||
<HostEventModal
|
isOpen={isHostModalOpen}
|
||||||
onClose={handleHostModalClose}
|
hostEvent={hostEvent}
|
||||||
isOpen={isHostModalOpen}
|
/>
|
||||||
hostEvent={hostEvent}
|
)}
|
||||||
/>
|
<OutputHeader>
|
||||||
)}
|
<HeaderTitle>
|
||||||
<OutputHeader>
|
<StatusIcon status={job.status} />
|
||||||
<HeaderTitle>
|
<h1>{job.name}</h1>
|
||||||
<StatusIcon status={job.status} />
|
</HeaderTitle>
|
||||||
<h1>{job.name}</h1>
|
<OutputToolbar
|
||||||
</HeaderTitle>
|
job={job}
|
||||||
<OutputToolbar
|
jobStatus={jobStatus}
|
||||||
job={job}
|
onCancel={() => setShowCancelModal(true)}
|
||||||
jobStatus={jobStatus}
|
onDelete={deleteJob}
|
||||||
onCancel={() => setShowCancelModal(true)}
|
isDeleteDisabled={isDeleting}
|
||||||
onDelete={deleteJob}
|
/>
|
||||||
isDeleteDisabled={isDeleting}
|
</OutputHeader>
|
||||||
/>
|
<HostStatusBar counts={job.host_status_counts} />
|
||||||
</OutputHeader>
|
<SearchToolbar
|
||||||
<HostStatusBar counts={job.host_status_counts} />
|
id="job_output-toolbar"
|
||||||
<SearchToolbar
|
clearAllFilters={handleRemoveAllSearchTerms}
|
||||||
id="job_output-toolbar"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
clearAllFilters={handleRemoveAllSearchTerms}
|
clearFiltersButtonText={i18n._(t`Clear all filters`)}
|
||||||
collapseListedFiltersBreakpoint="lg"
|
>
|
||||||
clearFiltersButtonText={i18n._(t`Clear all filters`)}
|
<SearchToolbarContent>
|
||||||
>
|
<ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
|
||||||
<SearchToolbarContent>
|
<ToolbarItem variant="search-filter">
|
||||||
<ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
|
{isJobRunning(job.status) ? (
|
||||||
<ToolbarItem variant="search-filter">
|
<Tooltip
|
||||||
{isJobRunning(job.status) ? (
|
content={i18n._(
|
||||||
<Tooltip
|
t`Search is disabled while the job is running`
|
||||||
content={i18n._(
|
|
||||||
t`Search is disabled while the job is running`
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{renderSearchComponent(i18n)}
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
renderSearchComponent(i18n)
|
|
||||||
)}
|
)}
|
||||||
<Tooltip
|
>
|
||||||
content={i18n._(t`Job output documentation`)}
|
{renderSearchComponent(i18n)}
|
||||||
position="bottom"
|
</Tooltip>
|
||||||
>
|
) : (
|
||||||
<Button
|
renderSearchComponent(i18n)
|
||||||
component="a"
|
|
||||||
variant="plain"
|
|
||||||
target="_blank"
|
|
||||||
href={`${getDocsBaseUrl(
|
|
||||||
config
|
|
||||||
)}/html/userguide/jobs.html#standard-out-pane`}
|
|
||||||
>
|
|
||||||
<QuestionCircleIcon />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</ToolbarItem>
|
|
||||||
</ToolbarToggleGroup>
|
|
||||||
</SearchToolbarContent>
|
|
||||||
</SearchToolbar>
|
|
||||||
<PageControls
|
|
||||||
onScrollFirst={handleScrollFirst}
|
|
||||||
onScrollLast={handleScrollLast}
|
|
||||||
onScrollNext={handleScrollNext}
|
|
||||||
onScrollPrevious={handleScrollPrevious}
|
|
||||||
/>
|
|
||||||
<OutputWrapper cssMap={cssMap}>
|
|
||||||
<InfiniteLoader
|
|
||||||
isRowLoaded={isRowLoaded}
|
|
||||||
loadMoreRows={loadMoreRows}
|
|
||||||
rowCount={remoteRowCount}
|
|
||||||
>
|
|
||||||
{({ onRowsRendered, registerChild }) => (
|
|
||||||
<AutoSizer nonce={window.NONCE_ID} onResize={handleResize}>
|
|
||||||
{({ width, height }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{hasContentLoading ? (
|
|
||||||
<div style={{ width }}>
|
|
||||||
<ContentLoading />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<List
|
|
||||||
ref={ref => {
|
|
||||||
registerChild(ref);
|
|
||||||
listRef.current = ref;
|
|
||||||
}}
|
|
||||||
deferredMeasurementCache={cache}
|
|
||||||
height={height || 1}
|
|
||||||
onRowsRendered={onRowsRendered}
|
|
||||||
rowCount={remoteRowCount}
|
|
||||||
rowHeight={cache.rowHeight}
|
|
||||||
rowRenderer={rowRenderer}
|
|
||||||
scrollToAlignment="start"
|
|
||||||
width={width || 1}
|
|
||||||
overscanRowCount={20}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</AutoSizer>
|
|
||||||
)}
|
)}
|
||||||
</InfiniteLoader>
|
</ToolbarItem>
|
||||||
<OutputFooter />
|
</ToolbarToggleGroup>
|
||||||
</OutputWrapper>
|
</SearchToolbarContent>
|
||||||
</CardBody>
|
</SearchToolbar>
|
||||||
{showCancelModal && isJobRunning(job.status) && (
|
<PageControls
|
||||||
<AlertModal
|
onScrollFirst={handleScrollFirst}
|
||||||
isOpen={showCancelModal}
|
onScrollLast={handleScrollLast}
|
||||||
|
onScrollNext={handleScrollNext}
|
||||||
|
onScrollPrevious={handleScrollPrevious}
|
||||||
|
/>
|
||||||
|
<OutputWrapper cssMap={cssMap}>
|
||||||
|
<InfiniteLoader
|
||||||
|
isRowLoaded={isRowLoaded}
|
||||||
|
loadMoreRows={loadMoreRows}
|
||||||
|
rowCount={remoteRowCount}
|
||||||
|
>
|
||||||
|
{({ onRowsRendered, registerChild }) => (
|
||||||
|
<AutoSizer nonce={window.NONCE_ID} onResize={handleResize}>
|
||||||
|
{({ width, height }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{hasContentLoading ? (
|
||||||
|
<div style={{ width }}>
|
||||||
|
<ContentLoading />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<List
|
||||||
|
ref={ref => {
|
||||||
|
registerChild(ref);
|
||||||
|
listRef.current = ref;
|
||||||
|
}}
|
||||||
|
deferredMeasurementCache={cache}
|
||||||
|
height={height || 1}
|
||||||
|
onRowsRendered={onRowsRendered}
|
||||||
|
rowCount={remoteRowCount}
|
||||||
|
rowHeight={cache.rowHeight}
|
||||||
|
rowRenderer={rowRenderer}
|
||||||
|
scrollToAlignment="start"
|
||||||
|
width={width || 1}
|
||||||
|
overscanRowCount={20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</AutoSizer>
|
||||||
|
)}
|
||||||
|
</InfiniteLoader>
|
||||||
|
<OutputFooter />
|
||||||
|
</OutputWrapper>
|
||||||
|
</CardBody>
|
||||||
|
{showCancelModal && isJobRunning(job.status) && (
|
||||||
|
<AlertModal
|
||||||
|
isOpen={showCancelModal}
|
||||||
|
variant="danger"
|
||||||
|
onClose={() => setShowCancelModal(false)}
|
||||||
|
title={i18n._(t`Cancel Job`)}
|
||||||
|
label={i18n._(t`Cancel Job`)}
|
||||||
|
actions={[
|
||||||
|
<Button
|
||||||
|
id="cancel-job-confirm-button"
|
||||||
|
key="delete"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
onClose={() => setShowCancelModal(false)}
|
isDisabled={isCancelling}
|
||||||
title={i18n._(t`Cancel Job`)}
|
aria-label={i18n._(t`Cancel job`)}
|
||||||
label={i18n._(t`Cancel Job`)}
|
onClick={cancelJob}
|
||||||
actions={[
|
|
||||||
<Button
|
|
||||||
id="cancel-job-confirm-button"
|
|
||||||
key="delete"
|
|
||||||
variant="danger"
|
|
||||||
isDisabled={isCancelling}
|
|
||||||
aria-label={i18n._(t`Cancel job`)}
|
|
||||||
onClick={cancelJob}
|
|
||||||
>
|
|
||||||
{i18n._(t`Cancel job`)}
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
id="cancel-job-return-button"
|
|
||||||
key="cancel"
|
|
||||||
variant="secondary"
|
|
||||||
aria-label={i18n._(t`Return`)}
|
|
||||||
onClick={() => setShowCancelModal(false)}
|
|
||||||
>
|
|
||||||
{i18n._(t`Return`)}
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
{i18n._(
|
{i18n._(t`Cancel job`)}
|
||||||
t`Are you sure you want to submit the request to cancel this job?`
|
</Button>,
|
||||||
)}
|
<Button
|
||||||
</AlertModal>
|
id="cancel-job-return-button"
|
||||||
)}
|
key="cancel"
|
||||||
{dismissableDeleteError && (
|
variant="secondary"
|
||||||
<AlertModal
|
aria-label={i18n._(t`Return`)}
|
||||||
isOpen={dismissableDeleteError}
|
onClick={() => setShowCancelModal(false)}
|
||||||
variant="danger"
|
|
||||||
onClose={dismissDeleteError}
|
|
||||||
title={i18n._(t`Job Delete Error`)}
|
|
||||||
label={i18n._(t`Job Delete Error`)}
|
|
||||||
>
|
>
|
||||||
<ErrorDetail error={dismissableDeleteError} />
|
{i18n._(t`Return`)}
|
||||||
</AlertModal>
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{i18n._(
|
||||||
|
t`Are you sure you want to submit the request to cancel this job?`
|
||||||
)}
|
)}
|
||||||
{dismissableCancelError && (
|
</AlertModal>
|
||||||
<AlertModal
|
|
||||||
isOpen={dismissableCancelError}
|
|
||||||
variant="danger"
|
|
||||||
onClose={dismissCancelError}
|
|
||||||
title={i18n._(t`Job Cancel Error`)}
|
|
||||||
label={i18n._(t`Job Cancel Error`)}
|
|
||||||
>
|
|
||||||
<ErrorDetail error={dismissableCancelError} />
|
|
||||||
</AlertModal>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</I18n>
|
{dismissableDeleteError && (
|
||||||
|
<AlertModal
|
||||||
|
isOpen={dismissableDeleteError}
|
||||||
|
variant="danger"
|
||||||
|
onClose={dismissDeleteError}
|
||||||
|
title={i18n._(t`Job Delete Error`)}
|
||||||
|
label={i18n._(t`Job Delete Error`)}
|
||||||
|
>
|
||||||
|
<ErrorDetail error={dismissableDeleteError} />
|
||||||
|
</AlertModal>
|
||||||
|
)}
|
||||||
|
{dismissableCancelError && (
|
||||||
|
<AlertModal
|
||||||
|
isOpen={dismissableCancelError}
|
||||||
|
variant="danger"
|
||||||
|
onClose={dismissCancelError}
|
||||||
|
title={i18n._(t`Job Cancel Error`)}
|
||||||
|
label={i18n._(t`Job Cancel Error`)}
|
||||||
|
>
|
||||||
|
<ErrorDetail error={dismissableCancelError} />
|
||||||
|
</AlertModal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,7 @@ function OrganizationDetail({ i18n, organization }) {
|
|||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.organization(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.organization(
|
||||||
organization,
|
organization
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasContentLoading) {
|
if (hasContentLoading) {
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ function OrganizationsList({ i18n }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.organization(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.organization(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,14 +2,21 @@ import React from 'react';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
|
|
||||||
|
import { en } from 'make-plural/plurals';
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
|
import english from '../../../locales/en/messages';
|
||||||
import OrganizationListItem from './OrganizationListItem';
|
import OrganizationListItem from './OrganizationListItem';
|
||||||
|
|
||||||
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
|
i18n.load({ en: english });
|
||||||
|
i18n.activate('en');
|
||||||
|
|
||||||
describe('<OrganizationListItem />', () => {
|
describe('<OrganizationListItem />', () => {
|
||||||
test('initially renders successfully', () => {
|
test('initially renders successfully', () => {
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -40,7 +47,7 @@ describe('<OrganizationListItem />', () => {
|
|||||||
|
|
||||||
test('edit button shown to users with edit capabilities', () => {
|
test('edit button shown to users with edit capabilities', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -72,7 +79,7 @@ describe('<OrganizationListItem />', () => {
|
|||||||
|
|
||||||
test('edit button hidden from users without edit capabilities', () => {
|
test('edit button hidden from users without edit capabilities', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -53,10 +53,7 @@ function ProjectDetail({ project, i18n }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.project(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.project(project);
|
||||||
project,
|
|
||||||
i18n
|
|
||||||
);
|
|
||||||
let optionsList = '';
|
let optionsList = '';
|
||||||
if (
|
if (
|
||||||
scm_clean ||
|
scm_clean ||
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ function ProjectList({ i18n }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.project(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.project(
|
||||||
selected[0],
|
selected[0]
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { bool, oneOf, shape, string } from 'prop-types';
|
import { shape, string } from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useField } from 'formik';
|
import { useField } from 'formik';
|
||||||
import {
|
import {
|
||||||
@@ -35,20 +35,20 @@ const FormGroup = styled(PFFormGroup)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SettingGroup = withI18n()(
|
const SettingGroup = ({
|
||||||
({
|
children,
|
||||||
i18n,
|
defaultValue,
|
||||||
children,
|
fieldId,
|
||||||
defaultValue,
|
helperTextInvalid,
|
||||||
fieldId,
|
isDisabled,
|
||||||
helperTextInvalid,
|
isRequired,
|
||||||
isDisabled,
|
label,
|
||||||
isRequired,
|
onRevertCallback,
|
||||||
label,
|
popoverContent,
|
||||||
onRevertCallback,
|
validated,
|
||||||
popoverContent,
|
}) => {
|
||||||
validated,
|
const { i18n } = useLingui();
|
||||||
}) => (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId={fieldId}
|
fieldId={fieldId}
|
||||||
helperTextInvalid={helperTextInvalid}
|
helperTextInvalid={helperTextInvalid}
|
||||||
@@ -73,43 +73,42 @@ const SettingGroup = withI18n()(
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)
|
);
|
||||||
);
|
};
|
||||||
|
const BooleanField = ({ ariaLabel = '', name, config, disabled = false }) => {
|
||||||
|
const [field, meta, helpers] = useField(name);
|
||||||
|
const { i18n } = useLingui();
|
||||||
|
|
||||||
const BooleanField = withI18n()(
|
return config ? (
|
||||||
({ i18n, ariaLabel = '', name, config, disabled = false }) => {
|
<SettingGroup
|
||||||
const [field, meta, helpers] = useField(name);
|
defaultValue={config.default ?? false}
|
||||||
return config ? (
|
fieldId={name}
|
||||||
<SettingGroup
|
helperTextInvalid={meta.error}
|
||||||
defaultValue={config.default ?? false}
|
isDisabled={disabled}
|
||||||
fieldId={name}
|
label={config.label}
|
||||||
helperTextInvalid={meta.error}
|
popoverContent={config.help_text}
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
id={name}
|
||||||
|
ouiaId={name}
|
||||||
|
isChecked={field.value}
|
||||||
isDisabled={disabled}
|
isDisabled={disabled}
|
||||||
label={config.label}
|
label={i18n._(t`On`)}
|
||||||
popoverContent={config.help_text}
|
labelOff={i18n._(t`Off`)}
|
||||||
>
|
onChange={checked => helpers.setValue(checked)}
|
||||||
<Switch
|
aria-label={ariaLabel || config.label}
|
||||||
id={name}
|
/>
|
||||||
ouiaId={name}
|
</SettingGroup>
|
||||||
isChecked={field.value}
|
) : null;
|
||||||
isDisabled={disabled}
|
};
|
||||||
label={i18n._(t`On`)}
|
|
||||||
labelOff={i18n._(t`Off`)}
|
|
||||||
onChange={checked => helpers.setValue(checked)}
|
|
||||||
aria-label={ariaLabel || config.label}
|
|
||||||
/>
|
|
||||||
</SettingGroup>
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
BooleanField.propTypes = {
|
BooleanField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
ariaLabel: string,
|
|
||||||
disabled: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ChoiceField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
const ChoiceField = ({ name, config, isRequired = false }) => {
|
||||||
|
const { i18n } = useLingui();
|
||||||
|
|
||||||
const validate = isRequired ? required(null, i18n) : null;
|
const validate = isRequired ? required(null, i18n) : null;
|
||||||
const [field, meta] = useField({ name, validate });
|
const [field, meta] = useField({ name, validate });
|
||||||
const isValid = !meta.error || !meta.touched;
|
const isValid = !meta.error || !meta.touched;
|
||||||
@@ -137,133 +136,130 @@ const ChoiceField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
) : null;
|
) : null;
|
||||||
});
|
};
|
||||||
ChoiceField.propTypes = {
|
ChoiceField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
isRequired: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const EncryptedField = withI18n()(
|
const EncryptedField = ({ name, config, isRequired = false }) => {
|
||||||
({ i18n, name, config, isRequired = false }) => {
|
const { i18n } = useLingui();
|
||||||
const validate = isRequired ? required(null, i18n) : null;
|
|
||||||
const [, meta] = useField({ name, validate });
|
|
||||||
const isValid = !(meta.touched && meta.error);
|
|
||||||
|
|
||||||
return config ? (
|
const validate = isRequired ? required(null, i18n) : null;
|
||||||
<SettingGroup
|
const [, meta] = useField({ name, validate });
|
||||||
defaultValue={config.default ?? ''}
|
const isValid = !(meta.touched && meta.error);
|
||||||
fieldId={name}
|
|
||||||
helperTextInvalid={meta.error}
|
return config ? (
|
||||||
isRequired={isRequired}
|
<SettingGroup
|
||||||
label={config.label}
|
defaultValue={config.default ?? ''}
|
||||||
popoverContent={config.help_text}
|
fieldId={name}
|
||||||
validated={isValid ? 'default' : 'error'}
|
helperTextInvalid={meta.error}
|
||||||
>
|
isRequired={isRequired}
|
||||||
<InputGroup>
|
label={config.label}
|
||||||
<PasswordInput
|
popoverContent={config.help_text}
|
||||||
id={name}
|
validated={isValid ? 'default' : 'error'}
|
||||||
name={name}
|
>
|
||||||
label={config.label}
|
<InputGroup>
|
||||||
validate={validate}
|
<PasswordInput
|
||||||
isRequired={isRequired}
|
id={name}
|
||||||
/>
|
name={name}
|
||||||
</InputGroup>
|
label={config.label}
|
||||||
</SettingGroup>
|
validate={validate}
|
||||||
) : null;
|
isRequired={isRequired}
|
||||||
}
|
/>
|
||||||
);
|
</InputGroup>
|
||||||
|
</SettingGroup>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
EncryptedField.propTypes = {
|
EncryptedField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
isRequired: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const InputField = withI18n()(
|
const InputField = ({ name, config, type = 'text', isRequired = false }) => {
|
||||||
({ i18n, name, config, type = 'text', isRequired = false }) => {
|
const { i18n } = useLingui();
|
||||||
const min_value = config?.min_value ?? Number.MIN_SAFE_INTEGER;
|
|
||||||
const max_value = config?.max_value ?? Number.MAX_SAFE_INTEGER;
|
|
||||||
const validators = [
|
|
||||||
...(isRequired ? [required(null, i18n)] : []),
|
|
||||||
...(type === 'url' ? [url(i18n)] : []),
|
|
||||||
...(type === 'number'
|
|
||||||
? [integer(i18n), minMaxValue(min_value, max_value, i18n)]
|
|
||||||
: []),
|
|
||||||
];
|
|
||||||
const [field, meta] = useField({ name, validate: combine(validators) });
|
|
||||||
const isValid = !(meta.touched && meta.error);
|
|
||||||
|
|
||||||
return config ? (
|
const min_value = config?.min_value ?? Number.MIN_SAFE_INTEGER;
|
||||||
<SettingGroup
|
const max_value = config?.max_value ?? Number.MAX_SAFE_INTEGER;
|
||||||
defaultValue={config.default ?? ''}
|
const validators = [
|
||||||
fieldId={name}
|
...(isRequired ? [required(null, i18n)] : []),
|
||||||
helperTextInvalid={meta.error}
|
...(type === 'url' ? [url(i18n)] : []),
|
||||||
|
...(type === 'number'
|
||||||
|
? [integer(i18n), minMaxValue(min_value, max_value, i18n)]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
const [field, meta] = useField({ name, validate: combine(validators) });
|
||||||
|
const isValid = !(meta.touched && meta.error);
|
||||||
|
|
||||||
|
return config ? (
|
||||||
|
<SettingGroup
|
||||||
|
defaultValue={config.default ?? ''}
|
||||||
|
fieldId={name}
|
||||||
|
helperTextInvalid={meta.error}
|
||||||
|
isRequired={isRequired}
|
||||||
|
label={config.label}
|
||||||
|
popoverContent={config.help_text}
|
||||||
|
validated={isValid ? 'default' : 'error'}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
id={name}
|
||||||
isRequired={isRequired}
|
isRequired={isRequired}
|
||||||
label={config.label}
|
placeholder={config.placeholder}
|
||||||
popoverContent={config.help_text}
|
|
||||||
validated={isValid ? 'default' : 'error'}
|
validated={isValid ? 'default' : 'error'}
|
||||||
>
|
value={field.value}
|
||||||
<TextInput
|
onBlur={field.onBlur}
|
||||||
id={name}
|
onChange={(value, event) => {
|
||||||
isRequired={isRequired}
|
field.onChange(event);
|
||||||
placeholder={config.placeholder}
|
}}
|
||||||
validated={isValid ? 'default' : 'error'}
|
/>
|
||||||
value={field.value}
|
</SettingGroup>
|
||||||
onBlur={field.onBlur}
|
) : null;
|
||||||
onChange={(value, event) => {
|
};
|
||||||
field.onChange(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingGroup>
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
InputField.propTypes = {
|
InputField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
type: oneOf(['text', 'number', 'url']),
|
|
||||||
isRequired: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TextAreaField = withI18n()(
|
const TextAreaField = ({ name, config, isRequired = false }) => {
|
||||||
({ i18n, name, config, isRequired = false }) => {
|
const { i18n } = useLingui();
|
||||||
const validate = isRequired ? required(null, i18n) : null;
|
|
||||||
const [field, meta] = useField({ name, validate });
|
|
||||||
const isValid = !(meta.touched && meta.error);
|
|
||||||
|
|
||||||
return config ? (
|
const validate = isRequired ? required(null, i18n) : null;
|
||||||
<SettingGroup
|
const [field, meta] = useField({ name, validate });
|
||||||
defaultValue={config.default || ''}
|
const isValid = !(meta.touched && meta.error);
|
||||||
fieldId={name}
|
|
||||||
helperTextInvalid={meta.error}
|
return config ? (
|
||||||
|
<SettingGroup
|
||||||
|
defaultValue={config.default || ''}
|
||||||
|
fieldId={name}
|
||||||
|
helperTextInvalid={meta.error}
|
||||||
|
isRequired={isRequired}
|
||||||
|
label={config.label}
|
||||||
|
popoverContent={config.help_text}
|
||||||
|
validated={isValid ? 'default' : 'error'}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
id={name}
|
||||||
isRequired={isRequired}
|
isRequired={isRequired}
|
||||||
label={config.label}
|
placeholder={config.placeholder}
|
||||||
popoverContent={config.help_text}
|
|
||||||
validated={isValid ? 'default' : 'error'}
|
validated={isValid ? 'default' : 'error'}
|
||||||
>
|
value={field.value}
|
||||||
<TextArea
|
onBlur={field.onBlur}
|
||||||
id={name}
|
onChange={(value, event) => {
|
||||||
isRequired={isRequired}
|
field.onChange(event);
|
||||||
placeholder={config.placeholder}
|
}}
|
||||||
validated={isValid ? 'default' : 'error'}
|
resizeOrientation="vertical"
|
||||||
value={field.value}
|
/>
|
||||||
onBlur={field.onBlur}
|
</SettingGroup>
|
||||||
onChange={(value, event) => {
|
) : null;
|
||||||
field.onChange(event);
|
};
|
||||||
}}
|
|
||||||
resizeOrientation="vertical"
|
|
||||||
/>
|
|
||||||
</SettingGroup>
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
TextAreaField.propTypes = {
|
TextAreaField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
isRequired: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ObjectField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
const ObjectField = ({ name, config, isRequired = false }) => {
|
||||||
|
const { i18n } = useLingui();
|
||||||
|
|
||||||
const validate = isRequired ? required(null, i18n) : null;
|
const validate = isRequired ? required(null, i18n) : null;
|
||||||
const [field, meta, helpers] = useField({ name, validate });
|
const [field, meta, helpers] = useField({ name, validate });
|
||||||
const isValid = !(meta.touched && meta.error);
|
const isValid = !(meta.touched && meta.error);
|
||||||
@@ -297,76 +293,79 @@ const ObjectField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
) : null;
|
) : null;
|
||||||
});
|
};
|
||||||
ObjectField.propTypes = {
|
ObjectField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
isRequired: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FileUploadIconWrapper = styled.div`
|
const FileUploadIconWrapper = styled.div`
|
||||||
margin: var(--pf-global--spacer--md);
|
margin: var(--pf-global--spacer--md);
|
||||||
`;
|
`;
|
||||||
const FileUploadField = withI18n()(
|
const FileUploadField = ({
|
||||||
({ i18n, name, config, type = 'text', isRequired = false }) => {
|
name,
|
||||||
const validate = isRequired ? required(null, i18n) : null;
|
config,
|
||||||
const [filename, setFilename] = useState('');
|
type = 'text',
|
||||||
const [fileIsUploading, setFileIsUploading] = useState(false);
|
isRequired = false,
|
||||||
const [field, meta, helpers] = useField({ name, validate });
|
}) => {
|
||||||
const isValid = !(meta.touched && meta.error);
|
const { i18n } = useLingui();
|
||||||
|
|
||||||
return config ? (
|
const validate = isRequired ? required(null, i18n) : null;
|
||||||
<FormFullWidthLayout>
|
const [filename, setFilename] = useState('');
|
||||||
<SettingGroup
|
const [fileIsUploading, setFileIsUploading] = useState(false);
|
||||||
defaultValue={config.default ?? ''}
|
const [field, meta, helpers] = useField({ name, validate });
|
||||||
fieldId={name}
|
const isValid = !(meta.touched && meta.error);
|
||||||
helperTextInvalid={meta.error}
|
|
||||||
isRequired={isRequired}
|
return config ? (
|
||||||
label={config.label}
|
<FormFullWidthLayout>
|
||||||
popoverContent={config.help_text}
|
<SettingGroup
|
||||||
|
defaultValue={config.default ?? ''}
|
||||||
|
fieldId={name}
|
||||||
|
helperTextInvalid={meta.error}
|
||||||
|
isRequired={isRequired}
|
||||||
|
label={config.label}
|
||||||
|
popoverContent={config.help_text}
|
||||||
|
validated={isValid ? 'default' : 'error'}
|
||||||
|
onRevertCallback={() => setFilename('')}
|
||||||
|
>
|
||||||
|
<FileUpload
|
||||||
|
{...field}
|
||||||
|
id={name}
|
||||||
|
type={type}
|
||||||
|
filename={filename}
|
||||||
|
onChange={(value, title) => {
|
||||||
|
helpers.setValue(value);
|
||||||
|
setFilename(title);
|
||||||
|
}}
|
||||||
|
onReadStarted={() => setFileIsUploading(true)}
|
||||||
|
onReadFinished={() => setFileIsUploading(false)}
|
||||||
|
isLoading={fileIsUploading}
|
||||||
|
allowEditingUploadedText
|
||||||
validated={isValid ? 'default' : 'error'}
|
validated={isValid ? 'default' : 'error'}
|
||||||
onRevertCallback={() => setFilename('')}
|
hideDefaultPreview={type === 'dataURL'}
|
||||||
>
|
>
|
||||||
<FileUpload
|
{type === 'dataURL' && (
|
||||||
{...field}
|
<FileUploadIconWrapper>
|
||||||
id={name}
|
{field.value ? (
|
||||||
type={type}
|
<img
|
||||||
filename={filename}
|
src={field.value}
|
||||||
onChange={(value, title) => {
|
alt={filename}
|
||||||
helpers.setValue(value);
|
height="200px"
|
||||||
setFilename(title);
|
width="200px"
|
||||||
}}
|
/>
|
||||||
onReadStarted={() => setFileIsUploading(true)}
|
) : (
|
||||||
onReadFinished={() => setFileIsUploading(false)}
|
<FileUploadIcon size="lg" />
|
||||||
isLoading={fileIsUploading}
|
)}
|
||||||
allowEditingUploadedText
|
</FileUploadIconWrapper>
|
||||||
validated={isValid ? 'default' : 'error'}
|
)}
|
||||||
hideDefaultPreview={type === 'dataURL'}
|
</FileUpload>
|
||||||
>
|
</SettingGroup>
|
||||||
{type === 'dataURL' && (
|
</FormFullWidthLayout>
|
||||||
<FileUploadIconWrapper>
|
) : null;
|
||||||
{field.value ? (
|
};
|
||||||
<img
|
|
||||||
src={field.value}
|
|
||||||
alt={filename}
|
|
||||||
height="200px"
|
|
||||||
width="200px"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FileUploadIcon size="lg" />
|
|
||||||
)}
|
|
||||||
</FileUploadIconWrapper>
|
|
||||||
)}
|
|
||||||
</FileUpload>
|
|
||||||
</SettingGroup>
|
|
||||||
</FormFullWidthLayout>
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
FileUploadField.propTypes = {
|
FileUploadField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
config: shape({}).isRequired,
|
config: shape({}).isRequired,
|
||||||
isRequired: bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { mount } from 'enzyme';
|
|||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
import {
|
import {
|
||||||
BooleanField,
|
BooleanField,
|
||||||
@@ -13,13 +14,17 @@ import {
|
|||||||
ObjectField,
|
ObjectField,
|
||||||
TextAreaField,
|
TextAreaField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
import en from '../../../locales/en/messages';
|
||||||
|
|
||||||
describe('Setting form fields', () => {
|
describe('Setting form fields', () => {
|
||||||
test('BooleanField renders the expected content', async () => {
|
test('BooleanField renders the expected content', async () => {
|
||||||
const outerNode = document.createElement('div');
|
const outerNode = document.createElement('div');
|
||||||
document.body.appendChild(outerNode);
|
document.body.appendChild(outerNode);
|
||||||
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
|
i18n.load({ en });
|
||||||
|
i18n.activate('en');
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
boolean: true,
|
boolean: true,
|
||||||
@@ -244,7 +249,10 @@ describe('Setting form fields', () => {
|
|||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('FileUploadField')).toHaveLength(1);
|
|
||||||
|
expect(
|
||||||
|
wrapper.find('FileUploadField[value="mock file value"]')
|
||||||
|
).toHaveLength(1);
|
||||||
expect(wrapper.find('label').text()).toEqual('mock file label');
|
expect(wrapper.find('label').text()).toEqual('mock file label');
|
||||||
expect(wrapper.find('input#mock_file-filename').prop('value')).toEqual('');
|
expect(wrapper.find('input#mock_file-filename').prop('value')).toEqual('');
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|||||||
@@ -2,14 +2,21 @@ import React from 'react';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
|
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
|
import { en } from 'make-plural/plurals';
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import TeamListItem from './TeamListItem';
|
import TeamListItem from './TeamListItem';
|
||||||
|
import english from '../../../locales/en/messages';
|
||||||
|
|
||||||
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
|
i18n.load({ en: english });
|
||||||
|
i18n.activate('en');
|
||||||
|
|
||||||
describe('<TeamListItem />', () => {
|
describe('<TeamListItem />', () => {
|
||||||
test('initially renders succesfully', () => {
|
test('initially renders succesfully', () => {
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -35,7 +42,7 @@ describe('<TeamListItem />', () => {
|
|||||||
});
|
});
|
||||||
test('edit button shown to users with edit capabilities', () => {
|
test('edit button shown to users with edit capabilities', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -62,7 +69,7 @@ describe('<TeamListItem />', () => {
|
|||||||
});
|
});
|
||||||
test('edit button hidden from users without edit capabilities', () => {
|
test('edit button hidden from users without edit capabilities', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ function JobTemplateDetail({ i18n, template }) {
|
|||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
||||||
template,
|
template
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
const canLaunch =
|
const canLaunch =
|
||||||
summary_fields.user_capabilities && summary_fields.user_capabilities.start;
|
summary_fields.user_capabilities && summary_fields.user_capabilities.start;
|
||||||
|
|||||||
@@ -104,8 +104,7 @@ function WorkflowJobTemplateDetail({ template, i18n }) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
||||||
template,
|
template
|
||||||
i18n
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,10 +2,17 @@ import React from 'react';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
|
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
|
import { en } from 'make-plural/plurals';
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import mockDetails from '../data.user.json';
|
import mockDetails from '../data.user.json';
|
||||||
import UserListItem from './UserListItem';
|
import UserListItem from './UserListItem';
|
||||||
|
import english from '../../../locales/en/messages';
|
||||||
|
|
||||||
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
|
i18n.load({ en: english });
|
||||||
|
i18n.activate('en');
|
||||||
|
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
@@ -16,7 +23,7 @@ afterEach(() => {
|
|||||||
describe('UserListItem with full permissions', () => {
|
describe('UserListItem with full permissions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/users']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/users']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -52,7 +59,7 @@ describe('UserListItem with full permissions', () => {
|
|||||||
describe('UserListItem without full permissions', () => {
|
describe('UserListItem without full permissions', () => {
|
||||||
test('edit button hidden from users without edit capabilities', () => {
|
test('edit button hidden from users without edit capabilities', () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/users']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/users']} initialIndex={0}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
|
import { en } from 'make-plural/plurals';
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
import UserTeamListItem from './UserTeamListItem';
|
import UserTeamListItem from './UserTeamListItem';
|
||||||
|
import english from '../../../locales/en/messages';
|
||||||
|
|
||||||
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
|
i18n.load({ en: english });
|
||||||
|
i18n.activate('en');
|
||||||
|
|
||||||
describe('<UserTeamListItem />', () => {
|
describe('<UserTeamListItem />', () => {
|
||||||
test('should render item', () => {
|
test('should render item', () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<I18nProvider>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
<MemoryRouter initialEntries={['/teams']} initialIndex={0}>
|
||||||
<UserTeamListItem
|
<UserTeamListItem
|
||||||
team={{
|
team={{
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
WorkflowJobTemplatesAPI,
|
WorkflowJobTemplatesAPI,
|
||||||
WorkflowJobTemplateNodesAPI,
|
WorkflowJobTemplateNodesAPI,
|
||||||
CredentialsAPI,
|
CredentialsAPI,
|
||||||
|
ExecutionEnvironmentsAPI,
|
||||||
|
CredentialInputSourcesAPI,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
|
||||||
jest.mock('../api/models/Credentials');
|
jest.mock('../api/models/Credentials');
|
||||||
@@ -19,17 +21,8 @@ jest.mock('../api/models/JobTemplates');
|
|||||||
jest.mock('../api/models/Projects');
|
jest.mock('../api/models/Projects');
|
||||||
jest.mock('../api/models/WorkflowJobTemplates');
|
jest.mock('../api/models/WorkflowJobTemplates');
|
||||||
jest.mock('../api/models/WorkflowJobTemplateNodes');
|
jest.mock('../api/models/WorkflowJobTemplateNodes');
|
||||||
|
jest.mock('../api/models/CredentialInputSources');
|
||||||
const i18n = {
|
jest.mock('../api/models/ExecutionEnvironments');
|
||||||
_: key => {
|
|
||||||
if (key.values) {
|
|
||||||
Object.entries(key.values).forEach(([k, v]) => {
|
|
||||||
key.id = key.id.replace(new RegExp(`\\{${k}\\}`), v);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return key.id;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('delete details', () => {
|
describe('delete details', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -38,7 +31,7 @@ describe('delete details', () => {
|
|||||||
|
|
||||||
test('should call api for credentials list', () => {
|
test('should call api for credentials list', () => {
|
||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.credential({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.credential({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(InventoriesAPI.read).toBeCalledWith({
|
expect(InventoriesAPI.read).toBeCalledWith({
|
||||||
insights_credential: 1,
|
insights_credential: 1,
|
||||||
@@ -52,7 +45,7 @@ describe('delete details', () => {
|
|||||||
|
|
||||||
test('should call api for projects list', () => {
|
test('should call api for projects list', () => {
|
||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.project({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.project({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
||||||
unified_job_template: 1,
|
unified_job_template: 1,
|
||||||
@@ -65,7 +58,7 @@ describe('delete details', () => {
|
|||||||
|
|
||||||
test('should call api for templates list', () => {
|
test('should call api for templates list', () => {
|
||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.template({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.template({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
||||||
unified_job_template: 1,
|
unified_job_template: 1,
|
||||||
@@ -74,7 +67,7 @@ describe('delete details', () => {
|
|||||||
|
|
||||||
test('should call api for credential type list', () => {
|
test('should call api for credential type list', () => {
|
||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.credentialType({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.credentialType({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(CredentialsAPI.read).toBeCalledWith({
|
expect(CredentialsAPI.read).toBeCalledWith({
|
||||||
credential_type__id: 1,
|
credential_type__id: 1,
|
||||||
@@ -83,7 +76,7 @@ describe('delete details', () => {
|
|||||||
|
|
||||||
test('should call api for inventory list', () => {
|
test('should call api for inventory list', () => {
|
||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.inventory({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.inventory({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(JobTemplatesAPI.read).toBeCalledWith({ inventory: 1 });
|
expect(JobTemplatesAPI.read).toBeCalledWith({ inventory: 1 });
|
||||||
expect(WorkflowJobTemplatesAPI.read).toBeCalledWith({
|
expect(WorkflowJobTemplatesAPI.read).toBeCalledWith({
|
||||||
@@ -96,7 +89,7 @@ describe('delete details', () => {
|
|||||||
data: [{ inventory_source: 2 }],
|
data: [{ inventory_source: 2 }],
|
||||||
});
|
});
|
||||||
await getRelatedResourceDeleteCounts(
|
await getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.inventorySource(1, i18n)
|
relatedResourceDeleteRequests.inventorySource(1)
|
||||||
);
|
);
|
||||||
expect(InventoriesAPI.updateSources).toBeCalledWith(1);
|
expect(InventoriesAPI.updateSources).toBeCalledWith(1);
|
||||||
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
||||||
@@ -106,7 +99,7 @@ describe('delete details', () => {
|
|||||||
|
|
||||||
test('should call api for organization list', async () => {
|
test('should call api for organization list', async () => {
|
||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.organization({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.organization({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(CredentialsAPI.read).toBeCalledWith({ organization: 1 });
|
expect(CredentialsAPI.read).toBeCalledWith({ organization: 1 });
|
||||||
});
|
});
|
||||||
@@ -123,7 +116,7 @@ describe('delete details', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { error } = await getRelatedResourceDeleteCounts(
|
const { error } = await getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.inventorySource(1, i18n)
|
relatedResourceDeleteRequests.inventorySource(1)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(InventoriesAPI.updateSources).toBeCalledWith(1);
|
expect(InventoriesAPI.updateSources).toBeCalledWith(1);
|
||||||
@@ -131,14 +124,24 @@ describe('delete details', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should return proper results', async () => {
|
test('should return proper results', async () => {
|
||||||
JobTemplatesAPI.read.mockResolvedValue({ data: { count: 0 } });
|
JobTemplatesAPI.read.mockResolvedValue({ data: { count: 1 } });
|
||||||
|
|
||||||
|
InventorySourcesAPI.read.mockResolvedValue({ data: { count: 10 } });
|
||||||
|
CredentialInputSourcesAPI.read.mockResolvedValue({ data: { count: 20 } });
|
||||||
|
ExecutionEnvironmentsAPI.read.mockResolvedValue({ data: { count: 30 } });
|
||||||
ProjectsAPI.read.mockResolvedValue({ data: { count: 2 } });
|
ProjectsAPI.read.mockResolvedValue({ data: { count: 2 } });
|
||||||
InventoriesAPI.read.mockResolvedValue({ data: { count: 3 } });
|
InventoriesAPI.read.mockResolvedValue({ data: { count: 3 } });
|
||||||
InventorySourcesAPI.read.mockResolvedValue({ data: { count: 0 } });
|
|
||||||
|
|
||||||
const { results } = await getRelatedResourceDeleteCounts(
|
const { results } = await getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.credential({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.credential({ id: 1 })
|
||||||
);
|
);
|
||||||
expect(results).toEqual({ Projects: 2, Inventories: 3 });
|
expect(results).toEqual({
|
||||||
|
'Job Templates': 1,
|
||||||
|
Projects: 2,
|
||||||
|
Inventories: 3,
|
||||||
|
'Inventory Sources': 10,
|
||||||
|
'Credential Input Sources': 20,
|
||||||
|
'Execution Environments': 30,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
import { i18n } from '@lingui/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
UnifiedJobTemplatesAPI,
|
UnifiedJobTemplatesAPI,
|
||||||
@@ -46,7 +47,7 @@ export async function getRelatedResourceDeleteCounts(requests) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const relatedResourceDeleteRequests = {
|
export const relatedResourceDeleteRequests = {
|
||||||
credential: (selected, i18n) => [
|
credential: selected => [
|
||||||
{
|
{
|
||||||
request: () =>
|
request: () =>
|
||||||
JobTemplatesAPI.read({
|
JobTemplatesAPI.read({
|
||||||
@@ -77,7 +78,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
CredentialInputSourcesAPI.read({
|
CredentialInputSourcesAPI.read({
|
||||||
source_credential: selected.id,
|
source_credential: selected.id,
|
||||||
}),
|
}),
|
||||||
label: i18n._(t`Credential`),
|
label: i18n._(t`Credential Input Sources`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
request: () =>
|
request: () =>
|
||||||
@@ -88,7 +89,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
credentialType: (selected, i18n) => [
|
credentialType: selected => [
|
||||||
{
|
{
|
||||||
request: async () =>
|
request: async () =>
|
||||||
CredentialsAPI.read({
|
CredentialsAPI.read({
|
||||||
@@ -98,7 +99,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
inventory: (selected, i18n) => [
|
inventory: selected => [
|
||||||
{
|
{
|
||||||
request: async () =>
|
request: async () =>
|
||||||
JobTemplatesAPI.read({
|
JobTemplatesAPI.read({
|
||||||
@@ -112,7 +113,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
inventorySource: (inventoryId, i18n, inventorySource) => [
|
inventorySource: (inventoryId, inventorySource) => [
|
||||||
{
|
{
|
||||||
request: async () => {
|
request: async () => {
|
||||||
try {
|
try {
|
||||||
@@ -147,7 +148,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
project: (selected, i18n) => [
|
project: selected => [
|
||||||
{
|
{
|
||||||
request: () =>
|
request: () =>
|
||||||
JobTemplatesAPI.read({
|
JobTemplatesAPI.read({
|
||||||
@@ -171,7 +172,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
template: (selected, i18n) => [
|
template: selected => [
|
||||||
{
|
{
|
||||||
request: async () =>
|
request: async () =>
|
||||||
WorkflowJobTemplateNodesAPI.read({
|
WorkflowJobTemplateNodesAPI.read({
|
||||||
@@ -181,7 +182,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
organization: (selected, i18n) => [
|
organization: selected => [
|
||||||
{
|
{
|
||||||
request: async () =>
|
request: async () =>
|
||||||
CredentialsAPI.read({
|
CredentialsAPI.read({
|
||||||
@@ -232,7 +233,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
label: i18n._(t`Applications`),
|
label: i18n._(t`Applications`),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
executionEnvironment: (selected, i18n) => [
|
executionEnvironment: selected => [
|
||||||
{
|
{
|
||||||
request: async () =>
|
request: async () =>
|
||||||
UnifiedJobTemplatesAPI.read({
|
UnifiedJobTemplatesAPI.read({
|
||||||
@@ -283,7 +284,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
label: [i18n._(t`Workflow Job Template Nodes`)],
|
label: [i18n._(t`Workflow Job Template Nodes`)],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
instanceGroup: (selected, i18n) => [
|
instanceGroup: selected => [
|
||||||
{
|
{
|
||||||
request: () => OrganizationsAPI.read({ instance_groups: selected.id }),
|
request: () => OrganizationsAPI.read({ instance_groups: selected.id }),
|
||||||
label: i18n._(t`Organizations`),
|
label: i18n._(t`Organizations`),
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('required returns default message if value missing', () => {
|
test('required returns default message if value missing', () => {
|
||||||
expect(required(null, i18n)('')).toEqual({
|
expect(required(null, i18n)('')).toEqual('This field must not be blank');
|
||||||
id: 'This field must not be blank',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('required returns custom message if value missing', () => {
|
test('required returns custom message if value missing', () => {
|
||||||
@@ -30,18 +28,14 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('required interprets white space as empty value', () => {
|
test('required interprets white space as empty value', () => {
|
||||||
expect(required(null, i18n)(' ')).toEqual({
|
expect(required(null, i18n)(' ')).toEqual('This field must not be blank');
|
||||||
id: 'This field must not be blank',
|
expect(required(null, i18n)('\t')).toEqual('This field must not be blank');
|
||||||
});
|
|
||||||
expect(required(null, i18n)('\t')).toEqual({
|
|
||||||
id: 'This field must not be blank',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('required interprets undefined as empty value', () => {
|
test('required interprets undefined as empty value', () => {
|
||||||
expect(required(null, i18n)(undefined)).toEqual({
|
expect(required(null, i18n)(undefined)).toEqual(
|
||||||
id: 'This field must not be blank',
|
'This field must not be blank'
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('required interprets 0 as non-empty value', () => {
|
test('required interprets 0 as non-empty value', () => {
|
||||||
@@ -57,10 +51,9 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('maxLength rejects value above max', () => {
|
test('maxLength rejects value above max', () => {
|
||||||
expect(maxLength(8, i18n)('abracadbra')).toEqual({
|
expect(maxLength(8, i18n)('abracadbra')).toEqual(
|
||||||
id: 'This field must not exceed {max} characters',
|
'This field must not exceed {max} characters'
|
||||||
values: { max: 8 },
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('minLength accepts value above min', () => {
|
test('minLength accepts value above min', () => {
|
||||||
@@ -72,22 +65,21 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('minLength rejects value below min', () => {
|
test('minLength rejects value below min', () => {
|
||||||
expect(minLength(12, i18n)('abracadbra')).toEqual({
|
expect(minLength(12, i18n)('abracadbra')).toEqual(
|
||||||
id: 'This field must be at least {min} characters',
|
'This field must be at least {min} characters'
|
||||||
values: { min: 12 },
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('noWhiteSpace returns error', () => {
|
test('noWhiteSpace returns error', () => {
|
||||||
expect(noWhiteSpace(i18n)('this has spaces')).toEqual({
|
expect(noWhiteSpace(i18n)('this has spaces')).toEqual(
|
||||||
id: 'This field must not contain spaces',
|
'This field must not contain spaces'
|
||||||
});
|
);
|
||||||
expect(noWhiteSpace(i18n)('this has\twhitespace')).toEqual({
|
expect(noWhiteSpace(i18n)('this has\twhitespace')).toEqual(
|
||||||
id: 'This field must not contain spaces',
|
'This field must not contain spaces'
|
||||||
});
|
);
|
||||||
expect(noWhiteSpace(i18n)('this\nhas\nnewlines')).toEqual({
|
expect(noWhiteSpace(i18n)('this\nhas\nnewlines')).toEqual(
|
||||||
id: 'This field must not contain spaces',
|
'This field must not contain spaces'
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('noWhiteSpace should accept valid string', () => {
|
test('noWhiteSpace should accept valid string', () => {
|
||||||
@@ -103,15 +95,11 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('integer should reject decimal/float', () => {
|
test('integer should reject decimal/float', () => {
|
||||||
expect(integer(i18n)(13.1)).toEqual({
|
expect(integer(i18n)(13.1)).toEqual('This field must be an integer');
|
||||||
id: 'This field must be an integer',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('integer should reject string containing alphanum', () => {
|
test('integer should reject string containing alphanum', () => {
|
||||||
expect(integer(i18n)('15a')).toEqual({
|
expect(integer(i18n)('15a')).toEqual('This field must be an integer');
|
||||||
id: 'This field must be an integer',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('number should accept number (number)', () => {
|
test('number should accept number (number)', () => {
|
||||||
@@ -136,15 +124,11 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('number should reject string containing alphanum', () => {
|
test('number should reject string containing alphanum', () => {
|
||||||
expect(number(i18n)('15a')).toEqual({
|
expect(number(i18n)('15a')).toEqual('This field must be a number');
|
||||||
id: 'This field must be a number',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('url should reject incomplete url', () => {
|
test('url should reject incomplete url', () => {
|
||||||
expect(url(i18n)('abcd')).toEqual({
|
expect(url(i18n)('abcd')).toEqual('Please enter a valid URL');
|
||||||
id: 'Please enter a valid URL',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('url should accept fully qualified url', () => {
|
test('url should accept fully qualified url', () => {
|
||||||
@@ -156,43 +140,37 @@ describe('validators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('url should reject short protocol', () => {
|
test('url should reject short protocol', () => {
|
||||||
expect(url(i18n)('h://example.com/foo')).toEqual({
|
expect(url(i18n)('h://example.com/foo')).toEqual(
|
||||||
id: 'Please enter a valid URL',
|
'Please enter a valid URL'
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('combine should run all validators', () => {
|
test('combine should run all validators', () => {
|
||||||
const validators = [required(null, i18n), noWhiteSpace(i18n)];
|
const validators = [required(null, i18n), noWhiteSpace(i18n)];
|
||||||
expect(combine(validators)('')).toEqual({
|
expect(combine(validators)('')).toEqual('This field must not be blank');
|
||||||
id: 'This field must not be blank',
|
expect(combine(validators)('one two')).toEqual(
|
||||||
});
|
'This field must not contain spaces'
|
||||||
expect(combine(validators)('one two')).toEqual({
|
);
|
||||||
id: 'This field must not contain spaces',
|
|
||||||
});
|
|
||||||
expect(combine(validators)('ok')).toBeUndefined();
|
expect(combine(validators)('ok')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('combine should skip null validators', () => {
|
test('combine should skip null validators', () => {
|
||||||
const validators = [required(null, i18n), null];
|
const validators = [required(null, i18n), null];
|
||||||
expect(combine(validators)('')).toEqual({
|
expect(combine(validators)('')).toEqual('This field must not be blank');
|
||||||
id: 'This field must not be blank',
|
|
||||||
});
|
|
||||||
expect(combine(validators)('ok')).toBeUndefined();
|
expect(combine(validators)('ok')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('regExp rejects invalid regular expression', () => {
|
test('regExp rejects invalid regular expression', () => {
|
||||||
expect(regExp(i18n)('[')).toEqual({
|
expect(regExp(i18n)('[')).toEqual(
|
||||||
id: 'This field must be a regular expression',
|
'This field must be a regular expression'
|
||||||
});
|
);
|
||||||
expect(regExp(i18n)('')).toBeUndefined();
|
expect(regExp(i18n)('')).toBeUndefined();
|
||||||
expect(regExp(i18n)('ok')).toBeUndefined();
|
expect(regExp(i18n)('ok')).toBeUndefined();
|
||||||
expect(regExp(i18n)('[^a-zA-Z]')).toBeUndefined();
|
expect(regExp(i18n)('[^a-zA-Z]')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('email validator rejects obviously invalid email ', () => {
|
test('email validator rejects obviously invalid email ', () => {
|
||||||
expect(requiredEmail(i18n)('foobar321')).toEqual({
|
expect(requiredEmail(i18n)('foobar321')).toEqual('Invalid email address');
|
||||||
id: 'Invalid email address',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('bob has email', () => {
|
test('bob has email', () => {
|
||||||
|
|||||||
@@ -3,41 +3,20 @@
|
|||||||
* derived from https://lingui.js.org/guides/testing.html
|
* derived from https://lingui.js.org/guides/testing.html
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shape, object, string, arrayOf } from 'prop-types';
|
import { shape, string, arrayOf } from 'prop-types';
|
||||||
import { mount, shallow } from 'enzyme';
|
import { mount, shallow } from 'enzyme';
|
||||||
import { MemoryRouter, Router } from 'react-router-dom';
|
import { MemoryRouter, Router } from 'react-router-dom';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
import { ConfigProvider } from '../src/contexts/Config'
|
import { i18n } from '@lingui/core';
|
||||||
|
import { en } from 'make-plural/plurals';
|
||||||
|
import english from '../src/locales/en/messages';
|
||||||
|
import { ConfigProvider } from '../src/contexts/Config';
|
||||||
|
|
||||||
const language = 'en-US';
|
i18n.loadLocaleData({ en: { plurals: en } });
|
||||||
const intlProvider = new I18nProvider(
|
i18n.load({ en: english });
|
||||||
{
|
i18n.activate('en');
|
||||||
language,
|
|
||||||
catalogs: {
|
|
||||||
[language]: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const {
|
|
||||||
linguiPublisher: { i18n: originalI18n },
|
|
||||||
} = intlProvider.getChildContext();
|
|
||||||
|
|
||||||
const defaultContexts = {
|
const defaultContexts = {
|
||||||
linguiPublisher: {
|
|
||||||
i18n: {
|
|
||||||
...originalI18n,
|
|
||||||
_: key => {
|
|
||||||
if (key.values) {
|
|
||||||
Object.entries(key.values).forEach(([k, v]) => {
|
|
||||||
key.id = key.id.replace(new RegExp(`\\{${k}\\}`), v);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return key.id;
|
|
||||||
}, // provide _ macro, for just passing down the key
|
|
||||||
toJSON: () => '/i18n/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
ansible_version: null,
|
ansible_version: null,
|
||||||
custom_virtualenvs: [],
|
custom_virtualenvs: [],
|
||||||
@@ -45,8 +24,8 @@ const defaultContexts = {
|
|||||||
me: { is_superuser: true },
|
me: { is_superuser: true },
|
||||||
toJSON: () => '/config/',
|
toJSON: () => '/config/',
|
||||||
license_info: {
|
license_info: {
|
||||||
valid_key: true
|
valid_key: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
history_: {
|
history_: {
|
||||||
@@ -89,15 +68,19 @@ function wrapContexts(node, context) {
|
|||||||
const component = React.cloneElement(children, props);
|
const component = React.cloneElement(children, props);
|
||||||
if (router.history) {
|
if (router.history) {
|
||||||
return (
|
return (
|
||||||
<ConfigProvider value={config}>
|
<I18nProvider i18n={i18n}>
|
||||||
<Router history={router.history}>{component}</Router>
|
<ConfigProvider value={config}>
|
||||||
</ConfigProvider>
|
<Router history={router.history}>{component}</Router>
|
||||||
|
</ConfigProvider>
|
||||||
|
</I18nProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ConfigProvider value={config}>
|
<I18nProvider i18n={i18n}>
|
||||||
<MemoryRouter>{component}</MemoryRouter>
|
<ConfigProvider value={config}>
|
||||||
</ConfigProvider>
|
<MemoryRouter>{component}</MemoryRouter>
|
||||||
|
</ConfigProvider>
|
||||||
|
</I18nProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,9 +110,6 @@ export function shallowWithContexts(node, options = {}) {
|
|||||||
export function mountWithContexts(node, options = {}) {
|
export function mountWithContexts(node, options = {}) {
|
||||||
const context = applyDefaultContexts(options.context);
|
const context = applyDefaultContexts(options.context);
|
||||||
const childContextTypes = {
|
const childContextTypes = {
|
||||||
linguiPublisher: shape({
|
|
||||||
i18n: object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
}).isRequired,
|
|
||||||
config: shape({
|
config: shape({
|
||||||
ansible_version: string,
|
ansible_version: string,
|
||||||
custom_virtualenvs: arrayOf(string),
|
custom_virtualenvs: arrayOf(string),
|
||||||
|
|||||||
Reference in New Issue
Block a user