mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
updates several dependencies
This commit is contained in:
parent
e77d297a28
commit
3def23883e
@ -12,7 +12,6 @@
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"prettier",
|
||||
"prettier/react",
|
||||
"plugin:jsx-a11y/strict",
|
||||
"plugin:i18next/recommended"
|
||||
],
|
||||
@ -22,7 +21,7 @@
|
||||
},
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
paths: ["src"]
|
||||
"paths": ["src"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -134,6 +133,7 @@
|
||||
"object-curly-newline": "off",
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-unused-expressions": ["error", { "allowShortCircuit": true }],
|
||||
"react/jsx-props-no-spreading":["off"],
|
||||
"react/prefer-stateless-function": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/sort-comp": ["error", {}],
|
||||
|
||||
26098
awx/ui_next/package-lock.json
generated
26098
awx/ui_next/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -44,19 +44,19 @@
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.14.0",
|
||||
"enzyme-to-json": "^3.3.5",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-airbnb": "^17.1.0",
|
||||
"eslint-config-prettier": "^5.0.0",
|
||||
"eslint": "7.30.0",
|
||||
"eslint-config-airbnb": "18.2.1",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-import-resolver-webpack": "0.11.1",
|
||||
"eslint-plugin-i18next": "^5.0.0",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"eslint-plugin-react-hooks": "^2.2.0",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"http-proxy-middleware": "^1.0.3",
|
||||
"jest-websocket-mock": "^2.0.2",
|
||||
"mock-socket": "^9.0.3",
|
||||
"prettier": "^1.18.2",
|
||||
"prettier": "2.3.2",
|
||||
"react-scripts": "^4.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -16,7 +16,7 @@ const defaultHttp = axios.create({
|
||||
},
|
||||
});
|
||||
|
||||
defaultHttp.interceptors.response.use(response => {
|
||||
defaultHttp.interceptors.response.use((response) => {
|
||||
const timeout = response?.headers['session-timeout'];
|
||||
if (timeout) {
|
||||
const timeoutDate = new Date().getTime() + timeout * 1000;
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
function isEqual(array1, array2) {
|
||||
return (
|
||||
array1.length === array2.length &&
|
||||
array1.every((element, index) => {
|
||||
return element.id === array2[index].id;
|
||||
})
|
||||
array1.every((element, index) => element.id === array2[index].id)
|
||||
);
|
||||
}
|
||||
|
||||
const InstanceGroupsMixin = parent =>
|
||||
const InstanceGroupsMixin = (parent) =>
|
||||
class extends parent {
|
||||
readInstanceGroups(resourceId, params) {
|
||||
return this.http.get(`${this.baseUrl}${resourceId}/instance_groups/`, {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const LaunchUpdateMixin = parent =>
|
||||
const LaunchUpdateMixin = (parent) =>
|
||||
class extends parent {
|
||||
launchUpdate(id, data) {
|
||||
return this.http.post(`${this.baseUrl}${id}/update/`, data);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const NotificationsMixin = parent =>
|
||||
const NotificationsMixin = (parent) =>
|
||||
class extends parent {
|
||||
readOptionsNotificationTemplates(id) {
|
||||
return this.http.options(`${this.baseUrl}${id}/notification_templates/`);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Runnable = parent =>
|
||||
const Runnable = (parent) =>
|
||||
class extends parent {
|
||||
jobEventSlug = '/events/';
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const SchedulesMixin = parent =>
|
||||
const SchedulesMixin = (parent) =>
|
||||
class extends parent {
|
||||
createSchedule(id, data) {
|
||||
return this.http.post(`${this.baseUrl}${id}/schedules/`, data);
|
||||
|
||||
@ -25,7 +25,7 @@ class CredentialTypes extends Base {
|
||||
}
|
||||
return results
|
||||
.concat(nextResults)
|
||||
.filter(type => acceptableKinds.includes(type.kind));
|
||||
.filter((type) => acceptableKinds.includes(type.kind));
|
||||
}
|
||||
|
||||
test(id, data) {
|
||||
|
||||
@ -22,9 +22,10 @@ describe('TeamsAPI', () => {
|
||||
await TeamsAPI.associateRole(teamId, roleId);
|
||||
|
||||
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
mockHttp.post.mock.calls[0]
|
||||
).toContainEqual(`/api/v2/teams/${teamId}/roles/`, { id: roleId });
|
||||
expect(mockHttp.post.mock.calls[0]).toContainEqual(
|
||||
`/api/v2/teams/${teamId}/roles/`,
|
||||
{ id: roleId }
|
||||
);
|
||||
});
|
||||
|
||||
test('read teams calls post with expected params', async () => {
|
||||
|
||||
@ -19,9 +19,10 @@ describe('UsersAPI', () => {
|
||||
await UsersAPI.associateRole(userId, roleId);
|
||||
|
||||
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
mockHttp.post.mock.calls[0]
|
||||
).toContainEqual(`/api/v2/users/${userId}/roles/`, { id: roleId });
|
||||
expect(mockHttp.post.mock.calls[0]).toContainEqual(
|
||||
`/api/v2/users/${userId}/roles/`,
|
||||
{ id: roleId }
|
||||
);
|
||||
});
|
||||
|
||||
test('read users calls post with expected params', async () => {
|
||||
|
||||
@ -65,7 +65,7 @@ function AdHocCommands({
|
||||
request: launchAdHocCommands,
|
||||
} = useRequest(
|
||||
useCallback(
|
||||
async values => {
|
||||
async (values) => {
|
||||
const { data } = await InventoriesAPI.launchAdHocCommands(id, values);
|
||||
history.push(`/jobs/command/${data.id}/output`);
|
||||
},
|
||||
@ -74,7 +74,11 @@ function AdHocCommands({
|
||||
)
|
||||
);
|
||||
|
||||
const handleSubmit = async values => {
|
||||
const { error, dismissError } = useDismissableError(
|
||||
launchError || fetchError
|
||||
);
|
||||
|
||||
const handleSubmit = async (values) => {
|
||||
const { credential, execution_environment, ...remainingValues } = values;
|
||||
const newCredential = credential[0].id;
|
||||
|
||||
@ -85,13 +89,9 @@ function AdHocCommands({
|
||||
};
|
||||
await launchAdHocCommands(manipulatedValues);
|
||||
};
|
||||
useEffect(() => onLaunchLoading(isLaunchLoading), [
|
||||
isLaunchLoading,
|
||||
onLaunchLoading,
|
||||
]);
|
||||
|
||||
const { error, dismissError } = useDismissableError(
|
||||
launchError || fetchError
|
||||
useEffect(
|
||||
() => onLaunchLoading(isLaunchLoading),
|
||||
[isLaunchLoading, onLaunchLoading]
|
||||
);
|
||||
|
||||
if (error && isWizardOpen) {
|
||||
|
||||
@ -109,7 +109,7 @@ describe('<AdHocCommands />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'button[aria-label="Run Command"]',
|
||||
el => el.length === 1
|
||||
(el) => el.length === 1
|
||||
);
|
||||
await act(async () =>
|
||||
wrapper.find('button[aria-label="Run Command"]').prop('onClick')()
|
||||
@ -164,7 +164,7 @@ describe('<AdHocCommands />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'button[aria-label="Run Command"]',
|
||||
el => el.length === 1
|
||||
(el) => el.length === 1
|
||||
);
|
||||
await act(async () =>
|
||||
wrapper.find('button[aria-label="Run Command"]').prop('onClick')()
|
||||
@ -190,15 +190,12 @@ describe('<AdHocCommands />', () => {
|
||||
await act(async () =>
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0);
|
||||
|
||||
// second step of wizard
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-2')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-2').find('input').simulate('click');
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -211,13 +208,10 @@ describe('<AdHocCommands />', () => {
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
// third step of wizard
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-4')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-4').find('input').simulate('click');
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -322,7 +316,7 @@ describe('<AdHocCommands />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'button[aria-label="Run Command"]',
|
||||
el => el.length === 1
|
||||
(el) => el.length === 1
|
||||
);
|
||||
await act(async () =>
|
||||
wrapper.find('button[aria-label="Run Command"]').prop('onClick')()
|
||||
@ -353,15 +347,12 @@ describe('<AdHocCommands />', () => {
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0);
|
||||
|
||||
// second step of wizard
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-2')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-2').find('input').simulate('click');
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -374,13 +365,10 @@ describe('<AdHocCommands />', () => {
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
// third step of wizard
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-4')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-4').find('input').simulate('click');
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -399,7 +387,7 @@ describe('<AdHocCommands />', () => {
|
||||
await act(async () =>
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
await waitForElement(wrapper, 'ErrorDetail', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'ErrorDetail', (el) => el.length > 0);
|
||||
});
|
||||
|
||||
test('should disable run command button due to lack of list items', async () => {
|
||||
@ -420,7 +408,7 @@ describe('<AdHocCommands />', () => {
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0);
|
||||
const runCommandsButton = wrapper.find('button[aria-label="Run Command"]');
|
||||
expect(runCommandsButton.prop('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
@ -58,7 +58,7 @@ function AdHocCommandsWizard({
|
||||
|
||||
const FormikApp = withFormik({
|
||||
mapPropsToValues({ adHocItems, verbosityOptions }) {
|
||||
const adHocItemStrings = adHocItems.map(item => item.name).join(', ');
|
||||
const adHocItemStrings = adHocItems.map((item) => item.name).join(', ');
|
||||
return {
|
||||
limit: adHocItemStrings || 'all',
|
||||
credential: [],
|
||||
|
||||
@ -61,7 +61,7 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
});
|
||||
|
||||
test('launch button should be disabled', async () => {
|
||||
waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||
waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0);
|
||||
|
||||
expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(
|
||||
false
|
||||
@ -108,7 +108,7 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
CredentialsAPI.readOptions.mockResolvedValue({
|
||||
data: { actions: { GET: {} } },
|
||||
});
|
||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||
@ -132,17 +132,14 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
|
||||
// step 2
|
||||
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0);
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(
|
||||
false
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-1')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-1').find('input').simulate('click');
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -161,14 +158,11 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
wrapper.update();
|
||||
// step 3
|
||||
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0);
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-1')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-1').find('input').simulate('click');
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -204,7 +198,7 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
});
|
||||
|
||||
test('should show error in navigation bar', async () => {
|
||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||
@ -215,7 +209,7 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
target: { value: '', name: 'module_args' },
|
||||
});
|
||||
});
|
||||
waitForElement(wrapper, 'ExclamationCircleIcon', el => el.length > 0);
|
||||
waitForElement(wrapper, 'ExclamationCircleIcon', (el) => el.length > 0);
|
||||
});
|
||||
|
||||
test('expect credential step to throw error', async () => {
|
||||
@ -234,7 +228,7 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
CredentialsAPI.readOptions.mockResolvedValue({
|
||||
data: { actions: { GET: {} } },
|
||||
});
|
||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||
|
||||
@ -58,10 +58,10 @@ function AdHocCredentialStep({ credentialTypeId }) {
|
||||
credentialCount: count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [credentialTypeId, history.location.search]),
|
||||
{
|
||||
@ -135,7 +135,7 @@ function AdHocCredentialStep({ credentialTypeId }) {
|
||||
},
|
||||
]}
|
||||
name="credential"
|
||||
selectItem={value => {
|
||||
selectItem={(value) => {
|
||||
helpers.setValue([value]);
|
||||
}}
|
||||
deselectItem={() => {
|
||||
|
||||
@ -42,11 +42,11 @@ describe('<AdHocCredentialStep />', () => {
|
||||
});
|
||||
|
||||
test('should mount properly', async () => {
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0);
|
||||
});
|
||||
|
||||
test('should call api', async () => {
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0);
|
||||
expect(CredentialsAPI.read).toHaveBeenCalled();
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
});
|
||||
|
||||
@ -30,9 +30,8 @@ function AdHocDetailsStep({ verbosityOptions, moduleOptions }) {
|
||||
|
||||
const [variablesField] = useField('extra_vars');
|
||||
const [diffModeField, , diffModeHelpers] = useField('diff_mode');
|
||||
const [becomeEnabledField, , becomeEnabledHelpers] = useField(
|
||||
'become_enabled'
|
||||
);
|
||||
const [becomeEnabledField, , becomeEnabledHelpers] =
|
||||
useField('become_enabled');
|
||||
const [verbosityField, verbosityMeta, verbosityHelpers] = useField({
|
||||
name: 'verbosity',
|
||||
validate: required(null),
|
||||
@ -82,7 +81,7 @@ function AdHocDetailsStep({ verbosityOptions, moduleOptions }) {
|
||||
label: t`Choose a module`,
|
||||
isDisabled: true,
|
||||
},
|
||||
...moduleOptions.map(value => ({
|
||||
...moduleOptions.map((value) => ({
|
||||
value: value[0],
|
||||
label: value[0],
|
||||
key: value[0],
|
||||
@ -238,7 +237,7 @@ function AdHocDetailsStep({ verbosityOptions, moduleOptions }) {
|
||||
}
|
||||
id="become_enabled"
|
||||
isChecked={becomeEnabledField.value}
|
||||
onChange={checked => {
|
||||
onChange={(checked) => {
|
||||
becomeEnabledHelpers.setValue(checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -39,11 +39,11 @@ describe('<AdHocExecutionEnvironmentStep />', () => {
|
||||
});
|
||||
|
||||
test('should mount properly', async () => {
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0);
|
||||
});
|
||||
|
||||
test('should call api', async () => {
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0);
|
||||
expect(ExecutionEnvironmentsAPI.read).toHaveBeenCalled();
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
});
|
||||
|
||||
@ -59,10 +59,10 @@ function AdHocExecutionEnvironmentStep({ organizationId }) {
|
||||
executionEnvironmentsCount: count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [history.location.search, organizationId]),
|
||||
{
|
||||
@ -127,7 +127,7 @@ function AdHocExecutionEnvironmentStep({ organizationId }) {
|
||||
name="execution_environment"
|
||||
searchableKeys={searchableKeys}
|
||||
relatedSearchableKeys={relatedSearchableKeys}
|
||||
selectItem={value => {
|
||||
selectItem={(value) => {
|
||||
executionEnvironmentHelpers.setValue([value]);
|
||||
}}
|
||||
deselectItem={() => {
|
||||
|
||||
@ -34,7 +34,7 @@ export default function useAdHocExecutionEnvironmentStep(
|
||||
helpers.setError('A credential must be selected');
|
||||
}
|
||||
},
|
||||
setTouched: setFieldTouched => {
|
||||
setTouched: (setFieldTouched) => {
|
||||
setFieldTouched('credential', true, false);
|
||||
},
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ export default function useAdHocDetailsStep(
|
||||
}
|
||||
}
|
||||
},
|
||||
setTouched: setFieldTouched => {
|
||||
setTouched: (setFieldTouched) => {
|
||||
setFieldTouched('module_name', true, false);
|
||||
setFieldTouched('module_args', true, false);
|
||||
},
|
||||
|
||||
@ -17,32 +17,28 @@ export default function useAdHocLaunchSteps(
|
||||
useAdHocCredentialStep(visited, credentialTypeId),
|
||||
];
|
||||
|
||||
const hasErrors = steps.some(step => step.hasError);
|
||||
const hasErrors = steps.some((step) => step.hasError);
|
||||
|
||||
steps.push(useAdHocPreviewStep(hasErrors));
|
||||
return {
|
||||
steps: steps.map(s => s.step).filter(s => s != null),
|
||||
validateStep: stepId =>
|
||||
steps
|
||||
.find(s => {
|
||||
return s?.step.id === stepId;
|
||||
})
|
||||
.validate(),
|
||||
steps: steps.map((s) => s.step).filter((s) => s != null),
|
||||
validateStep: (stepId) =>
|
||||
steps.find((s) => s?.step.id === stepId).validate(),
|
||||
visitStep: (prevStepId, setFieldTouched) => {
|
||||
setVisited({
|
||||
...visited,
|
||||
[prevStepId]: true,
|
||||
});
|
||||
steps.find(s => s?.step?.id === prevStepId).setTouched(setFieldTouched);
|
||||
steps.find((s) => s?.step?.id === prevStepId).setTouched(setFieldTouched);
|
||||
},
|
||||
visitAllSteps: setFieldTouched => {
|
||||
visitAllSteps: (setFieldTouched) => {
|
||||
setVisited({
|
||||
details: true,
|
||||
executionEnvironment: true,
|
||||
credential: true,
|
||||
preview: true,
|
||||
});
|
||||
steps.forEach(s => s.setTouched(setFieldTouched));
|
||||
steps.forEach((s) => s.setTouched(setFieldTouched));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef, useEffect, Fragment } from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { t } from '@lingui/macro';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Dropdown, DropdownPosition } from '@patternfly/react-core';
|
||||
@ -11,7 +11,7 @@ function AddDropDownButton({ dropdownItems, ouiaId }) {
|
||||
const element = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const toggle = e => {
|
||||
const toggle = (e) => {
|
||||
if (!isKebabified && (!element || !element.current.contains(e.target))) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
@ -24,7 +24,7 @@ function AddDropDownButton({ dropdownItems, ouiaId }) {
|
||||
}, [isKebabified]);
|
||||
|
||||
if (isKebabified) {
|
||||
return <Fragment>{dropdownItems}</Fragment>;
|
||||
return <>{dropdownItems}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { t } from '@lingui/macro';
|
||||
@ -8,12 +8,12 @@ import Wizard from '../Wizard';
|
||||
import SelectResourceStep from './SelectResourceStep';
|
||||
import SelectRoleStep from './SelectRoleStep';
|
||||
|
||||
const readUsers = async queryParams =>
|
||||
const readUsers = async (queryParams) =>
|
||||
UsersAPI.read(Object.assign(queryParams, { is_superuser: false }));
|
||||
|
||||
const readUsersOptions = async () => UsersAPI.readOptions();
|
||||
|
||||
const readTeams = async queryParams => TeamsAPI.read(queryParams);
|
||||
const readTeams = async (queryParams) => TeamsAPI.read(queryParams);
|
||||
|
||||
const readTeamsOptions = async () => TeamsAPI.readOptions();
|
||||
|
||||
@ -77,9 +77,9 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
const [currentStepId, setCurrentStepId] = useState(1);
|
||||
const [maxEnabledStep, setMaxEnabledStep] = useState(1);
|
||||
|
||||
const handleResourceCheckboxClick = user => {
|
||||
const handleResourceCheckboxClick = (user) => {
|
||||
const selectedIndex = selectedResourceRows.findIndex(
|
||||
selectedRow => selectedRow.id === user.id
|
||||
(selectedRow) => selectedRow.id === user.id
|
||||
);
|
||||
if (selectedIndex > -1) {
|
||||
selectedResourceRows.splice(selectedIndex, 1);
|
||||
@ -98,9 +98,9 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
}
|
||||
}, [currentStepId, history, maxEnabledStep]);
|
||||
|
||||
const handleRoleCheckboxClick = role => {
|
||||
const handleRoleCheckboxClick = (role) => {
|
||||
const selectedIndex = selectedRoleRows.findIndex(
|
||||
selectedRow => selectedRow.id === role.id
|
||||
(selectedRow) => selectedRow.id === role.id
|
||||
);
|
||||
|
||||
if (selectedIndex > -1) {
|
||||
@ -112,18 +112,18 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleResourceSelect = resourceType => {
|
||||
const handleResourceSelect = (resourceType) => {
|
||||
setSelectedResource(resourceType);
|
||||
setSelectedResourceRows([]);
|
||||
setSelectedRoleRows([]);
|
||||
};
|
||||
|
||||
const handleWizardNext = step => {
|
||||
const handleWizardNext = (step) => {
|
||||
setCurrentStepId(step.id);
|
||||
setMaxEnabledStep(step.id);
|
||||
};
|
||||
|
||||
const handleWizardGoToStep = step => {
|
||||
const handleWizardGoToStep = (step) => {
|
||||
setCurrentStepId(step.id);
|
||||
};
|
||||
|
||||
@ -163,7 +163,7 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
// showing role choices for team access
|
||||
const selectableRoles = { ...roles };
|
||||
if (selectedResource === 'teams') {
|
||||
Object.keys(roles).forEach(key => {
|
||||
Object.keys(roles).forEach((key) => {
|
||||
if (selectableRoles[key].user_only) {
|
||||
delete selectableRoles[key];
|
||||
}
|
||||
@ -219,7 +219,7 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
id: 2,
|
||||
name: t`Select Items from List`,
|
||||
component: (
|
||||
<Fragment>
|
||||
<>
|
||||
{selectedResource === 'users' && (
|
||||
<SelectResourceStep
|
||||
searchColumns={userSearchColumns}
|
||||
@ -244,7 +244,7 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
selectedResourceRows={selectedResourceRows}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
),
|
||||
enableNext: selectedResourceRows.length > 0,
|
||||
nextButtonText: t`Next`,
|
||||
@ -269,17 +269,17 @@ function AddResourceRole({ onSave, onClose, roles, resource, onError }) {
|
||||
},
|
||||
];
|
||||
|
||||
const currentStep = steps.find(step => step.id === currentStepId);
|
||||
const currentStep = steps.find((step) => step.id === currentStepId);
|
||||
|
||||
return (
|
||||
<Wizard
|
||||
style={{ overflow: 'scroll' }}
|
||||
isOpen
|
||||
onNext={handleWizardNext}
|
||||
onBack={step => setCurrentStepId(step.id)}
|
||||
onBack={(step) => setCurrentStepId(step.id)}
|
||||
onClose={onClose}
|
||||
onSave={handleWizardSave}
|
||||
onGoToStep={step => handleWizardGoToStep(step)}
|
||||
onGoToStep={(step) => handleWizardGoToStep(step)}
|
||||
steps={steps}
|
||||
title={wizardTitle}
|
||||
nextButtonText={currentStep.nextButtonText || undefined}
|
||||
|
||||
@ -68,7 +68,7 @@ describe('<_AddResourceRole />', () => {
|
||||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
roles={roles}
|
||||
i18n={{ _: val => val.toString() }}
|
||||
i18n={{ _: (val) => val.toString() }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@ -93,7 +93,7 @@ describe('<_AddResourceRole />', () => {
|
||||
wrapper.update();
|
||||
|
||||
// Step 2
|
||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
expect(wrapper.find('Chip').length).toBe(0);
|
||||
act(() =>
|
||||
wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true)
|
||||
@ -161,7 +161,7 @@ describe('<_AddResourceRole />', () => {
|
||||
wrapper.update();
|
||||
|
||||
// Step 2
|
||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
act(() =>
|
||||
wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true)
|
||||
);
|
||||
@ -213,7 +213,7 @@ describe('<_AddResourceRole />', () => {
|
||||
wrapper.update();
|
||||
|
||||
// Step 2
|
||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
act(() =>
|
||||
wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true)
|
||||
);
|
||||
@ -246,7 +246,7 @@ describe('<_AddResourceRole />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'SelectableCard[label="Users"]',
|
||||
el => el.prop('isSelected') === true
|
||||
(el) => el.prop('isSelected') === true
|
||||
);
|
||||
act(() => wrapper.find('SelectableCard[label="Teams"]').prop('onClick')());
|
||||
wrapper.update();
|
||||
@ -254,7 +254,7 @@ describe('<_AddResourceRole />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'SelectableCard[label="Teams"]',
|
||||
el => el.prop('isSelected') === true
|
||||
(el) => el.prop('isSelected') === true
|
||||
);
|
||||
});
|
||||
|
||||
@ -279,7 +279,7 @@ describe('<_AddResourceRole />', () => {
|
||||
wrapper.update();
|
||||
|
||||
// Step 2
|
||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
act(() =>
|
||||
wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true)
|
||||
);
|
||||
@ -322,17 +322,17 @@ describe('<_AddResourceRole />', () => {
|
||||
wrapper.update();
|
||||
|
||||
// Make sure no teams have been selected
|
||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
wrapper
|
||||
.find('DataListCheck')
|
||||
.map(item => expect(item.prop('checked')).toBe(false));
|
||||
.map((item) => expect(item.prop('checked')).toBe(false));
|
||||
act(() => wrapper.find('Button[type="submit"]').prop('onClick')());
|
||||
wrapper.update();
|
||||
|
||||
// Make sure that no roles have been selected
|
||||
wrapper
|
||||
.find('Checkbox')
|
||||
.map(card => expect(card.prop('isChecked')).toBe(false));
|
||||
.map((card) => expect(card.prop('isChecked')).toBe(false));
|
||||
|
||||
// Make sure the save button is disabled
|
||||
expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(true);
|
||||
@ -379,7 +379,7 @@ describe('<_AddResourceRole />', () => {
|
||||
wrapper.update();
|
||||
|
||||
// Step 2
|
||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
expect(wrapper.find('Chip').length).toBe(0);
|
||||
act(() =>
|
||||
wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Checkbox as PFCheckbox } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
@ -28,10 +28,10 @@ class CheckboxCard extends Component {
|
||||
aria-label={name}
|
||||
id={`checkbox-card-${itemId}`}
|
||||
label={
|
||||
<Fragment>
|
||||
<>
|
||||
<div style={{ fontWeight: 'bold' }}>{name}</div>
|
||||
<div>{description}</div>
|
||||
</Fragment>
|
||||
</>
|
||||
}
|
||||
value={itemId}
|
||||
/>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter, useLocation } from 'react-router-dom';
|
||||
import { t } from '@lingui/macro';
|
||||
@ -10,15 +10,16 @@ import CheckboxListItem from '../CheckboxListItem';
|
||||
import { SelectedList } from '../SelectedList';
|
||||
import PaginatedTable, { HeaderCell, HeaderRow } from '../PaginatedTable';
|
||||
|
||||
const QS_Config = sortColumns => {
|
||||
return getQSConfig('resource', {
|
||||
const QS_Config = (sortColumns) =>
|
||||
getQSConfig('resource', {
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
order_by: `${
|
||||
sortColumns.filter(col => col.key === 'name').length ? 'name' : 'username'
|
||||
sortColumns.filter((col) => col.key === 'name').length
|
||||
? 'name'
|
||||
: 'username'
|
||||
}`,
|
||||
});
|
||||
};
|
||||
function SelectResourceStep({
|
||||
searchColumns,
|
||||
sortColumns,
|
||||
@ -54,10 +55,10 @@ function SelectResourceStep({
|
||||
itemCount: count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [location, fetchItems, fetchOptions, sortColumns]),
|
||||
{
|
||||
@ -73,7 +74,7 @@ function SelectResourceStep({
|
||||
}, [readResourceList]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<div>
|
||||
{t`Choose the resources that will be receiving new roles. You'll be able to select the roles to apply in the next step. Note that the resources chosen here will receive all roles chosen in the next step.`}
|
||||
</div>
|
||||
@ -108,7 +109,7 @@ function SelectResourceStep({
|
||||
}
|
||||
renderRow={(item, index) => (
|
||||
<CheckboxListItem
|
||||
isSelected={selectedResourceRows.some(i => i.id === item.id)}
|
||||
isSelected={selectedResourceRows.some((i) => i.id === item.id)}
|
||||
itemId={item.id}
|
||||
item={item}
|
||||
rowIndex={index}
|
||||
@ -120,10 +121,10 @@ function SelectResourceStep({
|
||||
onDeselect={() => onRowClick(item)}
|
||||
/>
|
||||
)}
|
||||
renderToolbar={props => <DataListToolbar {...props} fillWidth />}
|
||||
renderToolbar={(props) => <DataListToolbar {...props} fillWidth />}
|
||||
showPageSizeOptions={false}
|
||||
/>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ describe('<SelectResourceStep />', () => {
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
});
|
||||
waitForElement(wrapper, 'CheckBoxListItem', el => el.length === 2);
|
||||
waitForElement(wrapper, 'CheckBoxListItem', (el) => el.length === 2);
|
||||
});
|
||||
|
||||
test('clicking on row fires callback with correct params', async () => {
|
||||
|
||||
@ -15,7 +15,7 @@ function RolesStep({
|
||||
selectedRoleRows,
|
||||
}) {
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<div>
|
||||
{t`Choose roles to apply to the selected resources. Note that all selected roles will be applied to all selected resources.`}
|
||||
</div>
|
||||
@ -37,12 +37,12 @@ function RolesStep({
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
{Object.keys(roles).map(role => (
|
||||
{Object.keys(roles).map((role) => (
|
||||
<CheckboxCard
|
||||
description={roles[role].description}
|
||||
itemId={roles[role].id}
|
||||
isSelected={selectedRoleRows.some(
|
||||
item => item.id === roles[role].id
|
||||
(item) => item.id === roles[role].id
|
||||
)}
|
||||
key={roles[role].id}
|
||||
name={roles[role].name}
|
||||
@ -50,7 +50,7 @@ function RolesStep({
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ function AnsibleSelect({
|
||||
className={className}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
{data.map(option => (
|
||||
{data.map((option) => (
|
||||
<FormSelectOption
|
||||
key={option.key}
|
||||
value={option.value}
|
||||
|
||||
@ -166,7 +166,7 @@ describe('<AppContainer />', () => {
|
||||
|
||||
// open about modal
|
||||
(
|
||||
await waitForElement(wrapper, aboutButton, el => !el.props().disabled)
|
||||
await waitForElement(wrapper, aboutButton, (el) => !el.props().disabled)
|
||||
).simulate('click');
|
||||
|
||||
// check about modal content
|
||||
|
||||
@ -47,21 +47,19 @@ function PageHeaderToolbar({
|
||||
const [isUserOpen, setIsUserOpen] = useState(false);
|
||||
const config = useConfig();
|
||||
|
||||
const {
|
||||
request: fetchPendingApprovalCount,
|
||||
result: pendingApprovals,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const {
|
||||
data: { count },
|
||||
} = await WorkflowApprovalsAPI.read({
|
||||
status: 'pending',
|
||||
page_size: 1,
|
||||
});
|
||||
return count;
|
||||
}, []),
|
||||
0
|
||||
);
|
||||
const { request: fetchPendingApprovalCount, result: pendingApprovals } =
|
||||
useRequest(
|
||||
useCallback(async () => {
|
||||
const {
|
||||
data: { count },
|
||||
} = await WorkflowApprovalsAPI.read({
|
||||
status: 'pending',
|
||||
page_size: 1,
|
||||
});
|
||||
return count;
|
||||
}, []),
|
||||
0
|
||||
);
|
||||
|
||||
const pendingApprovalsCount = useWsPendingApprovalCount(
|
||||
pendingApprovals,
|
||||
|
||||
@ -6,9 +6,8 @@ export default function useWsPendingApprovalCount(
|
||||
initialCount,
|
||||
fetchApprovalsCount
|
||||
) {
|
||||
const [pendingApprovalCount, setPendingApprovalCount] = useState(
|
||||
initialCount
|
||||
);
|
||||
const [pendingApprovalCount, setPendingApprovalCount] =
|
||||
useState(initialCount);
|
||||
const [reloadCount, setReloadCount] = useState(false);
|
||||
const throttledFetch = useThrottle(reloadCount, 1000);
|
||||
const lastMessage = useWebsocket({
|
||||
@ -20,27 +19,21 @@ export default function useWsPendingApprovalCount(
|
||||
setPendingApprovalCount(initialCount);
|
||||
}, [initialCount]);
|
||||
|
||||
useEffect(
|
||||
function reloadTheCount() {
|
||||
(async () => {
|
||||
if (!throttledFetch) {
|
||||
return;
|
||||
}
|
||||
setReloadCount(false);
|
||||
fetchApprovalsCount();
|
||||
})();
|
||||
},
|
||||
[throttledFetch, fetchApprovalsCount]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
function processWsMessage() {
|
||||
if (lastMessage?.type === 'workflow_approval') {
|
||||
setReloadCount(true);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!throttledFetch) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
[lastMessage]
|
||||
);
|
||||
setReloadCount(false);
|
||||
fetchApprovalsCount();
|
||||
})();
|
||||
}, [throttledFetch, fetchApprovalsCount]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastMessage?.type === 'workflow_approval') {
|
||||
setReloadCount(true);
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
return pendingApprovalCount;
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ describe('useWsPendingApprovalCount hook', () => {
|
||||
*/
|
||||
jest.mock('../../hooks/useThrottle', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(val => val),
|
||||
default: jest.fn((val) => val),
|
||||
}));
|
||||
debug = global.console.debug; // eslint-disable-line prefer-destructuring
|
||||
global.console.debug = () => {};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useEffect, useCallback } from 'react';
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
@ -8,13 +8,12 @@ import { getQSConfig, parseQueryString } from 'util/qs';
|
||||
import useSelected from 'hooks/useSelected';
|
||||
import OptionsList from '../OptionsList';
|
||||
|
||||
const QS_CONFIG = (order_by = 'name') => {
|
||||
return getQSConfig('associate', {
|
||||
const QS_CONFIG = (order_by = 'name') =>
|
||||
getQSConfig('associate', {
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
order_by,
|
||||
});
|
||||
};
|
||||
|
||||
function AssociateModal({
|
||||
header = t`Items`,
|
||||
@ -53,10 +52,10 @@ function AssociateModal({
|
||||
itemCount: count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [fetchRequest, optionsRequest, history.location.search, displayKey]),
|
||||
{
|
||||
@ -75,7 +74,7 @@ function AssociateModal({
|
||||
const parts = history.location.search.replace(/^\?/, '').split('&');
|
||||
const { namespace } = QS_CONFIG(displayKey);
|
||||
const otherParts = parts.filter(
|
||||
param => !param.startsWith(`${namespace}.`)
|
||||
(param) => !param.startsWith(`${namespace}.`)
|
||||
);
|
||||
history.replace(`${history.location.pathname}?${otherParts.join('&')}`);
|
||||
};
|
||||
@ -92,7 +91,7 @@ function AssociateModal({
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<Modal
|
||||
ouiaId={ouiaId}
|
||||
variant="large"
|
||||
@ -160,7 +159,7 @@ function AssociateModal({
|
||||
relatedSearchableKeys={relatedSearchableKeys}
|
||||
/>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ describe('<AssociateModal />', () => {
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -61,10 +61,7 @@ describe('<AssociateModal />', () => {
|
||||
test('should update selected list chips when items are selected', () => {
|
||||
expect(wrapper.find('SelectedList Chip')).toHaveLength(0);
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('CheckboxListItem')
|
||||
.first()
|
||||
.invoke('onSelect')();
|
||||
wrapper.find('CheckboxListItem').first().invoke('onSelect')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('SelectedList Chip')).toHaveLength(1);
|
||||
@ -74,10 +71,7 @@ describe('<AssociateModal />', () => {
|
||||
|
||||
test('save button should call onAssociate', () => {
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('CheckboxListItem')
|
||||
.first()
|
||||
.invoke('onSelect')();
|
||||
wrapper.find('CheckboxListItem').first().invoke('onSelect')();
|
||||
});
|
||||
wrapper.find('button[aria-label="Save"]').simulate('click');
|
||||
expect(onAssociate).toHaveBeenCalledTimes(1);
|
||||
|
||||
@ -3,8 +3,8 @@ import React, { Fragment } from 'react';
|
||||
import { BackgroundImage } from '@patternfly/react-core';
|
||||
|
||||
export default ({ children }) => (
|
||||
<Fragment>
|
||||
<>
|
||||
<BackgroundImage />
|
||||
{children}
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -43,7 +43,7 @@ const CheckboxListItem = ({
|
||||
/>
|
||||
|
||||
{columns?.length > 0 ? (
|
||||
columns.map(col => (
|
||||
columns.map((col) => (
|
||||
<Td aria-label={col.name} dataLabel={col.key} key={col.key}>
|
||||
{item[col.key]}
|
||||
</Td>
|
||||
|
||||
@ -7,11 +7,8 @@ describe('ChipGroup', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<ChipGroup numChips={5} totalChips={10} />
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('ChipGroup')
|
||||
.at(1)
|
||||
.props().collapsedText
|
||||
).toEqual('5 more');
|
||||
expect(wrapper.find('ChipGroup').at(1).props().collapsedText).toEqual(
|
||||
'5 more'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ const AceEditor = styled(ReactAce)`
|
||||
display: none;
|
||||
}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.hasErrors &&
|
||||
`
|
||||
&& {
|
||||
@ -59,7 +59,7 @@ const AceEditor = styled(ReactAce)`
|
||||
border-bottom-width: var(--pf-c-form-control--invalid--BorderBottomWidth);
|
||||
}`}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.setOptions.readOnly &&
|
||||
`
|
||||
&& .ace_cursor {
|
||||
@ -92,21 +92,18 @@ function CodeEditor({
|
||||
const wrapper = useRef(null);
|
||||
const editor = useRef(null);
|
||||
|
||||
useEffect(
|
||||
function removeTextareaTabIndex() {
|
||||
const editorInput = editor.current.refEditor?.querySelector('textarea');
|
||||
if (!editorInput) {
|
||||
return;
|
||||
}
|
||||
if (!readOnly) {
|
||||
editorInput.tabIndex = -1;
|
||||
}
|
||||
editorInput.id = id;
|
||||
},
|
||||
[readOnly, id]
|
||||
);
|
||||
useEffect(() => {
|
||||
const editorInput = editor.current.refEditor?.querySelector('textarea');
|
||||
if (!editorInput) {
|
||||
return;
|
||||
}
|
||||
if (!readOnly) {
|
||||
editorInput.tabIndex = -1;
|
||||
}
|
||||
editorInput.id = id;
|
||||
}, [readOnly, id]);
|
||||
|
||||
const listen = useCallback(event => {
|
||||
const listen = useCallback((event) => {
|
||||
if (wrapper.current === document.activeElement && event.key === 'Enter') {
|
||||
const editorInput = editor.current.refEditor?.querySelector('textarea');
|
||||
if (!editorInput) {
|
||||
@ -118,7 +115,7 @@ function CodeEditor({
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(function addKeyEventListeners() {
|
||||
useEffect(() => {
|
||||
const wrapperEl = wrapper.current;
|
||||
wrapperEl.addEventListener('keydown', listen);
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ describe('CodeEditor', () => {
|
||||
});
|
||||
|
||||
it('should trigger onChange prop', () => {
|
||||
debounce.mockImplementation(fn => fn);
|
||||
debounce.mockImplementation((fn) => fn);
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mountWithContexts(
|
||||
<CodeEditor value="---" onChange={onChange} mode="yaml" />
|
||||
|
||||
@ -43,7 +43,7 @@ function CodeEditorField({
|
||||
id={id}
|
||||
{...rest}
|
||||
{...field}
|
||||
onChange={value => {
|
||||
onChange={(value) => {
|
||||
helpers.setValue(value);
|
||||
}}
|
||||
mode={mode}
|
||||
|
||||
@ -196,7 +196,7 @@ function ModeToggle({
|
||||
[JSON_MODE, 'JSON'],
|
||||
]}
|
||||
value={mode}
|
||||
onChange={newMode => {
|
||||
onChange={(newMode) => {
|
||||
setMode(newMode);
|
||||
}}
|
||||
name={name}
|
||||
|
||||
@ -39,7 +39,7 @@ function VariablesField({
|
||||
const [shouldValidate, setShouldValidate] = useState(false);
|
||||
const [mode, setMode] = useState(initialMode || YAML_MODE);
|
||||
const validate = useCallback(
|
||||
value => {
|
||||
(value) => {
|
||||
if (!shouldValidate) {
|
||||
return undefined;
|
||||
}
|
||||
@ -58,7 +58,7 @@ function VariablesField({
|
||||
);
|
||||
const [field, meta, helpers] = useField({ name, validate });
|
||||
|
||||
useEffect(function initializeJSON() {
|
||||
useEffect(() => {
|
||||
if (isJsonString(field.value)) {
|
||||
// mode's useState above couldn't be initialized to JSON_MODE because
|
||||
// the field value had to be defined below it
|
||||
@ -69,7 +69,7 @@ function VariablesField({
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
useEffect(
|
||||
function validateOnBlur() {
|
||||
() => {
|
||||
if (shouldValidate) {
|
||||
helpers.setError(validate(field.value));
|
||||
}
|
||||
@ -82,7 +82,7 @@ function VariablesField({
|
||||
const [isJsonEdited, setIsJsonEdited] = useState(false);
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const handleModeChange = newMode => {
|
||||
const handleModeChange = (newMode) => {
|
||||
if (newMode === YAML_MODE && !isJsonEdited && lastYamlValue !== null) {
|
||||
helpers.setValue(lastYamlValue, false);
|
||||
setMode(newMode);
|
||||
@ -103,7 +103,7 @@ function VariablesField({
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = newVal => {
|
||||
const handleChange = (newVal) => {
|
||||
helpers.setValue(newVal);
|
||||
if (mode === JSON_MODE) {
|
||||
setIsJsonEdited(true);
|
||||
@ -201,7 +201,7 @@ function VariablesFieldInternals({
|
||||
}) {
|
||||
const [field, meta, helpers] = useField(name);
|
||||
|
||||
useEffect(function formatJsonString() {
|
||||
useEffect(() => {
|
||||
if (mode === YAML_MODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -140,10 +140,7 @@ describe('VariablesField', () => {
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
wrapper
|
||||
.find('Button')
|
||||
.at(1)
|
||||
.simulate('click');
|
||||
wrapper.find('Button').at(1).simulate('click');
|
||||
wrapper.update();
|
||||
|
||||
const field = wrapper.find('CodeEditor');
|
||||
@ -173,7 +170,7 @@ describe('VariablesField', () => {
|
||||
const handleSubmit = jest.fn();
|
||||
const wrapper = mountWithContexts(
|
||||
<Formik initialValues={{ variables: value }} onSubmit={handleSubmit}>
|
||||
{formik => (
|
||||
{(formik) => (
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<VariablesField id="the-field" name="variables" label="Variables" />
|
||||
<button type="submit" id="submit">
|
||||
@ -200,7 +197,7 @@ describe('VariablesField', () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Formik initialValues={{ variables: value }} onSubmit={jest.fn()}>
|
||||
{formik => (
|
||||
{(formik) => (
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<VariablesField
|
||||
id="the-field"
|
||||
@ -224,7 +221,7 @@ describe('VariablesField', () => {
|
||||
const value = '---';
|
||||
const wrapper = mountWithContexts(
|
||||
<Formik initialValues={{ variables: value }} onSubmit={jest.fn()}>
|
||||
{formik => (
|
||||
{(formik) => (
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<VariablesField id="the-field" name="variables" label="Variables" />
|
||||
<button type="submit" id="submit">
|
||||
@ -249,7 +246,7 @@ describe('VariablesField', () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Formik initialValues={{ variables: value }} onSubmit={jest.fn()}>
|
||||
{formik => (
|
||||
{(formik) => (
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<VariablesField
|
||||
id="the-field"
|
||||
|
||||
@ -26,7 +26,7 @@ function VariablesInput(props) {
|
||||
const isControlled = !!props.onChange;
|
||||
/* eslint-enable react/destructuring-assignment */
|
||||
|
||||
const onChange = newValue => {
|
||||
const onChange = (newValue) => {
|
||||
if (isControlled) {
|
||||
props.onChange(newValue);
|
||||
}
|
||||
@ -48,7 +48,7 @@ function VariablesInput(props) {
|
||||
[JSON_MODE, 'JSON'],
|
||||
]}
|
||||
value={mode}
|
||||
onChange={newMode => {
|
||||
onChange={(newMode) => {
|
||||
try {
|
||||
if (mode === JSON_MODE) {
|
||||
onChange(jsonToYaml(value));
|
||||
|
||||
@ -26,7 +26,7 @@ function ContentError({ error, children, isNotFound }) {
|
||||
isNotFound || (error && error.response && error.response.status === 404);
|
||||
const is401 = error && error.response && error.response.status === 401;
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
{is401 ? (
|
||||
<Redirect to="/login" />
|
||||
) : (
|
||||
@ -44,7 +44,7 @@ function ContentError({ error, children, isNotFound }) {
|
||||
{error && <ErrorDetail error={error} />}
|
||||
</EmptyState>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
ContentError.propTypes = {
|
||||
|
||||
@ -13,13 +13,11 @@ const EmptyState = styled(PFEmptyState)`
|
||||
`;
|
||||
|
||||
// TODO: Better loading state - skeleton lines / spinner, etc.
|
||||
const ContentLoading = ({ className }) => {
|
||||
return (
|
||||
<EmptyState variant="full" className={className}>
|
||||
<EmptyStateIcon variant="container" component={Spinner} />
|
||||
</EmptyState>
|
||||
);
|
||||
};
|
||||
const ContentLoading = ({ className }) => (
|
||||
<EmptyState variant="full" className={className}>
|
||||
<EmptyStateIcon variant="container" component={Spinner} />
|
||||
</EmptyState>
|
||||
);
|
||||
|
||||
export { ContentLoading as _ContentLoading };
|
||||
export default ContentLoading;
|
||||
|
||||
@ -16,9 +16,11 @@ function CopyButton({
|
||||
errorMessage,
|
||||
ouiaId,
|
||||
}) {
|
||||
const { isLoading, error: copyError, request: copyItemToAPI } = useRequest(
|
||||
copyItem
|
||||
);
|
||||
const {
|
||||
isLoading,
|
||||
error: copyError,
|
||||
request: copyItemToAPI,
|
||||
} = useRequest(copyItem);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
|
||||
@ -66,7 +66,7 @@ function DataListToolbar({
|
||||
const dropdownPosition =
|
||||
viewportWidth >= 992 ? DropdownPosition.right : DropdownPosition.left;
|
||||
|
||||
const onShowAdvancedSearch = shown => {
|
||||
const onShowAdvancedSearch = (shown) => {
|
||||
setIsAdvancedSearchShown(shown);
|
||||
setIsKebabOpen(false);
|
||||
};
|
||||
@ -165,7 +165,7 @@ function DataListToolbar({
|
||||
toggle={
|
||||
<KebabToggle
|
||||
data-cy="actions-kebab-toogle"
|
||||
onToggle={isOpen => {
|
||||
onToggle={(isOpen) => {
|
||||
if (!isKebabModalOpen) {
|
||||
setIsKebabOpen(isOpen);
|
||||
}
|
||||
@ -182,7 +182,7 @@ function DataListToolbar({
|
||||
)}
|
||||
{!isAdvancedSearchShown && (
|
||||
<ToolbarGroup>
|
||||
{additionalControls.map(control => (
|
||||
{additionalControls.map((control) => (
|
||||
<ToolbarItem key={control.key}>{control}</ToolbarItem>
|
||||
))}
|
||||
</ToolbarGroup>
|
||||
|
||||
@ -245,7 +245,7 @@ describe('<DataListToolbar />', () => {
|
||||
|
||||
const search = toolbar.find('Search');
|
||||
expect(
|
||||
search.prop('columns').filter(col => col.key === 'advanced').length
|
||||
search.prop('columns').filter((col) => col.key === 'advanced').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ function DeleteButton({
|
||||
const [deleteDetails, setDeleteDetails] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const toggleModal = async isModalOpen => {
|
||||
const toggleModal = async (isModalOpen) => {
|
||||
setIsLoading(true);
|
||||
if (deleteDetailsRequests?.length && isModalOpen) {
|
||||
const { results, error } = await getRelatedResourceDeleteCounts(
|
||||
|
||||
@ -42,7 +42,7 @@ describe('<DeleteButton />', () => {
|
||||
wrapper.find('button').prop('onClick')();
|
||||
});
|
||||
|
||||
await waitForElement(wrapper, 'Modal', el => el.length > 0);
|
||||
await waitForElement(wrapper, 'Modal', (el) => el.length > 0);
|
||||
expect(wrapper.find('Modal')).toHaveLength(1);
|
||||
|
||||
expect(wrapper.find('div[aria-label="Delete this?"]')).toHaveLength(1);
|
||||
|
||||
@ -24,7 +24,7 @@ function ArrayDetail({ label, value, dataCy }) {
|
||||
{label}
|
||||
</DetailName>
|
||||
<Value component={TextListItemVariants.dd} data-cy={valueCy}>
|
||||
{vals.map(v => (
|
||||
{vals.map((v) => (
|
||||
<div key={v}>{v}</div>
|
||||
))}
|
||||
</Value>
|
||||
|
||||
@ -8,7 +8,7 @@ const DetailName = styled(({ fullWidth, ...props }) => (
|
||||
<TextListItem {...props} />
|
||||
))`
|
||||
font-weight: var(--pf-global--FontWeight--bold);
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.fullWidth &&
|
||||
`
|
||||
grid-column: 1;
|
||||
@ -21,12 +21,12 @@ const DetailValue = styled(
|
||||
)
|
||||
)`
|
||||
word-break: break-all;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.fullWidth &&
|
||||
`
|
||||
grid-column: 2 / -1;
|
||||
`}
|
||||
${props =>
|
||||
${(props) =>
|
||||
(props.isEncrypted || props.isNotConfigured) &&
|
||||
`
|
||||
color: var(--pf-global--Color--400);
|
||||
|
||||
@ -12,7 +12,7 @@ export default styled(DetailList)`
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
align-items: start;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.stacked
|
||||
? `
|
||||
grid-template-columns: auto 1fr;
|
||||
|
||||
@ -41,7 +41,7 @@ function DisassociateButton({
|
||||
if (verifyCannotDisassociate) {
|
||||
const itemsUnableToDisassociate = itemsToDisassociate
|
||||
.filter(cannotDisassociate)
|
||||
.map(item => item.name)
|
||||
.map((item) => item.name)
|
||||
.join(', ');
|
||||
|
||||
if (itemsToDisassociate.some(cannotDisassociate)) {
|
||||
@ -130,7 +130,7 @@ function DisassociateButton({
|
||||
|
||||
<div>{t`This action will disassociate the following:`}</div>
|
||||
|
||||
{itemsToDisassociate.map(item => (
|
||||
{itemsToDisassociate.map((item) => (
|
||||
<span key={item.id}>
|
||||
<strong>{item.hostname ? item.hostname : item.name}</strong>
|
||||
<br />
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, Fragment } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
@ -48,7 +48,7 @@ function ErrorDetail({ error }) {
|
||||
const message = getErrorMessage(response);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<CardBody>
|
||||
{response?.config?.method.toUpperCase()} {response?.config?.url}{' '}
|
||||
<strong>{response?.status}</strong>
|
||||
@ -56,7 +56,7 @@ function ErrorDetail({ error }) {
|
||||
<CardBody>
|
||||
{Array.isArray(message) ? (
|
||||
<ul>
|
||||
{message.map(m =>
|
||||
{message.map((m) =>
|
||||
typeof m === 'string' ? <li key={m}>{m}</li> : null
|
||||
)}
|
||||
</ul>
|
||||
@ -64,13 +64,11 @@ function ErrorDetail({ error }) {
|
||||
message
|
||||
)}
|
||||
</CardBody>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderStack = () => {
|
||||
return <CardBody>{error.stack}</CardBody>;
|
||||
};
|
||||
const renderStack = () => <CardBody>{error.stack}</CardBody>;
|
||||
|
||||
return (
|
||||
<Expandable
|
||||
|
||||
@ -14,7 +14,7 @@ const Button = styled(PFButton)`
|
||||
margin: 0;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.isActive
|
||||
? `
|
||||
background-color: #007bba;
|
||||
@ -33,7 +33,7 @@ const ToolbarItem = styled(PFToolbarItem)`
|
||||
// with ExpandingContainer
|
||||
function ExpandCollapse({ isCompact, onCompact, onExpand }) {
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<ToolbarItem>
|
||||
<Button
|
||||
ouiaId="toolbar-collapse-button"
|
||||
@ -56,7 +56,7 @@ function ExpandCollapse({ isCompact, onCompact, onExpand }) {
|
||||
<EqualsIcon />
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,33 +5,31 @@ import { t } from '@lingui/macro';
|
||||
import { ActionGroup, Button } from '@patternfly/react-core';
|
||||
import { FormFullWidthLayout } from '../FormLayout';
|
||||
|
||||
const FormActionGroup = ({ onCancel, onSubmit, submitDisabled }) => {
|
||||
return (
|
||||
<FormFullWidthLayout>
|
||||
<ActionGroup>
|
||||
<Button
|
||||
ouiaId="form-save-button"
|
||||
aria-label={t`Save`}
|
||||
variant="primary"
|
||||
type="button"
|
||||
onClick={onSubmit}
|
||||
isDisabled={submitDisabled}
|
||||
>
|
||||
{t`Save`}
|
||||
</Button>
|
||||
<Button
|
||||
ouiaId="form-cancel-button"
|
||||
aria-label={t`Cancel`}
|
||||
variant="link"
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t`Cancel`}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
</FormFullWidthLayout>
|
||||
);
|
||||
};
|
||||
const FormActionGroup = ({ onCancel, onSubmit, submitDisabled }) => (
|
||||
<FormFullWidthLayout>
|
||||
<ActionGroup>
|
||||
<Button
|
||||
ouiaId="form-save-button"
|
||||
aria-label={t`Save`}
|
||||
variant="primary"
|
||||
type="button"
|
||||
onClick={onSubmit}
|
||||
isDisabled={submitDisabled}
|
||||
>
|
||||
{t`Save`}
|
||||
</Button>
|
||||
<Button
|
||||
ouiaId="form-cancel-button"
|
||||
aria-label={t`Cancel`}
|
||||
variant="link"
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t`Cancel`}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
</FormFullWidthLayout>
|
||||
);
|
||||
|
||||
FormActionGroup.propTypes = {
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
|
||||
@ -40,8 +40,8 @@ function ArrayTextField(props) {
|
||||
{...rest}
|
||||
{...field}
|
||||
value={value.join('\n')}
|
||||
onChange={val => {
|
||||
helpers.setValue(val.split('\n').map(v => v.trim()));
|
||||
onChange={(val) => {
|
||||
helpers.setValue(val.split('\n').map((v) => v.trim()));
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@ -29,7 +29,7 @@ function FormSubmitError({ error }) {
|
||||
isInline
|
||||
title={
|
||||
Array.isArray(errorMessage)
|
||||
? errorMessage.map(msg => <div key={msg}>{msg}</div>)
|
||||
? errorMessage.map((msg) => <div key={msg}>{msg}</div>)
|
||||
: errorMessage
|
||||
}
|
||||
/>
|
||||
|
||||
@ -28,7 +28,7 @@ export default function sortErrorMessages(error, formValues = {}) {
|
||||
function parseFieldErrors(obj, formValues) {
|
||||
let fieldErrors = {};
|
||||
let formErrors = [];
|
||||
Object.keys(obj).forEach(key => {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = obj[key];
|
||||
if (typeof value === 'string') {
|
||||
if (typeof formValues[key] === 'undefined') {
|
||||
|
||||
@ -13,12 +13,11 @@ import Popover from '../Popover';
|
||||
|
||||
const InventoryLookupField = ({ isDisabled }) => {
|
||||
const { setFieldValue, setFieldTouched } = useFormikContext();
|
||||
const [inventoryField, inventoryMeta, inventoryHelpers] = useField(
|
||||
'inventory'
|
||||
);
|
||||
const [inventoryField, inventoryMeta, inventoryHelpers] =
|
||||
useField('inventory');
|
||||
|
||||
const handleInventoryUpdate = useCallback(
|
||||
value => {
|
||||
(value) => {
|
||||
setFieldValue('inventory', value);
|
||||
setFieldTouched('inventory', true, false);
|
||||
},
|
||||
@ -76,55 +75,53 @@ const HostForm = ({
|
||||
isInventoryVisible,
|
||||
submitError,
|
||||
disableInventoryLookup,
|
||||
}) => {
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
name: host.name,
|
||||
description: host.description,
|
||||
inventory: host.summary_fields?.inventory || null,
|
||||
variables: host.variables,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<FormField
|
||||
id="host-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={t`Name`}
|
||||
validate={required(null)}
|
||||
isRequired
|
||||
}) => (
|
||||
<Formik
|
||||
initialValues={{
|
||||
name: host.name,
|
||||
description: host.description,
|
||||
inventory: host.summary_fields?.inventory || null,
|
||||
variables: host.variables,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(formik) => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<FormField
|
||||
id="host-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={t`Name`}
|
||||
validate={required(null)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="host-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={t`Description`}
|
||||
/>
|
||||
{isInventoryVisible && (
|
||||
<InventoryLookupField isDisabled={disableInventoryLookup} />
|
||||
)}
|
||||
<FormFullWidthLayout>
|
||||
<VariablesField
|
||||
id="host-variables"
|
||||
name="variables"
|
||||
label={t`Variables`}
|
||||
/>
|
||||
<FormField
|
||||
id="host-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={t`Description`}
|
||||
/>
|
||||
{isInventoryVisible && (
|
||||
<InventoryLookupField isDisabled={disableInventoryLookup} />
|
||||
)}
|
||||
<FormFullWidthLayout>
|
||||
<VariablesField
|
||||
id="host-variables"
|
||||
name="variables"
|
||||
label={t`Variables`}
|
||||
/>
|
||||
</FormFullWidthLayout>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
</FormFullWidthLayout>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
|
||||
HostForm.propTypes = {
|
||||
handleCancel: func.isRequired,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import 'styled-components/macro';
|
||||
import React, { Fragment, useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { Switch, Tooltip } from '@patternfly/react-core';
|
||||
@ -20,7 +20,12 @@ function HostToggle({
|
||||
const [isEnabled, setIsEnabled] = useState(host.enabled);
|
||||
const [showError, setShowError] = useState(false);
|
||||
|
||||
const { result, isLoading, error, request: toggleHost } = useRequest(
|
||||
const {
|
||||
result,
|
||||
isLoading,
|
||||
error,
|
||||
request: toggleHost,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
await HostsAPI.update(host.id, {
|
||||
enabled: !isEnabled,
|
||||
@ -46,7 +51,7 @@ function HostToggle({
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<Tooltip content={tooltip} position="top">
|
||||
<Switch
|
||||
className={className}
|
||||
@ -75,7 +80,7 @@ function HostToggle({
|
||||
<ErrorDetail error={error} />
|
||||
</AlertModal>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,12 @@ function InstanceToggle({ className, fetchInstances, instance, onToggle }) {
|
||||
const [isEnabled, setIsEnabled] = useState(instance.enabled);
|
||||
const [showError, setShowError] = useState(false);
|
||||
|
||||
const { result, isLoading, error, request: toggleInstance } = useRequest(
|
||||
const {
|
||||
result,
|
||||
isLoading,
|
||||
error,
|
||||
request: toggleInstance,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
await InstancesAPI.update(instance.id, { enabled: !isEnabled });
|
||||
await fetchInstances();
|
||||
|
||||
@ -23,9 +23,8 @@ function JobCancelButton({
|
||||
}, [job.id, job.type]),
|
||||
{}
|
||||
);
|
||||
const { error, dismissError: dismissCancelError } = useDismissableError(
|
||||
cancelError
|
||||
);
|
||||
const { error, dismissError: dismissCancelError } =
|
||||
useDismissableError(cancelError);
|
||||
|
||||
const isAlreadyCancelled = cancelError?.response?.status === 405;
|
||||
|
||||
|
||||
@ -58,10 +58,10 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
count: response.data.count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
},
|
||||
[location] // eslint-disable-line react-hooks/exhaustive-deps
|
||||
@ -79,7 +79,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
|
||||
// TODO: update QS_CONFIG to be safe for deps array
|
||||
const fetchJobsById = useCallback(
|
||||
async ids => {
|
||||
async (ids) => {
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
params.id__in = ids.join(',');
|
||||
const { data } = await UnifiedJobsAPI.read(params);
|
||||
@ -90,40 +90,34 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
|
||||
const jobs = useWsJobs(results, fetchJobsById, qsConfig);
|
||||
|
||||
const {
|
||||
selected,
|
||||
isAllSelected,
|
||||
handleSelect,
|
||||
selectAll,
|
||||
clearSelected,
|
||||
} = useSelected(jobs);
|
||||
const { selected, isAllSelected, handleSelect, selectAll, clearSelected } =
|
||||
useSelected(jobs);
|
||||
|
||||
const { expanded, isAllExpanded, handleExpand, expandAll } = useExpanded(
|
||||
jobs
|
||||
);
|
||||
const { expanded, isAllExpanded, handleExpand, expandAll } =
|
||||
useExpanded(jobs);
|
||||
|
||||
const {
|
||||
error: cancelJobsError,
|
||||
isLoading: isCancelLoading,
|
||||
request: cancelJobs,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
return Promise.all(
|
||||
selected.map(job => {
|
||||
if (isJobRunning(job.status)) {
|
||||
return getJobModel(job.type).cancel(job.id);
|
||||
}
|
||||
return Promise.resolve();
|
||||
})
|
||||
);
|
||||
}, [selected]),
|
||||
useCallback(
|
||||
async () =>
|
||||
Promise.all(
|
||||
selected.map((job) => {
|
||||
if (isJobRunning(job.status)) {
|
||||
return getJobModel(job.type).cancel(job.id);
|
||||
}
|
||||
return Promise.resolve();
|
||||
})
|
||||
),
|
||||
[selected]
|
||||
),
|
||||
{}
|
||||
);
|
||||
|
||||
const {
|
||||
error: cancelError,
|
||||
dismissError: dismissCancelError,
|
||||
} = useDismissableError(cancelJobsError);
|
||||
const { error: cancelError, dismissError: dismissCancelError } =
|
||||
useDismissableError(cancelJobsError);
|
||||
|
||||
const {
|
||||
isLoading: isDeleteLoading,
|
||||
@ -131,13 +125,13 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
deletionError,
|
||||
clearDeletionError,
|
||||
} = useDeleteItems(
|
||||
useCallback(() => {
|
||||
return Promise.all(
|
||||
selected.map(({ type, id }) => {
|
||||
return getJobModel(type).destroy(id);
|
||||
})
|
||||
);
|
||||
}, [selected]),
|
||||
useCallback(
|
||||
() =>
|
||||
Promise.all(
|
||||
selected.map(({ type, id }) => getJobModel(type).destroy(id))
|
||||
),
|
||||
[selected]
|
||||
),
|
||||
{
|
||||
qsConfig,
|
||||
allItemsSelected: isAllSelected,
|
||||
@ -155,7 +149,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
clearSelected();
|
||||
};
|
||||
|
||||
const cannotDeleteItems = selected.filter(job => isJobRunning(job.status));
|
||||
const cannotDeleteItems = selected.filter((job) => isJobRunning(job.status));
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -229,7 +223,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
clearSelected={clearSelected}
|
||||
toolbarSearchableKeys={searchableKeys}
|
||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||
renderToolbar={props => (
|
||||
renderToolbar={(props) => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
isAllExpanded={isAllExpanded}
|
||||
@ -246,7 +240,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
return item;
|
||||
})}
|
||||
pluralizedItemName={t`Jobs`}
|
||||
cannotDelete={item =>
|
||||
cannotDelete={(item) =>
|
||||
isJobRunning(item.status) ||
|
||||
!item.summary_fields.user_capabilities.delete
|
||||
}
|
||||
@ -270,12 +264,12 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
<JobListItem
|
||||
key={job.id}
|
||||
job={job}
|
||||
isExpanded={expanded.some(row => row.id === job.id)}
|
||||
isExpanded={expanded.some((row) => row.id === job.id)}
|
||||
onExpand={() => handleExpand(job)}
|
||||
isSuperUser={me?.is_superuser}
|
||||
showTypeColumn={showTypeColumn}
|
||||
onSelect={() => handleSelect(job)}
|
||||
isSelected={selected.some(row => row.id === job.id)}
|
||||
isSelected={selected.some((row) => row.id === job.id)}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -120,7 +120,7 @@ function waitForLoaded(wrapper) {
|
||||
return waitForElement(
|
||||
wrapper,
|
||||
'JobList',
|
||||
el => el.find('ContentLoading').length === 0
|
||||
(el) => el.find('ContentLoading').length === 0
|
||||
);
|
||||
}
|
||||
|
||||
@ -167,35 +167,23 @@ describe('<JobList />', () => {
|
||||
await waitForLoaded(wrapper);
|
||||
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('JobListItem')
|
||||
.first()
|
||||
.invoke('onSelect')(mockItem);
|
||||
wrapper.find('JobListItem').first().invoke('onSelect')(mockItem);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper
|
||||
.find('JobListItem')
|
||||
.first()
|
||||
.prop('isSelected')
|
||||
).toEqual(true);
|
||||
expect(wrapper.find('JobListItem').first().prop('isSelected')).toEqual(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper.find('ToolbarDeleteButton').prop('itemsToDelete')
|
||||
).toHaveLength(1);
|
||||
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('JobListItem')
|
||||
.first()
|
||||
.invoke('onSelect')(mockItem);
|
||||
wrapper.find('JobListItem').first().invoke('onSelect')(mockItem);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper
|
||||
.find('JobListItem')
|
||||
.first()
|
||||
.prop('isSelected')
|
||||
).toEqual(false);
|
||||
expect(wrapper.find('JobListItem').first().prop('isSelected')).toEqual(
|
||||
false
|
||||
);
|
||||
expect(
|
||||
wrapper.find('ToolbarDeleteButton').prop('itemsToDelete')
|
||||
).toHaveLength(0);
|
||||
@ -344,10 +332,7 @@ describe('<JobList />', () => {
|
||||
});
|
||||
await waitForLoaded(wrapper);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('JobListItem')
|
||||
.at(1)
|
||||
.invoke('onSelect')();
|
||||
wrapper.find('JobListItem').at(1).invoke('onSelect')();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
@ -358,7 +343,7 @@ describe('<JobList />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'Modal',
|
||||
el => el.props().isOpen === true && el.props().title === 'Error!'
|
||||
(el) => el.props().isOpen === true && el.props().title === 'Error!'
|
||||
);
|
||||
});
|
||||
|
||||
@ -411,10 +396,7 @@ describe('<JobList />', () => {
|
||||
});
|
||||
await waitForLoaded(wrapper);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('JobListItem')
|
||||
.at(1)
|
||||
.invoke('onSelect')();
|
||||
wrapper.find('JobListItem').at(1).invoke('onSelect')();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
@ -425,7 +407,7 @@ describe('<JobList />', () => {
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'Modal',
|
||||
el => el.props().isOpen === true && el.props().title === 'Error!'
|
||||
(el) => el.props().isOpen === true && el.props().title === 'Error!'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -41,10 +41,10 @@ function JobListCancelButton({ jobsToCancel, onCancel }) {
|
||||
const renderTooltip = () => {
|
||||
const cannotCancelPermissions = jobsToCancel
|
||||
.filter(cannotCancelBecausePermissions)
|
||||
.map(job => job.name);
|
||||
.map((job) => job.name);
|
||||
const cannotCancelNotRunning = jobsToCancel
|
||||
.filter(cannotCancelBecauseNotRunning)
|
||||
.map(job => job.name);
|
||||
.map((job) => job.name);
|
||||
const numJobsUnableToCancel = cannotCancelPermissions.concat(
|
||||
cannotCancelNotRunning
|
||||
).length;
|
||||
@ -170,7 +170,7 @@ function JobListCancelButton({ jobsToCancel, onCancel }) {
|
||||
other="This action will cancel the following jobs:"
|
||||
/>
|
||||
</div>
|
||||
{jobsToCancel.map(job => (
|
||||
{jobsToCancel.map((job) => (
|
||||
<span key={job.id}>
|
||||
<strong>{job.name}</strong>
|
||||
<br />
|
||||
|
||||
@ -222,7 +222,7 @@ function JobListItem({
|
||||
label={t`Credentials`}
|
||||
value={
|
||||
<ChipGroup numChips={5} totalChips={credentials.length}>
|
||||
{credentials.map(c => (
|
||||
{credentials.map((c) => (
|
||||
<CredentialChip key={c.id} credential={c} isReadOnly />
|
||||
))}
|
||||
</ChipGroup>
|
||||
@ -235,7 +235,7 @@ function JobListItem({
|
||||
label={t`Labels`}
|
||||
value={
|
||||
<ChipGroup numChips={5} totalChips={labels.results.length}>
|
||||
{labels.results.map(l => (
|
||||
{labels.results.map((l) => (
|
||||
<Chip key={l.id} isReadOnly>
|
||||
{l.name}
|
||||
</Chip>
|
||||
|
||||
@ -20,9 +20,9 @@ export default function useWsJobs(initialJobs, fetchJobsById, qsConfig) {
|
||||
setJobs(initialJobs);
|
||||
}, [initialJobs]);
|
||||
|
||||
const enqueueJobId = id => {
|
||||
const enqueueJobId = (id) => {
|
||||
if (!jobsToFetch.includes(id)) {
|
||||
setJobsToFetch(ids => ids.concat(id));
|
||||
setJobsToFetch((ids) => ids.concat(id));
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@ -33,7 +33,7 @@ export default function useWsJobs(initialJobs, fetchJobsById, qsConfig) {
|
||||
setJobsToFetch([]);
|
||||
const newJobs = await fetchJobsById(throttledJobsToFetch);
|
||||
const deduplicated = newJobs.filter(
|
||||
job => !jobs.find(j => j.id === job.id)
|
||||
(job) => !jobs.find((j) => j.id === job.id)
|
||||
);
|
||||
if (deduplicated.length) {
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
@ -56,7 +56,7 @@ export default function useWsJobs(initialJobs, fetchJobsById, qsConfig) {
|
||||
}
|
||||
|
||||
const jobId = lastMessage.unified_job_id;
|
||||
const index = jobs.findIndex(j => j.id === jobId);
|
||||
const index = jobs.findIndex((j) => j.id === jobId);
|
||||
if (index > -1) {
|
||||
setJobs(sortJobs(updateJob(jobs, index, lastMessage), params));
|
||||
} else {
|
||||
|
||||
@ -23,7 +23,7 @@ describe('useWsJobs hook', () => {
|
||||
*/
|
||||
jest.mock('../../hooks/useThrottle', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(val => val),
|
||||
default: jest.fn((val) => val),
|
||||
}));
|
||||
debug = global.console.debug; // eslint-disable-line prefer-destructuring
|
||||
global.console.debug = () => {};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { number, shape } from 'prop-types';
|
||||
|
||||
@ -71,7 +71,7 @@ function LaunchButton({ resource, children, history }) {
|
||||
}
|
||||
};
|
||||
|
||||
const launchWithParams = async params => {
|
||||
const launchWithParams = async (params) => {
|
||||
try {
|
||||
let jobPromise;
|
||||
|
||||
@ -94,7 +94,7 @@ function LaunchButton({ resource, children, history }) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRelaunch = async params => {
|
||||
const handleRelaunch = async (params) => {
|
||||
let readRelaunch;
|
||||
let relaunch;
|
||||
|
||||
@ -148,7 +148,7 @@ function LaunchButton({ resource, children, history }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
{children({
|
||||
handleLaunch,
|
||||
handleRelaunch,
|
||||
@ -174,7 +174,7 @@ function LaunchButton({ resource, children, history }) {
|
||||
onCancel={() => setShowLaunchPrompt(false)}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ function ReLaunchDropDown({
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const onToggle = () => {
|
||||
setIsOpen(prev => !prev);
|
||||
setIsOpen((prev) => !prev);
|
||||
};
|
||||
|
||||
const dropdownItems = [
|
||||
|
||||
@ -41,7 +41,7 @@ function PromptModalForm({
|
||||
setValue('inventory_id', values.inventory?.id);
|
||||
setValue(
|
||||
'credentials',
|
||||
values.credentials?.map(c => c.id)
|
||||
values.credentials?.map((c) => c.id)
|
||||
);
|
||||
setValue('job_type', values.job_type);
|
||||
setValue('limit', values.limit);
|
||||
@ -78,7 +78,7 @@ function PromptModalForm({
|
||||
isOpen
|
||||
onClose={onCancel}
|
||||
onSave={handleSubmit}
|
||||
onBack={async nextStep => {
|
||||
onBack={async (nextStep) => {
|
||||
validateStep(nextStep.id);
|
||||
}}
|
||||
onNext={async (nextStep, prevStep) => {
|
||||
@ -141,9 +141,9 @@ function LaunchPrompt({
|
||||
resourceDefaultCredentials = [],
|
||||
}) {
|
||||
return (
|
||||
<Formik initialValues={{}} onSubmit={values => onLaunch(values)}>
|
||||
<Formik initialValues={{}} onSubmit={(values) => onLaunch(values)}>
|
||||
<PromptModalForm
|
||||
onSubmit={values => onLaunch(values)}
|
||||
onSubmit={(values) => onLaunch(values)}
|
||||
onCancel={onCancel}
|
||||
launchConfig={launchConfig}
|
||||
surveyConfig={surveyConfig}
|
||||
|
||||
@ -19,7 +19,7 @@ function CredentialPasswordsStep({ launchConfig }) {
|
||||
!launchConfig.ask_credential_on_launch &&
|
||||
launchConfig.passwords_needed_to_start
|
||||
) {
|
||||
launchConfig.passwords_needed_to_start.forEach(password => {
|
||||
launchConfig.passwords_needed_to_start.forEach((password) => {
|
||||
if (password === 'ssh_password') {
|
||||
showcredentialPasswordSsh = true;
|
||||
} else if (password === 'become_password') {
|
||||
@ -32,10 +32,10 @@ function CredentialPasswordsStep({ launchConfig }) {
|
||||
}
|
||||
});
|
||||
} else if (credentials) {
|
||||
credentials.forEach(credential => {
|
||||
credentials.forEach((credential) => {
|
||||
if (!credential.inputs) {
|
||||
const launchConfigCredential = launchConfig.defaults.credentials.find(
|
||||
defaultCred => defaultCred.id === credential.id
|
||||
(defaultCred) => defaultCred.id === credential.id
|
||||
);
|
||||
|
||||
if (launchConfigCredential?.passwords_needed.length > 0) {
|
||||
@ -56,10 +56,10 @@ function CredentialPasswordsStep({ launchConfig }) {
|
||||
}
|
||||
|
||||
const vaultPasswordIds = launchConfigCredential.passwords_needed
|
||||
.filter(passwordNeeded =>
|
||||
.filter((passwordNeeded) =>
|
||||
passwordNeeded.startsWith('vault_password')
|
||||
)
|
||||
.map(vaultPassword => vaultPassword.split(/\.(.+)/)[1] || '');
|
||||
.map((vaultPassword) => vaultPassword.split(/\.(.+)/)[1] || '');
|
||||
|
||||
vaultsThatPrompt.push(...vaultPasswordIds);
|
||||
}
|
||||
@ -85,7 +85,7 @@ function CredentialPasswordsStep({ launchConfig }) {
|
||||
|
||||
return (
|
||||
<Form
|
||||
onSubmit={e => {
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
@ -113,7 +113,7 @@ function CredentialPasswordsStep({ launchConfig }) {
|
||||
isRequired
|
||||
/>
|
||||
)}
|
||||
{vaultsThatPrompt.map(credId => (
|
||||
{vaultsThatPrompt.map((credId) => (
|
||||
<PasswordField
|
||||
id={`launch-vault-password-${credId}`}
|
||||
key={credId}
|
||||
|
||||
@ -32,13 +32,12 @@ function CredentialsStep({
|
||||
}) {
|
||||
const [field, meta, helpers] = useField({
|
||||
name: 'credentials',
|
||||
validate: val => {
|
||||
return credentialsValidator(
|
||||
validate: (val) =>
|
||||
credentialsValidator(
|
||||
defaultCredentials,
|
||||
allowCredentialsWithPasswords,
|
||||
val
|
||||
);
|
||||
},
|
||||
),
|
||||
});
|
||||
const [selectedType, setSelectedType] = useState(null);
|
||||
const history = useHistory();
|
||||
@ -53,7 +52,7 @@ function CredentialsStep({
|
||||
const loadedTypes = await CredentialTypesAPI.loadAllTypes();
|
||||
if (loadedTypes.length) {
|
||||
const match =
|
||||
loadedTypes.find(type => type.kind === 'ssh') || loadedTypes[0];
|
||||
loadedTypes.find((type) => type.kind === 'ssh') || loadedTypes[0];
|
||||
setSelectedType(match);
|
||||
}
|
||||
return loadedTypes;
|
||||
@ -88,10 +87,10 @@ function CredentialsStep({
|
||||
count: data.count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [selectedType, history.location.search]),
|
||||
{ credentials: [], count: 0, relatedSearchableKeys: [], searchableKeys: [] }
|
||||
@ -122,17 +121,15 @@ function CredentialsStep({
|
||||
|
||||
const isVault = selectedType?.kind === 'vault';
|
||||
|
||||
const renderChip = ({ item, removeItem, canDelete }) => {
|
||||
return (
|
||||
<CredentialChip
|
||||
id={`credential-chip-${item.id}`}
|
||||
key={item.id}
|
||||
onClick={() => removeItem(item)}
|
||||
isReadOnly={!canDelete}
|
||||
credential={item}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const renderChip = ({ item, removeItem, canDelete }) => (
|
||||
<CredentialChip
|
||||
id={`credential-chip-${item.id}`}
|
||||
key={item.id}
|
||||
onClick={() => removeItem(item)}
|
||||
isReadOnly={!canDelete}
|
||||
credential={item}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -148,7 +145,7 @@ function CredentialsStep({
|
||||
css="flex: 1 1 75%;"
|
||||
id="multiCredentialsLookUp-select"
|
||||
label={t`Selected Category`}
|
||||
data={types.map(type => ({
|
||||
data={types.map((type) => ({
|
||||
key: type.id,
|
||||
value: type.id,
|
||||
label: type.name,
|
||||
@ -156,7 +153,7 @@ function CredentialsStep({
|
||||
}))}
|
||||
value={selectedType && selectedType.id}
|
||||
onChange={(e, id) => {
|
||||
setSelectedType(types.find(o => o.id === parseInt(id, 10)));
|
||||
setSelectedType(types.find((o) => o.id === parseInt(id, 10)));
|
||||
}}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
@ -194,20 +191,20 @@ function CredentialsStep({
|
||||
name="credentials"
|
||||
qsConfig={QS_CONFIG}
|
||||
readOnly={false}
|
||||
selectItem={item => {
|
||||
const hasSameVaultID = val =>
|
||||
selectItem={(item) => {
|
||||
const hasSameVaultID = (val) =>
|
||||
val?.inputs?.vault_id !== undefined &&
|
||||
val?.inputs?.vault_id === item?.inputs?.vault_id;
|
||||
const hasSameCredentialType = val =>
|
||||
const hasSameCredentialType = (val) =>
|
||||
val.credential_type === item.credential_type;
|
||||
const newItems = field.value.filter(i =>
|
||||
const newItems = field.value.filter((i) =>
|
||||
isVault ? !hasSameVaultID(i) : !hasSameCredentialType(i)
|
||||
);
|
||||
newItems.push(item);
|
||||
helpers.setValue(newItems);
|
||||
}}
|
||||
deselectItem={item => {
|
||||
helpers.setValue(field.value.filter(i => i.id !== item.id));
|
||||
deselectItem={(item) => {
|
||||
helpers.setValue(field.value.filter((i) => i.id !== item.id));
|
||||
}}
|
||||
renderItemChip={renderChip}
|
||||
/>
|
||||
|
||||
@ -180,19 +180,11 @@ describe('CredentialsStep', () => {
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-2')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-2').find('input').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(1);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Alert')
|
||||
.text()
|
||||
.includes('Cred 2')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('Alert').text().includes('Cred 2')).toBe(true);
|
||||
});
|
||||
|
||||
test('error should be toggled when default machine credential is removed and then replaced', async () => {
|
||||
@ -234,17 +226,9 @@ describe('CredentialsStep', () => {
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(1);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Alert')
|
||||
.text()
|
||||
.includes('Machine')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('Alert').text().includes('Machine')).toBe(true);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-5')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-5').find('input').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(0);
|
||||
@ -307,21 +291,13 @@ describe('CredentialsStep', () => {
|
||||
wrapper.update();
|
||||
expect(wrapper.find('CredentialChip').length).toBe(1);
|
||||
expect(wrapper.find('Alert').length).toBe(1);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Alert')
|
||||
.text()
|
||||
.includes('Vault | foo')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('Alert').text().includes('Vault | foo')).toBe(true);
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').invoke('onChange')({}, 3);
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('td#check-action-item-33')
|
||||
.find('input')
|
||||
.simulate('click');
|
||||
wrapper.find('td#check-action-item-33').find('input').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(0);
|
||||
|
||||
@ -44,10 +44,10 @@ function InventoryStep({ warningMessage = null }) {
|
||||
count: data.count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [history.location]),
|
||||
{
|
||||
|
||||
@ -23,7 +23,7 @@ const FieldHeader = styled.div`
|
||||
function OtherPromptsStep({ launchConfig, variablesMode, onVarModeChange }) {
|
||||
return (
|
||||
<Form
|
||||
onSubmit={e => {
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
|
||||
@ -37,8 +37,8 @@ function PreviewStep({ resource, launchConfig, surveyConfig, formErrors }) {
|
||||
launchConfig.ask_variables_on_launch && (overrides.extra_vars || '---');
|
||||
if (surveyConfig?.spec) {
|
||||
const passwordFields = surveyConfig.spec
|
||||
.filter(q => q.type === 'password')
|
||||
.map(q => q.variable);
|
||||
.filter((q) => q.type === 'password')
|
||||
.map((q) => q.variable);
|
||||
const masked = maskPasswords(surveyValues, passwordFields);
|
||||
overrides.extra_vars = yaml.safeDump(
|
||||
mergeExtraVars(initialExtraVars, masked)
|
||||
@ -54,7 +54,7 @@ function PreviewStep({ resource, launchConfig, surveyConfig, formErrors }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
{formErrors && (
|
||||
<ErrorMessageWrapper>
|
||||
{t`Some of the previous step(s) have errors`}
|
||||
@ -72,7 +72,7 @@ function PreviewStep({ resource, launchConfig, surveyConfig, formErrors }) {
|
||||
launchConfig={launchConfig}
|
||||
overrides={overrides}
|
||||
/>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -32,11 +32,11 @@ function SurveyStep({ surveyConfig }) {
|
||||
};
|
||||
return (
|
||||
<Form
|
||||
onSubmit={e => {
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
{surveyConfig.spec.map(question => {
|
||||
{surveyConfig.spec.map((question) => {
|
||||
const Field = fieldTypes[question.type];
|
||||
return <Field key={question.variable} question={question} />;
|
||||
})}
|
||||
@ -131,7 +131,7 @@ function MultipleChoiceField({ question }) {
|
||||
helpers.setValue('');
|
||||
}}
|
||||
>
|
||||
{options.map(opt => (
|
||||
{options.map((opt) => (
|
||||
<SelectOption key={opt} value={opt} />
|
||||
))}
|
||||
</Select>
|
||||
@ -175,7 +175,7 @@ function MultiSelectField({ question }) {
|
||||
onToggle={setIsOpen}
|
||||
onSelect={(event, option) => {
|
||||
if (field?.value?.includes(option)) {
|
||||
helpers.setValue(field.value.filter(o => o !== option));
|
||||
helpers.setValue(field.value.filter((o) => o !== option));
|
||||
} else {
|
||||
helpers.setValue(field.value.concat(option));
|
||||
}
|
||||
@ -188,7 +188,7 @@ function MultiSelectField({ question }) {
|
||||
helpers.setValue([]);
|
||||
}}
|
||||
>
|
||||
{options.map(opt => (
|
||||
{options.map((opt) => (
|
||||
<SelectOption key={opt} value={opt} />
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
const credentialPromptsForPassword = credential =>
|
||||
const credentialPromptsForPassword = (credential) =>
|
||||
credential?.inputs?.password === 'ASK' ||
|
||||
credential?.inputs?.ssh_key_unlock === 'ASK' ||
|
||||
credential?.inputs?.become_password === 'ASK' ||
|
||||
@ -13,10 +13,10 @@ export default function credentialsValidator(
|
||||
) {
|
||||
if (defaultCredentials.length > 0 && selectedCredentials) {
|
||||
const missingCredentialTypes = [];
|
||||
defaultCredentials.forEach(defaultCredential => {
|
||||
defaultCredentials.forEach((defaultCredential) => {
|
||||
if (
|
||||
!selectedCredentials.find(selectedCredential => {
|
||||
return (
|
||||
!selectedCredentials.find(
|
||||
(selectedCredential) =>
|
||||
(selectedCredential?.credential_type ===
|
||||
defaultCredential?.credential_type &&
|
||||
!selectedCredential.inputs?.vault_id &&
|
||||
@ -24,8 +24,7 @@ export default function credentialsValidator(
|
||||
(defaultCredential.inputs?.vault_id &&
|
||||
selectedCredential.inputs?.vault_id ===
|
||||
defaultCredential.inputs?.vault_id)
|
||||
);
|
||||
})
|
||||
)
|
||||
) {
|
||||
missingCredentialTypes.push(
|
||||
defaultCredential.inputs?.vault_id
|
||||
@ -44,7 +43,7 @@ export default function credentialsValidator(
|
||||
|
||||
if (!allowCredentialsWithPasswords && selectedCredentials) {
|
||||
const credentialsThatPrompt = [];
|
||||
selectedCredentials.forEach(selectedCredential => {
|
||||
selectedCredentials.forEach((selectedCredential) => {
|
||||
if (credentialPromptsForPassword(selectedCredential)) {
|
||||
credentialsThatPrompt.push(selectedCredential.name);
|
||||
}
|
||||
|
||||
@ -6,9 +6,7 @@ import StepName from './StepName';
|
||||
|
||||
const STEP_ID = 'credentialPasswords';
|
||||
|
||||
const isValueMissing = val => {
|
||||
return !val || val === '';
|
||||
};
|
||||
const isValueMissing = (val) => !val || val === '';
|
||||
|
||||
export default function useCredentialPasswordsStep(
|
||||
launchConfig,
|
||||
@ -38,8 +36,8 @@ export default function useCredentialPasswordsStep(
|
||||
isReady: true,
|
||||
contentError: null,
|
||||
hasError,
|
||||
setTouched: setFieldTouched => {
|
||||
Object.keys(values.credential_passwords).forEach(credentialValueKey =>
|
||||
setTouched: (setFieldTouched) => {
|
||||
Object.keys(values.credential_passwords).forEach((credentialValueKey) =>
|
||||
setFieldTouched(
|
||||
`credential_passwords['${credentialValueKey}']`,
|
||||
true,
|
||||
@ -48,7 +46,7 @@ export default function useCredentialPasswordsStep(
|
||||
);
|
||||
},
|
||||
validate: () => {
|
||||
const setPasswordFieldError = fieldName => {
|
||||
const setPasswordFieldError = (fieldName) => {
|
||||
setFieldError(fieldName, t`This field may not be blank`);
|
||||
};
|
||||
|
||||
@ -56,20 +54,21 @@ export default function useCredentialPasswordsStep(
|
||||
!launchConfig.ask_credential_on_launch &&
|
||||
launchConfig.passwords_needed_to_start
|
||||
) {
|
||||
launchConfig.passwords_needed_to_start.forEach(password => {
|
||||
launchConfig.passwords_needed_to_start.forEach((password) => {
|
||||
if (isValueMissing(values.credential_passwords[password])) {
|
||||
setPasswordFieldError(`credential_passwords['${password}']`);
|
||||
}
|
||||
});
|
||||
} else if (values.credentials) {
|
||||
values.credentials.forEach(credential => {
|
||||
values.credentials.forEach((credential) => {
|
||||
if (!credential.inputs) {
|
||||
const launchConfigCredential = launchConfig.defaults.credentials.find(
|
||||
defaultCred => defaultCred.id === credential.id
|
||||
);
|
||||
const launchConfigCredential =
|
||||
launchConfig.defaults.credentials.find(
|
||||
(defaultCred) => defaultCred.id === credential.id
|
||||
);
|
||||
|
||||
if (launchConfigCredential?.passwords_needed.length > 0) {
|
||||
launchConfigCredential.passwords_needed.forEach(password => {
|
||||
launchConfigCredential.passwords_needed.forEach((password) => {
|
||||
if (isValueMissing(values.credential_passwords[password])) {
|
||||
setPasswordFieldError(`credential_passwords['${password}']`);
|
||||
}
|
||||
@ -137,20 +136,20 @@ function getInitialValues(launchConfig, selectedCredentials = []) {
|
||||
!launchConfig.ask_credential_on_launch &&
|
||||
launchConfig.passwords_needed_to_start
|
||||
) {
|
||||
launchConfig.passwords_needed_to_start.forEach(password => {
|
||||
launchConfig.passwords_needed_to_start.forEach((password) => {
|
||||
initialValues.credential_passwords[password] = '';
|
||||
});
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
selectedCredentials.forEach(credential => {
|
||||
selectedCredentials.forEach((credential) => {
|
||||
if (!credential.inputs) {
|
||||
const launchConfigCredential = launchConfig.defaults.credentials.find(
|
||||
defaultCred => defaultCred.id === credential.id
|
||||
(defaultCred) => defaultCred.id === credential.id
|
||||
);
|
||||
|
||||
if (launchConfigCredential?.passwords_needed.length > 0) {
|
||||
launchConfigCredential.passwords_needed.forEach(password => {
|
||||
launchConfigCredential.passwords_needed.forEach((password) => {
|
||||
initialValues.credential_passwords[password] = '';
|
||||
});
|
||||
}
|
||||
@ -189,20 +188,20 @@ function checkForError(launchConfig, values) {
|
||||
!launchConfig.ask_credential_on_launch &&
|
||||
launchConfig.passwords_needed_to_start
|
||||
) {
|
||||
launchConfig.passwords_needed_to_start.forEach(password => {
|
||||
launchConfig.passwords_needed_to_start.forEach((password) => {
|
||||
if (isValueMissing(values.credential_passwords[password])) {
|
||||
hasError = true;
|
||||
}
|
||||
});
|
||||
} else if (values.credentials) {
|
||||
values.credentials.forEach(credential => {
|
||||
values.credentials.forEach((credential) => {
|
||||
if (!credential.inputs) {
|
||||
const launchConfigCredential = launchConfig.defaults.credentials.find(
|
||||
defaultCred => defaultCred.id === credential.id
|
||||
(defaultCred) => defaultCred.id === credential.id
|
||||
);
|
||||
|
||||
if (launchConfigCredential?.passwords_needed.length > 0) {
|
||||
launchConfigCredential.passwords_needed.forEach(password => {
|
||||
launchConfigCredential.passwords_needed.forEach((password) => {
|
||||
if (isValueMissing(values.credential_passwords[password])) {
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ export default function useCredentialsStep(
|
||||
isReady: true,
|
||||
contentError: null,
|
||||
hasError: launchConfig.ask_credential_on_launch && formError,
|
||||
setTouched: setFieldTouched => {
|
||||
setTouched: (setFieldTouched) => {
|
||||
setFieldTouched('credentials', true, false);
|
||||
},
|
||||
validate: () => {
|
||||
|
||||
@ -27,7 +27,7 @@ export default function useInventoryStep(launchConfig, resource, visitedSteps) {
|
||||
isReady: true,
|
||||
contentError: null,
|
||||
hasError: launchConfig.ask_inventory_on_launch && formError,
|
||||
setTouched: setFieldTouched => {
|
||||
setTouched: (setFieldTouched) => {
|
||||
setFieldTouched('inventory', true, false);
|
||||
},
|
||||
validate: () => {
|
||||
|
||||
@ -9,7 +9,7 @@ const STEP_ID = 'other';
|
||||
export const YAML_MODE = 'yaml';
|
||||
export const JSON_MODE = 'javascript';
|
||||
|
||||
const getVariablesData = resource => {
|
||||
const getVariablesData = (resource) => {
|
||||
if (resource?.extra_data) {
|
||||
return jsonToYaml(JSON.stringify(resource.extra_data));
|
||||
}
|
||||
@ -34,7 +34,7 @@ export default function useOtherPromptsStep(launchConfig, resource) {
|
||||
const [variablesMode, setVariablesMode] = useState(null);
|
||||
const [isTouched, setIsTouched] = useState(false);
|
||||
|
||||
const handleModeChange = mode => {
|
||||
const handleModeChange = (mode) => {
|
||||
setVariablesMode(mode);
|
||||
};
|
||||
|
||||
@ -63,9 +63,11 @@ export default function useOtherPromptsStep(launchConfig, resource) {
|
||||
isReady: true,
|
||||
contentError: null,
|
||||
hasError,
|
||||
setTouched: setFieldTouched => {
|
||||
setTouched: (setFieldTouched) => {
|
||||
setIsTouched(true);
|
||||
FIELD_NAMES.forEach(fieldName => setFieldTouched(fieldName, true, false));
|
||||
FIELD_NAMES.forEach((fieldName) =>
|
||||
setFieldTouched(fieldName, true, false)
|
||||
);
|
||||
},
|
||||
validate: () => {},
|
||||
};
|
||||
|
||||
@ -35,17 +35,17 @@ export default function useSurveyStep(
|
||||
isReady: true,
|
||||
contentError: null,
|
||||
hasError,
|
||||
setTouched: setFieldTouched => {
|
||||
setTouched: (setFieldTouched) => {
|
||||
if (!surveyConfig?.spec) {
|
||||
return;
|
||||
}
|
||||
surveyConfig.spec.forEach(question => {
|
||||
surveyConfig.spec.forEach((question) => {
|
||||
setFieldTouched(`survey_${question.variable}`, true, false);
|
||||
});
|
||||
},
|
||||
validate: () => {
|
||||
if (launchConfig.survey_enabled && surveyConfig.spec) {
|
||||
surveyConfig.spec.forEach(question => {
|
||||
surveyConfig.spec.forEach((question) => {
|
||||
const errMessage = validateSurveyField(
|
||||
question,
|
||||
values[`survey_${question.variable}`]
|
||||
@ -66,7 +66,7 @@ function getInitialValues(launchConfig, surveyConfig, resource) {
|
||||
|
||||
const values = {};
|
||||
if (surveyConfig?.spec) {
|
||||
surveyConfig.spec.forEach(question => {
|
||||
surveyConfig.spec.forEach((question) => {
|
||||
if (question.type === 'multiselect') {
|
||||
values[`survey_${question.variable}`] = question.default
|
||||
? question.default.split('\n')
|
||||
@ -116,7 +116,7 @@ function validateSurveyField(question, value) {
|
||||
function checkForError(launchConfig, surveyConfig, values) {
|
||||
let hasError = false;
|
||||
if (launchConfig.survey_enabled && surveyConfig.spec) {
|
||||
surveyConfig.spec.forEach(question => {
|
||||
surveyConfig.spec.forEach((question) => {
|
||||
const value = values[`survey_${question.variable}`];
|
||||
const isTextField = ['text', 'textarea'].includes(question.type);
|
||||
const isNumeric = ['integer', 'float'].includes(question.type);
|
||||
|
||||
@ -17,10 +17,10 @@ function showCredentialPasswordsStep(credentials = [], launchConfig) {
|
||||
|
||||
let credentialPasswordStepRequired = false;
|
||||
|
||||
credentials.forEach(credential => {
|
||||
credentials.forEach((credential) => {
|
||||
if (!credential.inputs) {
|
||||
const launchConfigCredential = launchConfig.defaults.credentials.find(
|
||||
defaultCred => defaultCred.id === credential.id
|
||||
(defaultCred) => defaultCred.id === credential.id
|
||||
);
|
||||
|
||||
if (launchConfigCredential?.passwords_needed.length > 0) {
|
||||
@ -60,30 +60,31 @@ export default function useLaunchSteps(launchConfig, surveyConfig, resource) {
|
||||
useSurveyStep(launchConfig, surveyConfig, resource, visited),
|
||||
];
|
||||
const { resetForm } = useFormikContext();
|
||||
const hasErrors = steps.some(step => step.hasError);
|
||||
const hasErrors = steps.some((step) => step.hasError);
|
||||
|
||||
steps.push(
|
||||
usePreviewStep(launchConfig, resource, surveyConfig, hasErrors, true)
|
||||
);
|
||||
|
||||
const pfSteps = steps.map(s => s.step).filter(s => s != null);
|
||||
const stepsAreReady = !steps.some(s => !s.isReady);
|
||||
const pfSteps = steps.map((s) => s.step).filter((s) => s != null);
|
||||
const stepsAreReady = !steps.some((s) => !s.isReady);
|
||||
|
||||
useEffect(() => {
|
||||
if (!stepsAreReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
const initialValues = steps.reduce((acc, cur) => {
|
||||
return {
|
||||
const initialValues = steps.reduce(
|
||||
(acc, cur) => ({
|
||||
...acc,
|
||||
...cur.initialValues,
|
||||
};
|
||||
}, {});
|
||||
}),
|
||||
{}
|
||||
);
|
||||
|
||||
const newFormValues = { ...initialValues };
|
||||
|
||||
Object.keys(formikValues).forEach(formikValueKey => {
|
||||
Object.keys(formikValues).forEach((formikValueKey) => {
|
||||
if (
|
||||
formikValueKey === 'credential_passwords' &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
@ -93,7 +94,7 @@ export default function useLaunchSteps(launchConfig, surveyConfig, resource) {
|
||||
) {
|
||||
const formikCredentialPasswords = formikValues.credential_passwords;
|
||||
Object.keys(formikCredentialPasswords).forEach(
|
||||
credentialPasswordValueKey => {
|
||||
(credentialPasswordValueKey) => {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
newFormValues.credential_passwords,
|
||||
@ -121,23 +122,23 @@ export default function useLaunchSteps(launchConfig, surveyConfig, resource) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [formikValues.credentials, stepsAreReady]);
|
||||
|
||||
const stepWithError = steps.find(s => s.contentError);
|
||||
const stepWithError = steps.find((s) => s.contentError);
|
||||
const contentError = stepWithError ? stepWithError.contentError : null;
|
||||
|
||||
return {
|
||||
steps: pfSteps,
|
||||
isReady,
|
||||
validateStep: stepId => {
|
||||
steps.find(s => s?.step?.id === stepId).validate();
|
||||
validateStep: (stepId) => {
|
||||
steps.find((s) => s?.step?.id === stepId).validate();
|
||||
},
|
||||
visitStep: (prevStepId, setFieldTouched) => {
|
||||
setVisited({
|
||||
...visited,
|
||||
[prevStepId]: true,
|
||||
});
|
||||
steps.find(s => s?.step?.id === prevStepId).setTouched(setFieldTouched);
|
||||
steps.find((s) => s?.step?.id === prevStepId).setTouched(setFieldTouched);
|
||||
},
|
||||
visitAllSteps: setFieldTouched => {
|
||||
visitAllSteps: (setFieldTouched) => {
|
||||
setVisited({
|
||||
inventory: true,
|
||||
credentials: true,
|
||||
@ -146,7 +147,7 @@ export default function useLaunchSteps(launchConfig, surveyConfig, resource) {
|
||||
survey: true,
|
||||
preview: true,
|
||||
});
|
||||
steps.forEach(s => s.setTouched(setFieldTouched));
|
||||
steps.forEach((s) => s.setTouched(setFieldTouched));
|
||||
},
|
||||
contentError,
|
||||
};
|
||||
|
||||
@ -66,7 +66,7 @@ class ListHeader extends React.Component {
|
||||
handleRemoveAll() {
|
||||
const { location, qsConfig } = this.props;
|
||||
const oldParams = parseQueryString(qsConfig, location.search);
|
||||
Object.keys(oldParams).forEach(key => {
|
||||
Object.keys(oldParams).forEach((key) => {
|
||||
oldParams[key] = null;
|
||||
});
|
||||
delete oldParams.page_size;
|
||||
@ -106,7 +106,7 @@ class ListHeader extends React.Component {
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
const isEmpty = itemCount === 0 && Object.keys(params).length === 0;
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
{isEmpty ? (
|
||||
<Toolbar
|
||||
id={`${qsConfig.namespace}-list-toolbar`}
|
||||
@ -120,7 +120,7 @@ class ListHeader extends React.Component {
|
||||
</ToolbarContent>
|
||||
</Toolbar>
|
||||
) : (
|
||||
<Fragment>
|
||||
<>
|
||||
{renderToolbar({
|
||||
itemCount,
|
||||
searchColumns,
|
||||
@ -135,9 +135,9 @@ class ListHeader extends React.Component {
|
||||
qsConfig,
|
||||
pagination,
|
||||
})}
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -153,7 +153,7 @@ ListHeader.propTypes = {
|
||||
};
|
||||
|
||||
ListHeader.defaultProps = {
|
||||
renderToolbar: props => <DataListToolbar {...props} />,
|
||||
renderToolbar: (props) => <DataListToolbar {...props} />,
|
||||
searchableKeys: [],
|
||||
sortColumns: null,
|
||||
relatedSearchableKeys: [],
|
||||
|
||||
@ -41,10 +41,10 @@ function ApplicationLookup({ onChange, value, label, fieldName, validate }) {
|
||||
itemCount: count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse?.data?.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [location]),
|
||||
{
|
||||
@ -56,7 +56,7 @@ function ApplicationLookup({ onChange, value, label, fieldName, validate }) {
|
||||
);
|
||||
|
||||
const checkApplicationName = useCallback(
|
||||
async name => {
|
||||
async (name) => {
|
||||
if (!name) {
|
||||
onChange(null);
|
||||
return;
|
||||
@ -128,8 +128,8 @@ function ApplicationLookup({ onChange, value, label, fieldName, validate }) {
|
||||
relatedSearchableKeys={relatedSearchableKeys}
|
||||
readOnly={!canDelete}
|
||||
name="application"
|
||||
selectItem={item => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -84,7 +84,7 @@ function CredentialLookup({
|
||||
|
||||
const searchKeys = Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable);
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable);
|
||||
const item = searchKeys.indexOf('type');
|
||||
if (item) {
|
||||
searchKeys[item] = 'credential_type__kind';
|
||||
@ -95,7 +95,7 @@ function CredentialLookup({
|
||||
credentials: data.results,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: searchKeys,
|
||||
};
|
||||
}, [
|
||||
@ -115,7 +115,7 @@ function CredentialLookup({
|
||||
);
|
||||
|
||||
const checkCredentialName = useCallback(
|
||||
async name => {
|
||||
async (name) => {
|
||||
if (!name) {
|
||||
onChange(null);
|
||||
return;
|
||||
@ -209,9 +209,9 @@ function CredentialLookup({
|
||||
relatedSearchableKeys={relatedSearchableKeys}
|
||||
readOnly={!canDelete}
|
||||
name="credential"
|
||||
selectItem={item => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
sortSelectedItems={selectedItems =>
|
||||
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
sortSelectedItems={(selectedItems) =>
|
||||
dispatch({ type: 'SET_SELECTED_ITEMS', selectedItems })
|
||||
}
|
||||
multiple={multiple}
|
||||
|
||||
@ -107,10 +107,10 @@ function ExecutionEnvironmentLookup({
|
||||
count: data.count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [
|
||||
location,
|
||||
@ -129,7 +129,7 @@ function ExecutionEnvironmentLookup({
|
||||
);
|
||||
|
||||
const checkExecutionEnvironmentName = useCallback(
|
||||
async name => {
|
||||
async (name) => {
|
||||
if (!name) {
|
||||
onChange(null);
|
||||
return;
|
||||
@ -190,8 +190,8 @@ function ExecutionEnvironmentLookup({
|
||||
name="executionEnvironments"
|
||||
qsConfig={QS_CONFIG}
|
||||
readOnly={!canDelete}
|
||||
selectItem={item => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -129,7 +129,7 @@ function HostFilterLookup({
|
||||
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||
const searchColumns = buildSearchColumns();
|
||||
|
||||
const parseRelatedSearchFields = searchFields => {
|
||||
const parseRelatedSearchFields = (searchFields) => {
|
||||
if (searchFields.indexOf('__search') !== -1) {
|
||||
return searchFields.slice(0, -8);
|
||||
}
|
||||
@ -143,7 +143,7 @@ function HostFilterLookup({
|
||||
isLoading,
|
||||
} = useRequest(
|
||||
useCallback(
|
||||
async orgId => {
|
||||
async (orgId) => {
|
||||
const params = parseQueryString(QS_CONFIG, location.search);
|
||||
const [{ data }, { data: actions }] = await Promise.all([
|
||||
HostsAPI.read(
|
||||
@ -158,7 +158,7 @@ function HostFilterLookup({
|
||||
parseRelatedSearchFields
|
||||
),
|
||||
searchableKeys: Object.keys(actions?.actions.GET || {}).filter(
|
||||
key => actions.actions?.GET[key].filterable
|
||||
(key) => actions.actions?.GET[key].filterable
|
||||
),
|
||||
};
|
||||
},
|
||||
@ -213,7 +213,7 @@ function HostFilterLookup({
|
||||
const chipsArray = [];
|
||||
|
||||
if (Array.isArray(filter[param])) {
|
||||
filter[param].forEach(val =>
|
||||
filter[param].forEach((val) =>
|
||||
chipsArray.push({
|
||||
key: `${param}:${val}`,
|
||||
node: `${val}`,
|
||||
@ -274,7 +274,7 @@ function HostFilterLookup({
|
||||
numChips={5}
|
||||
totalChips={chips[key]?.chips?.length || 0}
|
||||
>
|
||||
{chips[key]?.chips?.map(chip => (
|
||||
{chips[key]?.chips?.map((chip) => (
|
||||
<Chip key={chip.key} isReadOnly>
|
||||
{chip.node}
|
||||
</Chip>
|
||||
@ -284,18 +284,18 @@ function HostFilterLookup({
|
||||
{/* Parse advanced search chips */}
|
||||
{Object.keys(chips).length > 0 &&
|
||||
Object.keys(chips)
|
||||
.filter(val => chips[val].chips.length > 0)
|
||||
.filter((val) => chips[val].chips.length > 0)
|
||||
.filter(
|
||||
val => searchColumns.map(val2 => val2.key).indexOf(val) === -1
|
||||
(val) => searchColumns.map((val2) => val2.key).indexOf(val) === -1
|
||||
)
|
||||
.map(leftoverKey => (
|
||||
.map((leftoverKey) => (
|
||||
<ChipGroup
|
||||
categoryName={chips[leftoverKey].key}
|
||||
key={chips[leftoverKey].key}
|
||||
numChips={5}
|
||||
totalChips={chips[leftoverKey]?.chips?.length || 0}
|
||||
>
|
||||
{chips[leftoverKey]?.chips?.map(chip => (
|
||||
{chips[leftoverKey]?.chips?.map((chip) => (
|
||||
<Chip key={chip.key} isReadOnly>
|
||||
{chip.node}
|
||||
</Chip>
|
||||
@ -372,8 +372,8 @@ function HostFilterLookup({
|
||||
<HeaderCell>{t`Inventory`}</HeaderCell>
|
||||
</HeaderRow>
|
||||
}
|
||||
renderRow={item => <HostListItem key={item.id} item={item} />}
|
||||
renderToolbar={props => (
|
||||
renderRow={(item) => <HostListItem key={item.id} item={item} />}
|
||||
renderToolbar={(props) => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
fillWidth
|
||||
|
||||
@ -23,17 +23,7 @@ describe('HostListItem', () => {
|
||||
</table>
|
||||
);
|
||||
expect(wrapper.find('HostListItem').length).toBe(1);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(0)
|
||||
.text()
|
||||
).toBe('Foo');
|
||||
expect(
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(1)
|
||||
.text()
|
||||
).toBe('Bar');
|
||||
expect(wrapper.find('Td').at(0).text()).toBe('Foo');
|
||||
expect(wrapper.find('Td').at(1).text()).toBe('Bar');
|
||||
});
|
||||
});
|
||||
|
||||
@ -46,10 +46,10 @@ function InstanceGroupsLookup({
|
||||
count: data.count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
).filter((key) => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [history.location]),
|
||||
{
|
||||
@ -123,9 +123,9 @@ function InstanceGroupsLookup({
|
||||
name="instanceGroups"
|
||||
qsConfig={QS_CONFIG}
|
||||
readOnly={!canDelete}
|
||||
selectItem={item => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
sortSelectedItems={selectedItems =>
|
||||
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
sortSelectedItems={(selectedItems) =>
|
||||
dispatch({ type: 'SET_SELECTED_ITEMS', selectedItems })
|
||||
}
|
||||
isSelectedDraggable
|
||||
|
||||
@ -65,10 +65,10 @@ function InventoryLookup({
|
||||
count: data.count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
).map((val) => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => {
|
||||
).filter((key) => {
|
||||
if (['kind', 'host_filter'].includes(key) && hideSmartInventories) {
|
||||
return false;
|
||||
}
|
||||
@ -89,7 +89,7 @@ function InventoryLookup({
|
||||
);
|
||||
|
||||
const checkInventoryName = useCallback(
|
||||
async name => {
|
||||
async (name) => {
|
||||
if (!name) {
|
||||
onChange(null);
|
||||
return;
|
||||
@ -169,8 +169,8 @@ function InventoryLookup({
|
||||
name="inventory"
|
||||
qsConfig={QS_CONFIG}
|
||||
readOnly={!canDelete}
|
||||
selectItem={item => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -225,8 +225,8 @@ function InventoryLookup({
|
||||
name="inventory"
|
||||
qsConfig={QS_CONFIG}
|
||||
readOnly={!canDelete}
|
||||
selectItem={item => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
|
||||
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user