foreman_rh_cloud 14.2.0 → 14.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/lib/foreman_rh_cloud/version.rb +1 -1
  3. data/package.json +1 -1
  4. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/EmptyResults.js +15 -6
  5. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/emptyResults.scss +1 -5
  6. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/EmptyState.js +15 -7
  7. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/EmptyState.test.js +1 -3
  8. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/emptyState.scss +1 -5
  9. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/ErrorState.js +2 -2
  10. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/ListItem.test.js +0 -1
  11. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountList.test.js +2 -6
  12. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/Components/ClearButton/ClearButton.js +13 -10
  13. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/InventoryFilter.js +30 -11
  14. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/integration.test.js +3 -1
  15. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/inventoryFilter.scss +28 -22
  16. data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettings.js +4 -4
  17. data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettings.scss +16 -3
  18. data/webpack/ForemanInventoryUpload/Components/InventorySettings/MinimalInventoryDropdown.js +13 -14
  19. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +7 -7
  20. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.scss +3 -2
  21. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageTitle.js +63 -46
  22. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +4 -7
  23. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js +83 -12
  24. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudPingModal/index.js +31 -38
  25. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButton.test.js +3 -1
  26. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/InventoryAutoUpload.js +29 -22
  27. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/InventoryAutoUpload.test.js +6 -2
  28. data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryHelpers.test.js +6 -3
  29. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +90 -23
  30. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableActions.js +1 -2
  31. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableConstants.js +24 -30
  32. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableHelpers.js +5 -6
  33. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +193 -8
  34. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js +4 -1
  35. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +8 -9
  36. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +26 -13
  37. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationTableConstants.js +12 -10
  38. data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +21 -9
  39. data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +12 -19
  40. data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncActions.test.js +0 -1
  41. data/webpack/common/DropdownToggle.js +20 -6
  42. metadata +1 -44
  43. data/webpack/ForemanInventoryUpload/Components/FileDownload/FileDownload.fixtures.js +0 -0
  44. data/webpack/ForemanInventoryUpload/Components/FileDownload/FileDownload.js +0 -25
  45. data/webpack/ForemanInventoryUpload/Components/FileDownload/FileDownloadHelper.js +0 -0
  46. data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/FileDownload.test.js +0 -17
  47. data/webpack/ForemanInventoryUpload/Components/FileDownload/fileDownload.scss +0 -5
  48. data/webpack/ForemanInventoryUpload/Components/FileDownload/index.js +0 -1
  49. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.fixtures.js +0 -20
  50. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.js +0 -66
  51. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainerHelper.js +0 -0
  52. data/webpack/ForemanInventoryUpload/Components/NavContainer/__tests__/NavContainer.test.js +0 -69
  53. data/webpack/ForemanInventoryUpload/Components/NavContainer/index.js +0 -1
  54. data/webpack/ForemanInventoryUpload/Components/NavContainer/navContainer.scss +0 -9
  55. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/ScheduledRun.fixtures.js +0 -4
  56. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/ScheduledRun.js +0 -29
  57. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/ScheduledRunHelper.js +0 -0
  58. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/ScheduledRun.test.js +0 -33
  59. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/index.js +0 -12
  60. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/scheduledRun.scss +0 -13
  61. data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.fixtures.js +0 -0
  62. data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.js +0 -57
  63. data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChartHelper.js +0 -0
  64. data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/StatusChart.test.js +0 -30
  65. data/webpack/ForemanInventoryUpload/Components/StatusChart/index.js +0 -1
  66. data/webpack/ForemanInventoryUpload/Components/StatusChart/statusChart.scss +0 -10
  67. data/webpack/ForemanInventoryUpload/Components/TabContainer/TabContainer.fixtures.js +0 -0
  68. data/webpack/ForemanInventoryUpload/Components/TabContainer/TabContainer.js +0 -24
  69. data/webpack/ForemanInventoryUpload/Components/TabContainer/TabContainerHelper.js +0 -0
  70. data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/TabContainer.test.js +0 -15
  71. data/webpack/ForemanInventoryUpload/Components/TabContainer/index.js +0 -1
  72. data/webpack/ForemanInventoryUpload/Components/TabContainer/tabContainer.scss +0 -10
  73. data/webpack/ForemanInventoryUpload/Components/TabFooter/TabFooter.fixtures.js +0 -0
  74. data/webpack/ForemanInventoryUpload/Components/TabFooter/TabFooter.js +0 -19
  75. data/webpack/ForemanInventoryUpload/Components/TabFooter/TabFooterHelper.js +0 -0
  76. data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/TabFooter.test.js +0 -15
  77. data/webpack/ForemanInventoryUpload/Components/TabFooter/index.js +0 -1
  78. data/webpack/ForemanInventoryUpload/Components/TabFooter/tabFooter.scss +0 -0
  79. data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.fixtures.js +0 -0
  80. data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.js +0 -85
  81. data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeaderHelper.js +0 -0
  82. data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/TabHeader.test.js +0 -114
  83. data/webpack/ForemanInventoryUpload/Components/TabHeader/index.js +0 -1
  84. data/webpack/ForemanInventoryUpload/Components/TabHeader/tabHeader.scss +0 -23
  85. data/webpack/common/Switcher/index.js +0 -80
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
- import { fireEvent, render, screen } from '@testing-library/react';
2
+ import PropTypes from 'prop-types';
3
+ import { act, fireEvent, render, screen } from '@testing-library/react';
4
+ import '@testing-library/jest-dom';
3
5
  import PageTitle from '../PageTitle';
4
6
 
5
7
  let mockIopMode = false;
@@ -15,10 +17,30 @@ jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
15
17
  }),
16
18
  }));
17
19
 
18
- jest.mock('../components/CloudPingModal', () => () => (
19
- <div data-testid="cloud-ping-modal">CloudPingModal</div>
20
- ));
21
- jest.mock('foremanReact/common/helpers', () => ({ getDocsURL: () => {} }));
20
+ jest.mock('../components/CloudPingModal', () => {
21
+ const React = require('react');
22
+ const PropTypes = require('prop-types');
23
+
24
+ const MockCloudPingModal = ({ isOpen, title }) =>
25
+ isOpen ? (
26
+ <div role="dialog" aria-label={title}>
27
+ Organization status
28
+ </div>
29
+ ) : null;
30
+
31
+ MockCloudPingModal.propTypes = {
32
+ isOpen: PropTypes.bool.isRequired,
33
+ title: PropTypes.string.isRequired,
34
+ };
35
+
36
+ return {
37
+ __esModule: true,
38
+ default: MockCloudPingModal,
39
+ };
40
+ });
41
+ jest.mock('foremanReact/common/helpers', () => ({
42
+ getDocsURL: () => '/links/manual/test',
43
+ }));
22
44
 
23
45
  describe('PageTitle', () => {
24
46
  afterEach(() => {
@@ -31,21 +53,70 @@ describe('PageTitle', () => {
31
53
  });
32
54
 
33
55
  it('renders the kebab dropdown', () => {
34
- const { container } = render(<PageTitle />);
35
- expect(container.querySelector('.title-dropdown')).toBeTruthy();
56
+ render(<PageTitle />);
57
+ expect(screen.getByLabelText('Actions')).toBeTruthy();
58
+ });
59
+
60
+ it('renders cloud-ping dropdown item when not in IoP mode', async () => {
61
+ mockIopMode = false;
62
+ render(<PageTitle />);
63
+
64
+ await act(async () => {
65
+ fireEvent.click(screen.getByLabelText('Actions'));
66
+ });
67
+
68
+ // Verify all dropdown items are present
69
+ const connectivityItem = screen.getByText('Connectivity test');
70
+ expect(connectivityItem).toBeTruthy();
71
+
72
+ const tasksHistoryLink = screen.getByRole('menuitem', {
73
+ name: 'Actions history',
74
+ });
75
+ const inventoryDocsLink = screen.getByRole('menuitem', {
76
+ name: 'Documentation',
77
+ });
78
+
79
+ expect(tasksHistoryLink).toBeTruthy();
80
+ expect(inventoryDocsLink).toBeTruthy();
81
+
82
+ // Verify links open in a new tab
83
+ expect(tasksHistoryLink).toHaveAttribute('target', '_blank');
84
+ expect(inventoryDocsLink).toHaveAttribute('target', '_blank');
85
+
86
+ // Verify links have the expected URL patterns
87
+ expect(tasksHistoryLink.getAttribute('href')).toContain(
88
+ '/foreman_tasks/tasks'
89
+ );
90
+ expect(inventoryDocsLink.getAttribute('href')).toContain('/links/manual/');
36
91
  });
37
92
 
38
- it('renders cloud-ping dropdown item when not in IoP mode', () => {
93
+ it('opens CloudPingModal when clicking Connectivity test', async () => {
39
94
  mockIopMode = false;
40
95
  render(<PageTitle />);
41
- fireEvent.click(screen.getByLabelText('Actions'));
42
- expect(screen.getByText('Connectivity test')).toBeTruthy();
96
+
97
+ // Modal should not be visible initially
98
+ expect(screen.queryByRole('dialog')).toBeNull();
99
+
100
+ await act(async () => {
101
+ fireEvent.click(screen.getByLabelText('Actions'));
102
+ });
103
+
104
+ await act(async () => {
105
+ fireEvent.click(screen.getByText('Connectivity test'));
106
+ });
107
+
108
+ // Modal should now be visible
109
+ const dialog = screen.getByRole('dialog');
110
+ expect(dialog).toBeTruthy();
111
+ expect(dialog).toHaveTextContent('Organization status');
43
112
  });
44
113
 
45
- it('does not render cloud-ping dropdown item when in IoP mode', () => {
114
+ it('does not render cloud-ping dropdown item when in IoP mode', async () => {
46
115
  mockIopMode = true;
47
116
  render(<PageTitle />);
48
- fireEvent.click(screen.getByLabelText('Actions'));
117
+ await act(async () => {
118
+ fireEvent.click(screen.getByLabelText('Actions'));
119
+ });
49
120
  expect(screen.queryByText('Connectivity test')).toBeNull();
50
121
  mockIopMode = false;
51
122
  });
@@ -2,11 +2,7 @@
2
2
  import React, { useCallback, useEffect, useState } from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import { useDispatch, useSelector } from 'react-redux';
5
- import {
6
- Table,
7
- TableBody,
8
- TableHeader,
9
- } from '@patternfly/react-table/deprecated';
5
+ import { Table, Tbody, Tr, Td } from '@patternfly/react-table';
10
6
  import {
11
7
  Card,
12
8
  CardTitle,
@@ -21,6 +17,10 @@ import {
21
17
  CheckCircleIcon,
22
18
  ExclamationCircleIcon,
23
19
  } from '@patternfly/react-icons';
20
+ import {
21
+ global_danger_color_200 as dangerColor,
22
+ global_success_color_100 as successColor,
23
+ } from '@patternfly/react-tokens';
24
24
  import { get } from 'foremanReact/redux/API';
25
25
  import { translate as __, sprintf } from 'foremanReact/common/I18n';
26
26
  import { STATUS } from 'foremanReact/constants';
@@ -30,34 +30,20 @@ import { inventoryUrl } from '../../../../ForemanInventoryHelpers';
30
30
  export const API_KEY = 'CLOUD_PING';
31
31
 
32
32
  const CloudPingModal = ({ title, isOpen, toggle }) => {
33
- const [rows, setRows] = useState([]);
33
+ const [certAuths, setCertAuths] = useState([]);
34
34
  const dispatch = useDispatch();
35
+ const status = useSelector(state => selectAPIStatus(state, API_KEY));
36
+ const isPending = status === STATUS.PENDING;
37
+
35
38
  const handleSuccess = useCallback(
36
39
  ({
37
40
  data: {
38
41
  ping: { cert_auth = [] },
39
42
  },
40
43
  }) => {
41
- cert_auth.length &&
42
- setRows(
43
- cert_auth.map(cert => ({
44
- cells: [
45
- {
46
- title: (
47
- <>
48
- <StatusIcon
49
- isPending={status === STATUS.PENDING}
50
- authStatus={cert}
51
- />{' '}
52
- {cert.org_name} {cert.error}
53
- </>
54
- ),
55
- },
56
- ],
57
- }))
58
- );
44
+ setCertAuths(cert_auth);
59
45
  },
60
- [status]
46
+ []
61
47
  );
62
48
 
63
49
  useEffect(() => {
@@ -71,10 +57,6 @@ const CloudPingModal = ({ title, isOpen, toggle }) => {
71
57
  );
72
58
  }, [isOpen, dispatch, handleSuccess]);
73
59
 
74
- const status = useSelector(state => selectAPIStatus(state, API_KEY));
75
- const isPending = status === STATUS.PENDING;
76
- // const error = useSelector(state => selectAPIErrorMessage(state, API_KEY));
77
-
78
60
  return (
79
61
  <>
80
62
  <Modal
@@ -97,17 +79,28 @@ const CloudPingModal = ({ title, isOpen, toggle }) => {
97
79
  ) : (
98
80
  <>
99
81
  <Text className="pull-right" ouiaId="text-org-count">
100
- {sprintf(__('%s organizations'), rows.length)}
82
+ {sprintf(__('%s organizations'), certAuths.length)}
101
83
  </Text>
102
84
  <Table
103
- aria-label="Simple Table"
85
+ aria-label="Organization status"
104
86
  ouiaId="simple-table"
105
- cells={['']}
106
- rows={rows}
87
+ variant="compact"
88
+ borders={false}
107
89
  >
108
- <TableHeader />
109
- <TableBody />
110
- </Table>{' '}
90
+ <Tbody>
91
+ {certAuths.map((cert, idx) => (
92
+ <Tr
93
+ key={cert.org_name || idx}
94
+ ouiaId={`org-status-row-${idx}`}
95
+ >
96
+ <Td dataLabel={__('Organization')}>
97
+ <StatusIcon isPending={isPending} authStatus={cert} />{' '}
98
+ {cert.org_name} {cert.error}
99
+ </Td>
100
+ </Tr>
101
+ ))}
102
+ </Tbody>
103
+ </Table>
111
104
  </>
112
105
  )}
113
106
  </CardBody>
@@ -121,13 +114,13 @@ const StatusIcon = ({ isPending, authStatus }) => {
121
114
  if (isPending) return <Spinner size="sm" />;
122
115
  if (authStatus.success)
123
116
  return (
124
- <Icon color="green">
117
+ <Icon color={successColor.value}>
125
118
  <CheckCircleIcon />
126
119
  </Icon>
127
120
  );
128
121
  if (authStatus.error)
129
122
  return (
130
- <Icon color="red">
123
+ <Icon color={dangerColor.value}>
131
124
  <ExclamationCircleIcon />
132
125
  </Icon>
133
126
  );
@@ -5,7 +5,9 @@ import SyncButton from '../SyncButton';
5
5
  describe('SyncButton', () => {
6
6
  it('renders the sync button text', () => {
7
7
  render(<SyncButton handleSync={jest.fn()} />);
8
- expect(screen.getByRole('button', { name: /Sync all inventory status/ })).toBeTruthy();
8
+ expect(
9
+ screen.getByRole('button', { name: /Sync all inventory status/ })
10
+ ).toBeTruthy();
9
11
  });
10
12
 
11
13
  it('calls handleSync on click', () => {
@@ -1,11 +1,18 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Text, TextVariants, Popover, Button } from '@patternfly/react-core';
3
+ import {
4
+ Text,
5
+ TextVariants,
6
+ Popover,
7
+ Button,
8
+ FormGroup,
9
+ Grid,
10
+ GridItem,
11
+ } from '@patternfly/react-core';
4
12
  import { InfoAltIcon, CaretRightIcon } from '@patternfly/react-icons';
5
- import { FormGroup, Grid } from 'patternfly-react';
6
13
  import { translate as __ } from 'foremanReact/common/I18n';
7
14
  import { foremanUrl } from '../../../ForemanRhCloudHelpers';
8
- import Switcher from '../../../common/Switcher';
15
+ import SwitcherPF4 from '../../../common/Switcher/SwitcherPF4';
9
16
  import { settingsDict } from '../../Components/InventorySettings/AdvancedSetting/AdvancedSettingsConstants';
10
17
  import InventorySettings from '../../Components/InventorySettings/InventorySettings';
11
18
 
@@ -27,19 +34,19 @@ const InventoryAutoUploadSwitcher = ({
27
34
  <Grid>
28
35
  <h3>{__('Red Hat Cloud Inventory')}</h3>
29
36
  <hr />
30
- <Grid.Row>
31
- <Switcher
32
- id="auto-upload"
33
- label={__('Inventory Auto Upload')}
34
- tooltip={__(
35
- 'Enable automatic upload of your hosts inventory to the Red Hat cloud'
36
- )}
37
- isChecked={autoUploadEnabled}
38
- onChange={handleToggle}
39
- labelCol={5}
40
- />
41
-
42
- <Grid.Col sm={5}>
37
+ <Grid hasGutter>
38
+ <GridItem span={7}>
39
+ <SwitcherPF4
40
+ id="auto-upload"
41
+ label={__('Inventory Auto Upload')}
42
+ tooltip={__(
43
+ 'Enable automatic upload of your hosts inventory to the Red Hat cloud'
44
+ )}
45
+ isChecked={autoUploadEnabled}
46
+ onChange={handleToggle}
47
+ />
48
+ </GridItem>
49
+ <GridItem span={5}>
43
50
  <Popover
44
51
  headerContent={
45
52
  <strong>{__('Advanced Inventory Settings')}</strong>
@@ -55,11 +62,11 @@ const InventoryAutoUploadSwitcher = ({
55
62
  {__('Show Advanced Settings')} <CaretRightIcon />
56
63
  </Button>
57
64
  </Popover>
58
- </Grid.Col>
59
- </Grid.Row>
65
+ </GridItem>
66
+ </Grid>
60
67
  <br />
61
- <Grid.Row>
62
- <Grid.Col sm={12}>
68
+ <Grid>
69
+ <GridItem span={12}>
63
70
  <Text component={TextVariants.p} ouiaId="text-more-details">
64
71
  <InfoAltIcon /> {__('More details can be found in')}{' '}
65
72
  <Text
@@ -76,8 +83,8 @@ const InventoryAutoUploadSwitcher = ({
76
83
  </strong>
77
84
  </Text>
78
85
  </Text>
79
- </Grid.Col>
80
- </Grid.Row>
86
+ </GridItem>
87
+ </Grid>
81
88
  </Grid>
82
89
  </FormGroup>
83
90
  );
@@ -22,12 +22,16 @@ describe('InventoryAutoUpload', () => {
22
22
 
23
23
  it('renders the auto upload switcher', () => {
24
24
  render(<InventoryAutoUpload {...buildProps()} />);
25
- expect(screen.getByText('Inventory Auto Upload')).toBeTruthy();
25
+ expect(
26
+ screen.getByRole('checkbox', { name: /Inventory Auto Upload/ })
27
+ ).toBeTruthy();
26
28
  });
27
29
 
28
30
  it('renders the advanced settings button', () => {
29
31
  render(<InventoryAutoUpload {...buildProps()} />);
30
- expect(screen.getByRole('button', { name: /Show Advanced Settings/ })).toBeTruthy();
32
+ expect(
33
+ screen.getByRole('button', { name: /Show Advanced Settings/ })
34
+ ).toBeTruthy();
31
35
  });
32
36
 
33
37
  it('calls getSettings on mount', () => {
@@ -1,10 +1,13 @@
1
+ import { getDocsURL } from 'foremanReact/common/helpers';
2
+ import {
3
+ getInventoryDocsUrl,
4
+ isExitCodeLoading,
5
+ } from '../ForemanInventoryHelpers';
6
+
1
7
  jest.mock('foremanReact/common/helpers', () => ({
2
8
  getDocsURL: jest.fn(),
3
9
  }));
4
10
 
5
- import { getDocsURL } from 'foremanReact/common/helpers';
6
- import { getInventoryDocsUrl, isExitCodeLoading } from '../ForemanInventoryHelpers';
7
-
8
11
  describe('ForemanInventoryUpload helpers', () => {
9
12
  describe('getInventoryDocsUrl', () => {
10
13
  it('requests the Managing Hosts guide at the cloud connection chapter', () => {
@@ -3,9 +3,14 @@ import React, { useEffect } from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import {
5
5
  Table,
6
- TableHeader,
7
- TableBody,
8
- } from '@patternfly/react-table/deprecated';
6
+ Tbody,
7
+ Td,
8
+ Th,
9
+ Thead,
10
+ Tr,
11
+ SortByDirection,
12
+ } from '@patternfly/react-table';
13
+ import { translate as __ } from 'foremanReact/common/I18n';
9
14
  import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
10
15
  import SelectAllAlert from './SelectAllAlert';
11
16
  import {
@@ -51,14 +56,33 @@ const InsightsTable = ({
51
56
  const isIop = useIopConfig();
52
57
 
53
58
  useEffect(() => {
54
- setRows(
55
- modifySelectedRows(hits, selectedIds, showSelectAllAlert, hideHost, isIop)
56
- );
59
+ setRows(modifySelectedRows(hits, selectedIds, showSelectAllAlert, isIop));
57
60
 
58
61
  if (hideHost) setColumns(getColumnsWithoutHostname());
59
- }, [hits, selectedIds, hideHost]);
62
+ }, [hits, selectedIds, hideHost, isIop]);
60
63
 
61
64
  const hasSelectableRows = rows.some(row => !row.disableCheckbox);
65
+ const hasRows = rows.length > 0;
66
+ const selectedRowsCount = rows.filter(
67
+ row => !row.disableCheckbox && row.selected
68
+ ).length;
69
+ const selectableRowsCount = rows.filter(row => !row.disableCheckbox).length;
70
+ const allSelected = hasRows && selectableRowsCount === selectedRowsCount;
71
+ const isSortDirectionValid =
72
+ sortOrder === SortByDirection.asc || sortOrder === SortByDirection.desc;
73
+ const sortIndex = getSortColumnIndex(columns, sortBy);
74
+ const sortByState = isSortDirectionValid
75
+ ? {
76
+ index: hasSelectableRows ? sortIndex + 1 : sortIndex,
77
+ direction: sortOrder,
78
+ }
79
+ : undefined;
80
+
81
+ const getCellContent = (row, col) => {
82
+ if (col.id === 'actions') return col.formatter(row);
83
+ const value = row[col.id];
84
+ return col.formatter ? col.formatter(value) : value;
85
+ };
62
86
 
63
87
  return (
64
88
  <React.Fragment>
@@ -73,24 +97,67 @@ const InsightsTable = ({
73
97
  className="rh-cloud-recommendations-table"
74
98
  ouiaId="rh-cloud-recommendations-table"
75
99
  aria-label="Recommendations Table"
76
- {...(hasSelectableRows && {
77
- onSelect: (_event, isSelected, rowId) =>
78
- onTableSelect(isSelected, rowId, rows, selectedIds),
79
- })}
80
- canSelectAll={hasSelectableRows}
81
- sortBy={{
82
- index: getSortColumnIndex(columns, sortBy),
83
- direction: sortOrder,
84
- }}
85
- onSort={(_event, index, direction) =>
86
- onTableSort(columns, index, direction)
87
- }
88
- cells={columns}
89
- rows={rows}
90
100
  variant="compact"
91
101
  >
92
- <TableHeader />
93
- <TableBody />
102
+ <Thead>
103
+ <Tr ouiaId="recommendations-table-head-row">
104
+ {hasSelectableRows && (
105
+ <Th
106
+ aria-label={__('Select all recommendations')}
107
+ select={{
108
+ onSelect: (_event, isSelected) =>
109
+ onTableSelect(isSelected, -1, rows, selectedIds),
110
+ isSelected: allSelected,
111
+ }}
112
+ />
113
+ )}
114
+ {columns.map((column, index) => (
115
+ <Th
116
+ key={column.id}
117
+ width={column.width}
118
+ screenReaderText={!column.title ? __('Actions') : undefined}
119
+ sort={
120
+ column.sortKey
121
+ ? {
122
+ sortBy: sortByState,
123
+ onSort: (_event, colIndex, direction) =>
124
+ onTableSort(
125
+ columns,
126
+ colIndex - (hasSelectableRows ? 1 : 0),
127
+ direction
128
+ ),
129
+ columnIndex: hasSelectableRows ? index + 1 : index,
130
+ }
131
+ : undefined
132
+ }
133
+ >
134
+ {column.title}
135
+ </Th>
136
+ ))}
137
+ </Tr>
138
+ </Thead>
139
+ <Tbody>
140
+ {rows.map((row, rowIndex) => (
141
+ <Tr key={row.id} ouiaId={`recommendations-row-${row.id}`}>
142
+ {hasSelectableRows && (
143
+ <Td
144
+ select={{
145
+ rowIndex,
146
+ isDisabled: row.disableCheckbox,
147
+ onSelect: (_event, isSelected) =>
148
+ onTableSelect(isSelected, rowIndex, rows, selectedIds),
149
+ isSelected: row.selected,
150
+ }}
151
+ />
152
+ )}
153
+ {columns.map(column => (
154
+ <Td key={`${row.id}-${column.id}`}>
155
+ {getCellContent(row, column)}
156
+ </Td>
157
+ ))}
158
+ </Tr>
159
+ ))}
160
+ </Tbody>
94
161
  </Table>
95
162
  <TableEmptyState status={status} error={error} rowsLength={rows.length} />
96
163
  <Pagination variant="bottom" />
@@ -96,8 +96,7 @@ export const clearAllSelection = () => dispatch => {
96
96
  };
97
97
 
98
98
  export const onTableSort = (columns, index, direction) => {
99
- // The checkbox column shifts the data columns by 1;
100
- const { sortKey } = columns[index - 1];
99
+ const { sortKey } = columns[index];
101
100
  return fetchInsights({
102
101
  sortBy: sortKey,
103
102
  sortOrder: direction,
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable camelcase */
2
2
  import React from 'react';
3
- import { DropdownItem } from '@patternfly/react-core/deprecated';
4
- import { sortable, cellWidth } from '@patternfly/react-table';
3
+ import { DropdownItem } from '@patternfly/react-core';
5
4
  import { AnsibeTowerIcon, ExternalLinkAltIcon } from '@patternfly/react-icons';
6
5
  import { translate as __ } from 'foremanReact/common/I18n';
7
6
  import { foremanUrl } from '../../../ForemanRhCloudHelpers';
@@ -9,26 +8,23 @@ import DropdownToggle from '../../../common/DropdownToggle';
9
8
  import InsightsSection from './InsightsSection';
10
9
  import InsightsLabel from './InsightsLabel';
11
10
 
12
- export const totalRiskFormatter = ({ title: totalRisk }) => ({
13
- children: (
14
- <InsightsSection className="insights-total-risk" type="icon-group">
15
- <InsightsLabel value={totalRisk} />
16
- </InsightsSection>
17
- ),
18
- });
11
+ export const totalRiskFormatter = totalRisk => (
12
+ <InsightsSection className="insights-total-risk" type="icon-group">
13
+ <InsightsLabel value={totalRisk} />
14
+ </InsightsSection>
15
+ );
19
16
 
20
- export const hasPlaybookFormatter = ({ title: hasPlaybook }) => ({
21
- children: hasPlaybook ? (
17
+ export const hasPlaybookFormatter = hasPlaybook =>
18
+ hasPlaybook ? (
22
19
  <span className="td-insights-remediate-playbook">
23
20
  <AnsibeTowerIcon />
24
21
  {__('Playbook')}
25
22
  </span>
26
23
  ) : (
27
24
  <span className="td-insights-remediate-manual">{__('Manual')}</span>
28
- ),
29
- });
25
+ );
30
26
 
31
- export const actionsFormatter = (props, { rowData = {} }) => {
27
+ export const actionsFormatter = rowData => {
32
28
  const { recommendationUrl, accessRHUrl, isLocalAdvisorEngine } = rowData;
33
29
  const dropdownItems = [];
34
30
 
@@ -51,9 +47,7 @@ export const actionsFormatter = (props, { rowData = {} }) => {
51
47
  </DropdownItem>
52
48
  );
53
49
 
54
- return {
55
- children: <DropdownToggle items={dropdownItems} />,
56
- };
50
+ return <DropdownToggle items={dropdownItems} />;
57
51
  };
58
52
 
59
53
  export const columns = [
@@ -61,39 +55,39 @@ export const columns = [
61
55
  id: 'hostname',
62
56
  sortKey: 'hostname',
63
57
  title: __('Hostname'),
64
- transforms: [cellWidth(20), sortable],
58
+ width: 20,
65
59
  },
66
60
  {
67
61
  id: 'recommendation',
68
62
  sortKey: 'title',
69
63
  title: __('Recommendation'),
70
- transforms: [cellWidth(50), sortable],
64
+ width: 50,
71
65
  },
72
66
  {
73
- id: 'total risk',
67
+ id: 'total_risk',
74
68
  sortKey: 'total_risk',
75
69
  title: __('Total risk'),
76
- transforms: [cellWidth(15), sortable],
77
- cellTransforms: [totalRiskFormatter],
70
+ width: 15,
71
+ formatter: totalRiskFormatter,
78
72
  },
79
73
  {
80
- id: 'remediate',
74
+ id: 'has_playbook',
81
75
  title: __('Remediate'),
82
- transforms: [cellWidth(10)],
83
- cellTransforms: [hasPlaybookFormatter],
76
+ width: 10,
77
+ formatter: hasPlaybookFormatter,
84
78
  },
85
79
  {
86
80
  id: 'actions',
87
81
  title: '',
88
- transforms: [cellWidth(5)],
89
- cellTransforms: [actionsFormatter],
82
+ width: 5,
83
+ formatter: actionsFormatter,
90
84
  },
91
85
  ];
92
86
 
93
87
  export const getColumnsWithoutHostname = () => {
94
- const nextCols = columns.slice(1);
95
- nextCols[0].transforms = [cellWidth(70), sortable];
96
- return nextCols;
88
+ return columns
89
+ .slice(1)
90
+ .map(col => (col.id === 'recommendation' ? { ...col, width: 70 } : col));
97
91
  };
98
92
 
99
93
  export const paginationTitles = {
@@ -6,7 +6,6 @@ export const modifySelectedRows = (
6
6
  hits,
7
7
  selectedIds,
8
8
  showSelectAllAlert,
9
- hideHost,
10
9
  isLocalAdvisorEngine
11
10
  ) => {
12
11
  if (hits.length === 0) return [];
@@ -24,12 +23,13 @@ export const modifySelectedRows = (
24
23
  solution_url,
25
24
  }) => {
26
25
  const disableCheckbox = !has_playbook;
27
- const cells = [hostname, title, total_risk, has_playbook, results_url];
28
- if (hideHost) cells.shift();
29
26
  return {
30
- cells,
31
27
  disableCheckbox,
32
28
  id,
29
+ hostname,
30
+ recommendation: title,
31
+ total_risk,
32
+ has_playbook,
33
33
  /** The main table checkbox will be seen as selected only if all rows are selected,
34
34
  * in this case we need to select also the disabled once and hide it with css */
35
35
  selected: selectedIds[id] || (disableCheckbox && showSelectAllAlert),
@@ -45,8 +45,7 @@ export const getSortColumnIndex = (columns, sortBy) => {
45
45
  let colIndex = 0;
46
46
  columns.forEach((col, index) => {
47
47
  if (col.sortKey === sortBy) {
48
- // The checkbox column shifts the data columns by 1;
49
- colIndex = index + 1;
48
+ colIndex = index;
50
49
  }
51
50
  });
52
51
  return colIndex;