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