use VariablesDetail for displaying variables field in details views

This commit is contained in:
Keith Grant
2019-12-16 16:11:24 -08:00
parent cde39413c9
commit 2f7607a080
6 changed files with 90 additions and 107 deletions

View File

@@ -1 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export { default as TabbedCardHeader } from './TabbedCardHeader'; export { default as TabbedCardHeader } from './TabbedCardHeader';

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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>

View File

@@ -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);
}); });
}); });

View File

@@ -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}