From b1ce5e24e3c5499440166a567766e6f86f64cdaf Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Mon, 22 Feb 2021 14:48:48 -0800 Subject: [PATCH 1/6] add expand button to VariablesField --- awx/ui_next/.eslintrc | 3 +- .../src/components/CodeEditor/CodeEditor.jsx | 14 ++- .../components/CodeEditor/VariablesDetail.jsx | 89 +++++++++----- .../components/CodeEditor/VariablesField.jsx | 113 +++++++++++++++--- .../src/components/DetailList/CodeDetail.jsx | 13 +- .../src/screens/Host/HostFacts/HostFacts.jsx | 2 +- .../InventoryHostFacts/InventoryHostFacts.jsx | 2 +- .../screens/Setting/shared/SharedFields.jsx | 2 +- 8 files changed, 172 insertions(+), 66 deletions(-) diff --git a/awx/ui_next/.eslintrc b/awx/ui_next/.eslintrc index f82900134e..b7c86c305a 100644 --- a/awx/ui_next/.eslintrc +++ b/awx/ui_next/.eslintrc @@ -77,7 +77,8 @@ "resizeOrientation", "src", "theme", - "gridColumns" + "gridColumns", + "rows" ], "ignore": ["Ansible", "Tower", "JSON", "YAML", "lg"], "ignoreComponent": [ diff --git a/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx b/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx index 0218a83c3c..981cf01610 100644 --- a/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx +++ b/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx @@ -77,6 +77,13 @@ function CodeEditor({ className, i18n, }) { + if (typeof rows !== 'number' && rows !== 'auto') { + // eslint-disable-next-line no-console + console.warning( + `CodeEditor: Unexpected value for 'rows': ${rows}; expected number or 'auto'` + ); + } + const wrapper = useRef(null); const editor = useRef(null); @@ -117,7 +124,8 @@ function CodeEditor({ jinja2: 'django', }; - const numRows = fullHeight ? value.split('\n').length : rows; + const numRows = rows === 'auto' ? value.split('\n').length : rows; + const height = fullHeight ? '50vh' : `${numRows * LINE_HEIGHT + PADDING}px`; return ( <> @@ -132,7 +140,7 @@ function CodeEditor({ editorProps={{ $blockScrolling: true }} fontSize={16} width="100%" - height={`${numRows * LINE_HEIGHT + PADDING}px`} + height={height} hasErrors={hasErrors} setOptions={{ readOnly, @@ -178,7 +186,7 @@ CodeEditor.propTypes = { readOnly: bool, hasErrors: bool, fullHeight: bool, - rows: number, + rows: oneOf(number, string), className: string, }; CodeEditor.defaultProps = { diff --git a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx index f5cfd91373..602ef6695f 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx @@ -2,7 +2,14 @@ import 'styled-components/macro'; import React, { useState, useEffect } from 'react'; import { node, number, oneOfType, shape, string, arrayOf } from 'prop-types'; import { Trans, withI18n } from '@lingui/react'; -import { Split, SplitItem, TextListItemVariants } from '@patternfly/react-core'; +import { t } from '@lingui/macro'; +import { + Split, + SplitItem, + TextListItemVariants, + Button, +} from '@patternfly/react-core'; +import { ExpandArrowsAltIcon } from '@patternfly/react-icons'; import { DetailName, DetailValue } from '../DetailList'; import MultiButtonToggle from '../MultiButtonToggle'; import Popover from '../Popover'; @@ -29,13 +36,22 @@ function getValueAsMode(value, mode) { return mode === YAML_MODE ? jsonToYaml(value) : yamlToJson(value); } -function VariablesDetail({ dataCy, helpText, value, label, rows, fullHeight }) { +function VariablesDetail({ + dataCy, + helpText, + value, + label, + rows, + fullHeight, + i18n, +}) { const [mode, setMode] = useState( isJsonObject(value) || isJsonString(value) ? JSON_MODE : YAML_MODE ); const [currentValue, setCurrentValue] = useState( isJsonObject(value) ? JSON.stringify(value, null, 2) : value || '---' ); + const [isExpanded, setIsExpanded] = useState(false); const [error, setError] = useState(null); useEffect(() => { @@ -61,35 +77,48 @@ function VariablesDetail({ dataCy, helpText, value, label, rows, fullHeight }) { css="grid-column: 1 / -1" > - -
- - {label} - - {helpText && ( - - )} -
+ + + +
+ + {label} + + {helpText && ( + + )} +
+
+ + { + try { + setCurrentValue(getValueAsMode(currentValue, newMode)); + setMode(newMode); + } catch (err) { + setError(err); + } + }} + /> + +
- { - try { - setCurrentValue(getValueAsMode(currentValue, newMode)); - setMode(newMode); - } catch (err) { - setError(err); - } - }} - /> +
@@ -122,7 +151,7 @@ function VariablesDetail({ dataCy, helpText, value, label, rows, fullHeight }) { VariablesDetail.propTypes = { value: oneOfType([shape({}), arrayOf(string), string]).isRequired, label: node.isRequired, - rows: number, + rows: oneOfType(number, string), dataCy: string, helpText: string, }; diff --git a/awx/ui_next/src/components/CodeEditor/VariablesField.jsx b/awx/ui_next/src/components/CodeEditor/VariablesField.jsx index 8fbf1e4bfa..35756132e8 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesField.jsx @@ -4,7 +4,8 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { useField } from 'formik'; import styled from 'styled-components'; -import { Split, SplitItem } from '@patternfly/react-core'; +import { Split, SplitItem, Button, Modal } from '@patternfly/react-core'; +import { ExpandArrowsAltIcon } from '@patternfly/react-icons'; import { CheckboxField } from '../FormField'; import MultiButtonToggle from '../MultiButtonToggle'; import { yamlToJson, jsonToYaml, isJsonString } from '../../util/yaml'; @@ -20,6 +21,7 @@ const FieldHeader = styled.div` const StyledCheckboxField = styled(CheckboxField)` --pf-c-check__label--FontSize: var(--pf-c-form__label--FontSize); + margin-left: auto; `; function VariablesField({ @@ -31,10 +33,91 @@ function VariablesField({ promptId, tooltip, }) { - const [field, meta, helpers] = useField(name); + const [field, meta] = useField(name); const [mode, setMode] = useState( isJsonString(field.value) ? JSON_MODE : YAML_MODE ); + const [isExpanded, setIsExpanded] = useState(false); + + return ( + <> + setIsExpanded(true)} + mode={mode} + setMode={setMode} + /> + setIsExpanded(false)} + actions={[ + , + ]} + > +
+ +
+
+ {meta.error ? ( +
+ {meta.error} +
+ ) : null} + + ); +} +VariablesField.propTypes = { + id: string.isRequired, + name: string.isRequired, + label: string.isRequired, + readOnly: bool, + promptId: string, +}; +VariablesField.defaultProps = { + readOnly: false, + promptId: null, +}; + +function VariablesFieldInternals({ + i18n, + id, + name, + label, + readOnly, + promptId, + tooltip, + fullHeight, + mode, + setMode, + onExpand, +}) { + const [field, meta, helpers] = useField(name); return (
@@ -75,6 +158,15 @@ function VariablesField({ name="ask_variables_on_launch" /> )} + {onExpand && ( + + )} { helpers.setValue(newVal); }} + fullHeight={fullHeight} hasErrors={!!meta.error} /> - {meta.error ? ( -
- {meta.error} -
- ) : null}
); } -VariablesField.propTypes = { - id: string.isRequired, - name: string.isRequired, - label: string.isRequired, - readOnly: bool, - promptId: string, -}; -VariablesField.defaultProps = { - readOnly: false, - promptId: null, -}; export default withI18n()(VariablesField); diff --git a/awx/ui_next/src/components/DetailList/CodeDetail.jsx b/awx/ui_next/src/components/DetailList/CodeDetail.jsx index 08c935b18e..90edb259e8 100644 --- a/awx/ui_next/src/components/DetailList/CodeDetail.jsx +++ b/awx/ui_next/src/components/DetailList/CodeDetail.jsx @@ -14,15 +14,7 @@ import { DetailName, DetailValue } from './Detail'; import CodeEditor from '../CodeEditor'; import Popover from '../Popover'; -function CodeDetail({ - value, - label, - mode, - rows, - fullHeight, - helpText, - dataCy, -}) { +function CodeDetail({ value, label, mode, rows, helpText, dataCy }) { const labelCy = dataCy ? `${dataCy}-label` : null; const valueCy = dataCy ? `${dataCy}-value` : null; @@ -57,7 +49,6 @@ function CodeDetail({ value={value} readOnly rows={rows} - fullHeight={fullHeight} css="margin-top: 10px" /> @@ -69,7 +60,7 @@ CodeDetail.propTypes = { label: node.isRequired, dataCy: string, helpText: string, - rows: number, + rows: oneOfType(number, string), mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired, }; CodeDetail.defaultProps = { diff --git a/awx/ui_next/src/screens/Host/HostFacts/HostFacts.jsx b/awx/ui_next/src/screens/Host/HostFacts/HostFacts.jsx index f33c989f5b..08b447a991 100644 --- a/awx/ui_next/src/screens/Host/HostFacts/HostFacts.jsx +++ b/awx/ui_next/src/screens/Host/HostFacts/HostFacts.jsx @@ -36,7 +36,7 @@ function HostFacts({ i18n, host }) { return ( - + ); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostFacts/InventoryHostFacts.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostFacts/InventoryHostFacts.jsx index 6bffd37ba4..4d93ce58c3 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHostFacts/InventoryHostFacts.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHostFacts/InventoryHostFacts.jsx @@ -35,7 +35,7 @@ function InventoryHostFacts({ i18n, host }) { return ( - + ); diff --git a/awx/ui_next/src/screens/Setting/shared/SharedFields.jsx b/awx/ui_next/src/screens/Setting/shared/SharedFields.jsx index b5e118088a..96dc973de5 100644 --- a/awx/ui_next/src/screens/Setting/shared/SharedFields.jsx +++ b/awx/ui_next/src/screens/Setting/shared/SharedFields.jsx @@ -286,7 +286,7 @@ const ObjectField = withI18n()(({ i18n, name, config, isRequired = false }) => { > { From d6a5a1e0d015d0156c07b8617a1f792177d15609 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Tue, 23 Feb 2021 16:33:11 -0800 Subject: [PATCH 2/6] add expand button to VariablesDetail --- .../components/CodeEditor/VariablesDetail.jsx | 174 ++++++++++++------ 1 file changed, 117 insertions(+), 57 deletions(-) diff --git a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx index 602ef6695f..4c3af1fbe8 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx @@ -1,13 +1,14 @@ import 'styled-components/macro'; import React, { useState, useEffect } from 'react'; import { node, number, oneOfType, shape, string, arrayOf } from 'prop-types'; -import { Trans, withI18n } from '@lingui/react'; +import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Split, SplitItem, TextListItemVariants, Button, + Modal, } from '@patternfly/react-core'; import { ExpandArrowsAltIcon } from '@patternfly/react-icons'; import { DetailName, DetailValue } from '../DetailList'; @@ -36,15 +37,7 @@ function getValueAsMode(value, mode) { return mode === YAML_MODE ? jsonToYaml(value) : yamlToJson(value); } -function VariablesDetail({ - dataCy, - helpText, - value, - label, - rows, - fullHeight, - i18n, -}) { +function VariablesDetail({ dataCy, helpText, value, label, rows, i18n }) { const [mode, setMode] = useState( isJsonObject(value) || isJsonString(value) ? JSON_MODE : YAML_MODE ); @@ -76,51 +69,18 @@ function VariablesDetail({ fullWidth css="grid-column: 1 / -1" > - - - - -
- - {label} - - {helpText && ( - - )} -
-
- - { - try { - setCurrentValue(getValueAsMode(currentValue, newMode)); - setMode(newMode); - } catch (err) { - setError(err); - } - }} - /> - -
-
- - - -
+ setIsExpanded(true)} + i18n={i18n} + /> {error && ( @@ -141,10 +100,48 @@ function VariablesDetail({ css="color: var(--pf-global--danger-color--100); font-size: var(--pf-global--FontSize--sm" > - Error: {error.message} + {i18n._(t`Error:`)} {error.message} )} + setIsExpanded(false)} + actions={[ + , + ]} + > +
+ + +
+
); } @@ -161,4 +158,67 @@ VariablesDetail.defaultProps = { helpText: '', }; +function ModeToggle({ + label, + helpText, + dataCy, + currentValue, + setCurrentValue, + mode, + setMode, + setError, + onExpand, + i18n, +}) { + return ( + + + + +
+ + {label} + + {helpText && ( + + )} +
+
+ + { + try { + setCurrentValue(getValueAsMode(currentValue, newMode)); + setMode(newMode); + } catch (err) { + setError(err); + } + }} + /> + +
+
+ {onExpand && ( + + + + )} +
+ ); +} + export default withI18n()(VariablesDetail); From f867c9e4761441e2880b154b0d082f8cee3ec44b Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Wed, 24 Feb 2021 08:54:36 -0800 Subject: [PATCH 3/6] fix type errors in VariablesDetail --- awx/ui_next/src/components/CodeEditor/CodeEditor.jsx | 6 +++--- awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx | 2 +- .../src/components/CodeEditor/VariablesField.test.jsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx b/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx index 981cf01610..ad930b7a20 100644 --- a/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx +++ b/awx/ui_next/src/components/CodeEditor/CodeEditor.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useCallback } from 'react'; -import { oneOf, bool, number, string, func } from 'prop-types'; +import { oneOf, bool, number, string, func, oneOfType } from 'prop-types'; import ReactAce from 'react-ace'; import 'ace-builds/src-noconflict/mode-json'; import 'ace-builds/src-noconflict/mode-javascript'; @@ -77,7 +77,7 @@ function CodeEditor({ className, i18n, }) { - if (typeof rows !== 'number' && rows !== 'auto') { + if (rows && typeof rows !== 'number' && rows !== 'auto') { // eslint-disable-next-line no-console console.warning( `CodeEditor: Unexpected value for 'rows': ${rows}; expected number or 'auto'` @@ -186,7 +186,7 @@ CodeEditor.propTypes = { readOnly: bool, hasErrors: bool, fullHeight: bool, - rows: oneOf(number, string), + rows: oneOfType([number, string]), className: string, }; CodeEditor.defaultProps = { diff --git a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx index 4c3af1fbe8..fa46a60f40 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx @@ -148,7 +148,7 @@ function VariablesDetail({ dataCy, helpText, value, label, rows, i18n }) { VariablesDetail.propTypes = { value: oneOfType([shape({}), arrayOf(string), string]).isRequired, label: node.isRequired, - rows: oneOfType(number, string), + rows: oneOfType([number, string]), dataCy: string, helpText: string, }; diff --git a/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx b/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx index e07ff9d40b..37cdc8a53d 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx @@ -32,7 +32,7 @@ describe('VariablesField', () => { ); const buttons = wrapper.find('Button'); - expect(buttons).toHaveLength(2); + expect(buttons).toHaveLength(3); expect(buttons.at(0).prop('variant')).toEqual('primary'); expect(buttons.at(1).prop('variant')).toEqual('secondary'); await act(async () => { From 659f68f280ad1b288acabc0dae7238cc76e9945b Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Thu, 25 Feb 2021 09:44:45 -0800 Subject: [PATCH 4/6] add VariablesField expand test --- .../CodeEditor/VariablesField.test.jsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx b/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx index 37cdc8a53d..24c896069e 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesField.test.jsx @@ -136,4 +136,27 @@ describe('VariablesField', () => { expect(wrapper.find('CodeEditor').prop('mode')).toEqual('javascript'); }); + + it('should open modal when expanded', async () => { + const value = '---'; + const wrapper = mountWithContexts( + + {formik => ( +
+ + + + )} +
+ ); + expect(wrapper.find('Modal').prop('isOpen')).toEqual(false); + + wrapper.find('Button[variant="plain"]').invoke('onClick')(); + wrapper.update(); + + expect(wrapper.find('Modal').prop('isOpen')).toEqual(true); + expect(wrapper.find('Modal CodeEditor')).toHaveLength(1); + }); }); From e8886a552563a2195af77331c712be0295cd2ae4 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Mon, 15 Mar 2021 11:42:09 -0700 Subject: [PATCH 5/6] fix InventoryGroupDetails test --- .../InventoryGroupDetail/InventoryGroupDetail.test.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.test.jsx index a4c584c121..dc7e47b646 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.test.jsx @@ -72,11 +72,12 @@ describe('', () => { }); test('should open delete modal and then call api to delete the group', async () => { + expect(wrapper.find('Modal').length).toBe(1); // variables modal already mounted await act(async () => { wrapper.find('button[aria-label="Delete"]').simulate('click'); }); - await waitForElement(wrapper, 'Modal', el => el.length === 1); - expect(wrapper.find('Modal').length).toBe(1); + wrapper.update(); + expect(wrapper.find('Modal').length).toBe(2); await act(async () => { wrapper.find('Radio[id="radio-delete"]').invoke('onChange')(); }); From fb257d0add5e2c55cc900e3d43aa0ae4f9433a8f Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Wed, 24 Mar 2021 11:45:19 -0700 Subject: [PATCH 6/6] add ouia ids to variables expand buttons --- awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx | 2 ++ awx/ui_next/src/components/CodeEditor/VariablesField.jsx | 2 ++ .../screens/Template/JobTemplateDetail/JobTemplateDetail.jsx | 1 + 3 files changed, 5 insertions(+) diff --git a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx index fa46a60f40..797c10673d 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesDetail.jsx @@ -115,6 +115,7 @@ function VariablesDetail({ dataCy, helpText, value, label, rows, i18n }) { key="select" variant="primary" onClick={() => setIsExpanded(false)} + ouiaId={`${dataCy}-unexpand`} > {i18n._(t`Done`)} , @@ -212,6 +213,7 @@ function ModeToggle({ variant="plain" aria-label={i18n._(t`Expand input`)} onClick={onExpand} + ouiaId={`${dataCy}-expand`} > diff --git a/awx/ui_next/src/components/CodeEditor/VariablesField.jsx b/awx/ui_next/src/components/CodeEditor/VariablesField.jsx index 35756132e8..6f039c6895 100644 --- a/awx/ui_next/src/components/CodeEditor/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeEditor/VariablesField.jsx @@ -64,6 +64,7 @@ function VariablesField({ key="select" variant="primary" onClick={() => setIsExpanded(false)} + ouiaId={`${id}-variables-unexpand`} > {i18n._(t`Done`)} , @@ -163,6 +164,7 @@ function VariablesFieldInternals({ variant="plain" aria-label={i18n._(t`Expand input`)} onClick={onExpand} + ouiaId={`${id}-variables-expand`} > diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx index d61819adcc..1a5cf4a4b1 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx @@ -372,6 +372,7 @@ function JobTemplateDetail({ i18n, template }) { value={extra_vars} rows={4} label={i18n._(t`Variables`)} + dataCy={`jt-details-${template.id}`} />