Merge pull request #198 from jlmitch5/translationUpdate

utilize i18n correctly
This commit is contained in:
John Mitchell 2019-05-16 13:54:05 -04:00 committed by GitHub
commit 69a25bb092
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 5818 additions and 4917 deletions

View File

@ -20,6 +20,7 @@ Have questions about this document or anything not covered here? Feel free to re
* [Typechecking with PropTypes](#typechecking-with-proptypes)
* [Naming Functions](#naming-functions)
* [Default State Initialization](#default-state-initialization)
* [Internationalization](#internationalization)
## Things to know prior to submitting code
@ -214,3 +215,30 @@ mountWithContexts(<Organization />< {
}
});
```
## Internationalization
Internationalization leans on the [lingui](https://github.com/lingui/js-lingui) project. [Official documentation here](https://lingui.js.org/). We use this libary to mark our strings for translation. If you want to see this in action you'll need to take the following steps:
### Marking strings for translation and replacement in the UI
The lingui library provides various React helpers for dealing with both marking strings for translation, and replacing strings that have been traslated. For consistency and ease of use, we have consolidated on one pattern for the codebase. To set strings to be translated in the UI:
- import the withI18n function and wrap the export of your component in it (i.e. `export default withI18n()(Foo)`)
- doing the above gives you access to the i18n object on props. Make sure to put it in the scope of the function that contains strings needed to be translated (i.e. `const { i18n } = this.props;`)
- import the t template tag function from the @lingui/macro package.
- wrap your string using the following format: ```i18n._(t`String to be translated`)```
**Note:** Variables that are put inside the t-marked template tag will not be translated. If you have a variable string with text that needs translating, you must wrap it in ```i18n._(t``)``` where it is defined.
**Note:** We do not use the `I18n` consumer, `i18nMark` function, or `<Trans>` component lingui gives us access to in this repo. i18nMark does not actually replace the string in the UI (leading to the potential for untranslated bugs), and the other helpers are redundant. Settling on a consistent, single pattern helps us ease the mental overhead of the need to understand the ins and outs of the lingui API.
You can learn more about the ways lingui and its React helpers at [this link](https://lingui.js.org/tutorials/react-patterns.html).
### Setting up .po files to give to translation team
1) `npm run add-locale` to add the language that you want to translate to (we should only have to do this once and the commit to repo afaik). Example: `npm run add-locale en es fr` # Add English, Spanish and French locale
2) `npm run extract-strings` to create .po files for each language specified. The .po files will be placed in src/locales but this is configurable.
3) Open up the .po file for the language you want to test and add some translations. In production we would pass this .po file off to the translation team.
4) Once you've edited your .po file (or we've gotten a .po file back from the translation team) run `npm run compile-strings`. This command takes the .po files and turns them into a minified JSON object and can be seen in the `messages.js` file in each locale directory. These files get loaded at the App root level (see: App.jsx).
5) Change the language in your browser and reload the page. You should see your specified translations in place of English strings.

View File

@ -27,13 +27,3 @@ To run a single test (in this case the login page test):
* `npm test -- __tests__/pages/Login.jsx`
**note:** Once the test watcher is up and running you can hit `a` to run all the tests
## Internationalization
Internationalization leans on the [lingui](https://github.com/lingui/js-lingui) project. [Official documentation here](https://lingui.js.org/). We use this libary to mark our strings for translation. For common React use cases see [this link](https://lingui.js.org/tutorials/react-patterns.html). If you want to see this in action you'll need to take the following steps:
1) `npm run add-locale` to add the language that you want to translate to (we should only have to do this once and the commit to repo afaik). Example: `npm run add-locale en es fr` # Add English, Spanish and French locale
2) `npm run extract-strings` to create .po files for each language specified. The .po files will be placed in src/locales but this is configurable.
3) Open up the .po file for the language you want to test and add some translations. In production we would pass this .po file off to the translation team.
4) Once you've edited your .po file (or we've gotten a .po file back from the translation team) run `npm run compile-strings`. This command takes the .po files and turns them into a minified JSON object and can be seen in the `messages.js` file in each locale directory. These files get loaded at the App root level (see: App.jsx).
5) Change the language in your browser and reload the page. You should see your specified translations in place of English strings.

View File

@ -21,29 +21,28 @@ exports[`mountWithContexts injected ConfigProvider should mount and render with
exports[`mountWithContexts injected I18nProvider should mount and render 1`] = `
<div>
<I18n
update={true}
withHash={true}
>
<span>
Text content
</span>
</I18n>
<span>
Text content
</span>
</div>
`;
exports[`mountWithContexts injected I18nProvider should mount and render deeply nested consumer 1`] = `
<Parent>
<Child>
<WithI18n>
<I18n
update={true}
withHash={true}
>
<div>
Text content
</div>
<Component
i18n={"/i18n/"}
>
<div>
Text content
</div>
</Component>
</I18n>
</Child>
</WithI18n>
</Parent>
`;

View File

@ -1,9 +1,9 @@
import React from 'react';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import AddResourceRole from '../../src/components/AddRole/AddResourceRole';
import AddResourceRole, { _AddResourceRole } from '../../src/components/AddRole/AddResourceRole';
describe('<AddResourceRole />', () => {
describe('<_AddResourceRole />', () => {
const readUsers = jest.fn().mockResolvedValue({
data: {
count: 2,
@ -31,21 +31,23 @@ describe('<AddResourceRole />', () => {
};
test('initially renders without crashing', () => {
shallow(
<AddResourceRole
<_AddResourceRole
api={api}
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
});
test('handleRoleCheckboxClick properly updates state', () => {
const wrapper = shallow(
<AddResourceRole
<_AddResourceRole
api={api}
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.setState({
@ -76,11 +78,12 @@ describe('<AddResourceRole />', () => {
});
test('handleResourceCheckboxClick properly updates state', () => {
const wrapper = shallow(
<AddResourceRole
<_AddResourceRole
api={api}
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.setState({
@ -106,14 +109,13 @@ describe('<AddResourceRole />', () => {
}]);
});
test('clicking user/team cards updates state', () => {
const spy = jest.spyOn(AddResourceRole.prototype, 'handleResourceSelect');
const spy = jest.spyOn(_AddResourceRole.prototype, 'handleResourceSelect');
const wrapper = mountWithContexts(
<AddResourceRole
onClose={() => {}}
onSave={() => {}}
api={api}
roles={roles}
/>
/>, { context: { network: { api, handleHttpError: () => {} } } }
).find('AddResourceRole');
const selectableCardWrapper = wrapper.find('SelectableCard');
expect(selectableCardWrapper.length).toBe(2);
@ -126,11 +128,12 @@ describe('<AddResourceRole />', () => {
});
test('readUsers and readTeams call out to corresponding api functions', () => {
const wrapper = shallow(
<AddResourceRole
<_AddResourceRole
api={api}
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.instance().readUsers({
@ -150,11 +153,12 @@ describe('<AddResourceRole />', () => {
test('handleResourceSelect clears out selected lists and sets selectedResource', () => {
const wrapper = shallow(
<AddResourceRole
<_AddResourceRole
api={api}
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.setState({
@ -193,11 +197,10 @@ describe('<AddResourceRole />', () => {
const handleSave = jest.fn();
const wrapper = mountWithContexts(
<AddResourceRole
api={api}
onClose={() => {}}
onSave={handleSave}
roles={roles}
/>
/>, { context: { network: { api, handleHttpError: () => {} } } }
).find('AddResourceRole');
wrapper.setState({
selectedResource: 'users',

View File

@ -1,6 +1,7 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import AnsibleSelect from '../../src/components/AnsibleSelect';
import { _AnsibleSelect } from '../../src/components/AnsibleSelect/AnsibleSelect';
const label = 'test select';
const mockData = ['/venv/baz/', '/venv/ansible/'];
@ -18,7 +19,7 @@ describe('<AnsibleSelect />', () => {
});
test('calls "onSelectChange" on dropdown select change', () => {
const spy = jest.spyOn(AnsibleSelect.prototype, 'onSelectChange');
const spy = jest.spyOn(_AnsibleSelect.prototype, 'onSelectChange');
const wrapper = mountWithContexts(
<AnsibleSelect
value="foo"

View File

@ -216,6 +216,7 @@ describe('<Lookup />', () => {
getItems={getItems}
handleHttpError={() => {}}
location={{ history }}
i18n={{ _: val => val.toString() }}
/>
);

View File

@ -11,6 +11,7 @@ describe('<NotifyAndRedirect />', () => {
to="foo"
setRootDialogMessage={setRootDialogMessage}
location={{ pathname: 'foo' }}
i18n={{ _: val => val.toString() }}
/>
);
expect(setRootDialogMessage).toHaveBeenCalled();

View File

@ -2,202 +2,202 @@
exports[`<ToolbarDeleteButton /> should render button 1`] = `
<ToolbarDeleteButton
i18n={"/i18n/"}
itemName="item"
itemsToDelete={Array []}
onDelete={[Function]}
>
<I18n
update={true}
withHash={true}
<Tooltip
appendTo={[Function]}
className={null}
content="Select a row to delete"
enableFlip={true}
entryDelay={500}
exitDelay={500}
maxWidth="18.75rem"
position="left"
trigger="mouseenter focus"
zIndex={9999}
>
<Tooltip
<Tippy
animateFill={false}
appendTo={[Function]}
className={null}
content="Select a row to delete"
enableFlip={true}
entryDelay={500}
exitDelay={500}
content={
<div
className="pf-c-tooltip"
role="tooltip"
>
<TooltipArrow
className={null}
/>
<TooltipContent
className={null}
>
Select a row to delete
</TooltipContent>
</div>
}
delay={
Array [
500,
500,
]
}
distance={15}
flip={true}
lazy={true}
maxWidth="18.75rem"
position="left"
onCreate={[Function]}
performance={true}
placement="left"
popperOptions={
Object {
"modifiers": Object {
"hide": Object {
"enabled": true,
},
"preventOverflow": Object {
"enabled": true,
},
},
}
}
theme="pf-tippy"
trigger="mouseenter focus"
zIndex={9999}
>
<Tippy
animateFill={false}
appendTo={[Function]}
content={
<div
className="pf-c-tooltip"
role="tooltip"
>
<TooltipArrow
className={null}
/>
<TooltipContent
className={null}
>
Select a row to delete
</TooltipContent>
</div>
}
delay={
Array [
500,
500,
]
}
distance={15}
flip={true}
lazy={true}
maxWidth="18.75rem"
onCreate={[Function]}
performance={true}
placement="left"
popperOptions={
Object {
"modifiers": Object {
"hide": Object {
"enabled": true,
},
"preventOverflow": Object {
"enabled": true,
},
},
}
}
theme="pf-tippy"
trigger="mouseenter focus"
zIndex={9999}
<ToolbarDeleteButton__Button
aria-label="Delete"
className="awx-ToolBarBtn"
isDisabled={true}
onClick={[Function]}
variant="plain"
>
<ToolbarDeleteButton__Button
<StyledComponent
aria-label="Delete"
className="awx-ToolBarBtn"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "ToolbarDeleteButton__Button-sc-1e3r0eg-0",
"isStatic": true,
"lastClassName": "iyjqWq",
"rules": Array [
"width:30px;height:30px;display:flex;justify-content:center;margin-right:20px;border-radius:3px;padding:0;&:disabled{cursor:not-allowed;&:hover{background-color:white;> svg{color:#d2d2d2;}}}&:hover{background-color:#d9534f;> svg{color:white;}}",
],
},
"displayName": "ToolbarDeleteButton__Button",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "ToolbarDeleteButton__Button-sc-1e3r0eg-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isDisabled={true}
onClick={[Function]}
variant="plain"
>
<StyledComponent
<Button
aria-label="Delete"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "ToolbarDeleteButton__Button-sc-1e3r0eg-0",
"isStatic": true,
"lastClassName": "iyjqWq",
"rules": Array [
"width:30px;height:30px;display:flex;justify-content:center;margin-right:20px;border-radius:3px;padding:0;&:disabled{cursor:not-allowed;&:hover{background-color:white;> svg{color:#d2d2d2;}}}&:hover{background-color:#d9534f;> svg{color:white;}}",
],
},
"displayName": "ToolbarDeleteButton__Button",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "ToolbarDeleteButton__Button-sc-1e3r0eg-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
className="awx-ToolBarBtn ToolbarDeleteButton__Button-sc-1e3r0eg-0 iyjqWq"
component="button"
isActive={false}
isBlock={false}
isDisabled={true}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="plain"
>
<Button
<button
aria-disabled={null}
aria-label="Delete"
className="ToolbarDeleteButton__Button-sc-1e3r0eg-0 iyjqWq"
component="button"
isActive={false}
isBlock={false}
isDisabled={true}
isFocus={false}
isHover={false}
className="pf-c-button pf-m-plain pf-m-disabled awx-ToolBarBtn ToolbarDeleteButton__Button-sc-1e3r0eg-0 iyjqWq"
disabled={true}
onClick={[Function]}
tabIndex={null}
type="button"
variant="plain"
>
<button
aria-disabled={null}
aria-label="Delete"
className="pf-c-button pf-m-plain pf-m-disabled ToolbarDeleteButton__Button-sc-1e3r0eg-0 iyjqWq"
disabled={true}
onClick={[Function]}
tabIndex={null}
type="button"
<TrashAltIcon
className="awx-ToolBarTrashCanIcon"
color="currentColor"
size="sm"
title={null}
>
<TrashAltIcon
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
<svg
aria-hidden={true}
aria-labelledby={null}
className="awx-ToolBarTrashCanIcon"
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
viewBox="0 0 448 512"
width="1em"
>
<path
d="M32 464a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V128H32zm272-256a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zM432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
transform=""
/>
</svg>
</TrashAltIcon>
</button>
</Button>
</StyledComponent>
</ToolbarDeleteButton__Button>
<Portal
containerInfo={
<div>
<div
class="pf-c-tooltip"
role="tooltip"
>
<div
class="pf-c-tooltip__arrow"
/>
<div
class="pf-c-tooltip__content"
}
viewBox="0 0 448 512"
width="1em"
>
Select a row to delete
</div>
</div>
</div>
}
>
<div
className="pf-c-tooltip"
role="tooltip"
>
<TooltipArrow
className={null}
<path
d="M32 464a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V128H32zm272-256a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zM432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
transform=""
/>
</svg>
</TrashAltIcon>
</button>
</Button>
</StyledComponent>
</ToolbarDeleteButton__Button>
<Portal
containerInfo={
<div>
<div
class="pf-c-tooltip"
role="tooltip"
>
<div
className="pf-c-tooltip__arrow"
class="pf-c-tooltip__arrow"
/>
</TooltipArrow>
<TooltipContent
className={null}
>
<div
className="pf-c-tooltip__content"
class="pf-c-tooltip__content"
>
Select a row to delete
</div>
</TooltipContent>
</div>
</div>
</Portal>
</Tippy>
</Tooltip>
</I18n>
}
>
<div
className="pf-c-tooltip"
role="tooltip"
>
<TooltipArrow
className={null}
>
<div
className="pf-c-tooltip__arrow"
/>
</TooltipArrow>
<TooltipContent
className={null}
>
<div
className="pf-c-tooltip__content"
>
Select a row to delete
</div>
</TooltipContent>
</div>
</Portal>
</Tippy>
</Tooltip>
</ToolbarDeleteButton>
`;

View File

@ -3,7 +3,7 @@ import { createMemoryHistory } from 'history';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import { sleep } from '../testUtils';
import SelectResourceStep, { _SelectResourceStep } from '../../src/components/AddRole/SelectResourceStep';
import SelectResourceStep from '../../src/components/AddRole/SelectResourceStep';
describe('<SelectResourceStep />', () => {
const columns = [
@ -67,15 +67,14 @@ describe('<SelectResourceStep />', () => {
initialEntries: ['/organizations/1/access?resource.page=1&resource.order_by=-username'],
});
const wrapper = await mountWithContexts(
<_SelectResourceStep
<SelectResourceStep
columns={columns}
displayKey="username"
onRowClick={() => {}}
onSearch={handleSearch}
selectedResourceRows={selectedResourceRows}
sortedColumnKey="username"
location={history.location}
/>
/>, { context: { router: { history, route: { location: history.location } } } }
).find('SelectResourceStep');
await wrapper.instance().readResourceList();
expect(handleSearch).toHaveBeenCalledWith({

View File

@ -1,5 +1,6 @@
import React from 'react';
import { mount, shallow } from 'enzyme';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import SelectRoleStep from '../../src/components/AddRole/SelectRoleStep';
describe('<SelectRoleStep />', () => {
@ -42,7 +43,7 @@ describe('<SelectRoleStep />', () => {
});
test('clicking role fires onRolesClick callback', () => {
const onRolesClick = jest.fn();
wrapper = mount(
wrapper = mountWithContexts(
<SelectRoleStep
onRolesClick={onRolesClick}
roles={roles}

View File

@ -5,6 +5,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
canToggleNotifications={true}
detailUrl="/foo"
errorTurnedOn={false}
i18n={"/i18n/"}
notification={
Object {
"id": 9000,
@ -15,446 +16,526 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
successTurnedOn={false}
toggleNotification={[MockFunction]}
>
<I18n
update={true}
withHash={true}
<DataListItem
aria-labelledby="items-list-item-9000"
className=""
isExpanded={false}
key="9000"
>
<DataListItem
<li
aria-labelledby="items-list-item-9000"
className=""
isExpanded={false}
key="9000"
className="pf-c-data-list__item"
>
<li
aria-labelledby="items-list-item-9000"
className="pf-c-data-list__item"
<DataListItemRow
className=""
key=".0"
rowid="items-list-item-9000"
>
<DataListItemRow
className=""
key=".0"
rowid="items-list-item-9000"
<div
className="pf-c-data-list__item-row"
>
<div
className="pf-c-data-list__item-row"
>
<DataListItemCells
className=""
dataListCells={
Array [
<ForwardRef>
<ForwardRef
to={
Object {
"pathname": "/foo",
}
}
>
<b
id="items-list-item-9000"
>
Foo
</b>
</ForwardRef>
<ForwardRef
isRead={true}
>
slack
</ForwardRef>
</ForwardRef>,
<DataListItemCells
className=""
dataListCells={
Array [
<ForwardRef>
<ForwardRef
righthalf="true"
>
<ForwardRef
aria-label="Toggle notification success"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
/>
<ForwardRef
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
/>
</ForwardRef>,
]
}
key=".0"
rowid="items-list-item-9000"
>
<div
className="pf-c-data-list__item-content"
>
<NotificationListItem__DataListCell
key="name"
>
<StyledComponent
forwardedComponent={
to={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
"pathname": "/foo",
}
}
forwardedRef={null}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
isFilled={true}
isIcon={false}
width={1}
<b
id="items-list-item-9000"
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
Foo
</b>
</ForwardRef>
<ForwardRef
isRead={true}
>
slack
</ForwardRef>
</ForwardRef>,
<ForwardRef
righthalf="true"
>
<ForwardRef
aria-label="Toggle notification success"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
/>
<ForwardRef
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
/>
</ForwardRef>,
]
}
key=".0"
rowid="items-list-item-9000"
>
<div
className="pf-c-data-list__item-content"
>
<NotificationListItem__DataListCell
key="name"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
isFilled={true}
isIcon={false}
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
>
<Styled(Link)
to={
Object {
"pathname": "/foo",
}
}
>
<Styled(Link)
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
"isStatic": true,
"lastClassName": "eBseNd",
"rules": Array [
"margin-right: 1.5em;",
],
},
"displayName": "Styled(Link)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
to={
Object {
"pathname": "/foo",
}
}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
"isStatic": true,
"lastClassName": "eBseNd",
"rules": Array [
"margin-right: 1.5em;",
],
},
"displayName": "Styled(Link)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
<Link
className="sc-bdVaJa eBseNd"
replace={false}
to={
Object {
"pathname": "/foo",
}
}
>
<Link
<a
className="sc-bdVaJa eBseNd"
replace={false}
to={
Object {
"pathname": "/foo",
}
}
onClick={[Function]}
>
<a
className="sc-bdVaJa eBseNd"
onClick={[Function]}
<b
id="items-list-item-9000"
>
<b
id="items-list-item-9000"
>
Foo
</b>
</a>
</Link>
</StyledComponent>
</Styled(Link)>
<Styled(Badge)
Foo
</b>
</a>
</Link>
</StyledComponent>
</Styled(Link)>
<Styled(Badge)
isRead={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "chTbOZ",
"rules": Array [
"text-transform: capitalize;",
],
},
"displayName": "Styled(Badge)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isRead={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "chTbOZ",
"rules": Array [
"text-transform: capitalize;",
],
},
"displayName": "Styled(Badge)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
<Badge
className="sc-bwzfXH chTbOZ"
isRead={true}
>
<Badge
className="sc-bwzfXH chTbOZ"
isRead={true}
<span
className="pf-c-badge pf-m-read sc-bwzfXH chTbOZ"
>
<span
className="pf-c-badge pf-m-read sc-bwzfXH chTbOZ"
>
slack
</span>
</Badge>
</StyledComponent>
</Styled(Badge)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="toggles"
slack
</span>
</Badge>
</StyledComponent>
</Styled(Badge)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="toggles"
righthalf="true"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
righthalf="true"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 hoXOpW"
isFilled={true}
isIcon={false}
righthalf="true"
width={1}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 hoXOpW"
isFilled={true}
isIcon={false}
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 hoXOpW"
righthalf="true"
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 hoXOpW"
righthalf="true"
<NotificationListItem__Switch
aria-label="Toggle notification success"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
>
<NotificationListItem__Switch
<StyledComponent
aria-label="Toggle notification success"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
>
<StyledComponent
<Switch
aria-label="Toggle notification success"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
>
<Switch
aria-label="Toggle notification success"
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-success-toggle"
>
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-success-toggle"
<input
aria-label="Toggle notification success"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-success-toggle"
onChange={[Function]}
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
<input
aria-label="Toggle notification success"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-success-toggle"
onChange={[Function]}
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
Successful
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Successful
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
<NotificationListItem__Switch
Successful
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Successful
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
<NotificationListItem__Switch
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
>
<StyledComponent
aria-label="Toggle notification failure"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
>
<StyledComponent
<Switch
aria-label="Toggle notification failure"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
>
<Switch
aria-label="Toggle notification failure"
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-error-toggle"
>
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-error-toggle"
<input
aria-label="Toggle notification failure"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-error-toggle"
onChange={[Function]}
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
<input
aria-label="Toggle notification failure"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-error-toggle"
onChange={[Function]}
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
Failure
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Failure
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
</div>
</DataListItemCells>
</div>
</DataListItemRow>
</li>
</DataListItem>
</I18n>
Failure
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Failure
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
</div>
</DataListItemCells>
<NotificationListItem__Switch
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
key=".1"
label="Failure"
onChange={[Function]}
rowid="items-list-item-9000"
>
<StyledComponent
aria-label="Toggle notification failure"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
rowid="items-list-item-9000"
>
<Switch
aria-label="Toggle notification failure"
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
rowid="items-list-item-9000"
>
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-error-toggle"
>
<input
aria-label="Toggle notification failure"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-error-toggle"
onChange={[Function]}
rowid="items-list-item-9000"
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
Failure
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Failure
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
</div>
</DataListItemRow>
</li>
</DataListItem>
</NotificationListItem>
`;

View File

@ -1,7 +1,6 @@
import React from 'react';
import { Link } from 'react-router-dom';
// import { mount } from 'enzyme';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { mountWithContexts } from './enzymeHelpers';
import { Config } from '../src/contexts/Config';
@ -11,26 +10,19 @@ import { withRootDialog } from '../src/contexts/RootDialog';
describe('mountWithContexts', () => {
describe('injected I18nProvider', () => {
test('should mount and render', () => {
const wrapper = mountWithContexts(
const Child = withI18n()(({ i18n }) => (
<div>
<I18n>
{({ i18n }) => (
<span>{i18n._(t`Text content`)}</span>
)}
</I18n>
<span>{i18n._(t`Text content`)}</span>
</div>
);
));
const wrapper = mountWithContexts(<Child />);
expect(wrapper.find('div')).toMatchSnapshot();
});
test('should mount and render deeply nested consumer', () => {
const Child = () => (
<I18n>
{({ i18n }) => (
<div>{i18n._(t`Text content`)}</div>
)}
</I18n>
);
const Child = withI18n()(({ i18n }) => (
<div>{i18n._(t`Text content`)}</div>
));
const Parent = () => (<Child />);
const wrapper = mountWithContexts(<Parent />);
expect(wrapper.find('Parent')).toMatchSnapshot();

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Applications from '../../src/pages/Applications';
describe('<Applications />', () => {
@ -8,7 +8,7 @@ describe('<Applications />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Applications />);
pageWrapper = mountWithContexts(<Applications />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import AuthSettings from '../../src/pages/AuthSettings';
describe('<AuthSettings />', () => {
@ -8,7 +8,7 @@ describe('<AuthSettings />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<AuthSettings />);
pageWrapper = mountWithContexts(<AuthSettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import CredentialTypes from '../../src/pages/CredentialTypes';
describe('<CredentialTypes />', () => {
@ -8,7 +8,7 @@ describe('<CredentialTypes />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<CredentialTypes />);
pageWrapper = mountWithContexts(<CredentialTypes />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Credentials from '../../src/pages/Credentials';
describe('<Credentials />', () => {
@ -8,7 +8,7 @@ describe('<Credentials />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Credentials />);
pageWrapper = mountWithContexts(<Credentials />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Dashboard from '../../src/pages/Dashboard';
describe('<Dashboard />', () => {
@ -8,7 +8,7 @@ describe('<Dashboard />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Dashboard />);
pageWrapper = mountWithContexts(<Dashboard />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import InstanceGroups from '../../src/pages/InstanceGroups';
describe('<InstanceGroups />', () => {
@ -8,7 +8,7 @@ describe('<InstanceGroups />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<InstanceGroups />);
pageWrapper = mountWithContexts(<InstanceGroups />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Inventories from '../../src/pages/Inventories';
describe('<Inventories />', () => {
@ -8,7 +8,7 @@ describe('<Inventories />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Inventories />);
pageWrapper = mountWithContexts(<Inventories />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import InventoryScripts from '../../src/pages/InventoryScripts';
describe('<InventoryScripts />', () => {
@ -8,7 +8,7 @@ describe('<InventoryScripts />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<InventoryScripts />);
pageWrapper = mountWithContexts(<InventoryScripts />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Jobs from '../../src/pages/Jobs';
describe('<Jobs />', () => {
@ -8,7 +8,7 @@ describe('<Jobs />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Jobs />);
pageWrapper = mountWithContexts(<Jobs />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import JobsSettings from '../../src/pages/JobsSettings';
describe('<JobsSettings />', () => {
@ -8,7 +8,7 @@ describe('<JobsSettings />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<JobsSettings />);
pageWrapper = mountWithContexts(<JobsSettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import License from '../../src/pages/License';
describe('<License />', () => {
@ -8,7 +8,7 @@ describe('<License />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<License />);
pageWrapper = mountWithContexts(<License />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,9 +1,7 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { mount } from 'enzyme';
import { I18nProvider } from '@lingui/react';
import { mountWithContexts } from '../enzymeHelpers';
import { asyncFlush } from '../../jest.setup';
import { _AWXLogin } from '../../src/pages/Login';
import AWXLogin from '../../src/pages/Login';
import APIClient from '../../src/api';
describe('<Login />', () => {
@ -18,6 +16,12 @@ describe('<Login />', () => {
const api = new APIClient({});
const mountLogin = () => {
loginWrapper = mountWithContexts(<AWXLogin />, { context: { network: {
api, handleHttpError: () => {}
} } });
};
const findChildren = () => {
awxLogin = loginWrapper.find('AWXLogin');
loginPage = loginWrapper.find('LoginPage');
@ -28,22 +32,13 @@ describe('<Login />', () => {
loginHeaderLogo = loginPage.find('img');
};
beforeEach(() => {
loginWrapper = mount(
<MemoryRouter>
<I18nProvider>
<_AWXLogin api={api} clearRootDialogMessage={() => {}} handleHttpError={() => {}} />
</I18nProvider>
</MemoryRouter>
);
findChildren();
});
afterEach(() => {
loginWrapper.unmount();
});
test('initially renders without crashing', () => {
mountLogin();
findChildren();
expect(loginWrapper.length).toBe(1);
expect(loginPage.length).toBe(1);
expect(loginForm.length).toBe(1);
@ -58,13 +53,7 @@ describe('<Login />', () => {
});
test('custom logo renders Brand component with correct src and alt', () => {
loginWrapper = mount(
<MemoryRouter>
<I18nProvider>
<_AWXLogin api={api} logo="images/foo.jpg" alt="Foo Application" />
</I18nProvider>
</MemoryRouter>
);
loginWrapper = mountWithContexts(<AWXLogin logo="images/foo.jpg" alt="Foo Application" />);
findChildren();
expect(loginHeaderLogo.length).toBe(1);
expect(loginHeaderLogo.props().src).toBe('data:image/jpeg;images/foo.jpg');
@ -72,13 +61,7 @@ describe('<Login />', () => {
});
test('default logo renders Brand component with correct src and alt', () => {
loginWrapper = mount(
<MemoryRouter>
<I18nProvider>
<_AWXLogin api={api} />
</I18nProvider>
</MemoryRouter>
);
mountLogin();
findChildren();
expect(loginHeaderLogo.length).toBe(1);
expect(loginHeaderLogo.props().src).toBe('tower-logo-header.svg');
@ -86,6 +69,8 @@ describe('<Login />', () => {
});
test('state maps to un/pw input value props', () => {
mountLogin();
findChildren();
awxLogin.setState({ username: 'un', password: 'pw' });
expect(awxLogin.state().username).toBe('un');
expect(awxLogin.state().password).toBe('pw');
@ -95,6 +80,8 @@ describe('<Login />', () => {
});
test('updating un/pw clears out error', () => {
mountLogin();
findChildren();
awxLogin.setState({ isInputValid: false });
expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(1);
usernameInput.instance().value = 'uname';
@ -113,6 +100,8 @@ describe('<Login />', () => {
test('api.login not called when loading', () => {
api.login = jest.fn().mockImplementation(() => Promise.resolve({}));
mountLogin();
findChildren();
expect(awxLogin.state().isLoading).toBe(false);
awxLogin.setState({ isLoading: true });
submitButton.simulate('click');
@ -121,6 +110,8 @@ describe('<Login />', () => {
test('submit calls api.login successfully', async () => {
api.login = jest.fn().mockImplementation(() => Promise.resolve({}));
mountLogin();
findChildren();
expect(awxLogin.state().isLoading).toBe(false);
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
submitButton.simulate('click');
@ -137,6 +128,8 @@ describe('<Login />', () => {
err.response = { status: 401, message: 'problem' };
return Promise.reject(err);
});
mountLogin();
findChildren();
expect(awxLogin.state().isLoading).toBe(false);
expect(awxLogin.state().isInputValid).toBe(true);
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
@ -155,6 +148,8 @@ describe('<Login />', () => {
err.response = { status: 500, message: 'problem' };
return Promise.reject(err);
});
mountLogin();
findChildren();
expect(awxLogin.state().isLoading).toBe(false);
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
submitButton.simulate('click');
@ -166,6 +161,8 @@ describe('<Login />', () => {
});
test('render Redirect to / when already authenticated', () => {
mountLogin();
findChildren();
awxLogin.setState({ isAuthenticated: true });
const redirectElem = loginWrapper.find('Redirect');
expect(redirectElem.length).toBe(1);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import ManagementJobs from '../../src/pages/ManagementJobs';
describe('<ManagementJobs />', () => {
@ -8,7 +8,7 @@ describe('<ManagementJobs />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<ManagementJobs />);
pageWrapper = mountWithContexts(<ManagementJobs />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import NotificationTemplates from '../../src/pages/NotificationTemplates';
describe('<NotificationTemplates />', () => {
@ -8,7 +8,7 @@ describe('<NotificationTemplates />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<NotificationTemplates />);
pageWrapper = mountWithContexts(<NotificationTemplates />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -2,6 +2,7 @@
exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
<DeleteRoleConfirmationModal
i18n={"/i18n/"}
onCancel={[Function]}
onConfirm={[Function]}
role={
@ -16,11 +17,47 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
}
username="jane"
>
<I18n
update={true}
withHash={true}
<_default
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="danger"
>
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
isOpen={true}
onClose={[Function]}
title="Remove {0} Access"
variant="danger"
>
<_default
<Modal
actions={
Array [
<Button
@ -55,377 +92,237 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
</Button>,
]
}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
isLarge={false}
isOpen={true}
isSmall={false}
onClose={[Function]}
title="Remove Team Access"
variant="danger"
title="Remove {0} Access"
width={null}
>
<Modal
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="danger"
<Portal
containerInfo={
<div>
<div
class="pf-c-backdrop"
>
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
isLarge={false}
isOpen={true}
isSmall={false}
onClose={[Function]}
title="Remove Team Access"
width={null}
>
<Portal
containerInfo={
<div>
<div
class="pf-c-backdrop"
class="pf-l-bullseye"
>
<div
class="pf-l-bullseye"
>
<div
class="pf-l-bullseye"
aria-describedby="pf-modal-0"
aria-label="Remove {0} Access"
aria-modal="true"
class="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger"
role="dialog"
>
<div
aria-describedby="pf-modal-0"
aria-label="Remove Team Access"
aria-modal="true"
class="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger"
role="dialog"
<button
aria-label="Close"
class="pf-c-button pf-m-plain"
type="button"
>
<svg
aria-hidden="true"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
transform=""
/>
</svg>
</button>
<h3
class="pf-c-title pf-m-2xl"
>
Remove {0} Access
</h3>
<div
class="pf-c-modal-box__body"
id="pf-modal-0"
>
Are you sure you want to remove {0} access from {1}? Doing so affects all members of the team.
<br />
<br />
If you {0} want to remove access for this particular user, please remove them from the team.
<svg
aria-hidden="true"
class="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</div>
<div
class="pf-c-modal-box__footer"
>
<button
aria-label="Close"
class="pf-c-button pf-m-plain"
aria-label="Confirm delete"
class="pf-c-button pf-m-danger"
type="button"
>
<svg
aria-hidden="true"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
transform=""
/>
</svg>
Delete
</button>
<h3
class="pf-c-title pf-m-2xl"
<button
class="pf-c-button pf-m-secondary"
type="button"
>
Remove Team Access
</h3>
<div
class="pf-c-modal-box__body"
id="pf-modal-0"
>
<svg
aria-hidden="true"
class="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</div>
<div
class="pf-c-modal-box__footer"
>
<button
aria-label="Confirm delete"
class="pf-c-button pf-m-danger"
type="button"
>
Delete
</button>
<button
class="pf-c-button pf-m-secondary"
type="button"
>
Cancel
</button>
</div>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
}
>
<ModalContent
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="danger"
>
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
id="pf-modal-0"
isLarge={false}
isOpen={true}
isSmall={false}
onClose={[Function]}
title="Remove Team Access"
width={null}
>
<Backdrop
className=""
>
<div
className="pf-c-backdrop"
</div>
}
>
<ModalContent
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="danger"
>
<Bullseye
className=""
component="div"
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
id="pf-modal-0"
isLarge={false}
isOpen={true}
isSmall={false}
onClose={[Function]}
title="Remove {0} Access"
width={null}
>
<Backdrop
className=""
>
<div
className="pf-c-backdrop"
>
<Bullseye
className=""
component="div"
>
<div
className="pf-l-bullseye"
>
<div
<FocusTrap
_createFocusTrap={[Function]}
active={true}
className="pf-l-bullseye"
>
<FocusTrap
_createFocusTrap={[Function]}
active={true}
className="pf-l-bullseye"
focusTrapOptions={
Object {
"clickOutsideDeactivates": true,
}
focusTrapOptions={
Object {
"clickOutsideDeactivates": true,
}
paused={false}
tag="div"
}
paused={false}
tag="div"
>
<div
className="pf-l-bullseye"
>
<div
className="pf-l-bullseye"
<ModalBox
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
id="pf-modal-0"
isLarge={false}
isSmall={false}
style={
Object {
"width": null,
}
}
title="Remove {0} Access"
>
<ModalBox
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
id="pf-modal-0"
isLarge={false}
isSmall={false}
<div
aria-describedby="pf-modal-0"
aria-label="Remove {0} Access"
aria-modal="true"
className="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger"
role="dialog"
style={
Object {
"width": null,
}
}
title="Remove Team Access"
>
<div
aria-describedby="pf-modal-0"
aria-label="Remove Team Access"
aria-modal="true"
className="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger"
role="dialog"
style={
Object {
"width": null,
}
}
<ModalBoxCloseButton
className=""
onClose={[Function]}
>
<ModalBoxCloseButton
<Button
aria-label="Close"
className=""
onClose={[Function]}
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="plain"
>
<Button
<button
aria-disabled={null}
aria-label="Close"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
className="pf-c-button pf-m-plain"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
variant="plain"
>
<button
aria-disabled={null}
aria-label="Close"
className="pf-c-button pf-m-plain"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
<TimesIcon
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
transform=""
/>
</svg>
</TimesIcon>
</button>
</Button>
</ModalBoxCloseButton>
<ModalBoxHeader
className=""
hideTitle={false}
>
<Title
className=""
headingLevel="h3"
size="2xl"
>
<h3
className="pf-c-title pf-m-2xl"
>
Remove Team Access
</h3>
</Title>
</ModalBoxHeader>
<ModalBoxBody
className=""
id="pf-modal-0"
>
<div
className="pf-c-modal-box__body"
id="pf-modal-0"
>
<WithI18n
components={
Array [
<b />,
<b />,
<br />,
<br />,
<b />,
<i />,
]
}
id="Are you sure you want to remove<0> {0} </0>access from<1> {1}</1>? Doing so affects all members of the team.<2/><3/>If you<4><5> only </5></4>want to remove access for this particular user, please remove them from the team."
values={
Object {
"0": "Member",
"1": "The Team",
}
}
>
<I18n
update={true}
withHash={true}
>
<Trans
components={
Array [
<b />,
<b />,
<br />,
<br />,
<b />,
<i />,
]
}
i18n={"/i18n/"}
id="Are you sure you want to remove<0> {0} </0>access from<1> {1}</1>? Doing so affects all members of the team.<2/><3/>If you<4><5> only </5></4>want to remove access for this particular user, please remove them from the team."
values={
Object {
"0": "Member",
"1": "The Team",
}
}
>
<Render
value={null}
/>
</Trans>
</I18n>
</WithI18n>
<ExclamationCircleIcon
className="at-c-alertModal__icon"
<TimesIcon
color="currentColor"
size="sm"
title={null}
@ -433,7 +330,6 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
<svg
aria-hidden={true}
aria-labelledby={null}
className="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
@ -442,91 +338,150 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 512 512"
viewBox="0 0 352 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
transform=""
/>
</svg>
</ExclamationCircleIcon>
</div>
</ModalBoxBody>
<ModalBoxFooter
</TimesIcon>
</button>
</Button>
</ModalBoxCloseButton>
<ModalBoxHeader
className=""
hideTitle={false}
>
<Title
className=""
headingLevel="h3"
size="2xl"
>
<div
className="pf-c-modal-box__footer"
<h3
className="pf-c-title pf-m-2xl"
>
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
key="delete"
onClick={[Function]}
type="button"
variant="danger"
>
<button
aria-disabled={null}
aria-label="Confirm delete"
className="pf-c-button pf-m-danger"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
Delete
</button>
</Button>
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
key="cancel"
onClick={[Function]}
type="button"
variant="secondary"
>
<button
aria-disabled={null}
aria-label={null}
className="pf-c-button pf-m-secondary"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
Cancel
</button>
</Button>
Remove {0} Access
</div>
</ModalBoxFooter>
</div>
</ModalBox>
</div>
</FocusTrap>
</div>
</Bullseye>
</div>
</Backdrop>
</ModalContent>
</Portal>
</Modal>
</_default>
</I18n>
</h3>
</Title>
</ModalBoxHeader>
<ModalBoxBody
className=""
id="pf-modal-0"
>
<div
className="pf-c-modal-box__body"
id="pf-modal-0"
>
Are you sure you want to remove {0} access from {1}? Doing so affects all members of the team.
<br />
<br />
If you {0} want to remove access for this particular user, please remove them from the team.
<ExclamationCircleIcon
className="at-c-alertModal__icon"
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
className="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</ExclamationCircleIcon>
</div>
</ModalBoxBody>
<ModalBoxFooter
className=""
>
<div
className="pf-c-modal-box__footer"
>
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
key="delete"
onClick={[Function]}
type="button"
variant="danger"
>
<button
aria-disabled={null}
aria-label="Confirm delete"
className="pf-c-button pf-m-danger"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
Delete
</button>
</Button>
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
key="cancel"
onClick={[Function]}
type="button"
variant="secondary"
>
<button
aria-disabled={null}
aria-label={null}
className="pf-c-button pf-m-secondary"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
Cancel
</button>
</Button>
</div>
</ModalBoxFooter>
</div>
</ModalBox>
</div>
</FocusTrap>
</div>
</Bullseye>
</div>
</Backdrop>
</ModalContent>
</Portal>
</Modal>
</_default>
</DeleteRoleConfirmationModal>
`;

View File

@ -29,43 +29,146 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
"username": "jane",
}
}
i18n={"/i18n/"}
onRoleDelete={[Function]}
>
<I18n
update={true}
withHash={true}
<DataListItem
aria-labelledby="access-list-item"
className=""
isExpanded={false}
key="2"
>
<DataListItem
<li
aria-labelledby="access-list-item"
className=""
isExpanded={false}
key="2"
className="pf-c-data-list__item"
>
<li
aria-labelledby="access-list-item"
className="pf-c-data-list__item"
<DataListItemRow
className=""
key=".0"
rowid="access-list-item"
>
<DataListItemRow
className=""
key=".0"
rowid="access-list-item"
<div
className="pf-c-data-list__item-row"
>
<div
className="pf-c-data-list__item-row"
>
<DataListItemCells
className=""
dataListCells={
Array [
<DataListCell
alignRight={false}
<DataListItemCells
className=""
dataListCells={
Array [
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<TextContent
className=""
isFilled={true}
isIcon={false}
width={1}
style={
Object {
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<TextContent
<Link
replace={false}
to={
Object {
"pathname": "/bar",
}
}
>
<Text
className=""
component="h6"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
jane
</Text>
</Link>
</TextContent>
<Detail
customStyles={null}
label="Name"
url={null}
value="jane brown"
/>
</DataListCell>,
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<ul
style={
Object {
"display": "flex",
"flexWrap": "wrap",
}
}
>
<Text
className=""
component="h6"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
Team Roles
</Text>
<Chip
className="awx-c-chip"
closeBtnAriaLabel="close"
isOverflowChip={false}
onClick={[Function]}
tooltipPosition="top"
>
Member
</Chip>
</ul>
</DataListCell>,
]
}
key=".0"
rowid="access-list-item"
>
<div
className="pf-c-data-list__item-content"
>
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
key="name"
width={1}
>
<div
className="pf-c-data-list__cell"
>
<TextContent
className=""
style={
Object {
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<div
className="pf-c-content"
style={
Object {
"display": "grid",
@ -81,167 +184,8 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
}
}
>
<Text
className=""
component="h6"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
jane
</Text>
</Link>
</TextContent>
<Detail
customStyles={null}
label="Name"
url={null}
value="jane brown"
/>
</DataListCell>,
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<ul
style={
Object {
"display": "flex",
"flexWrap": "wrap",
}
}
>
<Text
className=""
component="h6"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
Team Roles
</Text>
<Chip
className="awx-c-chip"
closeBtnAriaLabel="close"
isOverflowChip={false}
onClick={[Function]}
tooltipPosition="top"
>
Member
</Chip>
</ul>
</DataListCell>,
]
}
key=".0"
rowid="access-list-item"
>
<div
className="pf-c-data-list__item-content"
>
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
key="name"
width={1}
>
<div
className="pf-c-data-list__cell"
>
<TextContent
className=""
style={
Object {
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<div
className="pf-c-content"
style={
Object {
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<Link
replace={false}
to={
Object {
"pathname": "/bar",
}
}
>
<a
onClick={[Function]}
>
<Text
className=""
component="h6"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
<h6
className=""
data-pf-content={true}
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
jane
</h6>
</Text>
</a>
</Link>
</div>
</TextContent>
<Detail
customStyles={null}
label="Name"
url={null}
value="jane brown"
>
<TextContent
className=""
style={
Object {
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<div
className="pf-c-content"
style={
Object {
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
<a
onClick={[Function]}
>
<Text
className=""
@ -265,70 +209,40 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
}
}
>
Name
jane
</h6>
</Text>
<Text
className=""
component="p"
style={
Object {
"lineHeight": "28px",
"overflow": "visible",
}
}
>
<p
className=""
data-pf-content={true}
style={
Object {
"lineHeight": "28px",
"overflow": "visible",
}
}
>
jane brown
</p>
</Text>
</div>
</TextContent>
</Detail>
</div>
</DataListCell>
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
key="roles"
width={1}
>
<div
className="pf-c-data-list__cell"
</a>
</Link>
</div>
</TextContent>
<Detail
customStyles={null}
label="Name"
url={null}
value="jane brown"
>
<ul
<TextContent
className=""
style={
Object {
"display": "flex",
"flexWrap": "wrap",
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<Text
className=""
component="h6"
<div
className="pf-c-content"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
"display": "grid",
"gridTemplateColumns": "minmax(70px, max-content) minmax(60px, max-content)",
}
}
>
<h6
<Text
className=""
data-pf-content={true}
component="h6"
style={
Object {
"fontWeight": "700",
@ -337,103 +251,185 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
}
}
>
Team Roles
</h6>
</Text>
<Chip
className="awx-c-chip"
closeBtnAriaLabel="close"
isOverflowChip={false}
key="3"
onClick={[Function]}
tooltipPosition="top"
>
<GenerateId
prefix="pf-random-id-"
>
<li
className="pf-c-chip awx-c-chip"
<h6
className=""
data-pf-content={true}
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
<span
className="pf-c-chip__text"
id="pf-random-id-0"
>
Member
</span>
<ChipButton
Name
</h6>
</Text>
<Text
className=""
component="p"
style={
Object {
"lineHeight": "28px",
"overflow": "visible",
}
}
>
<p
className=""
data-pf-content={true}
style={
Object {
"lineHeight": "28px",
"overflow": "visible",
}
}
>
jane brown
</p>
</Text>
</div>
</TextContent>
</Detail>
</div>
</DataListCell>
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
key="roles"
width={1}
>
<div
className="pf-c-data-list__cell"
>
<ul
style={
Object {
"display": "flex",
"flexWrap": "wrap",
}
}
>
<Text
className=""
component="h6"
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
<h6
className=""
data-pf-content={true}
style={
Object {
"fontWeight": "700",
"lineHeight": "24px",
"marginRight": "20px",
}
}
>
Team Roles
</h6>
</Text>
<Chip
className="awx-c-chip"
closeBtnAriaLabel="close"
isOverflowChip={false}
key="3"
onClick={[Function]}
tooltipPosition="top"
>
<GenerateId
prefix="pf-random-id-"
>
<li
className="pf-c-chip awx-c-chip"
>
<span
className="pf-c-chip__text"
id="pf-random-id-0"
>
Member
</span>
<ChipButton
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
ariaLabel="close"
className=""
id="remove_pf-random-id-0"
onClick={[Function]}
>
<Button
aria-label="close"
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
ariaLabel="close"
className=""
component="button"
id="remove_pf-random-id-0"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
type="button"
variant="plain"
>
<Button
<button
aria-disabled={null}
aria-label="close"
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
className=""
component="button"
className="pf-c-button pf-m-plain"
disabled={false}
id="remove_pf-random-id-0"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
onClick={[Function]}
tabIndex={null}
type="button"
variant="plain"
>
<button
aria-disabled={null}
aria-label="close"
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
className="pf-c-button pf-m-plain"
disabled={false}
id="remove_pf-random-id-0"
onClick={[Function]}
tabIndex={null}
type="button"
<TimesCircleIcon
aria-hidden="true"
color="currentColor"
size="sm"
title={null}
>
<TimesCircleIcon
<svg
aria-hidden="true"
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden="true"
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
viewBox="0 0 512 512"
width="1em"
>
<path
d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
transform=""
/>
</svg>
</TimesCircleIcon>
</button>
</Button>
</ChipButton>
</li>
</GenerateId>
</Chip>
</ul>
</div>
</DataListCell>
</div>
</DataListItemCells>
</div>
</DataListItemRow>
</li>
</DataListItem>
</I18n>
}
viewBox="0 0 512 512"
width="1em"
>
<path
d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
transform=""
/>
</svg>
</TimesCircleIcon>
</button>
</Button>
</ChipButton>
</li>
</GenerateId>
</Chip>
</ul>
</div>
</DataListCell>
</div>
</DataListItemCells>
</div>
</DataListItemRow>
</li>
</DataListItem>
</OrganizationAccessItem>
`;

View File

@ -5,6 +5,7 @@ exports[`<OrganizationAccess /> initially renders succesfully 1`] = `
api={"/api/"}
handleHttpError={[Function]}
history={"/history/"}
i18n={"/i18n/"}
location={
Object {
"hash": "",

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Portal from '../../src/pages/Portal';
describe('<Portal />', () => {
@ -8,7 +8,7 @@ describe('<Portal />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Portal />);
pageWrapper = mountWithContexts(<Portal />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Projects from '../../src/pages/Projects';
describe('<Projects />', () => {
@ -8,7 +8,7 @@ describe('<Projects />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Projects />);
pageWrapper = mountWithContexts(<Projects />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Schedules from '../../src/pages/Schedules';
describe('<Schedules />', () => {
@ -8,7 +8,7 @@ describe('<Schedules />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Schedules />);
pageWrapper = mountWithContexts(<Schedules />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import SystemSettings from '../../src/pages/SystemSettings';
describe('<SystemSettings />', () => {
@ -8,7 +8,7 @@ describe('<SystemSettings />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<SystemSettings />);
pageWrapper = mountWithContexts(<SystemSettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Teams from '../../src/pages/Teams';
describe('<Teams />', () => {
@ -8,7 +8,7 @@ describe('<Teams />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Teams />);
pageWrapper = mountWithContexts(<Teams />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Templates from '../../src/pages/Templates';
describe('<Templates />', () => {
@ -8,7 +8,7 @@ describe('<Templates />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Templates />);
pageWrapper = mountWithContexts(<Templates />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import UISettings from '../../src/pages/UISettings';
describe('<UISettings />', () => {
@ -8,7 +8,7 @@ describe('<UISettings />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<UISettings />);
pageWrapper = mountWithContexts(<UISettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import Users from '../../src/pages/Users';
describe('<Users />', () => {
@ -8,7 +8,7 @@ describe('<Users />', () => {
let title;
beforeEach(() => {
pageWrapper = mount(<Users />);
pageWrapper = mountWithContexts(<Users />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});

View File

@ -1,34 +1,36 @@
import { required, maxLength } from '../../src/util/validators';
const i18n = { _: val => val };
describe('validators', () => {
test('required returns undefined if value given', () => {
expect(required()('some value')).toBeUndefined();
expect(required('oops')('some value')).toBeUndefined();
expect(required(null, i18n)('some value')).toBeUndefined();
expect(required('oops', i18n)('some value')).toBeUndefined();
});
test('required returns default message if value missing', () => {
expect(required()('')).toEqual('This field must not be blank');
expect(required(null, i18n)('')).toEqual({ id: 'This field must not be blank' });
});
test('required returns custom message if value missing', () => {
expect(required('oops')('')).toEqual('oops');
expect(required('oops', i18n)('')).toEqual('oops');
});
test('required interprets white space as empty value', () => {
expect(required()(' ')).toEqual('This field must not be blank');
expect(required()('\t')).toEqual('This field must not be blank');
expect(required(null, i18n)(' ')).toEqual({ id: 'This field must not be blank' });
expect(required(null, i18n)('\t')).toEqual({ id: 'This field must not be blank' });
});
test('maxLength accepts value below max', () => {
expect(maxLength(10)('snazzy')).toBeUndefined();
expect(maxLength(10, i18n)('snazzy')).toBeUndefined();
});
test('maxLength accepts value equal to max', () => {
expect(maxLength(10)('abracadbra')).toBeUndefined();
expect(maxLength(10, i18n)('abracadbra')).toBeUndefined();
});
test('maxLength rejects value above max', () => {
expect(maxLength(8)('abracadbra'))
.toEqual('This field must not exceed 8 characters');
expect(maxLength(8, i18n)('abracadbra'))
.toEqual({ id: 'This field must not exceed {max} characters', values: { max: 8 } });
});
});

View File

@ -13,6 +13,10 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"
#: src/contexts/Network.jsx:56
msgid "404"
msgstr ""
#: src/pages/Organizations/components/OrganizationBreadcrumb.jsx:60
#~ msgid "> add"
#~ msgstr ""
@ -25,20 +29,34 @@ msgstr ""
msgid "About"
msgstr ""
#: src/components/About.jsx:60
#: src/components/About.jsx:59
msgid "AboutModal Logo"
msgstr ""
#: src/index.jsx:175
#: src/index.jsx:140
#: src/pages/Organizations/Organizations.jsx:43
#: src/pages/Organizations/screens/Organization/Organization.jsx:131
msgid "Access"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:266
#: src/pages/Organizations/components/OrganizationBreadcrumb.jsx:68
#: src/components/PaginatedDataList/ToolbarAddButton.jsx:33
#: src/components/PaginatedDataList/ToolbarAddButton.jsx:44
msgid "Add"
msgstr ""
#: src/index.jsx:196
#: src/components/AddRole/AddResourceRole.jsx:153
msgid "Add Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:150
msgid "Add Team Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:147
msgid "Add User Roles"
msgstr ""
#: src/index.jsx:161
msgid "Administration"
msgstr ""
@ -46,67 +64,154 @@ msgstr ""
#~ msgid "Admins"
#~ msgstr ""
#: src/components/About.jsx:77
#: src/pages/Organizations/components/OrganizationForm.jsx:123
#: src/pages/Organizations/components/OrganizationForm.jsx:128
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:142
msgid "Ansible Environment"
msgstr ""
#: src/components/About.jsx:75
msgid "Ansible Version"
msgstr ""
#: src/pages/Applications.jsx:17
#: src/pages/Applications.jsx:19
msgid "Applications"
msgstr ""
#: src/index.jsx:231
#: src/components/AddRole/AddResourceRole.jsx:209
msgid "Apply roles"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:165
msgid "Are you sure you want to delete:"
msgstr ""
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:51
msgid "Are you sure you want to remove {0} access from {1}? Doing so affects all members of the team."
msgstr ""
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:58
msgid "Are you sure you want to remove {0} access from {username}?"
msgstr ""
#: src/index.jsx:196
msgid "Authentication"
msgstr ""
#: src/pages/AuthSettings.jsx:17
#: src/pages/AuthSettings.jsx:19
msgid "Authentication Settings"
msgstr ""
#: src/components/About.jsx:58
#: src/components/About.jsx:57
msgid "Brand Image"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:241
#: src/components/FormActionGroup/FormActionGroup.jsx:30
#: src/components/FormActionGroup/FormActionGroup.jsx:30
#: src/components/Lookup/Lookup.jsx:169
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:161
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:45
msgid "Cancel"
msgstr ""
#: src/pages/Organizations/Organizations.jsx:77
msgid "Cannot find organization with ID"
msgstr ""
#: src/contexts/Network.jsx:57
msgid "Cannot find resource."
msgstr ""
#: src/components/NotifyAndRedirect.jsx:23
msgid "Cannot find route {0}."
msgstr ""
#: src/App.jsx:94
#: src/components/CardCloseButton.jsx:14
#: src/components/CardCloseButton.jsx:15
#: src/components/CardCloseButton.jsx:27
#: src/components/Lookup/Lookup.jsx:169
#: src/pages/Organizations/screens/OrganizationAdd.jsx:67
msgid "Close"
msgstr ""
#: src/components/ExpandCollapse/ExpandCollapse.jsx:34
msgid "Collapse"
msgstr ""
#: src/components/About.jsx:56
#: src/components/About.jsx:55
msgid "Copyright 2018 Red Hat, Inc."
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:29
#: src/pages/Organizations/Organizations.jsx:39
#: src/pages/Organizations/Organizations.jsx:25
msgid "Create New Organization"
msgstr ""
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:58
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:146
#: src/pages/Organizations/screens/OrganizationsList.jsx:164
msgid "Created"
msgstr ""
#: src/index.jsx:200
#: src/pages/CredentialTypes.jsx:17
#: src/index.jsx:165
#: src/pages/CredentialTypes.jsx:19
msgid "Credential Types"
msgstr ""
#: src/index.jsx:153
#: src/pages/Credentials.jsx:17
#: src/index.jsx:118
#: src/pages/Credentials.jsx:19
msgid "Credentials"
msgstr ""
#: src/index.jsx:122
#: src/pages/Dashboard.jsx:17
#: src/index.jsx:87
#: src/pages/Dashboard.jsx:19
msgid "Dashboard"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:252
#: src/components/DataListToolbar/DataListToolbar.jsx:257
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:109
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:130
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:153
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:42
msgid "Delete"
msgstr ""
#: src/pages/Organizations/components/OrganizationBreadcrumb.jsx:59
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:142
msgid "Delete {0}"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:141
msgid "Delete {itemName}"
msgstr ""
#: src/pages/Organizations/components/OrganizationForm.jsx:113
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:138
msgid "Description"
msgstr ""
#: src/pages/Organizations/Organizations.jsx:42
#: src/pages/Organizations/screens/Organization/Organization.jsx:130
msgid "Details"
msgstr ""
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:171
msgid "Edit"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:233
#: src/pages/Organizations/Organizations.jsx:41
msgid "Edit Details"
msgstr ""
#: src/components/ExpandCollapse/ExpandCollapse.jsx:44
msgid "Expand"
msgstr ""
#: src/components/Pagination/Pagination.jsx:169
#: src/components/NotificationsList/NotificationListItem.jsx:82
#: src/components/NotificationsList/NotificationListItem.jsx:97
msgid "Failure"
msgstr ""
#: src/components/Pagination/Pagination.jsx:170
msgid "First"
msgstr ""
@ -114,260 +219,436 @@ msgstr ""
msgid "Help"
msgstr ""
#: src/index.jsx:215
#: src/pages/InstanceGroups.jsx:17
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:54
msgid "If you {0} want to remove access for this particular user, please remove them from the team."
msgstr ""
#: src/components/PageHeaderToolbar.jsx:66
msgid "Info"
msgstr ""
#: src/index.jsx:180
#: src/pages/InstanceGroups.jsx:19
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:32
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:50
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:159
msgid "Instance Groups"
msgstr ""
#: src/index.jsx:220
#: src/index.jsx:185
msgid "Integrations"
msgstr ""
#: src/pages/Login.jsx:80
#: src/pages/Login.jsx:84
msgid "Invalid username or password. Please try again."
msgstr ""
#: src/index.jsx:163
#: src/pages/Inventories.jsx:17
#: src/index.jsx:128
#: src/pages/Inventories.jsx:19
msgid "Inventories"
msgstr ""
#: src/index.jsx:168
#: src/pages/InventoryScripts.jsx:17
#: src/index.jsx:133
#: src/pages/InventoryScripts.jsx:19
msgid "Inventory Scripts"
msgstr ""
#: src/index.jsx:127
#: src/index.jsx:236
#: src/pages/Jobs.jsx:17
#: src/components/Pagination/Pagination.jsx:142
msgid "Items Per Page"
msgstr ""
#: src/components/Pagination/Pagination.jsx:162
msgid "Items {itemMin} {itemMax} of {count}"
msgstr ""
#: src/index.jsx:92
#: src/index.jsx:201
#: src/pages/Jobs.jsx:19
msgid "Jobs"
msgstr ""
#: src/pages/JobsSettings.jsx:17
#: src/pages/JobsSettings.jsx:19
msgid "Jobs Settings"
msgstr ""
#: src/components/Pagination/Pagination.jsx:221
#: src/components/Pagination/Pagination.jsx:213
msgid "Last"
msgstr ""
#: src/index.jsx:251
#: src/pages/License.jsx:17
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:150
msgid "Last Modified"
msgstr ""
#: src/pages/Organizations/screens/Organization/OrganizationAccess.jsx:173
msgid "Last Name"
msgstr ""
#: src/index.jsx:216
#: src/pages/License.jsx:19
msgid "License"
msgstr ""
#: src/components/PageHeaderToolbar.jsx:116
#: src/components/AddRole/SelectResourceStep.jsx:96
msgid "Loading..."
msgstr ""
#: src/components/PageHeaderToolbar.jsx:120
msgid "Logout"
msgstr ""
#: src/index.jsx:210
#: src/pages/ManagementJobs.jsx:17
#: src/index.jsx:175
#: src/pages/ManagementJobs.jsx:19
msgid "Management Jobs"
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:28
#: src/pages/Organizations/components/OrganizationListItem.jsx:91
msgid "Members"
msgstr ""
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:57
#: src/pages/Organizations/screens/OrganizationsList.jsx:163
msgid "Modified"
msgstr ""
#: src/index.jsx:137
#: src/pages/Portal.jsx:17
#: src/index.jsx:102
#: src/pages/Portal.jsx:19
msgid "My View"
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:27
#: src/components/AddRole/AddResourceRole.jsx:140
#: src/components/PaginatedDataList/PaginatedDataList.jsx:116
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:56
#: src/pages/Organizations/components/OrganizationAccessItem.jsx:109
#: src/pages/Organizations/components/OrganizationForm.jsx:105
#: src/pages/Organizations/screens/Organization/OrganizationAccess.jsx:171
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:134
#: src/pages/Organizations/screens/OrganizationsList.jsx:162
msgid "Name"
msgstr ""
#: src/components/Pagination/Pagination.jsx:212
#: src/components/Pagination/Pagination.jsx:204
msgid "Next"
msgstr ""
#: src/pages/NotificationTemplates.jsx:17
#: src/components/PaginatedDataList/PaginatedDataList.jsx:133
#: src/components/PaginatedDataList/PaginatedDataList.jsx:196
msgid "No {0} Found"
msgstr ""
#: src/pages/NotificationTemplates.jsx:19
msgid "Notification Templates"
msgstr ""
#: src/index.jsx:205
#: src/index.jsx:170
#: src/pages/Organizations/Organizations.jsx:45
#: src/pages/Organizations/screens/Organization/Organization.jsx:137
msgid "Notifications"
msgstr ""
#: src/pages/Organizations/views/Organization.add.jsx:79
msgid "Organization Add"
msgstr ""
#~ msgid "Organization Add"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:72
#: src/pages/Organizations/screens/Organization/Organization.jsx:153
msgid "Organization detail tabs"
msgstr ""
#: src/index.jsx:179
#: src/pages/Organizations/views/Organization.view.jsx:63
#: src/pages/Organizations/views/Organizations.list.jsx:196
#: src/pages/Organizations/views/Organizations.list.jsx:202
#: src/index.jsx:144
#: src/pages/Organizations/Organizations.jsx:38
#: src/pages/Organizations/Organizations.jsx:24
msgid "Organizations"
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:218
msgid "Organizations List"
#~ msgid "Organizations List"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:190
msgid "Page"
msgstr ""
#: src/components/Pagination/Pagination.jsx:189
msgid "Page <0/> of {pageCount}"
msgstr ""
#~ msgid "Page <0/> of {pageCount}"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:192
#: src/components/Pagination/Pagination.jsx:193
msgid "Page Number"
msgstr ""
#: src/pages/Login.jsx:79
#: src/pages/Login.jsx:82
msgid "Password"
msgstr ""
#: src/components/Pagination/Pagination.jsx:158
msgid "Per Page"
#~ msgid "Per Page"
#~ msgstr ""
#: src/components/PaginatedDataList/PaginatedDataList.jsx:136
#: src/components/PaginatedDataList/PaginatedDataList.jsx:199
msgid "Please add {0} {itemName} to populate this list"
msgstr ""
#: src/App.jsx:203
#~ msgid "Portal Mode"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:178
#: src/components/Pagination/Pagination.jsx:179
msgid "Previous"
msgstr ""
#: src/index.jsx:115
#: src/index.jsx:80
msgid "Primary Navigation"
msgstr ""
#: src/index.jsx:158
#: src/pages/Projects.jsx:17
#: src/index.jsx:123
#: src/pages/Projects.jsx:19
msgid "Projects"
msgstr ""
#: src/index.jsx:144
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:28
msgid "Remove {0} Access"
msgstr ""
#: src/index.jsx:109
msgid "Resources"
msgstr ""
#: src/index.jsx:132
#: src/pages/Schedules.jsx:17
#: src/components/AddRole/AddResourceRole.jsx:220
#: src/components/FormActionGroup/FormActionGroup.jsx:27
#: src/components/FormActionGroup/FormActionGroup.jsx:27
#: src/components/Lookup/Lookup.jsx:168
msgid "Save"
msgstr ""
#: src/index.jsx:97
#: src/pages/Schedules.jsx:19
msgid "Schedules"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:191
#: src/components/Search/Search.jsx:138
msgid "Search"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:185
#: src/components/Search/Search.jsx:131
msgid "Search text input"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:160
#: src/components/AnsibleSelect/AnsibleSelect.jsx:28
msgid "Select Input"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:159
msgid "Select Users Or Teams"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:111
msgid "Select a row to delete"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:102
msgid "Select all"
msgstr ""
#: src/index.jsx:227
#: src/components/AddRole/AddResourceRole.jsx:178
msgid "Select items from list"
msgstr ""
#: src/pages/Organizations/components/OrganizationForm.jsx:141
msgid "Select the Instance Groups for this Organization to run on."
msgstr ""
#: src/components/Lookup/Lookup.jsx:164
msgid "Select {header}"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:187
#: src/components/AddRole/AddResourceRole.jsx:198
#: src/components/AddRole/AddResourceRole.jsx:215
#: src/components/AddRole/SelectRoleStep.jsx:29
#: src/components/Lookup/Lookup.jsx:193
msgid "Selected"
msgstr ""
#: src/index.jsx:192
msgid "Settings"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:222
#: src/components/Sort/Sort.jsx:135
msgid "Sort"
msgstr ""
#: src/index.jsx:241
#: src/components/NotificationsList/NotificationListItem.jsx:70
msgid "Successful"
msgstr ""
#: src/index.jsx:206
msgid "System"
msgstr ""
#: src/pages/SystemSettings.jsx:17
#: src/pages/SystemSettings.jsx:19
msgid "System Settings"
msgstr ""
#: src/index.jsx:189
#: src/pages/Organizations/components/OrganizationListItem.jsx:57
#: src/pages/Teams.jsx:17
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:28
msgid "Team"
msgstr ""
#: src/pages/Organizations/components/OrganizationAccessItem.jsx:144
msgid "Team Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:169
#: src/index.jsx:154
#: src/pages/Organizations/Organizations.jsx:44
#: src/pages/Organizations/components/OrganizationListItem.jsx:99
#: src/pages/Organizations/screens/Organization/Organization.jsx:132
#: src/pages/Teams.jsx:19
msgid "Teams"
msgstr ""
#: src/index.jsx:148
#: src/pages/Templates.jsx:17
#: src/index.jsx:113
#: src/pages/Templates.jsx:19
msgid "Templates"
msgstr ""
#: src/util/validators.jsx:6
msgid "This field must not be blank"
msgstr ""
#: src/util/validators.jsx:16
msgid "This field must not exceed {max} characters"
msgstr ""
#: src/components/NotificationsList/NotificationListItem.jsx:90
#: src/components/NotificationsList/NotificationListItem.jsx:105
msgid "Toggle notification failure"
msgstr ""
#: src/components/NotificationsList/NotificationListItem.jsx:78
msgid "Toggle notification success"
msgstr ""
#: src/components/TowerLogo/TowerLogo.jsx:48
msgid "Tower Brand Image"
msgstr ""
#: src/components/PageHeaderToolbar.jsx:108
#: src/components/AnsibleSelect/AnsibleSelect.jsx:35
msgid "Use Default {label}"
msgstr ""
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:28
msgid "User"
msgstr ""
#: src/components/PageHeaderToolbar.jsx:113
msgid "User Details"
msgstr ""
#: src/index.jsx:246
#: src/index.jsx:211
msgid "User Interface"
msgstr ""
#: src/pages/UISettings.jsx:17
#: src/pages/UISettings.jsx:19
msgid "User Interface Settings"
msgstr ""
#: src/pages/Login.jsx:78
#: src/pages/Organizations/components/OrganizationAccessItem.jsx:122
msgid "User Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:136
#: src/pages/Login.jsx:81
#: src/pages/Organizations/screens/Organization/OrganizationAccess.jsx:172
msgid "Username"
msgstr ""
#: src/index.jsx:184
#: src/pages/Organizations/components/OrganizationListItem.jsx:49
#: src/pages/Users.jsx:17
#: src/components/AddRole/AddResourceRole.jsx:164
#: src/index.jsx:149
#: src/pages/Users.jsx:19
msgid "Users"
msgstr ""
#: src/index.jsx:118
#: src/index.jsx:83
msgid "Views"
msgstr ""
#: src/pages/Login.jsx:74
#: src/pages/Login.jsx:76
msgid "Welcome to Ansible Tower! Please Sign In."
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:56
msgid "add {currentTab}"
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:104
msgid "You do not have permission to delete the following {0}: {itemsUnableToDelete}"
msgstr ""
#: src/contexts/Network.jsx:44
msgid "You have been logged out."
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:56
#~ msgid "add {currentTab}"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:45
msgid "adding {currentTab}"
#~ msgid "adding {currentTab}"
#~ msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:158
msgid "cancel delete"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:150
msgid "confirm delete"
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:38
msgid "confirm removal of {currentTab}/cancel and go back to {currentTab} view."
msgstr ""
#~ msgid "confirm removal of {currentTab}/cancel and go back to {currentTab} view."
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:60
msgid "delete {currentTab}"
msgstr ""
#~ msgid "delete {currentTab}"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:36
msgid "deleting {currentTab} association with orgs"
msgstr ""
#~ msgid "deleting {currentTab} association with orgs"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationEdit.jsx:20
msgid "edit view"
#~ msgid "edit view"
#~ msgstr ""
#: src/components/Lookup/Lookup.jsx:135
msgid "items"
msgstr ""
#: src/components/Pagination/Pagination.jsx:198
msgid "of {pageCount}"
msgstr ""
#: src/pages/Organizations/components/OrganizationEdit.jsx:22
msgid "save/cancel and go back to view"
msgstr ""
#~ msgid "save/cancel and go back to view"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:47
msgid "save/cancel and go back to {currentTab} view"
msgstr ""
#~ msgid "save/cancel and go back to {currentTab} view"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationListItem.jsx:29
msgid "select organization {itemId}"
msgstr ""
#~ msgid "select organization {itemId}"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:82
msgid "{0}"
#~ msgid "{0}"
#~ msgstr ""
#: src/components/PaginatedDataList/PaginatedDataList.jsx:153
#: src/components/PaginatedDataList/PaginatedDataList.jsx:215
msgid "{0} List"
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:54
msgid "{currentTab} detail view"
msgstr ""
#~ msgid "{currentTab} detail view"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:163
msgid "{itemMin} - {itemMax} of {count}"
msgstr ""
#~ msgid "{itemMin} - {itemMax} of {count}"
#~ msgstr ""

View File

@ -13,6 +13,10 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"
#: src/contexts/Network.jsx:56
msgid "404"
msgstr ""
#: src/pages/Organizations/components/OrganizationBreadcrumb.jsx:60
#~ msgid "> add"
#~ msgstr ""
@ -25,20 +29,34 @@ msgstr ""
msgid "About"
msgstr ""
#: src/components/About.jsx:60
#: src/components/About.jsx:59
msgid "AboutModal Logo"
msgstr ""
#: src/index.jsx:175
#: src/index.jsx:140
#: src/pages/Organizations/Organizations.jsx:43
#: src/pages/Organizations/screens/Organization/Organization.jsx:131
msgid "Access"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:266
#: src/pages/Organizations/components/OrganizationBreadcrumb.jsx:68
#: src/components/PaginatedDataList/ToolbarAddButton.jsx:33
#: src/components/PaginatedDataList/ToolbarAddButton.jsx:44
msgid "Add"
msgstr ""
#: src/index.jsx:196
#: src/components/AddRole/AddResourceRole.jsx:153
msgid "Add Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:150
msgid "Add Team Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:147
msgid "Add User Roles"
msgstr ""
#: src/index.jsx:161
msgid "Administration"
msgstr ""
@ -46,67 +64,154 @@ msgstr ""
#~ msgid "Admins"
#~ msgstr ""
#: src/components/About.jsx:77
#: src/pages/Organizations/components/OrganizationForm.jsx:123
#: src/pages/Organizations/components/OrganizationForm.jsx:128
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:142
msgid "Ansible Environment"
msgstr ""
#: src/components/About.jsx:75
msgid "Ansible Version"
msgstr ""
#: src/pages/Applications.jsx:17
#: src/pages/Applications.jsx:19
msgid "Applications"
msgstr ""
#: src/index.jsx:231
#: src/components/AddRole/AddResourceRole.jsx:209
msgid "Apply roles"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:165
msgid "Are you sure you want to delete:"
msgstr ""
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:51
msgid "Are you sure you want to remove {0} access from {1}? Doing so affects all members of the team."
msgstr ""
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:58
msgid "Are you sure you want to remove {0} access from {username}?"
msgstr ""
#: src/index.jsx:196
msgid "Authentication"
msgstr ""
#: src/pages/AuthSettings.jsx:17
#: src/pages/AuthSettings.jsx:19
msgid "Authentication Settings"
msgstr ""
#: src/components/About.jsx:58
#: src/components/About.jsx:57
msgid "Brand Image"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:241
#: src/components/FormActionGroup/FormActionGroup.jsx:30
#: src/components/FormActionGroup/FormActionGroup.jsx:30
#: src/components/Lookup/Lookup.jsx:169
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:161
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:45
msgid "Cancel"
msgstr ""
#: src/pages/Organizations/Organizations.jsx:77
msgid "Cannot find organization with ID"
msgstr ""
#: src/contexts/Network.jsx:57
msgid "Cannot find resource."
msgstr ""
#: src/components/NotifyAndRedirect.jsx:23
msgid "Cannot find route {0}."
msgstr ""
#: src/App.jsx:94
#: src/components/CardCloseButton.jsx:14
#: src/components/CardCloseButton.jsx:15
#: src/components/CardCloseButton.jsx:27
#: src/components/Lookup/Lookup.jsx:169
#: src/pages/Organizations/screens/OrganizationAdd.jsx:67
msgid "Close"
msgstr ""
#: src/components/ExpandCollapse/ExpandCollapse.jsx:34
msgid "Collapse"
msgstr ""
#: src/components/About.jsx:56
#: src/components/About.jsx:55
msgid "Copyright 2018 Red Hat, Inc."
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:29
#: src/pages/Organizations/Organizations.jsx:39
#: src/pages/Organizations/Organizations.jsx:25
msgid "Create New Organization"
msgstr ""
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:58
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:146
#: src/pages/Organizations/screens/OrganizationsList.jsx:164
msgid "Created"
msgstr ""
#: src/index.jsx:200
#: src/pages/CredentialTypes.jsx:17
#: src/index.jsx:165
#: src/pages/CredentialTypes.jsx:19
msgid "Credential Types"
msgstr ""
#: src/index.jsx:153
#: src/pages/Credentials.jsx:17
#: src/index.jsx:118
#: src/pages/Credentials.jsx:19
msgid "Credentials"
msgstr ""
#: src/index.jsx:122
#: src/pages/Dashboard.jsx:17
#: src/index.jsx:87
#: src/pages/Dashboard.jsx:19
msgid "Dashboard"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:252
#: src/components/DataListToolbar/DataListToolbar.jsx:257
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:109
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:130
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:153
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:42
msgid "Delete"
msgstr ""
#: src/pages/Organizations/components/OrganizationBreadcrumb.jsx:59
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:142
msgid "Delete {0}"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:141
msgid "Delete {itemName}"
msgstr ""
#: src/pages/Organizations/components/OrganizationForm.jsx:113
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:138
msgid "Description"
msgstr ""
#: src/pages/Organizations/Organizations.jsx:42
#: src/pages/Organizations/screens/Organization/Organization.jsx:130
msgid "Details"
msgstr ""
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:171
msgid "Edit"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:233
#: src/pages/Organizations/Organizations.jsx:41
msgid "Edit Details"
msgstr ""
#: src/components/ExpandCollapse/ExpandCollapse.jsx:44
msgid "Expand"
msgstr ""
#: src/components/Pagination/Pagination.jsx:169
#: src/components/NotificationsList/NotificationListItem.jsx:82
#: src/components/NotificationsList/NotificationListItem.jsx:97
msgid "Failure"
msgstr ""
#: src/components/Pagination/Pagination.jsx:170
msgid "First"
msgstr ""
@ -114,260 +219,436 @@ msgstr ""
msgid "Help"
msgstr ""
#: src/index.jsx:215
#: src/pages/InstanceGroups.jsx:17
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:54
msgid "If you {0} want to remove access for this particular user, please remove them from the team."
msgstr ""
#: src/components/PageHeaderToolbar.jsx:66
msgid "Info"
msgstr ""
#: src/index.jsx:180
#: src/pages/InstanceGroups.jsx:19
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:32
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:50
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:159
msgid "Instance Groups"
msgstr ""
#: src/index.jsx:220
#: src/index.jsx:185
msgid "Integrations"
msgstr ""
#: src/pages/Login.jsx:80
#: src/pages/Login.jsx:84
msgid "Invalid username or password. Please try again."
msgstr ""
#: src/index.jsx:163
#: src/pages/Inventories.jsx:17
#: src/index.jsx:128
#: src/pages/Inventories.jsx:19
msgid "Inventories"
msgstr ""
#: src/index.jsx:168
#: src/pages/InventoryScripts.jsx:17
#: src/index.jsx:133
#: src/pages/InventoryScripts.jsx:19
msgid "Inventory Scripts"
msgstr ""
#: src/index.jsx:127
#: src/index.jsx:236
#: src/pages/Jobs.jsx:17
#: src/components/Pagination/Pagination.jsx:142
msgid "Items Per Page"
msgstr ""
#: src/components/Pagination/Pagination.jsx:162
msgid "Items {itemMin} {itemMax} of {count}"
msgstr ""
#: src/index.jsx:92
#: src/index.jsx:201
#: src/pages/Jobs.jsx:19
msgid "Jobs"
msgstr ""
#: src/pages/JobsSettings.jsx:17
#: src/pages/JobsSettings.jsx:19
msgid "Jobs Settings"
msgstr ""
#: src/components/Pagination/Pagination.jsx:221
#: src/components/Pagination/Pagination.jsx:213
msgid "Last"
msgstr ""
#: src/index.jsx:251
#: src/pages/License.jsx:17
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:150
msgid "Last Modified"
msgstr ""
#: src/pages/Organizations/screens/Organization/OrganizationAccess.jsx:173
msgid "Last Name"
msgstr ""
#: src/index.jsx:216
#: src/pages/License.jsx:19
msgid "License"
msgstr ""
#: src/components/PageHeaderToolbar.jsx:116
#: src/components/AddRole/SelectResourceStep.jsx:96
msgid "Loading..."
msgstr ""
#: src/components/PageHeaderToolbar.jsx:120
msgid "Logout"
msgstr ""
#: src/index.jsx:210
#: src/pages/ManagementJobs.jsx:17
#: src/index.jsx:175
#: src/pages/ManagementJobs.jsx:19
msgid "Management Jobs"
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:28
#: src/pages/Organizations/components/OrganizationListItem.jsx:91
msgid "Members"
msgstr ""
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:57
#: src/pages/Organizations/screens/OrganizationsList.jsx:163
msgid "Modified"
msgstr ""
#: src/index.jsx:137
#: src/pages/Portal.jsx:17
#: src/index.jsx:102
#: src/pages/Portal.jsx:19
msgid "My View"
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:27
#: src/components/AddRole/AddResourceRole.jsx:140
#: src/components/PaginatedDataList/PaginatedDataList.jsx:116
#: src/pages/Organizations/components/InstanceGroupsLookup.jsx:56
#: src/pages/Organizations/components/OrganizationAccessItem.jsx:109
#: src/pages/Organizations/components/OrganizationForm.jsx:105
#: src/pages/Organizations/screens/Organization/OrganizationAccess.jsx:171
#: src/pages/Organizations/screens/Organization/OrganizationDetail.jsx:134
#: src/pages/Organizations/screens/OrganizationsList.jsx:162
msgid "Name"
msgstr ""
#: src/components/Pagination/Pagination.jsx:212
#: src/components/Pagination/Pagination.jsx:204
msgid "Next"
msgstr ""
#: src/pages/NotificationTemplates.jsx:17
#: src/components/PaginatedDataList/PaginatedDataList.jsx:133
#: src/components/PaginatedDataList/PaginatedDataList.jsx:196
msgid "No {0} Found"
msgstr ""
#: src/pages/NotificationTemplates.jsx:19
msgid "Notification Templates"
msgstr ""
#: src/index.jsx:205
#: src/index.jsx:170
#: src/pages/Organizations/Organizations.jsx:45
#: src/pages/Organizations/screens/Organization/Organization.jsx:137
msgid "Notifications"
msgstr ""
#: src/pages/Organizations/views/Organization.add.jsx:79
msgid "Organization Add"
msgstr ""
#~ msgid "Organization Add"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:72
#: src/pages/Organizations/screens/Organization/Organization.jsx:153
msgid "Organization detail tabs"
msgstr ""
#: src/index.jsx:179
#: src/pages/Organizations/views/Organization.view.jsx:63
#: src/pages/Organizations/views/Organizations.list.jsx:196
#: src/pages/Organizations/views/Organizations.list.jsx:202
#: src/index.jsx:144
#: src/pages/Organizations/Organizations.jsx:38
#: src/pages/Organizations/Organizations.jsx:24
msgid "Organizations"
msgstr ""
#: src/pages/Organizations/views/Organizations.list.jsx:218
msgid "Organizations List"
#~ msgid "Organizations List"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:190
msgid "Page"
msgstr ""
#: src/components/Pagination/Pagination.jsx:189
msgid "Page <0/> of {pageCount}"
msgstr ""
#~ msgid "Page <0/> of {pageCount}"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:192
#: src/components/Pagination/Pagination.jsx:193
msgid "Page Number"
msgstr ""
#: src/pages/Login.jsx:79
#: src/pages/Login.jsx:82
msgid "Password"
msgstr ""
#: src/components/Pagination/Pagination.jsx:158
msgid "Per Page"
#~ msgid "Per Page"
#~ msgstr ""
#: src/components/PaginatedDataList/PaginatedDataList.jsx:136
#: src/components/PaginatedDataList/PaginatedDataList.jsx:199
msgid "Please add {0} {itemName} to populate this list"
msgstr ""
#: src/App.jsx:203
#~ msgid "Portal Mode"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:178
#: src/components/Pagination/Pagination.jsx:179
msgid "Previous"
msgstr ""
#: src/index.jsx:115
#: src/index.jsx:80
msgid "Primary Navigation"
msgstr ""
#: src/index.jsx:158
#: src/pages/Projects.jsx:17
#: src/index.jsx:123
#: src/pages/Projects.jsx:19
msgid "Projects"
msgstr ""
#: src/index.jsx:144
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:28
msgid "Remove {0} Access"
msgstr ""
#: src/index.jsx:109
msgid "Resources"
msgstr ""
#: src/index.jsx:132
#: src/pages/Schedules.jsx:17
#: src/components/AddRole/AddResourceRole.jsx:220
#: src/components/FormActionGroup/FormActionGroup.jsx:27
#: src/components/FormActionGroup/FormActionGroup.jsx:27
#: src/components/Lookup/Lookup.jsx:168
msgid "Save"
msgstr ""
#: src/index.jsx:97
#: src/pages/Schedules.jsx:19
msgid "Schedules"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:191
#: src/components/Search/Search.jsx:138
msgid "Search"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:185
#: src/components/Search/Search.jsx:131
msgid "Search text input"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:160
#: src/components/AnsibleSelect/AnsibleSelect.jsx:28
msgid "Select Input"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:159
msgid "Select Users Or Teams"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:111
msgid "Select a row to delete"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:102
msgid "Select all"
msgstr ""
#: src/index.jsx:227
#: src/components/AddRole/AddResourceRole.jsx:178
msgid "Select items from list"
msgstr ""
#: src/pages/Organizations/components/OrganizationForm.jsx:141
msgid "Select the Instance Groups for this Organization to run on."
msgstr ""
#: src/components/Lookup/Lookup.jsx:164
msgid "Select {header}"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:187
#: src/components/AddRole/AddResourceRole.jsx:198
#: src/components/AddRole/AddResourceRole.jsx:215
#: src/components/AddRole/SelectRoleStep.jsx:29
#: src/components/Lookup/Lookup.jsx:193
msgid "Selected"
msgstr ""
#: src/index.jsx:192
msgid "Settings"
msgstr ""
#: src/components/DataListToolbar/DataListToolbar.jsx:222
#: src/components/Sort/Sort.jsx:135
msgid "Sort"
msgstr ""
#: src/index.jsx:241
#: src/components/NotificationsList/NotificationListItem.jsx:70
msgid "Successful"
msgstr ""
#: src/index.jsx:206
msgid "System"
msgstr ""
#: src/pages/SystemSettings.jsx:17
#: src/pages/SystemSettings.jsx:19
msgid "System Settings"
msgstr ""
#: src/index.jsx:189
#: src/pages/Organizations/components/OrganizationListItem.jsx:57
#: src/pages/Teams.jsx:17
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:28
msgid "Team"
msgstr ""
#: src/pages/Organizations/components/OrganizationAccessItem.jsx:144
msgid "Team Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:169
#: src/index.jsx:154
#: src/pages/Organizations/Organizations.jsx:44
#: src/pages/Organizations/components/OrganizationListItem.jsx:99
#: src/pages/Organizations/screens/Organization/Organization.jsx:132
#: src/pages/Teams.jsx:19
msgid "Teams"
msgstr ""
#: src/index.jsx:148
#: src/pages/Templates.jsx:17
#: src/index.jsx:113
#: src/pages/Templates.jsx:19
msgid "Templates"
msgstr ""
#: src/util/validators.jsx:6
msgid "This field must not be blank"
msgstr ""
#: src/util/validators.jsx:16
msgid "This field must not exceed {max} characters"
msgstr ""
#: src/components/NotificationsList/NotificationListItem.jsx:90
#: src/components/NotificationsList/NotificationListItem.jsx:105
msgid "Toggle notification failure"
msgstr ""
#: src/components/NotificationsList/NotificationListItem.jsx:78
msgid "Toggle notification success"
msgstr ""
#: src/components/TowerLogo/TowerLogo.jsx:48
msgid "Tower Brand Image"
msgstr ""
#: src/components/PageHeaderToolbar.jsx:108
#: src/components/AnsibleSelect/AnsibleSelect.jsx:35
msgid "Use Default {label}"
msgstr ""
#: src/pages/Organizations/components/DeleteRoleConfirmationModal.jsx:28
msgid "User"
msgstr ""
#: src/components/PageHeaderToolbar.jsx:113
msgid "User Details"
msgstr ""
#: src/index.jsx:246
#: src/index.jsx:211
msgid "User Interface"
msgstr ""
#: src/pages/UISettings.jsx:17
#: src/pages/UISettings.jsx:19
msgid "User Interface Settings"
msgstr ""
#: src/pages/Login.jsx:78
#: src/pages/Organizations/components/OrganizationAccessItem.jsx:122
msgid "User Roles"
msgstr ""
#: src/components/AddRole/AddResourceRole.jsx:136
#: src/pages/Login.jsx:81
#: src/pages/Organizations/screens/Organization/OrganizationAccess.jsx:172
msgid "Username"
msgstr ""
#: src/index.jsx:184
#: src/pages/Organizations/components/OrganizationListItem.jsx:49
#: src/pages/Users.jsx:17
#: src/components/AddRole/AddResourceRole.jsx:164
#: src/index.jsx:149
#: src/pages/Users.jsx:19
msgid "Users"
msgstr ""
#: src/index.jsx:118
#: src/index.jsx:83
msgid "Views"
msgstr ""
#: src/pages/Login.jsx:74
#: src/pages/Login.jsx:76
msgid "Welcome to Ansible Tower! Please Sign In."
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:56
msgid "add {currentTab}"
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:104
msgid "You do not have permission to delete the following {0}: {itemsUnableToDelete}"
msgstr ""
#: src/contexts/Network.jsx:44
msgid "You have been logged out."
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:56
#~ msgid "add {currentTab}"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:45
msgid "adding {currentTab}"
#~ msgid "adding {currentTab}"
#~ msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:158
msgid "cancel delete"
msgstr ""
#: src/components/PaginatedDataList/ToolbarDeleteButton.jsx:150
msgid "confirm delete"
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:38
msgid "confirm removal of {currentTab}/cancel and go back to {currentTab} view."
msgstr ""
#~ msgid "confirm removal of {currentTab}/cancel and go back to {currentTab} view."
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:60
msgid "delete {currentTab}"
msgstr ""
#~ msgid "delete {currentTab}"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:36
msgid "deleting {currentTab} association with orgs"
msgstr ""
#~ msgid "deleting {currentTab} association with orgs"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationEdit.jsx:20
msgid "edit view"
#~ msgid "edit view"
#~ msgstr ""
#: src/components/Lookup/Lookup.jsx:135
msgid "items"
msgstr ""
#: src/components/Pagination/Pagination.jsx:198
msgid "of {pageCount}"
msgstr ""
#: src/pages/Organizations/components/OrganizationEdit.jsx:22
msgid "save/cancel and go back to view"
msgstr ""
#~ msgid "save/cancel and go back to view"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:47
msgid "save/cancel and go back to {currentTab} view"
msgstr ""
#~ msgid "save/cancel and go back to {currentTab} view"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationListItem.jsx:29
msgid "select organization {itemId}"
msgstr ""
#~ msgid "select organization {itemId}"
#~ msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:82
msgid "{0}"
#~ msgid "{0}"
#~ msgstr ""
#: src/components/PaginatedDataList/PaginatedDataList.jsx:153
#: src/components/PaginatedDataList/PaginatedDataList.jsx:215
msgid "{0} List"
msgstr ""
#: src/pages/Organizations/components/OrganizationDetail.jsx:54
msgid "{currentTab} detail view"
msgstr ""
#~ msgid "{currentTab} detail view"
#~ msgstr ""
#: src/components/Pagination/Pagination.jsx:163
msgid "{itemMin} - {itemMax} of {count}"
msgstr ""
#~ msgid "{itemMin} - {itemMax} of {count}"
#~ msgstr ""

View File

@ -9,7 +9,7 @@ import {
Button
} from '@patternfly/react-core';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { RootDialog } from './contexts/RootDialog';
@ -66,92 +66,88 @@ class App extends Component {
render () {
const { isAboutModalOpen, isNavOpen } = this.state;
const { render, routeGroups = [], navLabel = '' } = this.props;
const { render, routeGroups = [], navLabel = '', i18n } = this.props;
return (
<Config>
{({ ansible_version, version, me }) => (
<I18n>
{({ i18n }) => (
<RootDialog>
{({
title,
bodyText,
variant = 'info',
clearRootDialogMessage
}) => (
<Fragment>
{(title || bodyText) && (
<AlertModal
variant={variant}
isOpen={!!(title || bodyText)}
onClose={clearRootDialogMessage}
title={title}
actions={[
<Button
key="close"
variant="secondary"
onClick={clearRootDialogMessage}
>
{i18n._(t`Close`)}
</Button>
]}
<RootDialog>
{({
title,
bodyText,
variant = 'info',
clearRootDialogMessage
}) => (
<Fragment>
{(title || bodyText) && (
<AlertModal
variant={variant}
isOpen={!!(title || bodyText)}
onClose={clearRootDialogMessage}
title={title}
actions={[
<Button
key="close"
variant="secondary"
onClick={clearRootDialogMessage}
>
{bodyText}
</AlertModal>
)}
<Page
usecondensed="True"
header={(
<PageHeader
showNavToggle
onNavToggle={this.onNavToggle}
logo={<TowerLogo linkTo="/" />}
toolbar={(
<PageHeaderToolbar
loggedInUser={me}
isAboutDisabled={!version}
onAboutClick={this.onAboutModalOpen}
onLogoutClick={this.onLogout}
/>
)}
/>
)}
sidebar={(
<PageSidebar
isNavOpen={isNavOpen}
nav={(
<Nav aria-label={navLabel}>
<NavList>
{routeGroups.map(
({ groupId, groupTitle, routes }) => (
<NavExpandableGroup
key={groupId}
groupId={groupId}
groupTitle={groupTitle}
routes={routes}
/>
)
)}
</NavList>
</Nav>
)}
/>
)}
>
{render && render({ routeGroups })}
</Page>
<About
ansible_version={ansible_version}
version={version}
isOpen={isAboutModalOpen}
onClose={this.onAboutModalClose}
/>
</Fragment>
{i18n._(t`Close`)}
</Button>
]}
>
{bodyText}
</AlertModal>
)}
</RootDialog>
<Page
usecondensed="True"
header={(
<PageHeader
showNavToggle
onNavToggle={this.onNavToggle}
logo={<TowerLogo linkTo="/" />}
toolbar={(
<PageHeaderToolbar
loggedInUser={me}
isAboutDisabled={!version}
onAboutClick={this.onAboutModalOpen}
onLogoutClick={this.onLogout}
/>
)}
/>
)}
sidebar={(
<PageSidebar
isNavOpen={isNavOpen}
nav={(
<Nav aria-label={navLabel}>
<NavList>
{routeGroups.map(
({ groupId, groupTitle, routes }) => (
<NavExpandableGroup
key={groupId}
groupId={groupId}
groupTitle={groupTitle}
routes={routes}
/>
)
)}
</NavList>
</Nav>
)}
/>
)}
>
{render && render({ routeGroups })}
</Page>
<About
ansible_version={ansible_version}
version={version}
isOpen={isAboutModalOpen}
onClose={this.onAboutModalClose}
/>
</Fragment>
)}
</I18n>
</RootDialog>
)}
</Config>
);
@ -159,4 +155,4 @@ class App extends Component {
}
export { App as _App };
export default withNetwork(App);
export default withI18n()(withNetwork(App));

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
AboutModal,
TextContent,
@ -41,46 +41,43 @@ class About extends React.Component {
ansible_version,
version,
isOpen,
onClose
onClose,
i18n
} = this.props;
const speechBubble = this.createSpeechBubble(version);
return (
<I18n>
{({ i18n }) => (
<AboutModal
isOpen={isOpen}
onClose={onClose}
productName="Ansible Tower"
trademark={i18n._(t`Copyright 2018 Red Hat, Inc.`)}
brandImageSrc={brandImg}
brandImageAlt={i18n._(t`Brand Image`)}
logoImageSrc={logoImg}
logoImageAlt={i18n._(t`AboutModal Logo`)}
>
<pre>
{ speechBubble }
{`
\\
\\ ^__^
(oo)\\_______
(__) A )\\
||----w |
|| ||
`}
</pre>
<TextContent>
<TextList component="dl">
<TextListItem component="dt">
<Trans>Ansible Version</Trans>
</TextListItem>
<TextListItem component="dd">{ ansible_version }</TextListItem>
</TextList>
</TextContent>
</AboutModal>
)}
</I18n>
<AboutModal
isOpen={isOpen}
onClose={onClose}
productName="Ansible Tower"
trademark={i18n._(t`Copyright 2018 Red Hat, Inc.`)}
brandImageSrc={brandImg}
brandImageAlt={i18n._(t`Brand Image`)}
logoImageSrc={logoImg}
logoImageAlt={i18n._(t`AboutModal Logo`)}
>
<pre>
{ speechBubble }
{`
\\
\\ ^__^
(oo)\\_______
(__) A )\\
||----w |
|| ||
`}
</pre>
<TextContent>
<TextList component="dl">
<TextListItem component="dt">
{i18n._(t`Ansible Version`)}
</TextListItem>
<TextListItem component="dd">{ ansible_version }</TextListItem>
</TextList>
</TextContent>
</AboutModal>
);
}
}
@ -98,4 +95,4 @@ About.defaultProps = {
version: null,
};
export default About;
export default withI18n()(About);

View File

@ -1,8 +1,9 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { I18n, i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Wizard } from '@patternfly/react-core';
import { withNetwork } from '../../contexts/Network';
import SelectResourceStep from './SelectResourceStep';
import SelectRoleStep from './SelectRoleStep';
import SelectableCard from './SelectableCard';
@ -127,113 +128,103 @@ class AddResourceRole extends React.Component {
} = this.state;
const {
onClose,
roles
roles,
i18n
} = this.props;
const userColumns = [
{ name: i18nMark('Username'), key: 'username', isSortable: true }
{ name: i18n._(t`Username`), key: 'username', isSortable: true }
];
const teamColumns = [
{ name: i18nMark('Name'), key: 'name', isSortable: true }
{ name: i18n._(t`Name`), key: 'name', isSortable: true }
];
let wizardTitle = '';
switch (selectedResource) {
case 'users':
wizardTitle = i18nMark('Add User Roles');
wizardTitle = i18n._(t`Add User Roles`);
break;
case 'teams':
wizardTitle = i18nMark('Add Team Roles');
wizardTitle = i18n._(t`Add Team Roles`);
break;
default:
wizardTitle = i18nMark('Add Roles');
wizardTitle = i18n._(t`Add Roles`);
}
const steps = [
{
id: 1,
name: i18nMark('Select Users Or Teams'),
name: i18n._(t`Select Users Or Teams`),
component: (
<I18n>
{({ i18n }) => (
<div style={{ display: 'flex' }}>
<SelectableCard
isSelected={selectedResource === 'users'}
label={i18n._(t`Users`)}
onClick={() => this.handleResourceSelect('users')}
/>
<SelectableCard
isSelected={selectedResource === 'teams'}
label={i18n._(t`Teams`)}
onClick={() => this.handleResourceSelect('teams')}
/>
</div>
)}
</I18n>
<div style={{ display: 'flex' }}>
<SelectableCard
isSelected={selectedResource === 'users'}
label={i18n._(t`Users`)}
onClick={() => this.handleResourceSelect('users')}
/>
<SelectableCard
isSelected={selectedResource === 'teams'}
label={i18n._(t`Teams`)}
onClick={() => this.handleResourceSelect('teams')}
/>
</div>
),
enableNext: selectedResource !== null
},
{
id: 2,
name: i18nMark('Select items from list'),
name: i18n._(t`Select items from list`),
component: (
<I18n>
{({ i18n }) => (
<Fragment>
{selectedResource === 'users' && (
<SelectResourceStep
columns={userColumns}
displayKey="username"
onRowClick={this.handleResourceCheckboxClick}
onSearch={this.readUsers}
selectedLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows}
sortedColumnKey="username"
itemName="user"
/>
)}
{selectedResource === 'teams' && (
<SelectResourceStep
columns={teamColumns}
onRowClick={this.handleResourceCheckboxClick}
onSearch={this.readTeams}
selectedLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows}
itemName="team"
/>
)}
</Fragment>
<Fragment>
{selectedResource === 'users' && (
<SelectResourceStep
columns={userColumns}
displayKey="username"
onRowClick={this.handleResourceCheckboxClick}
onSearch={this.readUsers}
selectedLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows}
sortedColumnKey="username"
itemName="user"
/>
)}
</I18n>
{selectedResource === 'teams' && (
<SelectResourceStep
columns={teamColumns}
onRowClick={this.handleResourceCheckboxClick}
onSearch={this.readTeams}
selectedLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows}
itemName="team"
/>
)}
</Fragment>
),
enableNext: selectedResourceRows.length > 0
},
{
id: 3,
name: i18nMark('Apply roles'),
name: i18n._(t`Apply roles`),
component: (
<I18n>
{({ i18n }) => (
<SelectRoleStep
onRolesClick={this.handleRoleCheckboxClick}
roles={roles}
selectedListKey={selectedResource === 'users' ? 'username' : 'name'}
selectedListLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows}
selectedRoleRows={selectedRoleRows}
/>
)}
</I18n>
<SelectRoleStep
onRolesClick={this.handleRoleCheckboxClick}
roles={roles}
selectedListKey={selectedResource === 'users' ? 'username' : 'name'}
selectedListLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows}
selectedRoleRows={selectedRoleRows}
/>
),
nextButtonText: i18nMark('Save'),
nextButtonText: i18n._(t`Save`),
enableNext: selectedRoleRows.length > 0
}
];
const currentStep = steps.find(step => step.id === currentStepId);
// TODO: somehow internationalize steps and currentStep.nextButtonText
return (
<Wizard
style={{ overflow: 'scroll' }}
@ -259,4 +250,5 @@ AddResourceRole.defaultProps = {
roles: {}
};
export default AddResourceRole;
export { AddResourceRole as _AddResourceRole };
export default withI18n()(withNetwork(AddResourceRole));

View File

@ -1,7 +1,8 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import PaginatedDataList from '../PaginatedDataList';
import CheckboxListItem from '../ListItem';
import SelectedList from '../SelectedList';
@ -87,11 +88,12 @@ class SelectResourceStep extends React.Component {
selectedLabel,
selectedResourceRows,
itemName,
i18n
} = this.props;
return (
<Fragment>
{isLoading && (<div>Loading...</div>)}
{isLoading && (<div>{i18n._(t`Loading...`)}</div>)}
{isInitialized && (
<Fragment>
{selectedResourceRows.length > 0 && (
@ -146,11 +148,11 @@ SelectResourceStep.propTypes = {
SelectResourceStep.defaultProps = {
displayKey: 'name',
onRowClick: () => {},
selectedLabel: i18nMark('Selected Items'),
selectedLabel: null,
selectedResourceRows: [],
sortedColumnKey: 'name',
itemName: 'item',
};
export { SelectResourceStep as _SelectResourceStep };
export default withRouter(SelectResourceStep);
export default withI18n()(withRouter(SelectResourceStep));

View File

@ -1,7 +1,8 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import CheckboxCard from './CheckboxCard';
import SelectedList from '../SelectedList';
@ -14,7 +15,8 @@ class RolesStep extends React.Component {
selectedListKey,
selectedListLabel,
selectedResourceRows,
selectedRoleRows
selectedRoleRows,
i18n
} = this.props;
return (
@ -24,7 +26,7 @@ class RolesStep extends React.Component {
<SelectedList
displayKey={selectedListKey}
isReadOnly
label={selectedListLabel}
label={selectedListLabel || i18n._(t`Selected`)}
selected={selectedResourceRows}
showOverflowAfter={5}
/>
@ -61,9 +63,9 @@ RolesStep.propTypes = {
RolesStep.defaultProps = {
onRolesClick: () => {},
selectedListKey: 'name',
selectedListLabel: i18nMark('Selected'),
selectedListLabel: null,
selectedResourceRows: [],
selectedRoleRows: []
};
export default RolesStep;
export default withI18n()(RolesStep);

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
FormSelect,
@ -20,29 +20,25 @@ class AnsibleSelect extends React.Component {
}
render () {
const { label, value, data, defaultSelected } = this.props;
const { label, value, data, defaultSelected, i18n } = this.props;
return (
<I18n>
{({ i18n }) => (
<FormSelect
value={value}
onChange={this.onSelectChange}
aria-label={i18n._(t`Select Input`)}
>
{data.map((datum) => (
datum === defaultSelected ? (
<FormSelectOption
key=""
value=""
label={i18n._(t`Use Default ${label}`)}
/>
) : (
<FormSelectOption key={datum} value={datum} label={datum} />
)
))}
</FormSelect>
)}
</I18n>
<FormSelect
value={value}
onChange={this.onSelectChange}
aria-label={i18n._(t`Select Input`)}
>
{data.map((datum) => (
datum === defaultSelected ? (
<FormSelectOption
key=""
value=""
label={i18n._(t`Use Default ${label}`)}
/>
) : (
<FormSelectOption key={datum} value={datum} label={datum} />
)
))}
</FormSelect>
);
}
}
@ -62,4 +58,5 @@ AnsibleSelect.propTypes = {
value: PropTypes.string.isRequired,
};
export default AnsibleSelect;
export { AnsibleSelect as _AnsibleSelect };
export default withI18n()(AnsibleSelect);

View File

@ -3,40 +3,32 @@ import { string } from 'prop-types';
import { Link } from 'react-router-dom';
import { Button } from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
function CardCloseButton ({ linkTo, ...props }) {
function CardCloseButton ({ linkTo, i18n, i18nHash, ...props }) {
if (linkTo) {
return (
<I18n>
{({ i18n }) => (
<Link
className="pf-c-button pf-c-card__close"
aria-label={i18n._(t`Close`)}
title={i18n._(t`Close`)}
to={linkTo}
{...props}
>
<TimesIcon />
</Link>
)}
</I18n>
<Link
className="pf-c-button pf-c-card__close"
aria-label={i18n._(t`Close`)}
title={i18n._(t`Close`)}
to={linkTo}
{...props}
>
<TimesIcon />
</Link>
);
}
return (
<I18n>
{({ i18n }) => (
<Button
variant="plain"
className="pf-c-card__close"
aria-label={i18n._(t`Close`)}
{...props}
>
<TimesIcon />
</Button>
)}
</I18n>
<Button
variant="plain"
className="pf-c-card__close"
aria-label={i18n._(t`Close`)}
{...props}
>
<TimesIcon />
</Button>
);
}
CardCloseButton.propTypes = {
@ -46,4 +38,4 @@ CardCloseButton.defaultProps = {
linkTo: null,
};
export default CardCloseButton;
export default withI18n()(CardCloseButton);

View File

@ -1,6 +1,6 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Checkbox,
@ -85,70 +85,66 @@ class DataListToolbar extends React.Component {
sortOrder,
sortedColumnKey,
additionalControls,
i18n
} = this.props;
const showExpandCollapse = (onCompact && onExpand);
return (
<I18n>
{({ i18n }) => (
<AWXToolbar>
<Toolbar marginleft={noLeftMargin ? 1 : 0}>
<ColumnLeft>
{ showSelectAll && (
<Fragment>
<ToolbarItem>
<Checkbox
checked={isAllSelected}
onChange={onSelectAll}
aria-label={i18n._(t`Select all`)}
id="select-all"
/>
</ToolbarItem>
<VerticalSeparator />
</Fragment>
)}
<ToolbarItem css="flex-grow: 1;">
<Search
columns={columns}
onSearch={onSearch}
sortedColumnKey={sortedColumnKey}
<AWXToolbar>
<Toolbar marginleft={noLeftMargin ? 1 : 0}>
<ColumnLeft>
{ showSelectAll && (
<Fragment>
<ToolbarItem>
<Checkbox
checked={isAllSelected}
onChange={onSelectAll}
aria-label={i18n._(t`Select all`)}
id="select-all"
/>
</ToolbarItem>
<VerticalSeparator />
</ColumnLeft>
<ColumnRight>
<ToolbarItem>
<Sort
columns={columns}
onSort={onSort}
sortOrder={sortOrder}
sortedColumnKey={sortedColumnKey}
</Fragment>
)}
<ToolbarItem css="flex-grow: 1;">
<Search
columns={columns}
onSearch={onSearch}
sortedColumnKey={sortedColumnKey}
/>
</ToolbarItem>
<VerticalSeparator />
</ColumnLeft>
<ColumnRight>
<ToolbarItem>
<Sort
columns={columns}
onSort={onSort}
sortOrder={sortOrder}
sortedColumnKey={sortedColumnKey}
/>
</ToolbarItem>
{showExpandCollapse && (
<Fragment>
<VerticalSeparator />
<ToolbarGroup>
<ExpandCollapse
isCompact={isCompact}
onCompact={onCompact}
onExpand={onExpand}
/>
</ToolbarItem>
{showExpandCollapse && (
<Fragment>
<VerticalSeparator />
<ToolbarGroup>
<ExpandCollapse
isCompact={isCompact}
onCompact={onCompact}
onExpand={onExpand}
/>
</ToolbarGroup>
{ additionalControls && (
<VerticalSeparator />
)}
</Fragment>
</ToolbarGroup>
{ additionalControls && (
<VerticalSeparator />
)}
<AdditionalControlsWrapper>
{additionalControls}
</AdditionalControlsWrapper>
</ColumnRight>
</Toolbar>
</AWXToolbar>
)}
</I18n>
</Fragment>
)}
<AdditionalControlsWrapper>
{additionalControls}
</AdditionalControlsWrapper>
</ColumnRight>
</Toolbar>
</AWXToolbar>
);
}
}
@ -184,4 +180,4 @@ DataListToolbar.defaultProps = {
additionalControls: [],
};
export default DataListToolbar;
export default withI18n()(DataListToolbar);

View File

@ -1,6 +1,6 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Button,
@ -22,36 +22,33 @@ class ExpandCollapse extends React.Component {
const {
onCompact,
onExpand,
isCompact
isCompact,
i18n
} = this.props;
return (
<I18n>
{({ i18n }) => (
<Fragment>
<ToolbarItem>
<Button
variant="plain"
aria-label={i18n._(t`Collapse`)}
onClick={onCompact}
style={isCompact ? ToolbarActiveStyle : null}
>
<BarsIcon />
</Button>
</ToolbarItem>
<ToolbarItem>
<Button
variant="plain"
aria-label={i18n._(t`Expand`)}
onClick={onExpand}
style={!isCompact ? ToolbarActiveStyle : null}
>
<EqualsIcon />
</Button>
</ToolbarItem>
</Fragment>
)}
</I18n>
<Fragment>
<ToolbarItem>
<Button
variant="plain"
aria-label={i18n._(t`Collapse`)}
onClick={onCompact}
style={isCompact ? ToolbarActiveStyle : null}
>
<BarsIcon />
</Button>
</ToolbarItem>
<ToolbarItem>
<Button
variant="plain"
aria-label={i18n._(t`Expand`)}
onClick={onExpand}
style={!isCompact ? ToolbarActiveStyle : null}
>
<EqualsIcon />
</Button>
</ToolbarItem>
</Fragment>
);
}
}
@ -64,4 +61,4 @@ ExpandCollapse.propTypes = {
ExpandCollapse.defaultProps = {};
export default ExpandCollapse;
export default withI18n()(ExpandCollapse);

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
ActionGroup,
@ -20,21 +20,17 @@ const buttonGroupStyle = {
marginRight: '20px'
};
const FormActionGroup = ({ onSubmit, submitDisabled, onCancel }) => (
<I18n>
{({ i18n }) => (
<ActionGroup style={formActionGroupStyle}>
<Toolbar>
<ToolbarGroup style={buttonGroupStyle}>
<Button aria-label={i18n._(t`Save`)} variant="primary" type="submit" onClick={onSubmit} isDisabled={submitDisabled}>{i18n._(t`Save`)}</Button>
</ToolbarGroup>
<ToolbarGroup>
<Button aria-label={i18n._(t`Cancel`)} variant="secondary" type="button" onClick={onCancel}>{i18n._(t`Cancel`)}</Button>
</ToolbarGroup>
</Toolbar>
</ActionGroup>
)}
</I18n>
const FormActionGroup = ({ onSubmit, submitDisabled, onCancel, i18n }) => (
<ActionGroup style={formActionGroupStyle}>
<Toolbar>
<ToolbarGroup style={buttonGroupStyle}>
<Button aria-label={i18n._(t`Save`)} variant="primary" type="submit" onClick={onSubmit} isDisabled={submitDisabled}>{i18n._(t`Save`)}</Button>
</ToolbarGroup>
<ToolbarGroup>
<Button aria-label={i18n._(t`Cancel`)} variant="secondary" type="button" onClick={onCancel}>{i18n._(t`Cancel`)}</Button>
</ToolbarGroup>
</Toolbar>
</ActionGroup>
);
FormActionGroup.propTypes = {
@ -47,4 +43,4 @@ FormActionGroup.defaultProps = {
submitDisabled: false,
};
export default FormActionGroup;
export default withI18n()(FormActionGroup);

View File

@ -9,7 +9,7 @@ import {
InputGroup,
Modal,
} from '@patternfly/react-core';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { withNetwork } from '../../contexts/Network';
@ -130,7 +130,9 @@ class Lookup extends React.Component {
results,
count,
} = this.state;
const { id, lookupHeader = 'items', value, columns } = this.props;
const { id, lookupHeader, value, columns, i18n } = this.props;
const header = lookupHeader || i18n._(t`items`);
const chips = value ? (
<div className="pf-c-chip-group">
@ -143,64 +145,60 @@ class Lookup extends React.Component {
) : null;
return (
<I18n>
{({ i18n }) => (
<Fragment>
<InputGroup className="awx-lookup">
<Button
aria-label="Search"
id={id}
onClick={this.handleModalToggle}
variant={ButtonVariant.tertiary}
>
<SearchIcon />
</Button>
<div className="pf-c-form-control">
{chips}
</div>
</InputGroup>
<Modal
className="awx-c-modal"
title={`Select ${lookupHeader}`}
isOpen={isModalOpen}
onClose={this.handleModalToggle}
actions={[
<Button key="save" variant="primary" onClick={this.saveModal} style={(results.length === 0) ? { display: 'none' } : {}}>{i18n._(t`Save`)}</Button>,
<Button key="cancel" variant="secondary" onClick={this.handleModalToggle}>{(results.length === 0) ? i18n._(t`Close`) : i18n._(t`Cancel`)}</Button>
]}
>
<PaginatedDataList
items={results}
itemCount={count}
itemName={lookupHeader}
qsConfig={this.qsConfig}
toolbarColumns={columns}
renderItem={item => (
<CheckboxListItem
key={item.id}
itemId={item.id}
name={item.name}
isSelected={lookupSelectedItems.some(i => i.id === item.id)}
onSelect={() => this.toggleSelected(item)}
/>
)}
alignToolbarLeft
showPageSizeOptions={false}
paginationStyling={paginationStyling}
<Fragment>
<InputGroup className="awx-lookup">
<Button
aria-label="Search"
id={id}
onClick={this.handleModalToggle}
variant={ButtonVariant.tertiary}
>
<SearchIcon />
</Button>
<div className="pf-c-form-control">
{chips}
</div>
</InputGroup>
<Modal
className="awx-c-modal"
title={i18n._(t`Select ${header}`)}
isOpen={isModalOpen}
onClose={this.handleModalToggle}
actions={[
<Button key="save" variant="primary" onClick={this.saveModal} style={(results.length === 0) ? { display: 'none' } : {}}>{i18n._(t`Save`)}</Button>,
<Button key="cancel" variant="secondary" onClick={this.handleModalToggle}>{(results.length === 0) ? i18n._(t`Close`) : i18n._(t`Cancel`)}</Button>
]}
>
<PaginatedDataList
items={results}
itemCount={count}
itemName={lookupHeader}
qsConfig={this.qsConfig}
toolbarColumns={columns}
renderItem={item => (
<CheckboxListItem
key={item.id}
itemId={item.id}
name={item.name}
isSelected={lookupSelectedItems.some(i => i.id === item.id)}
onSelect={() => this.toggleSelected(item)}
/>
{lookupSelectedItems.length > 0 && (
<SelectedList
label={i18n._(t`Selected`)}
selected={lookupSelectedItems}
showOverflowAfter={5}
onRemove={this.toggleSelected}
/>
)}
{ error ? <div>error</div> : '' }
</Modal>
</Fragment>
)}
</I18n>
)}
alignToolbarLeft
showPageSizeOptions={false}
paginationStyling={paginationStyling}
/>
{lookupSelectedItems.length > 0 && (
<SelectedList
label={i18n._(t`Selected`)}
selected={lookupSelectedItems}
showOverflowAfter={5}
onRemove={this.toggleSelected}
/>
)}
{ error ? <div>error</div> : '' }
</Modal>
</Fragment>
);
}
}
@ -217,9 +215,9 @@ Lookup.propTypes = {
Lookup.defaultProps = {
id: 'lookup-search',
lookupHeader: 'items',
lookupHeader: null,
name: null,
};
export { Lookup as _Lookup };
export default withNetwork(withRouter(Lookup));
export default withI18n()(withNetwork(withRouter(Lookup)));

View File

@ -1,6 +1,6 @@
import React from 'react';
import { shape, number, string, bool, func } from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Link } from 'react-router-dom';
import {
@ -37,66 +37,63 @@ function NotificationListItem (props) {
detailUrl,
successTurnedOn,
errorTurnedOn,
toggleNotification
toggleNotification,
i18n
} = props;
return (
<I18n>
{({ i18n }) => (
<DataListItem
aria-labelledby={`items-list-item-${notification.id}`}
key={notification.id}
>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="name">
<Link
to={{
pathname: detailUrl
}}
css="margin-right: 1.5em;"
>
<b id={`items-list-item-${notification.id}`}>{notification.name}</b>
</Link>
<Badge
css="text-transform: capitalize;"
isRead
>
{notification.notification_type}
</Badge>
</DataListCell>,
<DataListCell righthalf="true" key="toggles">
<Switch
id={`notification-${notification.id}-success-toggle`}
label={i18n._(t`Successful`)}
isChecked={successTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() => toggleNotification(
notification.id,
successTurnedOn,
'success'
)}
aria-label={i18n._(t`Toggle notification success`)}
/>
<Switch
id={`notification-${notification.id}-error-toggle`}
label={i18n._(t`Failure`)}
isChecked={errorTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() => toggleNotification(
notification.id,
errorTurnedOn,
'error'
)}
aria-label={i18n._(t`Toggle notification failure`)}
/>
</DataListCell>
]}
<DataListItem
aria-labelledby={`items-list-item-${notification.id}`}
key={notification.id}
>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="name">
<Link
to={{
pathname: detailUrl
}}
css="margin-right: 1.5em;"
>
<b id={`items-list-item-${notification.id}`}>{notification.name}</b>
</Link>
<Badge
css="text-transform: capitalize;"
isRead
>
{notification.notification_type}
</Badge>
</DataListCell>,
<DataListCell righthalf="true" key="toggles">
<Switch
id={`notification-${notification.id}-success-toggle`}
label={i18n._(t`Successful`)}
isChecked={successTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() => toggleNotification(
notification.id,
successTurnedOn,
'success'
)}
aria-label={i18n._(t`Toggle notification success`)}
/>
</DataListItemRow>
</DataListItem>
)}
</I18n>
<Switch
id={`notification-${notification.id}-error-toggle`}
label={i18n._(t`Failure`)}
isChecked={errorTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() => toggleNotification(
notification.id,
errorTurnedOn,
'error'
)}
aria-label={i18n._(t`Toggle notification failure`)}
/>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
);
}
@ -118,4 +115,4 @@ NotificationListItem.defaultProps = {
successTurnedOn: false,
};
export default NotificationListItem;
export default withI18n()(NotificationListItem);

View File

@ -1,8 +1,8 @@
import React from 'react';
import React, { Fragment } from 'react';
import { Redirect, withRouter } from 'react-router-dom';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { withRootDialog } from '../contexts/RootDialog';
@ -14,16 +14,13 @@ const NotifyAndRedirect = ({
strict,
sensitive,
setRootDialogMessage,
location
location,
i18n
}) => {
setRootDialogMessage({
title: '404',
bodyText: (
<Trans>
Cannot find route
<strong>{` ${location.pathname}`}</strong>
.
</Trans>
<Fragment>{i18n._(t`Cannot find route ${(<strong>{location.pathname}</strong>)}.`)}</Fragment>
),
variant: 'warning'
});
@ -41,4 +38,4 @@ const NotifyAndRedirect = ({
};
export { NotifyAndRedirect as _NotifyAndRedirect };
export default withRootDialog(withRouter(NotifyAndRedirect));
export default withI18n()(withRootDialog(withRouter(NotifyAndRedirect)));

View File

@ -1,8 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { I18n } from '@lingui/react';
import {
Dropdown,
DropdownItem,
@ -57,78 +56,75 @@ class PageHeaderToolbar extends Component {
isAboutDisabled,
onAboutClick,
onLogoutClick,
loggedInUser
loggedInUser,
i18n
} = this.props;
return (
<I18n>
{({ i18n }) => (
<Toolbar>
<ToolbarGroup>
<Tooltip position="left" content={<div>{i18n._(t`Info`)}</div>}>
<ToolbarItem>
<Dropdown
isPlain
isOpen={isHelpOpen}
position={DropdownPosition.right}
onSelect={this.handleHelpSelect}
toggle={(
<DropdownToggle onToggle={this.handleHelpToggle}>
<QuestionCircleIcon />
</DropdownToggle>
<Toolbar>
<ToolbarGroup>
<Tooltip position="left" content={<div>{i18n._(t`Info`)}</div>}>
<ToolbarItem>
<Dropdown
isPlain
isOpen={isHelpOpen}
position={DropdownPosition.right}
onSelect={this.handleHelpSelect}
toggle={(
<DropdownToggle onToggle={this.handleHelpToggle}>
<QuestionCircleIcon />
</DropdownToggle>
)}
dropdownItems={[
<DropdownItem key="help" target="_blank" href={DOCLINK}>
{i18n._(t`Help`)}
</DropdownItem>,
<DropdownItem
key="about"
component="button"
isDisabled={isAboutDisabled}
onClick={onAboutClick}
>
{i18n._(t`About`)}
</DropdownItem>
]}
/>
</ToolbarItem>
</Tooltip>
<Tooltip position="left" content={<div>User</div>}>
<ToolbarItem>
<Dropdown
isPlain
isOpen={isUserOpen}
position={DropdownPosition.right}
onSelect={this.handleUserSelect}
toggle={(
<DropdownToggle onToggle={this.handleUserToggle}>
<UserIcon />
{loggedInUser && (
<span style={{ marginLeft: '10px' }}>
{loggedInUser.username}
</span>
)}
dropdownItems={[
<DropdownItem key="help" target="_blank" href={DOCLINK}>
{i18n._(t`Help`)}
</DropdownItem>,
<DropdownItem
key="about"
component="button"
isDisabled={isAboutDisabled}
onClick={onAboutClick}
>
{i18n._(t`About`)}
</DropdownItem>
]}
/>
</ToolbarItem>
</Tooltip>
<Tooltip position="left" content={<div>User</div>}>
<ToolbarItem>
<Dropdown
isPlain
isOpen={isUserOpen}
position={DropdownPosition.right}
onSelect={this.handleUserSelect}
toggle={(
<DropdownToggle onToggle={this.handleUserToggle}>
<UserIcon />
{loggedInUser && (
<span style={{ marginLeft: '10px' }}>
{loggedInUser.username}
</span>
)}
</DropdownToggle>
)}
dropdownItems={[
<DropdownItem key="user" href="#/home">
{i18n._(t`User Details`)}
</DropdownItem>,
<DropdownItem
key="logout"
component="button"
onClick={onLogoutClick}
>
{i18n._(t`Logout`)}
</DropdownItem>
]}
/>
</ToolbarItem>
</Tooltip>
</ToolbarGroup>
</Toolbar>
)}
</I18n>
</DropdownToggle>
)}
dropdownItems={[
<DropdownItem key="user" href="#/home">
{i18n._(t`User Details`)}
</DropdownItem>,
<DropdownItem
key="logout"
component="button"
onClick={onLogoutClick}
>
{i18n._(t`Logout`)}
</DropdownItem>
]}
/>
</ToolbarItem>
</Tooltip>
</ToolbarGroup>
</Toolbar>
);
}
}
@ -143,4 +139,4 @@ PageHeaderToolbar.defaultProps = {
isAboutDisabled: false
};
export default PageHeaderToolbar;
export default withI18n()(PageHeaderToolbar);

View File

@ -14,8 +14,8 @@ import {
EmptyStateBody,
} from '@patternfly/react-core';
import { CubesIcon } from '@patternfly/react-icons';
import { I18n, i18nMark } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { withRouter, Link } from 'react-router-dom';
import Pagination from '../Pagination';
@ -108,13 +108,15 @@ class PaginatedDataList extends React.Component {
showPageSizeOptions,
paginationStyling,
location,
i18n
} = this.props;
const { error } = this.state;
const [orderBy, sortOrder] = this.getSortOrder();
const queryParams = parseNamespacedQueryString(qsConfig, location.search);
const columns = toolbarColumns.length ? toolbarColumns : [{ name: i18n._(t`Name`), key: 'name', isSortable: true }];
return (
<I18n>
{({ i18n }) => (
<Fragment>
{error && (
<Fragment>
{error && (
<Fragment>
@ -128,24 +130,10 @@ class PaginatedDataList extends React.Component {
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title size="lg">
<Trans>
No
{' '}
{ucFirst(itemNamePlural || pluralize(itemName))}
{' '}
Found
</Trans>
{i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found`)}
</Title>
<EmptyStateBody>
<Trans>
Please add
{' '}
{getArticle(itemName)}
{' '}
{itemName}
{' '}
to populate this list
</Trans>
{i18n._(t`Please add ${getArticle(itemName)} ${itemName} to populate this list`)}
</EmptyStateBody>
</EmptyState>
) : (
@ -153,7 +141,7 @@ class PaginatedDataList extends React.Component {
<DataListToolbar
sortedColumnKey={orderBy}
sortOrder={sortOrder}
columns={toolbarColumns}
columns={columns}
onSearch={() => { }}
onSort={this.handleSort}
showSelectAll={showSelectAll}
@ -199,9 +187,67 @@ class PaginatedDataList extends React.Component {
/>
</Fragment>
)}
</Fragment> // TODO: replace with proper error handling
)}
{items.length === 0 ? (
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title size="lg">
{i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found`)}
</Title>
<EmptyStateBody>
{i18n._(t`Please add ${getArticle(itemName)} ${itemName} to populate this list`)}
</EmptyStateBody>
</EmptyState>
) : (
<Fragment>
<DataListToolbar
sortedColumnKey={orderBy}
sortOrder={sortOrder}
columns={columns}
onSearch={() => { }}
onSort={this.handleSort}
showSelectAll={showSelectAll}
isAllSelected={isAllSelected}
onSelectAll={onSelectAll}
additionalControls={additionalControls}
/>
<DataList aria-label={i18n._(t`${ucFirst(pluralize(itemName))} List`)}>
{items.map(item => (renderItem ? renderItem(item) : (
<DataListItem
aria-labelledby={`items-list-item-${item.id}`}
key={item.id}
>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="team-name">
<TextContent style={detailWrapperStyle}>
<Link to={{ pathname: item.url }}>
<Text
id={`items-list-item-${item.id}`}
style={detailLabelStyle}
>
{item.name}
</Text>
</Link>
</TextContent>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
)))}
</DataList>
<Pagination
count={itemCount}
page={queryParams.page}
pageCount={this.getPageCount()}
page_size={queryParams.page_size}
onSetPage={this.handleSetPage}
/>
</Fragment>
)}
</I18n>
</Fragment>
);
}
}
@ -235,9 +281,7 @@ PaginatedDataList.propTypes = {
PaginatedDataList.defaultProps = {
renderItem: null,
toolbarColumns: [
{ name: i18nMark('Name'), key: 'name', isSortable: true },
],
toolbarColumns: [],
additionalControls: [],
itemName: 'item',
itemNamePlural: '',
@ -250,4 +294,4 @@ PaginatedDataList.defaultProps = {
};
export { PaginatedDataList as _PaginatedDataList };
export default withRouter(PaginatedDataList);
export default withI18n()(withRouter(PaginatedDataList));

View File

@ -3,7 +3,7 @@ import { string, func } from 'prop-types';
import { Link } from 'react-router-dom';
import { Button as PFButton } from '@patternfly/react-core';
import { PlusIcon } from '@patternfly/react-icons';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import styled from 'styled-components';
@ -20,39 +20,32 @@ const Button = styled(PFButton)`
}
`;
function ToolbarAddButton ({ linkTo, onClick }) {
function ToolbarAddButton ({ linkTo, onClick, i18n }) {
if (!linkTo && !onClick) {
throw new Error('ToolbarAddButton requires either `linkTo` or `onClick` prop');
}
if (linkTo) {
// TODO: This should only be a <Link> (no <Button>) but CSS is off
return (
<I18n>
{({ i18n }) => (
<Link to={linkTo}>
<Button
variant="primary"
aria-label={i18n._(t`Add`)}
>
<PlusIcon />
</Button>
</Link>
)}
</I18n>
);
}
return (
<I18n>
{({ i18n }) => (
<Link to={linkTo}>
<Button
variant="primary"
aria-label={i18n._(t`Add`)}
onClick={onClick}
>
<PlusIcon />
</Button>
)}
</I18n>
</Link>
);
}
return (
<Button
variant="primary"
aria-label={i18n._(t`Add`)}
onClick={onClick}
>
<PlusIcon />
</Button>
);
}
ToolbarAddButton.propTypes = {
@ -64,4 +57,4 @@ ToolbarAddButton.defaultProps = {
onClick: null
};
export default ToolbarAddButton;
export default withI18n()(ToolbarAddButton);

View File

@ -2,9 +2,9 @@ import React, { Fragment } from 'react';
import { func, bool, number, string, arrayOf, shape } from 'prop-types';
import { Button as PFButton, Tooltip } from '@patternfly/react-core';
import { TrashAltIcon } from '@patternfly/react-icons';
import { I18n, i18nMark } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import styled from 'styled-components';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import AlertModal from '../AlertModal';
import { pluralize } from '../../util/strings';
@ -89,103 +89,95 @@ class ToolbarDeleteButton extends React.Component {
}
renderTooltip () {
const { itemsToDelete, itemName } = this.props;
const { itemsToDelete, itemName, i18n } = this.props;
const itemsUnableToDelete = itemsToDelete
.filter(cannotDelete)
.map(item => (
<div key={item.id}>
{item.name}
</div>
));
if (itemsToDelete.some(cannotDelete)) {
return (
<div>
<Trans>
You dont have permission to delete the following
{' '}
{pluralize(itemName)}
:
</Trans>
{itemsToDelete
.filter(cannotDelete)
.map(item => (
<div key={item.id}>
{item.name}
</div>
))
}
{i18n._(t`You do not have permission to delete the following ${pluralize(itemName)}: ${itemsUnableToDelete}`)}
</div>
);
}
if (itemsToDelete.length) {
return i18nMark('Delete');
return i18n._(t`Delete`);
}
return i18nMark('Select a row to delete');
return i18n._(t`Select a row to delete`);
}
render () {
const { itemsToDelete, itemName } = this.props;
const { itemsToDelete, itemName, i18n } = this.props;
const { isModalOpen } = this.state;
const isDisabled = itemsToDelete.length === 0
|| itemsToDelete.some(cannotDelete);
return (
<I18n>
{({ i18n }) => (
<Fragment>
<Tooltip
content={this.renderTooltip()}
position="left"
>
<Fragment>
<Tooltip
content={this.renderTooltip()}
position="left"
>
<Button
className="awx-ToolBarBtn"
variant="plain"
aria-label={i18n._(t`Delete`)}
onClick={this.handleConfirmDelete}
isDisabled={isDisabled}
>
<TrashAltIcon className="awx-ToolBarTrashCanIcon" />
</Button>
</Tooltip>
{ isModalOpen && (
<AlertModal
variant="danger"
title={itemsToDelete === 1
? i18n._(t`Delete ${itemName}`)
: i18n._(t`Delete ${pluralize(itemName)}`)
}
isOpen={isModalOpen}
onClose={this.handleCancelDelete}
actions={[
<Button
variant="plain"
aria-label={i18n._(t`Delete`)}
onClick={this.handleConfirmDelete}
isDisabled={isDisabled}
>
<TrashAltIcon />
</Button>
</Tooltip>
{ isModalOpen && (
<AlertModal
key="delete"
variant="danger"
title={itemsToDelete === 1
? i18n._(t`Delete ${itemName}`)
: i18n._(t`Delete ${pluralize(itemName)}`)
}
isOpen={isModalOpen}
onClose={this.handleCancelDelete}
actions={[
<Button
key="delete"
variant="danger"
aria-label={i18n._(t`confirm delete`)}
onClick={this.handleDelete}
>
{i18n._(t`Delete`)}
</Button>,
<Button
key="cancel"
variant="secondary"
aria-label={i18n._(t`cancel delete`)}
onClick={this.handleCancelDelete}
>
{i18n._(t`Cancel`)}
</Button>
]}
aria-label={i18n._(t`confirm delete`)}
onClick={this.handleDelete}
>
{i18n._(t`Are you sure you want to delete:`)}
{i18n._(t`Delete`)}
</Button>,
<Button
key="cancel"
variant="secondary"
aria-label={i18n._(t`cancel delete`)}
onClick={this.handleCancelDelete}
>
{i18n._(t`Cancel`)}
</Button>
]}
>
{i18n._(t`Are you sure you want to delete:`)}
<br />
{itemsToDelete.map((item) => (
<span key={item.id}>
<strong>
{item.name}
</strong>
<br />
{itemsToDelete.map((item) => (
<span key={item.id}>
<strong>
{item.name}
</strong>
<br />
</span>
))}
<br />
</AlertModal>
)}
</Fragment>
</span>
))}
<br />
</AlertModal>
)}
</I18n>
</Fragment>
);
}
}
export default ToolbarDeleteButton;
export default withI18n()(ToolbarDeleteButton);

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Button,
Dropdown,
@ -106,7 +106,8 @@ class Pagination extends Component {
page_size,
pageSizeOptions,
showPageSizeOptions,
style
style,
i18n
} = this.props;
const { value, isOpen } = this.state;
let opts = [];
@ -135,98 +136,91 @@ class Pagination extends Component {
));
return (
<I18n>
{({ i18n }) => (
<div className="awx-pagination" style={style}>
{showPageSizeOptions && (
<div className="awx-pagination__page-size-selection">
<Trans>Items Per Page</Trans>
<Dropdown
<div className="awx-pagination" style={style}>
{showPageSizeOptions && (
<div className="awx-pagination__page-size-selection">
{i18n._(t`Items Per Page`)}
<Dropdown
onToggle={this.onTogglePageSize}
onSelect={this.onSelectPageSize}
direction={up}
isOpen={isOpen}
toggle={(
<DropdownToggle
className="togglePageSize"
onToggle={this.onTogglePageSize}
onSelect={this.onSelectPageSize}
direction={up}
isOpen={isOpen}
toggle={(
<DropdownToggle
className="togglePageSize"
onToggle={this.onTogglePageSize}
>
{page_size}
</DropdownToggle>
)}
dropdownItems={dropdownItems}
/>
</div>
)}
<div className="awx-pagination__counts">
<div className="awx-pagination__item-count">
<Trans>{`Items ${itemMin} ${itemMax} of ${count}`}</Trans>
</div>
{pageCount !== 1 && (
<div className="awx-pagination__page-count">
<div className="pf-c-input-group pf-m-previous">
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`First`)}
isDisabled={isOnFirst}
onClick={this.onFirst}
>
<i className="fas fa-angle-double-left" />
</Button>
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`Previous`)}
isDisabled={isOnFirst}
onClick={this.onPrevious}
>
<i className="fas fa-angle-left" />
</Button>
</div>
<form
className="awx-pagination__page-input-form"
onSubmit={this.onSubmit}
>
<Trans>
{'Page '}
<TextInput
className="awx-pagination__page-input"
aria-label={i18n._(t`Page Number`)}
value={value}
type="text"
onChange={this.onPageChange}
/>
{' of '}
{pageCount}
</Trans>
</form>
<div className="pf-c-input-group">
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`Next`)}
isDisabled={isOnLast}
onClick={this.onNext}
>
<i className="fas fa-angle-right" />
</Button>
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`Last`)}
isDisabled={isOnLast}
onClick={this.onLast}
>
<i className="fas fa-angle-double-right" />
</Button>
</div>
</div>
>
{page_size}
</DropdownToggle>
)}
</div>
dropdownItems={dropdownItems}
/>
</div>
)}
</I18n>
<div className="awx-pagination__counts">
<div className="awx-pagination__item-count">
{i18n._(t`Items ${itemMin} ${itemMax} of ${count}`)}
</div>
{pageCount !== 1 && (
<div className="awx-pagination__page-count">
<div className="pf-c-input-group pf-m-previous">
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`First`)}
isDisabled={isOnFirst}
onClick={this.onFirst}
>
<i className="fas fa-angle-double-left" />
</Button>
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`Previous`)}
isDisabled={isOnFirst}
onClick={this.onPrevious}
>
<i className="fas fa-angle-left" />
</Button>
</div>
<form
className="awx-pagination__page-input-form"
onSubmit={this.onSubmit}
>
{i18n._(t`Page `)}
<TextInput
className="awx-pagination__page-input"
aria-label={i18n._(t`Page Number`)}
value={value}
type="text"
onChange={this.onPageChange}
/>
{i18n._(t` of ${pageCount}`)}
</form>
<div className="pf-c-input-group">
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`Next`)}
isDisabled={isOnLast}
onClick={this.onNext}
>
<i className="fas fa-angle-right" />
</Button>
<Button
className="awx-pagination__page-button"
variant="tertiary"
aria-label={i18n._(t`Last`)}
isDisabled={isOnLast}
onClick={this.onLast}
>
<i className="fas fa-angle-double-right" />
</Button>
</div>
</div>
)}
</div>
</div>
);
}
}
@ -248,4 +242,4 @@ Pagination.defaultProps = {
showPageSizeOptions: true
};
export default Pagination;
export default withI18n()(Pagination);

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Button as PFButton,
@ -90,7 +90,8 @@ class Search extends React.Component {
render () {
const { up } = DropdownPosition;
const {
columns
columns,
i18n
} = this.props;
const {
isSearchDropdownOpen,
@ -109,41 +110,37 @@ class Search extends React.Component {
));
return (
<I18n>
{({ i18n }) => (
<div className="pf-c-input-group">
<Dropdown
<div className="pf-c-input-group">
<Dropdown
onToggle={this.handleDropdownToggle}
onSelect={this.handleDropdownSelect}
direction={up}
isOpen={isSearchDropdownOpen}
toggle={(
<DropdownToggle
id="awx-search"
onToggle={this.handleDropdownToggle}
onSelect={this.handleDropdownSelect}
direction={up}
isOpen={isSearchDropdownOpen}
toggle={(
<DropdownToggle
id="awx-search"
onToggle={this.handleDropdownToggle}
>
{searchColumnName}
</DropdownToggle>
)}
dropdownItems={searchDropdownItems}
/>
<TextInput
type="search"
aria-label="Search text input"
value={searchValue}
onChange={this.handleSearchInputChange}
style={{ height: '30px' }}
/>
<Button
variant="tertiary"
aria-label={i18n._(t`Search`)}
onClick={this.handleSearch}
>
<SearchIcon />
</Button>
</div>
)}
</I18n>
{searchColumnName}
</DropdownToggle>
)}
dropdownItems={searchDropdownItems}
/>
<TextInput
type="search"
aria-label={i18n._(t`Search text input`)}
value={searchValue}
onChange={this.handleSearchInputChange}
style={{ height: '30px' }}
/>
<Button
variant="tertiary"
aria-label={i18n._(t`Search`)}
onClick={this.handleSearch}
>
<SearchIcon />
</Button>
</div>
);
}
}
@ -159,4 +156,4 @@ Search.defaultProps = {
sortedColumnKey: 'name'
};
export default Search;
export default withI18n()(Search);

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Button,
@ -85,7 +85,8 @@ class Sort extends React.Component {
const {
columns,
sortedColumnKey,
sortOrder
sortOrder,
i18n
} = this.props;
const {
isSortDropdownOpen
@ -109,40 +110,36 @@ class Sort extends React.Component {
}
return (
<I18n>
{({ i18n }) => (
<React.Fragment>
{ sortDropdownItems.length > 1 && (
<Dropdown
style={{ marginRight: '20px' }}
<React.Fragment>
{ sortDropdownItems.length > 1 && (
<Dropdown
style={{ marginRight: '20px' }}
onToggle={this.handleDropdownToggle}
onSelect={this.handleDropdownSelect}
direction={up}
isOpen={isSortDropdownOpen}
toggle={(
<DropdownToggle
id="awx-sort"
onToggle={this.handleDropdownToggle}
onSelect={this.handleDropdownSelect}
direction={up}
isOpen={isSortDropdownOpen}
toggle={(
<DropdownToggle
id="awx-sort"
onToggle={this.handleDropdownToggle}
>
{sortedColumnName}
</DropdownToggle>
)}
dropdownItems={sortDropdownItems}
/>
>
{sortedColumnName}
</DropdownToggle>
)}
<Button
onClick={this.handleSort}
variant="plain"
aria-label={i18n._(t`Sort`)}
css="padding: 0;"
>
<IconWrapper>
<SortIcon />
</IconWrapper>
</Button>
</React.Fragment>
dropdownItems={sortDropdownItems}
/>
)}
</I18n>
<Button
onClick={this.handleSort}
variant="plain"
aria-label={i18n._(t`Sort`)}
css="padding: 0;"
>
<IconWrapper>
<SortIcon />
</IconWrapper>
</Button>
</React.Fragment>
);
}
}
@ -160,4 +157,4 @@ Sort.defaultProps = {
sortedColumnKey: 'name'
};
export default Sort;
export default withI18n()(Sort);

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Brand } from '@patternfly/react-core';
@ -34,6 +34,7 @@ class TowerLogo extends Component {
render () {
const { hover } = this.state;
const { i18n } = this.props;
let src = TowerLogoHeader;
@ -42,19 +43,15 @@ class TowerLogo extends Component {
}
return (
<I18n>
{({ i18n }) => (
<Brand
src={src}
alt={i18n._(t`Tower Brand Image`)}
onMouseOut={this.onHover}
onMouseOver={this.onHover}
onBlur={this.onHover}
onFocus={this.onHover}
onClick={this.onClick}
/>
)}
</I18n>
<Brand
src={src}
alt={i18n._(t`Tower Brand Image`)}
onMouseOut={this.onHover}
onMouseOver={this.onHover}
onBlur={this.onHover}
onFocus={this.onHover}
onClick={this.onClick}
/>
);
}
}
@ -67,4 +64,4 @@ TowerLogo.defaultProps = {
linkTo: null,
};
export default withRouter(TowerLogo);
export default withI18n()(withRouter(TowerLogo));

View File

@ -4,7 +4,8 @@ import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { withRootDialog } from './RootDialog';
@ -33,27 +34,27 @@ class Provider extends Component {
}
handle401 () {
const { handle401, history, setRootDialogMessage } = this.props;
const { handle401, history, setRootDialogMessage, i18n } = this.props;
if (handle401) {
handle401();
return;
}
history.replace('/login');
setRootDialogMessage({
bodyText: i18nMark('You have been logged out.')
bodyText: i18n._(t`You have been logged out.`)
});
}
handle404 () {
const { handle404, history, setRootDialogMessage } = this.props;
const { handle404, history, setRootDialogMessage, i18n } = this.props;
if (handle404) {
handle404();
return;
}
history.replace('/home');
setRootDialogMessage({
title: i18nMark('404'),
bodyText: i18nMark('Cannot find resource.'),
title: i18n._(t`404`),
bodyText: i18n._(t`Cannot find resource.`),
variant: 'warning'
});
}
@ -72,7 +73,7 @@ class Provider extends Component {
}
export { Provider as _NetworkProvider };
export const NetworkProvider = withRootDialog(withRouter(Provider));
export const NetworkProvider = withI18n()(withRootDialog(withRouter(Provider)));
export function withNetwork (Child) {
return (props) => (

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Applications extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Applications</Trans>
{i18n._(t`Applications`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Applications extends Component {
}
}
export default Applications;
export default withI18n()(Applications);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class AuthSettings extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Authentication Settings</Trans>
{i18n._(t`Authentication Settings`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class AuthSettings extends Component {
}
}
export default AuthSettings;
export default withI18n()(AuthSettings);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class CredentialTypes extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Credential Types</Trans>
{i18n._(t`Credential Types`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class CredentialTypes extends Component {
}
}
export default CredentialTypes;
export default withI18n()(CredentialTypes);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Credentials extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Credentials</Trans>
{i18n._(t`Credentials`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Credentials extends Component {
}
}
export default Credentials;
export default withI18n()(Credentials);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Dashboard extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Dashboard</Trans>
{i18n._(t`Dashboard`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Dashboard extends Component {
}
}
export default Dashboard;
export default withI18n()(Dashboard);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class InstanceGroups extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Instance Groups</Trans>
{i18n._(t`Instance Groups`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class InstanceGroups extends Component {
}
}
export default InstanceGroups;
export default withI18n()(InstanceGroups);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Inventories extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Inventories</Trans>
{i18n._(t`Inventories`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Inventories extends Component {
}
}
export default Inventories;
export default withI18n()(Inventories);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class InventoryScripts extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Inventory Scripts</Trans>
{i18n._(t`Inventory Scripts`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class InventoryScripts extends Component {
}
}
export default InventoryScripts;
export default withI18n()(InventoryScripts);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Jobs extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Jobs</Trans>
{i18n._(t`Jobs`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Jobs extends Component {
}
}
export default Jobs;
export default withI18n()(Jobs);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class JobsSettings extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Jobs Settings</Trans>
{i18n._(t`Jobs Settings`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class JobsSettings extends Component {
}
}
export default JobsSettings;
export default withI18n()(JobsSettings);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class License extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>License</Trans>
{i18n._(t`License`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class License extends Component {
}
}
export default License;
export default withI18n()(License);

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Redirect, withRouter } from 'react-router-dom';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
LoginForm,
@ -62,7 +62,7 @@ class AWXLogin extends Component {
render () {
const { username, password, isInputValid, isAuthenticated } = this.state;
const { alt, loginInfo, logo, bodyText: errorMessage } = this.props;
const { alt, loginInfo, logo, bodyText: errorMessage, i18n } = this.props;
const logoSrc = logo ? `data:image/jpeg;${logo}` : towerLogo;
if (isAuthenticated) {
@ -70,34 +70,30 @@ class AWXLogin extends Component {
}
return (
<I18n>
{({ i18n }) => (
<LoginPage
brandImgSrc={logoSrc}
brandImgAlt={alt || 'Ansible Tower'}
loginTitle={i18n._(t`Welcome to Ansible Tower! Please Sign In.`)}
textContent={loginInfo}
>
<LoginForm
className={errorMessage && 'pf-m-error'}
usernameLabel={i18n._(t`Username`)}
passwordLabel={i18n._(t`Password`)}
showHelperText={!isInputValid || !!errorMessage}
helperText={errorMessage || i18n._(t`Invalid username or password. Please try again.`)}
usernameValue={username}
passwordValue={password}
isValidUsername={isInputValid}
isValidPassword={isInputValid}
onChangeUsername={this.onChangeUsername}
onChangePassword={this.onChangePassword}
onLoginButtonClick={this.onLoginButtonClick}
/>
</LoginPage>
)}
</I18n>
<LoginPage
brandImgSrc={logoSrc}
brandImgAlt={alt || 'Ansible Tower'}
loginTitle={i18n._(t`Welcome to Ansible Tower! Please Sign In.`)}
textContent={loginInfo}
>
<LoginForm
className={errorMessage && 'pf-m-error'}
usernameLabel={i18n._(t`Username`)}
passwordLabel={i18n._(t`Password`)}
showHelperText={!isInputValid || !!errorMessage}
helperText={errorMessage || i18n._(t`Invalid username or password. Please try again.`)}
usernameValue={username}
passwordValue={password}
isValidUsername={isInputValid}
isValidPassword={isInputValid}
onChangeUsername={this.onChangeUsername}
onChangePassword={this.onChangePassword}
onLoginButtonClick={this.onLoginButtonClick}
/>
</LoginPage>
);
}
}
export { AWXLogin as _AWXLogin };
export default withNetwork(withRootDialog(withRouter(AWXLogin)));
export default withI18n()(withNetwork(withRootDialog(withRouter(AWXLogin))));

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class ManagementJobs extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Management Jobs</Trans>
{i18n._(t`Management Jobs`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class ManagementJobs extends Component {
}
}
export default ManagementJobs;
export default withI18n()(ManagementJobs);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class NotificationTemplates extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Notification Templates</Trans>
{i18n._(t`Notification Templates`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class NotificationTemplates extends Component {
}
}
export default NotificationTemplates;
export default withI18n()(NotificationTemplates);

View File

@ -1,7 +1,7 @@
import React, { Component, Fragment } from 'react';
import { Route, withRouter, Switch } from 'react-router-dom';
import { i18nMark } from '@lingui/react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Config } from '../../contexts/Config';
import { NetworkProvider } from '../../contexts/Network';
@ -14,34 +14,42 @@ import OrganizationAdd from './screens/OrganizationAdd';
import Organization from './screens/Organization/Organization';
class Organizations extends Component {
state = {
breadcrumbConfig: {
'/organizations': i18nMark('Organizations'),
'/organizations/add': i18nMark('Create New Organization')
}
};
constructor (props) {
super(props);
const { i18n } = props;
this.state = {
breadcrumbConfig: {
'/organizations': i18n._(t`Organizations`),
'/organizations/add': i18n._(t`Create New Organization`)
}
};
}
setBreadcrumbConfig = (organization) => {
const { i18n } = this.props;
if (!organization) {
return;
}
const breadcrumbConfig = {
'/organizations': i18nMark('Organizations'),
'/organizations/add': i18nMark('Create New Organization'),
'/organizations': i18n._(t`Organizations`),
'/organizations/add': i18n._(t`Create New Organization`),
[`/organizations/${organization.id}`]: `${organization.name}`,
[`/organizations/${organization.id}/edit`]: i18nMark('Edit Details'),
[`/organizations/${organization.id}/details`]: i18nMark('Details'),
[`/organizations/${organization.id}/access`]: i18nMark('Access'),
[`/organizations/${organization.id}/teams`]: i18nMark('Teams'),
[`/organizations/${organization.id}/notifications`]: i18nMark('Notifications'),
[`/organizations/${organization.id}/edit`]: i18n._(t`Edit Details`),
[`/organizations/${organization.id}/details`]: i18n._(t`Details`),
[`/organizations/${organization.id}/access`]: i18n._(t`Access`),
[`/organizations/${organization.id}/teams`]: i18n._(t`Teams`),
[`/organizations/${organization.id}/notifications`]: i18n._(t`Notifications`),
};
this.setState({ breadcrumbConfig });
}
render () {
const { match, history, location, setRootDialogMessage } = this.props;
const { match, history, location, setRootDialogMessage, i18n } = this.props;
const { breadcrumbConfig } = this.state;
return (
@ -65,11 +73,11 @@ class Organizations extends Component {
setRootDialogMessage({
title: '404',
bodyText: (
<Trans>
Cannot find organization with ID
<Fragment>
{i18n._(t`Cannot find organization with ID`)}
<strong>{` ${newRouteMatch.params.id}`}</strong>
.
</Trans>
</Fragment>
),
variant: 'warning'
});
@ -101,4 +109,4 @@ class Organizations extends Component {
}
export { Organizations as _Organizations };
export default withRootDialog(withRouter(Organizations));
export default withI18n()(withRootDialog(withRouter(Organizations)));

View File

@ -1,8 +1,8 @@
import React from 'react';
import React, { Fragment } from 'react';
import { func, string } from 'prop-types';
import { Button } from '@patternfly/react-core';
import { I18n, i18nMark } from '@lingui/react';
import { t, Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import AlertModal from '../../../components/AlertModal';
import { Role } from '../../../types';
@ -24,57 +24,43 @@ class DeleteRoleConfirmationModal extends React.Component {
}
render () {
const { role, username, onCancel, onConfirm } = this.props;
const title = `Remove ${this.isTeamRole() ? 'Team' : 'User'} Access`;
const { role, username, onCancel, onConfirm, i18n } = this.props;
const title = i18n._(t`Remove ${this.isTeamRole() ? i18n._(t`Team`) : i18n._(t`User`)} Access`);
return (
<I18n>
{({ i18n }) => (
<AlertModal
<AlertModal
variant="danger"
title={title}
isOpen
onClose={onCancel}
actions={[
<Button
key="delete"
variant="danger"
title={i18nMark(title)}
isOpen
onClose={onCancel}
actions={[
<Button
key="delete"
variant="danger"
aria-label="Confirm delete"
onClick={onConfirm}
>
{i18n._(t`Delete`)}
</Button>,
<Button key="cancel" variant="secondary" onClick={onCancel}>
{i18n._(t`Cancel`)}
</Button>
]}
aria-label="Confirm delete"
onClick={onConfirm}
>
{this.isTeamRole() ? (
<Trans>
Are you sure you want to remove
<b>{` ${role.name} `}</b>
access from
<b>{` ${role.team_name}`}</b>
? Doing so affects all members of the team.
<br />
<br />
If you
<b><i> only </i></b>
want to remove access for this particular user, please remove them from the team.
</Trans>
) : (
<Trans>
Are you sure you want to remove
<b>{` ${role.name} `}</b>
access from
<b>{` ${username}`}</b>
?
</Trans>
)}
</AlertModal>
{i18n._(t`Delete`)}
</Button>,
<Button key="cancel" variant="secondary" onClick={onCancel}>
{i18n._(t`Cancel`)}
</Button>
]}
>
{this.isTeamRole() ? (
<Fragment>
{i18n._(t`Are you sure you want to remove ${role.name} access from ${role.team_name}? Doing so affects all members of the team.`)}
<br />
<br />
{i18n._(t`If you ${(<b><i>only</i></b>)} want to remove access for this particular user, please remove them from the team.`)}
</Fragment>
) : (
<Fragment>
{i18n._(t`Are you sure you want to remove ${role.name} access from ${username}?`)}
</Fragment>
)}
</I18n>
</AlertModal>
);
}
}
export default DeleteRoleConfirmationModal;
export default withI18n()(DeleteRoleConfirmationModal);

View File

@ -1,20 +1,14 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { I18n, i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { FormGroup, Tooltip } from '@patternfly/react-core';
import { QuestionCircleIcon } from '@patternfly/react-icons';
import { t } from '@lingui/macro';
import Lookup from '../../../components/Lookup';
import { withNetwork } from '../../../contexts/Network';
const INSTANCE_GROUPS_LOOKUP_COLUMNS = [
{ name: i18nMark('Name'), key: 'name', isSortable: true },
{ name: i18nMark('Modified'), key: 'modified', isSortable: false, isNumeric: true },
{ name: i18nMark('Created'), key: 'created', isSortable: false, isNumeric: true }
];
class InstanceGroupsLookup extends React.Component {
constructor (props) {
super(props);
@ -29,43 +23,43 @@ class InstanceGroupsLookup extends React.Component {
}
render () {
const { value, tooltip, onChange } = this.props;
const { value, tooltip, onChange, i18n } = this.props;
return (
<I18n>
{({ i18n }) => (
<FormGroup
label={(
<Fragment>
{i18n._(t`Instance Groups`)}
{' '}
{
tooltip && (
<Tooltip
position="right"
content={tooltip}
>
<QuestionCircleIcon />
</Tooltip>
)
}
</Fragment>
)}
fieldId="org-instance-groups"
>
<Lookup
id="org-instance-groups"
lookupHeader={i18n._(t`Instance Groups`)}
name="instanceGroups"
value={value}
onLookupSave={onChange}
getItems={this.getInstanceGroups}
columns={INSTANCE_GROUPS_LOOKUP_COLUMNS}
sortedColumnKey="name"
/>
</FormGroup>
<FormGroup
label={(
<Fragment>
{i18n._(t`Instance Groups`)}
{' '}
{
tooltip && (
<Tooltip
position="right"
content={tooltip}
>
<QuestionCircleIcon />
</Tooltip>
)
}
</Fragment>
)}
</I18n>
fieldId="org-instance-groups"
>
<Lookup
id="org-instance-groups"
lookupHeader={i18n._(t`Instance Groups`)}
name="instanceGroups"
value={value}
onLookupSave={onChange}
getItems={this.getInstanceGroups}
columns={[
{ name: i18n._(t`Name`), key: 'name', isSortable: true },
{ name: i18n._(t`Modified`), key: 'modified', isSortable: false, isNumeric: true },
{ name: i18n._(t`Created`), key: 'created', isSortable: false, isNumeric: true }
]}
sortedColumnKey="name"
/>
</FormGroup>
);
}
}
@ -80,4 +74,4 @@ InstanceGroupsLookup.defaultProps = {
tooltip: '',
};
export default withNetwork(InstanceGroupsLookup);
export default withI18n()(withNetwork(InstanceGroupsLookup));

View File

@ -1,6 +1,6 @@
import React from 'react';
import { func } from 'prop-types';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
DataListItem,
@ -81,96 +81,92 @@ class OrganizationAccessItem extends React.Component {
}
render () {
const { accessRecord, onRoleDelete } = this.props;
const { accessRecord, onRoleDelete, i18n } = this.props;
const [teamRoles, userRoles] = this.getRoleLists();
return (
<I18n>
{({ i18n }) => (
<DataListItem aria-labelledby="access-list-item" key={accessRecord.id}>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="name">
{accessRecord.username && (
<TextContent style={detailWrapperStyle}>
{accessRecord.url ? (
<Link to={{ pathname: accessRecord.url }}>
<Text component={TextVariants.h6} style={detailLabelStyle}>
{accessRecord.username}
</Text>
</Link>
) : (
<Text component={TextVariants.h6} style={detailLabelStyle}>
{accessRecord.username}
</Text>
)}
</TextContent>
)}
{accessRecord.first_name || accessRecord.last_name ? (
<Detail
label={i18n._(t`Name`)}
value={`${accessRecord.first_name} ${accessRecord.last_name}`}
url={null}
customStyles={null}
/>
<DataListItem aria-labelledby="access-list-item" key={accessRecord.id}>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="name">
{accessRecord.username && (
<TextContent style={detailWrapperStyle}>
{accessRecord.url ? (
<Link to={{ pathname: accessRecord.url }}>
<Text component={TextVariants.h6} style={detailLabelStyle}>
{accessRecord.username}
</Text>
</Link>
) : (
null
<Text component={TextVariants.h6} style={detailLabelStyle}>
{accessRecord.username}
</Text>
)}
</DataListCell>,
<DataListCell key="roles">
{userRoles.length > 0 && (
<ul style={userRolesWrapperStyle}>
<Text component={TextVariants.h6} style={detailLabelStyle}>
{i18n._(t`User Roles`)}
</Text>
{userRoles.map(role => (
role.user_capabilities.unattach ? (
<Chip
key={role.id}
className="awx-c-chip"
onClick={() => { onRoleDelete(role, accessRecord); }}
>
{role.name}
</Chip>
) : (
<BasicChip key={role.id}>
{role.name}
</BasicChip>
)
))}
</ul>
)}
{teamRoles.length > 0 && (
<ul style={userRolesWrapperStyle}>
<Text component={TextVariants.h6} style={detailLabelStyle}>
{i18n._(t`Team Roles`)}
</Text>
{teamRoles.map(role => (
role.user_capabilities.unattach ? (
<Chip
key={role.id}
className="awx-c-chip"
onClick={() => { onRoleDelete(role, accessRecord); }}
>
{role.name}
</Chip>
) : (
<BasicChip key={role.id}>
{role.name}
</BasicChip>
)
))}
</ul>
)}
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
)}
</I18n>
</TextContent>
)}
{accessRecord.first_name || accessRecord.last_name ? (
<Detail
label={i18n._(t`Name`)}
value={`${accessRecord.first_name} ${accessRecord.last_name}`}
url={null}
customStyles={null}
/>
) : (
null
)}
</DataListCell>,
<DataListCell key="roles">
{userRoles.length > 0 && (
<ul style={userRolesWrapperStyle}>
<Text component={TextVariants.h6} style={detailLabelStyle}>
{i18n._(t`User Roles`)}
</Text>
{userRoles.map(role => (
role.user_capabilities.unattach ? (
<Chip
key={role.id}
className="awx-c-chip"
onClick={() => { onRoleDelete(role, accessRecord); }}
>
{role.name}
</Chip>
) : (
<BasicChip key={role.id}>
{role.name}
</BasicChip>
)
))}
</ul>
)}
{teamRoles.length > 0 && (
<ul style={userRolesWrapperStyle}>
<Text component={TextVariants.h6} style={detailLabelStyle}>
{i18n._(t`Team Roles`)}
</Text>
{teamRoles.map(role => (
role.user_capabilities.unattach ? (
<Chip
key={role.id}
className="awx-c-chip"
onClick={() => { onRoleDelete(role, accessRecord); }}
>
{role.name}
</Chip>
) : (
<BasicChip key={role.id}>
{role.name}
</BasicChip>
)
))}
</ul>
)}
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
);
}
}
export default OrganizationAccessItem;
export default withI18n()(OrganizationAccessItem);

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { Formik, Field } from 'formik';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Form,
@ -83,76 +83,72 @@ class OrganizationForm extends Component {
}
render () {
const { organization, handleCancel } = this.props;
const { organization, handleCancel, i18n } = this.props;
const { instanceGroups, formIsValid, error } = this.state;
const defaultVenv = '/venv/ansible/';
return (
<I18n>
{({ i18n }) => (
<Formik
initialValues={{
name: organization.name,
description: organization.description,
custom_virtualenv: organization.custom_virtualenv || '',
}}
onSubmit={this.handleSubmit}
render={formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow>
<FormField
id="org-name"
name="name"
type="text"
label={i18n._(t`Name`)}
validate={required()}
isRequired
/>
<FormField
id="org-description"
name="description"
type="text"
label={i18n._(t`Description`)}
/>
<Config>
{({ custom_virtualenvs }) => (
custom_virtualenvs && custom_virtualenvs.length > 1 && (
<Field
name="custom_virtualenv"
render={({ field }) => (
<FormGroup
fieldId="org-custom-virtualenv"
label={i18n._(t`Ansible Environment`)}
>
<AnsibleSelect
data={custom_virtualenvs}
defaultSelected={defaultVenv}
label={i18n._(t`Ansible Environment`)}
{...field}
/>
</FormGroup>
)}
/>
)
)}
</Config>
</FormRow>
<InstanceGroupsLookup
value={instanceGroups}
onChange={this.handleInstanceGroupsChange}
tooltip={i18n._(t`Select the Instance Groups for this Organization to run on.`)}
/>
<FormActionGroup
onCancel={handleCancel}
onSubmit={formik.handleSubmit}
submitDisabled={!formIsValid}
/>
{error ? <div>error</div> : null}
</Form>
)}
/>
<Formik
initialValues={{
name: organization.name,
description: organization.description,
custom_virtualenv: organization.custom_virtualenv || '',
}}
onSubmit={this.handleSubmit}
render={formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow>
<FormField
id="org-name"
name="name"
type="text"
label={i18n._(t`Name`)}
validate={required(null, i18n)}
isRequired
/>
<FormField
id="org-description"
name="description"
type="text"
label={i18n._(t`Description`)}
/>
<Config>
{({ custom_virtualenvs }) => (
custom_virtualenvs && custom_virtualenvs.length > 1 && (
<Field
name="custom_virtualenv"
render={({ field }) => (
<FormGroup
fieldId="org-custom-virtualenv"
label={i18n._(t`Ansible Environment`)}
>
<AnsibleSelect
data={custom_virtualenvs}
defaultSelected={defaultVenv}
label={i18n._(t`Ansible Environment`)}
{...field}
/>
</FormGroup>
)}
/>
)
)}
</Config>
</FormRow>
<InstanceGroupsLookup
value={instanceGroups}
onChange={this.handleInstanceGroupsChange}
tooltip={i18n._(t`Select the Instance Groups for this Organization to run on.`)}
/>
<FormActionGroup
onCancel={handleCancel}
onSubmit={formik.handleSubmit}
submitDisabled={!formIsValid}
/>
{error ? <div>error</div> : null}
</Form>
)}
</I18n>
/>
);
}
}
@ -176,4 +172,4 @@ OrganizationForm.contextTypes = {
};
export { OrganizationForm as _OrganizationForm };
export default withNetwork(withRouter(OrganizationForm));
export default withI18n()(withNetwork(withRouter(OrganizationForm)));

View File

@ -1,6 +1,7 @@
import React from 'react';
import { string, bool, func } from 'prop-types';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Badge as PFBadge,
DataListItem,
@ -62,6 +63,7 @@ class OrganizationListItem extends React.Component {
isSelected,
onSelect,
detailUrl,
i18n
} = this.props;
const labelId = `check-action-${organization.id}`;
return (
@ -86,13 +88,15 @@ class OrganizationListItem extends React.Component {
</DataListCell>,
<DataListCell key="org-members" righthalf="true" width={2}>
<ListGroup>
<Trans>Members</Trans>
{i18n._(t`Members`)}
<Badge isRead>
{organization.summary_fields.related_field_counts.users}
</Badge>
</ListGroup>
</DataListCell>,
<DataListCell key="teams">
<ListGroup>
<Trans>Teams</Trans>
{i18n._(t`Teams`)}
<Badge isRead>
{organization.summary_fields.related_field_counts.teams}
</Badge>
@ -105,4 +109,4 @@ class OrganizationListItem extends React.Component {
);
}
}
export default OrganizationListItem;
export default withI18n()(OrganizationListItem);

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { I18n, i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Switch, Route, withRouter, Redirect } from 'react-router-dom';
import { Card, CardHeader, PageSection } from '@patternfly/react-core';
@ -100,7 +100,8 @@ class Organization extends Component {
location,
match,
me,
history
history,
i18n
} = this.props;
const {
@ -126,14 +127,14 @@ class Organization extends Component {
);
const tabsArray = [
{ name: i18nMark('Details'), link: `${match.url}/details`, id: 0 },
{ name: i18nMark('Access'), link: `${match.url}/access`, id: 1 },
{ name: i18nMark('Teams'), link: `${match.url}/teams`, id: 2 }
{ name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 },
{ name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 },
{ name: i18n._(t`Teams`), link: `${match.url}/teams`, id: 2 }
];
if (canSeeNotificationsTab) {
tabsArray.push({
name: i18nMark('Notifications'),
name: i18n._(t`Notifications`),
link: `${match.url}/notifications`,
id: 3
});
@ -145,24 +146,18 @@ class Organization extends Component {
<CardHeader
style={tabsStyle}
>
<I18n>
{({ i18n }) => (
<React.Fragment>
<div className="awx-orgTabs-container">
<RoutedTabs
match={match}
history={history}
labeltext={i18n._(t`Organization detail tabs`)}
tabsArray={tabsArray}
/>
<CardCloseButton linkTo="/organizations" />
<div
className="awx-orgTabs__bottom-border"
/>
</div>
</React.Fragment>
)}
</I18n>
<div className="awx-orgTabs-container">
<RoutedTabs
match={match}
history={history}
labeltext={i18n._(t`Organization detail tabs`)}
tabsArray={tabsArray}
/>
<CardCloseButton linkTo="/organizations" />
<div
className="awx-orgTabs__bottom-border"
/>
</div>
</CardHeader>
));
if (!match) {
@ -245,5 +240,5 @@ class Organization extends Component {
);
}
}
export default withNetwork(withRouter(Organization));
export default withI18n()(withNetwork(withRouter(Organization)));
export { Organization as _Organization };

View File

@ -1,6 +1,7 @@
import React, { Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import PaginatedDataList, { ToolbarAddButton } from '../../../../components/PaginatedDataList';
import OrganizationAccessItem from '../../components/OrganizationAccessItem';
import DeleteRoleConfirmationModal from '../../components/DeleteRoleConfirmationModal';
@ -130,7 +131,7 @@ class OrganizationAccess extends React.Component {
}
render () {
const { api, organization } = this.props;
const { organization, i18n } = this.props;
const {
isLoading,
isInitialized,
@ -167,9 +168,9 @@ class OrganizationAccess extends React.Component {
itemName="role"
qsConfig={QS_CONFIG}
toolbarColumns={[
{ name: i18nMark('Name'), key: 'first_name', isSortable: true },
{ name: i18nMark('Username'), key: 'username', isSortable: true },
{ name: i18nMark('Last Name'), key: 'last_name', isSortable: true },
{ name: i18n._(t`Name`), key: 'first_name', isSortable: true },
{ name: i18n._(t`Username`), key: 'username', isSortable: true },
{ name: i18n._(t`Last Name`), key: 'last_name', isSortable: true },
]}
additionalControls={canEdit ? [
<ToolbarAddButton key="add" onClick={this.toggleAddModal} />
@ -187,7 +188,6 @@ class OrganizationAccess extends React.Component {
<AddResourceRole
onClose={this.toggleAddModal}
onSave={this.handleSuccessfulRoleAdd}
api={api}
roles={organization.summary_fields.object_roles}
/>
)}
@ -197,4 +197,4 @@ class OrganizationAccess extends React.Component {
}
export { OrganizationAccess as _OrganizationAccess };
export default withNetwork(withRouter(OrganizationAccess));
export default withI18n()(withNetwork(withRouter(OrganizationAccess)));

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
CardBody,
@ -103,7 +103,8 @@ class OrganizationDetail extends Component {
modified,
summary_fields
},
match
match,
i18n
} = this.props;
const showOverflowChipAfter = 5;
@ -127,58 +128,54 @@ class OrganizationDetail extends Component {
) : null;
return (
<I18n>
{({ i18n }) => (
<CardBody>
<div className="pf-l-grid pf-m-gutter pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-4-col-on-xl">
<Detail
label={i18n._(t`Name`)}
value={name}
/>
<Detail
label={i18n._(t`Description`)}
value={description}
/>
<Detail
label={i18n._(t`Ansible Environment`)}
value={custom_virtualenv}
/>
<Detail
label={i18n._(t`Created`)}
value={created}
/>
<Detail
label={i18n._(t`Last Modified`)}
value={modified}
/>
{(instanceGroups && instanceGroups.length > 0) && (
<TextContent style={{ display: 'flex', gridColumn: '1 / -1' }}>
<Text
component={TextVariants.h6}
style={detailLabelStyle}
>
<Trans>Instance Groups</Trans>
</Text>
<div style={detailValueStyle}>
{instanceGroupChips}
{overflowChip}
</div>
</TextContent>
)}
</div>
{summary_fields.user_capabilities.edit && (
<div style={{ display: 'flex', flexDirection: 'row-reverse', marginTop: '20px' }}>
<Link to={`/organizations/${match.params.id}/edit`}>
<Button><Trans>Edit</Trans></Button>
</Link>
<CardBody>
<div className="pf-l-grid pf-m-gutter pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-4-col-on-xl">
<Detail
label={i18n._(t`Name`)}
value={name}
/>
<Detail
label={i18n._(t`Description`)}
value={description}
/>
<Detail
label={i18n._(t`Ansible Environment`)}
value={custom_virtualenv}
/>
<Detail
label={i18n._(t`Created`)}
value={created}
/>
<Detail
label={i18n._(t`Last Modified`)}
value={modified}
/>
{(instanceGroups && instanceGroups.length > 0) && (
<TextContent style={{ display: 'flex', gridColumn: '1 / -1' }}>
<Text
component={TextVariants.h6}
style={detailLabelStyle}
>
{i18n._(t`Instance Groups`)}
</Text>
<div style={detailValueStyle}>
{instanceGroupChips}
{overflowChip}
</div>
)}
{error ? 'error!' : ''}
</CardBody>
</TextContent>
)}
</div>
{summary_fields.user_capabilities.edit && (
<div style={{ display: 'flex', flexDirection: 'row-reverse', marginTop: '20px' }}>
<Link to={`/organizations/${match.params.id}/edit`}>
<Button>{i18n._(t`Edit`)}</Button>
</Link>
</div>
)}
</I18n>
{error ? 'error!' : ''}
</CardBody>
);
}
}
export default withRouter(withNetwork(OrganizationDetail));
export default withI18n()(withRouter(withNetwork(OrganizationDetail)));

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { I18n } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
@ -57,30 +57,27 @@ class OrganizationAdd extends React.Component {
render () {
const { error } = this.state;
const { i18n } = this.props;
return (
<PageSection>
<I18n>
{({ i18n }) => (
<Card>
<CardHeader className="at-u-textRight">
<Tooltip
content={i18n._(t`Close`)}
position="top"
>
<CardCloseButton onClick={this.handleCancel} />
</Tooltip>
</CardHeader>
<CardBody>
<OrganizationForm
handleSubmit={this.handleSubmit}
handleCancel={this.handleCancel}
/>
{error ? <div>error</div> : ''}
</CardBody>
</Card>
)}
</I18n>
<Card>
<CardHeader className="at-u-textRight">
<Tooltip
content={i18n._(t`Close`)}
position="top"
>
<CardCloseButton onClick={this.handleCancel} />
</Tooltip>
</CardHeader>
<CardBody>
<OrganizationForm
handleSubmit={this.handleSubmit}
handleCancel={this.handleCancel}
/>
{error ? <div>error</div> : ''}
</CardBody>
</Card>
</PageSection>
);
}
@ -95,4 +92,4 @@ OrganizationAdd.contextTypes = {
};
export { OrganizationAdd as _OrganizationAdd };
export default withNetwork(withRouter(OrganizationAdd));
export default withI18n()(withNetwork(withRouter(OrganizationAdd)));

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { i18nMark } from '@lingui/react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Card,
PageSection,
@ -15,12 +16,6 @@ import PaginatedDataList, {
import OrganizationListItem from '../components/OrganizationListItem';
import { getQSConfig, parseNamespacedQueryString } from '../../../util/qs';
const COLUMNS = [
{ name: i18nMark('Name'), key: 'name', isSortable: true },
{ name: i18nMark('Modified'), key: 'modified', isSortable: true, isNumeric: true },
{ name: i18nMark('Created'), key: 'created', isSortable: true, isNumeric: true },
];
const QS_CONFIG = getQSConfig('organization', {
page: 1,
page_size: 5,
@ -36,7 +31,7 @@ class OrganizationsList extends Component {
isLoading: true,
isInitialized: false,
organizations: [],
selected: [],
selected: []
};
this.handleSelectAll = this.handleSelectAll.bind(this);
@ -148,9 +143,9 @@ class OrganizationsList extends Component {
isLoading,
isInitialized,
selected,
organizations,
organizations
} = this.state;
const { match } = this.props;
const { match, i18n } = this.props;
const isAllSelected = selected.length === organizations.length;
@ -163,7 +158,11 @@ class OrganizationsList extends Component {
itemCount={itemCount}
itemName="organization"
qsConfig={QS_CONFIG}
toolbarColumns={COLUMNS}
toolbarColumns={[
{ name: i18n._(t`Name`), key: 'name', isSortable: true },
{ name: i18n._(t`Modified`), key: 'modified', isSortable: true, isNumeric: true },
{ name: i18n._(t`Created`), key: 'created', isSortable: true, isNumeric: true },
]}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={this.handleSelectAll}
@ -198,4 +197,4 @@ class OrganizationsList extends Component {
}
export { OrganizationsList as _OrganizationsList };
export default withNetwork(withRouter(OrganizationsList));
export default withI18n()(withNetwork(withRouter(OrganizationsList)));

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Portal extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>My View</Trans>
{i18n._(t`My View`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Portal extends Component {
}
}
export default Portal;
export default withI18n()(Portal);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Projects extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Projects</Trans>
{i18n._(t`Projects`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Projects extends Component {
}
}
export default Projects;
export default withI18n()(Projects);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Schedules extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Schedules</Trans>
{i18n._(t`Schedules`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Schedules extends Component {
}
}
export default Schedules;
export default withI18n()(Schedules);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class SystemSettings extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>System Settings</Trans>
{i18n._(t`System Settings`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class SystemSettings extends Component {
}
}
export default SystemSettings;
export default withI18n()(SystemSettings);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Teams extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Teams</Trans>
{i18n._(t`Teams`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Teams extends Component {
}
}
export default Teams;
export default withI18n()(Teams);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Templates extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Templates</Trans>
{i18n._(t`Templates`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Templates extends Component {
}
}
export default Templates;
export default withI18n()(Templates);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class UISettings extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>User Interface Settings</Trans>
{i18n._(t`User Interface Settings`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class UISettings extends Component {
}
}
export default UISettings;
export default withI18n()(UISettings);

View File

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react';
import { Trans } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
@ -8,13 +9,14 @@ import {
class Users extends Component {
render () {
const { i18n } = this.props;
const { light, medium } = PageSectionVariants;
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Users</Trans>
{i18n._(t`Users`)}
</Title>
</PageSection>
<PageSection variant={medium} />
@ -23,4 +25,4 @@ class Users extends Component {
}
}
export default Users;
export default withI18n()(Users);

View File

@ -1,20 +0,0 @@
import { i18nMark } from '@lingui/react';
export function required (message) {
return value => {
if (!value.trim()) {
return message || i18nMark('This field must not be blank');
}
return undefined;
};
}
export function maxLength (max) {
return value => {
if (value.trim().length
> max) {
return i18nMark(`This field must not exceed ${max} characters`);
}
return undefined;
};
}

20
src/util/validators.jsx Normal file
View File

@ -0,0 +1,20 @@
import { t } from '@lingui/macro';
export function required (message, i18n) {
return value => {
if (!value.trim()) {
return message || i18n._(t`This field must not be blank`);
}
return undefined;
};
}
export function maxLength (max, i18n) {
return value => {
if (value.trim().length
> max) {
return i18n._(t`This field must not exceed ${max} characters`);
}
return undefined;
};
}