Fix UserDateDetail translation

Add UserDateDetail to Org detail & InventoryGroupDetail
Add VariablesDetail to InventoryGroupDetail
This commit is contained in:
Keith Grant 2019-12-18 11:44:38 -08:00
parent 3d510c5064
commit 8ff0902177
8 changed files with 67 additions and 140 deletions

View File

@ -112,7 +112,7 @@ afterEach(() => {
...
```
**Test Attributes** -
**Test Attributes** -
It should be noted that the `dataCy` prop, as well as its equivalent attribute `data-cy`, are used as flags for any UI test that wants to avoid relying on brittle CSS selectors such as `nth-of-type()`.
## Handling API Errors
@ -296,7 +296,7 @@ The lingui library provides various React helpers for dealing with both marking
**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.
**Note:** We try to avoid 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).

View File

@ -14,6 +14,10 @@ function VariablesDetail({ value, label, rows }) {
const [currentValue, setCurrentValue] = useState(value);
const [error, setError] = useState(null);
if (!value) {
return null;
}
return (
<>
<DetailName

View File

@ -1,20 +1,25 @@
import React from 'react';
import { node, string } from 'prop-types';
import { Trans } from '@lingui/macro';
import { Link } from 'react-router-dom';
import { formatDateString } from '@util/dates';
import Detail from './Detail';
import { SummaryFieldUser } from '../../types';
function UserDateDetail({ label, date, user }) {
const dateStr = formatDateString(date);
const username = user ? user.username : '';
return (
<Detail
label={label}
value={
<span>
{formatDateString(date)}
{user && ' by '}
{user && <Link to={`/users/${user.id}`}>{user.username}</Link>}
</span>
user ? (
<Trans>
{dateStr} by <Link to={`/users/${user.id}`}>{username}</Link>
</Trans>
) : (
dateStr
)
}
/>
);

View File

@ -68,13 +68,11 @@ function InventoryDetail({ inventory, i18n }) {
</ChipGroup>
}
/>
{inventory.variables && (
<VariablesDetail
label={i18n._(t`Variables`)}
value={inventory.variables}
rows={4}
/>
)}
<VariablesDetail
label={i18n._(t`Variables`)}
value={inventory.variables}
rows={4}
/>
<UserDateDetail
label={i18n._(t`Created`)}
date={inventory.created}

View File

@ -1,7 +1,6 @@
import React, { useEffect, useState } from 'react';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { CardHeader } from '@patternfly/react-core';
import { Switch, Route, withRouter, Link, Redirect } from 'react-router-dom';
import { GroupsAPI } from '@api';
@ -9,6 +8,7 @@ import CardCloseButton from '@components/CardCloseButton';
import RoutedTabs from '@components/RoutedTabs';
import ContentError from '@components/ContentError';
import ContentLoading from '@components/ContentLoading';
import { TabbedCardHeader } from '@components/Card';
import InventoryGroupEdit from '../InventoryGroupEdit/InventoryGroupEdit';
import InventoryGroupDetail from '../InventoryGroupDetail/InventoryGroupDetail';
@ -97,12 +97,12 @@ function InventoryGroups({ i18n, match, setBreadcrumb, inventory, history }) {
!history.location.pathname.endsWith('edit')
) {
cardHeader = (
<CardHeader style={{ padding: 0 }}>
<TabbedCardHeader>
<RoutedTabs history={history} tabsArray={tabsArray} />
<CardCloseButton
linkTo={`/inventories/inventory/${inventory.id}/groups`}
/>
</CardHeader>
</TabbedCardHeader>
);
}
return (

View File

@ -3,23 +3,16 @@ import { t } from '@lingui/macro';
import { CardBody, Button } from '@patternfly/react-core';
import { withI18n } from '@lingui/react';
import { withRouter, Link } from 'react-router-dom';
import { withRouter } from 'react-router-dom';
import styled from 'styled-components';
import { VariablesInput as CodeMirrorInput } from '@components/CodeMirrorInput';
import { VariablesDetail } from '@components/CodeMirrorInput';
import ErrorDetail from '@components/ErrorDetail';
import AlertModal from '@components/AlertModal';
import { formatDateString } from '@util/dates';
import { GroupsAPI } from '@api';
import { DetailList, Detail } from '@components/DetailList';
import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
const VariablesInput = styled(CodeMirrorInput)`
.pf-c-form__label {
font-weight: 600;
font-size: 16px;
}
margin: 20px 0;
`;
// TODO: extract this into a component for use in all relevant Detail views
const ActionButtonWrapper = styled.div`
display: flex;
justify-content: flex-end;
@ -28,6 +21,7 @@ const ActionButtonWrapper = styled.div`
margin-left: 20px;
}
`;
function InventoryGroupDetail({ i18n, history, match, inventoryGroup }) {
const {
summary_fields: { created_by, modified_by },
@ -50,52 +44,26 @@ function InventoryGroupDetail({ i18n, history, match, inventoryGroup }) {
}
};
let createdBy = '';
if (created) {
if (created_by && created_by.username) {
createdBy = (
<span>
{i18n._(t`${formatDateString(inventoryGroup.created)} by`)}{' '}
<Link to={`/users/${created_by.id}`}>{created_by.username}</Link>
</span>
);
} else {
createdBy = formatDateString(inventoryGroup.created);
}
}
let modifiedBy = '';
if (modified) {
if (modified_by && modified_by.username) {
modifiedBy = (
<span>
{i18n._(t`${formatDateString(inventoryGroup.modified)} by`)}{' '}
<Link to={`/users/${modified_by.id}`}>{modified_by.username}</Link>
</span>
);
} else {
modifiedBy = formatDateString(inventoryGroup.modified);
}
}
return (
<CardBody css="padding-top: 20px">
<CardBody>
<DetailList gutter="sm">
<Detail label={i18n._(t`Name`)} value={name} />
<Detail label={i18n._(t`Description`)} value={description} />
</DetailList>
<VariablesInput
id="inventoryGroup-variables"
readOnly
value={variables}
rows={4}
label={i18n._(t`Variables`)}
/>
<DetailList>
{createdBy && <Detail label={i18n._(t`Created`)} value={createdBy} />}
{modifiedBy && (
<Detail label={i18n._(t`Modified`)} value={modifiedBy} />
)}
<VariablesDetail
label={i18n._(t`Variables`)}
value={variables}
rows={4}
/>
<UserDateDetail
label={i18n._(t`Created`)}
date={created}
user={created_by}
/>
<UserDateDetail
label={i18n._(t`Last Modified`)}
date={modified}
user={modified_by}
/>
</DetailList>
<ActionButtonWrapper>
<Button

View File

@ -4,10 +4,9 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import styled from 'styled-components';
import { Project } from '@types';
import { formatDateString } from '@util/dates';
import { Config } from '@contexts/Config';
import { Button, CardBody, List, ListItem } from '@patternfly/react-core';
import { DetailList, Detail } from '@components/DetailList';
import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
import { CredentialChip } from '@components/Chip';
import { toTitleCase } from '@util/strings';
@ -64,30 +63,6 @@ function ProjectDetail({ project, i18n }) {
);
}
let createdBy = '';
if (created) {
if (summary_fields.created_by && summary_fields.created_by.username) {
createdBy = i18n._(
t`${formatDateString(created)} by ${summary_fields.created_by.username}`
);
} else {
createdBy = formatDateString(created);
}
}
let modifiedBy = '';
if (modified) {
if (summary_fields.modified_by && summary_fields.modified_by.username) {
modifiedBy = i18n._(
t`${formatDateString(modified)} by ${
summary_fields.modified_by.username
}`
);
} else {
modifiedBy = formatDateString(modified);
}
}
return (
<CardBody>
<DetailList gutter="sm">
@ -150,10 +125,16 @@ function ProjectDetail({ project, i18n }) {
)}
</Config>
<Detail label={i18n._(t`Playbook Directory`)} value={local_path} />
{/* TODO: Link to user in users */}
<Detail label={i18n._(t`Created`)} value={createdBy} />
{/* TODO: Link to user in users */}
<Detail label={i18n._(t`Last Modified`)} value={modifiedBy} />
<UserDateDetail
label={i18n._(t`Created`)}
date={created}
user={summary_fields.created_by}
/>
<UserDateDetail
label={i18n._(t`Last Modified`)}
date={modified}
user={summary_fields.modified_by}
/>
</DetailList>
<ActionButtonWrapper>
{summary_fields.user_capabilities &&

View File

@ -16,8 +16,7 @@ import ContentError from '@components/ContentError';
import LaunchButton from '@components/LaunchButton';
import ContentLoading from '@components/ContentLoading';
import { ChipGroup, Chip, CredentialChip } from '@components/Chip';
import { DetailList, Detail } from '@components/DetailList';
import { formatDateString } from '@util/dates';
import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
import { JobTemplatesAPI } from '@api';
const ButtonGroup = styled.div`
@ -112,32 +111,6 @@ class JobTemplateDetail extends Component {
const renderOptionsField =
become_enabled || host_config_key || allow_simultaneous || use_fact_cache;
let createdBy = '';
if (created) {
if (summary_fields.created_by && summary_fields.created_by.username) {
createdBy = i18n._(
t`${formatDateString(created)} by ${
summary_fields.created_by.username
}`
);
} else {
createdBy = formatDateString(created);
}
}
let modifiedBy = '';
if (modified) {
if (summary_fields.modified_by && summary_fields.modified_by.username) {
modifiedBy = i18n._(
t`${formatDateString(modified)} by ${
summary_fields.modified_by.username
}`
);
} else {
modifiedBy = formatDateString(modified);
}
}
const renderOptions = (
<TextList component={TextListVariants.ul}>
{become_enabled && (
@ -239,18 +212,16 @@ class JobTemplateDetail extends Component {
value={verbosityDetails[0].details}
/>
<Detail label={i18n._(t`Timeout`)} value={timeout || '0'} />
{createdBy && (
<Detail
label={i18n._(t`Created`)}
value={createdBy} // TODO: link to user in users
/>
)}
{modifiedBy && (
<Detail
label={i18n._(t`Last Modified`)}
value={modifiedBy} // TODO: link to user in users
/>
)}
<UserDateDetail
label={i18n._(t`Created`)}
date={created}
user={summary_fields.created_by}
/>
<UserDateDetail
label={i18n._(t`Last Modified`)}
date={modified}
user={summary_fields.modified_by}
/>
<Detail
label={i18n._(t`Show Changes`)}
value={diff_mode ? 'On' : 'Off'}