mirror of
https://github.com/ansible/awx.git
synced 2026-02-16 18:50:04 -03:30
use VariablesDetail for displaying variables field in details views
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export { default as TabbedCardHeader } from './TabbedCardHeader';
|
export { default as TabbedCardHeader } from './TabbedCardHeader';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { string, number } from 'prop-types';
|
import { string, number } from 'prop-types';
|
||||||
import { Split, SplitItem } from '@patternfly/react-core';
|
import { Split, SplitItem, TextListItemVariants } from '@patternfly/react-core';
|
||||||
|
import { DetailName, DetailValue } from '@components/DetailList';
|
||||||
import CodeMirrorInput from './CodeMirrorInput';
|
import CodeMirrorInput from './CodeMirrorInput';
|
||||||
import YamlJsonToggle from './YamlJsonToggle';
|
import YamlJsonToggle from './YamlJsonToggle';
|
||||||
import { yamlToJson, jsonToYaml, isJson } from '../../util/yaml';
|
import { yamlToJson, jsonToYaml, isJson } from '../../util/yaml';
|
||||||
@@ -10,54 +11,68 @@ const JSON_MODE = 'javascript';
|
|||||||
|
|
||||||
function VariablesDetail({ value, label, rows }) {
|
function VariablesDetail({ value, label, rows }) {
|
||||||
const [mode, setMode] = useState(isJson(value) ? JSON_MODE : YAML_MODE);
|
const [mode, setMode] = useState(isJson(value) ? JSON_MODE : YAML_MODE);
|
||||||
const [val, setVal] = useState(value);
|
const [currentValue, setCurrentValue] = useState(value);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div css="margin: 20px 0">
|
<>
|
||||||
<Split gutter="sm">
|
<DetailName
|
||||||
<SplitItem>
|
component={TextListItemVariants.dt}
|
||||||
<div className="pf-c-form__label">
|
fullWidth
|
||||||
<span
|
css="grid-column: 1 / -1"
|
||||||
className="pf-c-form__label-text"
|
>
|
||||||
css="font-weight: var(--pf-global--FontWeight--bold)"
|
<Split gutter="sm">
|
||||||
>
|
<SplitItem>
|
||||||
{label}
|
<div className="pf-c-form__label">
|
||||||
</span>
|
<span
|
||||||
</div>
|
className="pf-c-form__label-text"
|
||||||
</SplitItem>
|
css="font-weight: var(--pf-global--FontWeight--bold)"
|
||||||
<SplitItem>
|
>
|
||||||
<YamlJsonToggle
|
{label}
|
||||||
mode={mode}
|
</span>
|
||||||
onChange={newMode => {
|
</div>
|
||||||
try {
|
</SplitItem>
|
||||||
const newVal =
|
<SplitItem>
|
||||||
newMode === YAML_MODE ? jsonToYaml(val) : yamlToJson(val);
|
<YamlJsonToggle
|
||||||
setVal(newVal);
|
mode={mode}
|
||||||
setMode(newMode);
|
onChange={newMode => {
|
||||||
} catch (err) {
|
try {
|
||||||
setError(err);
|
const newVal =
|
||||||
}
|
newMode === YAML_MODE
|
||||||
}}
|
? jsonToYaml(currentValue)
|
||||||
/>
|
: yamlToJson(currentValue);
|
||||||
</SplitItem>
|
setCurrentValue(newVal);
|
||||||
</Split>
|
setMode(newMode);
|
||||||
<CodeMirrorInput
|
} catch (err) {
|
||||||
mode={mode}
|
setError(err);
|
||||||
value={val}
|
}
|
||||||
readOnly
|
}}
|
||||||
rows={rows}
|
/>
|
||||||
css="margin-top: 10px"
|
</SplitItem>
|
||||||
/>
|
</Split>
|
||||||
{error && (
|
</DetailName>
|
||||||
<div
|
<DetailValue
|
||||||
css="color: var(--pf-global--danger-color--100);
|
component={TextListItemVariants.dd}
|
||||||
|
fullWidth
|
||||||
|
css="grid-column: 1 / -1; margin-top: -20px"
|
||||||
|
>
|
||||||
|
<CodeMirrorInput
|
||||||
|
mode={mode}
|
||||||
|
value={currentValue}
|
||||||
|
readOnly
|
||||||
|
rows={rows}
|
||||||
|
css="margin-top: 10px"
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<div
|
||||||
|
css="color: var(--pf-global--danger-color--100);
|
||||||
font-size: var(--pf-global--FontSize--sm"
|
font-size: var(--pf-global--FontSize--sm"
|
||||||
>
|
>
|
||||||
Error: {error.message}
|
Error: {error.message}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</DetailValue>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
VariablesDetail.propTypes = {
|
VariablesDetail.propTypes = {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import YamlJsonToggle from './YamlJsonToggle';
|
|||||||
import { yamlToJson, jsonToYaml } from '../../util/yaml';
|
import { yamlToJson, jsonToYaml } from '../../util/yaml';
|
||||||
|
|
||||||
const YAML_MODE = 'yaml';
|
const YAML_MODE = 'yaml';
|
||||||
const JSON_MODE = 'javascript';
|
|
||||||
|
|
||||||
function VariablesField({ id, name, label, readOnly }) {
|
function VariablesField({ id, name, label, readOnly }) {
|
||||||
// TODO: detect initial mode
|
// TODO: detect initial mode
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import { withI18n } from '@lingui/react';
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Host } from '@types';
|
import { Host } from '@types';
|
||||||
import { formatDateString } from '@util/dates';
|
|
||||||
import { Button, CardBody } from '@patternfly/react-core';
|
import { Button, CardBody } from '@patternfly/react-core';
|
||||||
import { DetailList, Detail } from '@components/DetailList';
|
import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
|
||||||
import CodeMirrorInput from '@components/CodeMirrorInput';
|
import { VariablesDetail } from '@components/CodeMirrorInput';
|
||||||
|
|
||||||
const ActionButtonWrapper = styled.div`
|
const ActionButtonWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -21,30 +20,6 @@ const ActionButtonWrapper = styled.div`
|
|||||||
function HostDetail({ host, i18n }) {
|
function HostDetail({ host, i18n }) {
|
||||||
const { created, description, id, modified, name, summary_fields } = host;
|
const { created, description, id, modified, name, summary_fields } = host;
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<DetailList gutter="sm">
|
<DetailList gutter="sm">
|
||||||
@@ -66,23 +41,20 @@ function HostDetail({ host, i18n }) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* TODO: Link to user in users */}
|
<UserDateDetail
|
||||||
<Detail label={i18n._(t`Created`)} value={createdBy} />
|
label={i18n._(t`Created`)}
|
||||||
{/* TODO: Link to user in users */}
|
date={created}
|
||||||
<Detail label={i18n._(t`Last Modified`)} value={modifiedBy} />
|
user={summary_fields.created_by}
|
||||||
<Detail
|
/>
|
||||||
fullWidth
|
<UserDateDetail
|
||||||
|
label={i18n._(t`Last Modified`)}
|
||||||
|
date={modified}
|
||||||
|
user={summary_fields.modified_by}
|
||||||
|
/>
|
||||||
|
<VariablesDetail
|
||||||
label={i18n._(t`Variables`)}
|
label={i18n._(t`Variables`)}
|
||||||
value={
|
value={host.variables}
|
||||||
<CodeMirrorInput
|
rows={6}
|
||||||
mode="yaml"
|
|
||||||
readOnly
|
|
||||||
value={host.variables}
|
|
||||||
onChange={() => {}}
|
|
||||||
rows={6}
|
|
||||||
hasErrors={false}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</DetailList>
|
</DetailList>
|
||||||
<ActionButtonWrapper>
|
<ActionButtonWrapper>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ describe('<HostDetail />', () => {
|
|||||||
mountWithContexts(<HostDetail host={mockHost} />);
|
mountWithContexts(<HostDetail host={mockHost} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render Details', async done => {
|
test('should render Details', async () => {
|
||||||
const wrapper = mountWithContexts(<HostDetail host={mockHost} />);
|
const wrapper = mountWithContexts(<HostDetail host={mockHost} />);
|
||||||
const testParams = [
|
const testParams = [
|
||||||
{ label: 'Name', value: 'Foo' },
|
{ label: 'Name', value: 'Foo' },
|
||||||
@@ -46,23 +46,22 @@ describe('<HostDetail />', () => {
|
|||||||
expect(detail.find('dt').text()).toBe(label);
|
expect(detail.find('dt').text()).toBe(label);
|
||||||
expect(detail.find('dd').text()).toBe(value);
|
expect(detail.find('dd').text()).toBe(value);
|
||||||
}
|
}
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should show edit button for users with edit permission', async done => {
|
test('should show edit button for users with edit permission', async () => {
|
||||||
const wrapper = mountWithContexts(<HostDetail host={mockHost} />);
|
const wrapper = mountWithContexts(<HostDetail host={mockHost} />);
|
||||||
const editButton = await waitForElement(wrapper, 'HostDetail Button');
|
// VariablesDetail has two buttons
|
||||||
|
const editButton = wrapper.find('Button').at(2);
|
||||||
expect(editButton.text()).toEqual('Edit');
|
expect(editButton.text()).toEqual('Edit');
|
||||||
expect(editButton.prop('to')).toBe('/hosts/1/edit');
|
expect(editButton.prop('to')).toBe('/hosts/1/edit');
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should hide edit button for users without edit permission', async done => {
|
test('should hide edit button for users without edit permission', async () => {
|
||||||
const readOnlyHost = { ...mockHost };
|
const readOnlyHost = { ...mockHost };
|
||||||
readOnlyHost.summary_fields.user_capabilities.edit = false;
|
readOnlyHost.summary_fields.user_capabilities.edit = false;
|
||||||
const wrapper = mountWithContexts(<HostDetail host={readOnlyHost} />);
|
const wrapper = mountWithContexts(<HostDetail host={readOnlyHost} />);
|
||||||
await waitForElement(wrapper, 'HostDetail');
|
await waitForElement(wrapper, 'HostDetail');
|
||||||
expect(wrapper.find('HostDetail Button').length).toBe(0);
|
// VariablesDetail has two buttons
|
||||||
done();
|
expect(wrapper.find('Button').length).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,16 +56,13 @@ function InventoryDetail({ inventory, i18n }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</DetailList>
|
{inventory.variables && (
|
||||||
{inventory.variables && (
|
<VariablesDetail
|
||||||
<VariablesDetail
|
label={i18n._(t`Variables`)}
|
||||||
id="job-artifacts"
|
value={inventory.variables}
|
||||||
label={i18n._(t`Variables`)}
|
rows={4}
|
||||||
value={inventory.variables}
|
/>
|
||||||
rows={4}
|
)}
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<DetailList>
|
|
||||||
<UserDateDetail
|
<UserDateDetail
|
||||||
label={i18n._(t`Created`)}
|
label={i18n._(t`Created`)}
|
||||||
date={inventory.created}
|
date={inventory.created}
|
||||||
|
|||||||
Reference in New Issue
Block a user