From 33502daf45fbb895fa5584c607a4ed9921c496e7 Mon Sep 17 00:00:00 2001 From: nixocio Date: Fri, 9 Jul 2021 10:55:57 -0400 Subject: [PATCH] Add extra step to allow edition of login redirect URL Add extra step to allow edition of login redirect URL closes: https://github.com/ansible/awx/issues/10613 --- .../MiscAuthenticationEdit.js | 3 +- .../MiscAuthenticationEdit.test.js | 40 ++++++ .../screens/Setting/shared/SharedFields.js | 121 +++++++++++++++++- .../Setting/shared/SharedFields.test.js | 25 ++++ .../src/screens/Setting/shared/index.js | 1 + 5 files changed, 188 insertions(+), 2 deletions(-) diff --git a/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.js b/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.js index eb2d28d110..6fb9f6c919 100644 --- a/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.js +++ b/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.js @@ -16,6 +16,7 @@ import { BooleanField, InputField, ObjectField, + InputAlertField, } from '../../shared/SharedFields'; import { RevertAllAlert, RevertFormActionGroup } from '../../shared'; import { formatJson, pluck } from '../../shared/settingUtils'; @@ -211,7 +212,7 @@ function MiscAuthenticationEdit() { name="ALLOW_OAUTH2_FOR_EXTERNAL_USERS" config={authentication.ALLOW_OAUTH2_FOR_EXTERNAL_USERS} /> - diff --git a/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.test.js b/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.test.js index 1f08edd0a7..b3cbd31db2 100644 --- a/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.test.js +++ b/awx/ui_next/src/screens/Setting/MiscAuthentication/MiscAuthenticationEdit/MiscAuthenticationEdit.test.js @@ -64,6 +64,46 @@ describe('', () => { await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); }); + test('should enable edit login redirect once alert is confirmed', async () => { + expect( + wrapper.find('TextInput#LOGIN_REDIRECT_OVERRIDE').prop('isDisabled') + ).toBe(true); + await act(async () => + wrapper + .find('Button[ouiaId="confirm-edit-login-redirect"]') + .simulate('click') + ); + wrapper.update(); + const modal = wrapper.find('AlertModal'); + expect(modal).toHaveLength(1); + expect(modal.prop('isOpen')).toEqual(true); + await act(async () => + modal + .find('Button[aria-label="confirm edit login redirect"]') + .simulate('click') + ); + wrapper.update(); + expect( + wrapper.find('TextInput#LOGIN_REDIRECT_OVERRIDE').prop('isDisabled') + ).toBe(false); + + await act(async () => { + wrapper.find('TextInput#LOGIN_REDIRECT_OVERRIDE').invoke('onChange')( + null, + { + target: { + name: 'LOGIN_REDIRECT_OVERRIDE', + value: 'bar', + }, + } + ); + }); + wrapper.update(); + expect( + wrapper.find('TextInput#LOGIN_REDIRECT_OVERRIDE').prop('value') + ).toEqual('bar'); + }); + test('initially renders without crashing', async () => { expect(wrapper.find('MiscAuthenticationEdit').length).toBe(1); }); diff --git a/awx/ui_next/src/screens/Setting/shared/SharedFields.js b/awx/ui_next/src/screens/Setting/shared/SharedFields.js index 90bae3c6af..bb39074b88 100644 --- a/awx/ui_next/src/screens/Setting/shared/SharedFields.js +++ b/awx/ui_next/src/screens/Setting/shared/SharedFields.js @@ -10,8 +10,11 @@ import { Switch, TextArea, TextInput, + Tooltip, + ButtonVariant, } from '@patternfly/react-core'; import FileUploadIcon from '@patternfly/react-icons/dist/js/icons/file-upload-icon'; +import { ExclamationCircleIcon as PFExclamationCircleIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; import AnsibleSelect from 'components/AnsibleSelect'; import CodeEditor from 'components/CodeEditor'; @@ -22,6 +25,12 @@ import { combine, integer, minMaxValue, required, url } from 'util/validators'; import AlertModal from 'components/AlertModal'; import RevertButton from './RevertButton'; +const ExclamationCircleIcon = styled(PFExclamationCircleIcon)` + && { + color: var(--pf-global--danger-color--100); + } +`; + const FormGroup = styled(PFFormGroup)` .pf-c-form__group-label { display: inline-flex; @@ -30,6 +39,13 @@ const FormGroup = styled(PFFormGroup)` } `; +const Selected = styled.div` + display: flex; + justify-content: space-between; + background-color: white; + border-bottom-color: var(--pf-global--BorderColor--200); +`; + const SettingGroup = ({ children, defaultValue, @@ -114,7 +130,9 @@ const BooleanField = ({ {t`Cancel`} , ]} - >{t`Are you sure you want to disable local authentication? Doing so could impact users' ability to log in and the system administrator's ability to reverse this change.`} + > + {t`Are you sure you want to disable local authentication? Doing so could impact users' ability to log in and the system administrator's ability to reverse this change.`} + ); } @@ -215,6 +233,106 @@ EncryptedField.propTypes = { config: shape({}).isRequired, }; +const InputAlertField = ({ name, config }) => { + const [field, meta] = useField({ name }); + const isValid = !(meta.touched && meta.error); + const [isModalOpen, setIsModalOpen] = useState(false); + const [isDisable, setIsDisable] = useState(true); + + const handleSetIsOpen = () => { + setIsModalOpen(true); + }; + + const handleEnableTextInput = () => { + setIsDisable(false); + }; + + return config ? ( + <> + + + {isDisable && ( + + + + )} + { + field.onChange(event); + }} + isDisabled={isDisable} + /> + + + {isModalOpen && isDisable && ( + { + setIsModalOpen(false); + }} + actions={[ + , + , + ]} + > + {t`Are you sure you want to edit login redirect override URL? Doing so could impact users' ability to log in to the system once local authentication is also disabled.`} + + )} + + ) : null; +}; + +InputAlertField.propTypes = { + name: string.isRequired, + config: shape({}).isRequired, +}; + const InputField = ({ name, config, type = 'text', isRequired = false }) => { const min_value = config?.min_value ?? Number.MIN_SAFE_INTEGER; const max_value = config?.max_value ?? Number.MAX_SAFE_INTEGER; @@ -407,4 +525,5 @@ export { InputField, ObjectField, TextAreaField, + InputAlertField, }; diff --git a/awx/ui_next/src/screens/Setting/shared/SharedFields.test.js b/awx/ui_next/src/screens/Setting/shared/SharedFields.test.js index 35c494fe83..a081945e5b 100644 --- a/awx/ui_next/src/screens/Setting/shared/SharedFields.test.js +++ b/awx/ui_next/src/screens/Setting/shared/SharedFields.test.js @@ -9,6 +9,7 @@ import { ChoiceField, EncryptedField, FileUploadField, + InputAlertField, InputField, ObjectField, TextAreaField, @@ -158,6 +159,30 @@ describe('Setting form fields', () => { expect(wrapper.find('TextInputBase').prop('value')).toEqual(0); }); + test('InputAlertField initially renders disable TextInput', async () => { + const wrapper = mountWithContexts( + + {() => ( + + )} + + ); + expect(wrapper.find('TextInput')).toHaveLength(1); + expect(wrapper.find('TextInput').prop('value')).toEqual(''); + expect(wrapper.find('TextInput').prop('isDisabled')).toBe(true); + }); + test('TextAreaField renders the expected content', async () => { const wrapper = mountWithContexts(