foreman_openscap 12.1.2 → 13.0.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/foreman_openscap/scap_breakdown_chart.css +15 -0
  3. data/app/models/foreman_openscap/compliance_status.rb +2 -2
  4. data/app/views/compliance_hosts/show.html.erb +5 -3
  5. data/lib/foreman_openscap/engine.rb +1 -1
  6. data/lib/foreman_openscap/version.rb +1 -1
  7. data/test/unit/compliance_status_test.rb +50 -0
  8. data/webpack/components/LineChart/LineChart.fixtures.js +28 -0
  9. data/webpack/components/LineChart/LineChart.scss +7 -0
  10. data/webpack/components/LineChart/LineChart.test.js +266 -0
  11. data/webpack/components/LineChart/LineChartHelpers.js +206 -0
  12. data/webpack/components/LineChart/index.js +324 -0
  13. data/webpack/components/OpenscapRemediationWizard/constants.js +3 -0
  14. data/webpack/components/OpenscapRemediationWizard/steps/Finish.js +2 -2
  15. data/webpack/components/OpenscapRemediationWizard/steps/ReviewRemediation.js +10 -2
  16. data/webpack/components/OpenscapRemediationWizard/steps/SnippetSelect.js +3 -1
  17. data/webpack/global_index.js +3 -1
  18. data/webpack/index.js +2 -0
  19. data/webpack/test_setup.js +1 -0
  20. metadata +8 -19
  21. data/app/assets/javascripts/foreman_openscap/scap_hosts_show.js +0 -4
  22. data/webpack/components/ConfirmModal.js +0 -66
  23. data/webpack/components/ConfirmModal.scss +0 -3
  24. data/webpack/components/IndexLayout.js +0 -44
  25. data/webpack/components/IndexTable/IndexTableHelper.js +0 -6
  26. data/webpack/components/IndexTable/index.js +0 -73
  27. data/webpack/components/LinkButton.js +0 -42
  28. data/webpack/components/withDeleteModal.js +0 -51
  29. data/webpack/components/withLoading.js +0 -107
  30. data/webpack/helpers/commonHelper.js +0 -1
  31. data/webpack/helpers/globalIdHelper.js +0 -15
  32. data/webpack/helpers/mutationHelper.js +0 -68
  33. data/webpack/helpers/pageParamsHelper.js +0 -31
  34. data/webpack/helpers/permissionsHelper.js +0 -42
  35. data/webpack/helpers/tableHelper.js +0 -9
  36. data/webpack/helpers/toastHelper.js +0 -3
  37. data/webpack/testHelper.js +0 -127
@@ -0,0 +1,324 @@
1
+ import React, {
2
+ useMemo,
3
+ useRef,
4
+ useEffect,
5
+ useLayoutEffect,
6
+ useState,
7
+ useCallback,
8
+ } from 'react';
9
+ import PropTypes from 'prop-types';
10
+ import {
11
+ Chart,
12
+ ChartAxis,
13
+ ChartGroup,
14
+ ChartLine,
15
+ ChartLegend,
16
+ ChartLegendTooltip,
17
+ ChartThemeColor,
18
+ createContainer,
19
+ } from '@patternfly/react-charts';
20
+
21
+ import { translate as __ } from 'foremanReact/common/I18n';
22
+ import {
23
+ formatAxisTick,
24
+ formatTooltipTitle,
25
+ formatYAxisTick,
26
+ getLegendEvents,
27
+ getSeriesOpacity,
28
+ getXAxisTickValues,
29
+ InteractiveLegendLabel,
30
+ InteractiveLegendSymbol,
31
+ XAxisTickLabel,
32
+ } from 'foremanReact/components/common/charts/helpers/LegendHelpers';
33
+ import EmptyState from '../EmptyState';
34
+
35
+ import {
36
+ processChartData,
37
+ hasChartData,
38
+ getYTickValues,
39
+ sanitizeChartDimension,
40
+ clampChartPadding,
41
+ getTimeseriesXDomain,
42
+ buildLineChartLegendData,
43
+ formatTooltipValue,
44
+ } from './LineChartHelpers';
45
+ import './LineChart.scss';
46
+
47
+ const DEFAULT_HEIGHT = 350;
48
+ const DEFAULT_WIDTH = 1000;
49
+
50
+ /** Match AreaChart padding so angled x-axis tick labels are not clipped. */
51
+ const CHART_PADDING_BASE = { bottom: 125, right: 170, top: 50 };
52
+
53
+ const getDataClickEvents = onclick => {
54
+ if (!onclick) return null;
55
+
56
+ return {
57
+ target: 'data',
58
+ eventHandlers: {
59
+ onClick: () => [
60
+ {
61
+ target: 'data',
62
+ mutation: props => {
63
+ if (props.datum?.name) {
64
+ onclick({ id: props.datum.name });
65
+ }
66
+ return null;
67
+ },
68
+ },
69
+ ],
70
+ },
71
+ };
72
+ };
73
+
74
+ const LineChart = ({
75
+ data,
76
+ config,
77
+ noDataMsg,
78
+ xAxisDataLabel,
79
+ onclick,
80
+ id,
81
+ size,
82
+ title: _title,
83
+ unloadData: _unloadData,
84
+ axisOpts: _axisOpts,
85
+ }) => {
86
+ const chartData = useMemo(
87
+ () => processChartData(data, xAxisDataLabel, config),
88
+ [data, xAxisDataLabel, config]
89
+ );
90
+
91
+ const CursorVoronoiContainer = useMemo(
92
+ () => createContainer('voronoi', 'cursor'),
93
+ []
94
+ );
95
+
96
+ const [hiddenSeries, setHiddenSeries] = useState(() => new Set());
97
+ const [hoveredSeries, setHoveredSeries] = useState(null);
98
+ const toggleSeries = useCallback(name => {
99
+ setHiddenSeries(prev => {
100
+ const next = new Set(prev);
101
+ if (next.has(name)) next.delete(name);
102
+ else next.add(name);
103
+ return next;
104
+ });
105
+ }, []);
106
+
107
+ const containerRef = useRef(null);
108
+ const [observedSize, setObservedSize] = useState(null);
109
+
110
+ const updateObservedSize = useCallback(() => {
111
+ const el = containerRef.current;
112
+ if (!el) return;
113
+
114
+ const { width, height } = el.getBoundingClientRect();
115
+ if (width > 0 && height > 0) {
116
+ setObservedSize({ width, height });
117
+ }
118
+ }, []);
119
+
120
+ useLayoutEffect(() => {
121
+ updateObservedSize();
122
+ }, [updateObservedSize]);
123
+
124
+ useEffect(() => {
125
+ const el = containerRef.current;
126
+ if (!el || typeof ResizeObserver === 'undefined') return undefined;
127
+
128
+ const observer = new ResizeObserver(() => {
129
+ updateObservedSize();
130
+ });
131
+
132
+ observer.observe(el);
133
+ return () => observer.disconnect();
134
+ }, [updateObservedSize]);
135
+
136
+ const legendData = useMemo(
137
+ () => (chartData ? buildLineChartLegendData(chartData, hiddenSeries) : []),
138
+ [chartData, hiddenSeries]
139
+ );
140
+
141
+ const visibleSeries = useMemo(
142
+ () => chartData?.filter(series => !hiddenSeries.has(series.name)) ?? [],
143
+ [chartData, hiddenSeries]
144
+ );
145
+
146
+ const tickValues = useMemo(() => getXAxisTickValues(chartData, 6), [
147
+ chartData,
148
+ ]);
149
+ const yTickValues = useMemo(() => getYTickValues(chartData, hiddenSeries), [
150
+ chartData,
151
+ hiddenSeries,
152
+ ]);
153
+ const timeseriesXDomain = useMemo(
154
+ () =>
155
+ config === 'timeseries' ? getTimeseriesXDomain(chartData) : undefined,
156
+ [chartData, config]
157
+ );
158
+
159
+ if (!hasChartData(data, xAxisDataLabel) || !chartData) {
160
+ return (
161
+ <EmptyState
162
+ ouiaEmptyStateTitleId="openscap-line-chart-empty-state-title"
163
+ title={noDataMsg}
164
+ />
165
+ );
166
+ }
167
+
168
+ const hasExplicitSize = size?.width > 0 && size?.height > 0;
169
+ if (!hasExplicitSize && !observedSize) {
170
+ return <div ref={containerRef} className="line-chart-container" />;
171
+ }
172
+
173
+ const maxTickLabelLen =
174
+ yTickValues?.length > 0
175
+ ? Math.max(...yTickValues.map(t => formatYAxisTick(t).length))
176
+ : formatYAxisTick(0).length;
177
+ const dynamicLeft = maxTickLabelLen * 8 + 50;
178
+ const chartHeight = sanitizeChartDimension(
179
+ size?.height ?? observedSize?.height,
180
+ DEFAULT_HEIGHT
181
+ );
182
+ const chartWidth = sanitizeChartDimension(
183
+ size?.width ?? observedSize?.width,
184
+ DEFAULT_WIDTH
185
+ );
186
+ const padding = clampChartPadding(
187
+ {
188
+ ...CHART_PADDING_BASE,
189
+ left: dynamicLeft,
190
+ },
191
+ chartWidth,
192
+ chartHeight
193
+ );
194
+ const chartName = id || 'line-chart';
195
+ const legendEvents = getLegendEvents(chartName, toggleSeries);
196
+ const events = [getDataClickEvents(onclick), legendEvents].filter(Boolean);
197
+
198
+ return (
199
+ <div ref={containerRef} className="line-chart-container">
200
+ <Chart
201
+ name={chartName}
202
+ ariaDesc={__('Line chart')}
203
+ themeColor={ChartThemeColor.multi}
204
+ animate={false}
205
+ domainPadding={{ x: [20, 20], y: [10, 10] }}
206
+ scale={config === 'timeseries' ? { x: 'time' } : undefined}
207
+ containerComponent={
208
+ <CursorVoronoiContainer
209
+ cursorDimension="x"
210
+ labels={({ datum }) => formatTooltipValue(datum.y)}
211
+ labelComponent={
212
+ <ChartLegendTooltip
213
+ legendData={legendData}
214
+ title={formatTooltipTitle}
215
+ />
216
+ }
217
+ mouseFollowTooltips
218
+ voronoiDimension="x"
219
+ voronoiPadding={padding}
220
+ constrainToVisibleArea
221
+ />
222
+ }
223
+ height={chartHeight}
224
+ width={chartWidth}
225
+ padding={padding}
226
+ legendData={legendData}
227
+ legendOrientation="vertical"
228
+ legendPosition="right"
229
+ legendComponent={
230
+ <ChartLegend
231
+ dataComponent={
232
+ <InteractiveLegendSymbol
233
+ setHoveredSeries={setHoveredSeries}
234
+ toggleSeries={toggleSeries}
235
+ />
236
+ }
237
+ labelComponent={
238
+ <InteractiveLegendLabel
239
+ hiddenSeries={hiddenSeries}
240
+ hoveredSeries={hoveredSeries}
241
+ setHoveredSeries={setHoveredSeries}
242
+ toggleSeries={toggleSeries}
243
+ />
244
+ }
245
+ />
246
+ }
247
+ events={events}
248
+ >
249
+ <ChartAxis
250
+ tickValues={config === 'timeseries' ? tickValues : undefined}
251
+ tickFormat={config === 'timeseries' ? formatAxisTick : undefined}
252
+ domain={timeseriesXDomain}
253
+ tickLabelComponent={
254
+ config === 'timeseries' ? (
255
+ <XAxisTickLabel yAxisLabelOffset={-12} />
256
+ ) : (
257
+ undefined
258
+ )
259
+ }
260
+ style={
261
+ config === 'timeseries'
262
+ ? { tickLabels: { angle: -45, verticalAnchor: 'end' } }
263
+ : undefined
264
+ }
265
+ />
266
+ <ChartAxis
267
+ dependentAxis
268
+ showGrid
269
+ tickValues={yTickValues}
270
+ tickFormat={formatYAxisTick}
271
+ />
272
+ <ChartGroup>
273
+ {visibleSeries.map(series => (
274
+ <ChartLine
275
+ key={series.name}
276
+ name={series.name}
277
+ data={series.data}
278
+ style={{
279
+ data: {
280
+ ...(series.color && { stroke: series.color }),
281
+ opacity: getSeriesOpacity(
282
+ hoveredSeries && hoveredSeries !== series.name
283
+ ),
284
+ },
285
+ }}
286
+ />
287
+ ))}
288
+ </ChartGroup>
289
+ </Chart>
290
+ </div>
291
+ );
292
+ };
293
+
294
+ LineChart.propTypes = {
295
+ data: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
296
+ config: PropTypes.oneOf(['regular', 'timeseries']),
297
+ noDataMsg: PropTypes.string,
298
+ xAxisDataLabel: PropTypes.string,
299
+ onclick: PropTypes.func,
300
+ id: PropTypes.string,
301
+ size: PropTypes.shape({
302
+ height: PropTypes.number,
303
+ width: PropTypes.number,
304
+ }),
305
+ // Accepted for compatibility with the legacy Foreman wrapper; unused in PF5.
306
+ title: PropTypes.object,
307
+ unloadData: PropTypes.bool,
308
+ axisOpts: PropTypes.object,
309
+ };
310
+
311
+ LineChart.defaultProps = {
312
+ data: undefined,
313
+ config: 'regular',
314
+ noDataMsg: __('No data available'),
315
+ xAxisDataLabel: '',
316
+ onclick: undefined,
317
+ id: undefined,
318
+ size: undefined,
319
+ title: { type: 'percent' },
320
+ unloadData: false,
321
+ axisOpts: {},
322
+ };
323
+
324
+ export default LineChart;
@@ -15,6 +15,9 @@ export const JOB_INVOCATION_API_REQUEST_KEY = 'OPENSCAP_REX_JOB_INVOCATIONS';
15
15
  export const SNIPPET_SH = 'urn:xccdf:fix:script:sh';
16
16
  export const SNIPPET_ANSIBLE = 'urn:xccdf:fix:script:ansible';
17
17
 
18
+ export const TOOLTIP_COPIED_EXIT_DELAY_MS = 1500;
19
+ export const TOOLTIP_DEFAULT_EXIT_DELAY_MS = 600;
20
+
18
21
  export const WIZARD_TITLES = {
19
22
  snippetSelect: __('Select snippet'),
20
23
  reviewHosts: __('Review hosts'),
@@ -6,7 +6,7 @@ import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
6
6
 
7
7
  import { translate as __ } from 'foremanReact/common/I18n';
8
8
  import { foremanUrl } from 'foremanReact/common/helpers';
9
- import { STATUS } from 'foremanReact/constants';
9
+ import { STATUS, HTTP_STATUS_CODES } from 'foremanReact/constants';
10
10
  import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
11
11
  import Loading from 'foremanReact/components/Loading';
12
12
  import PermissionDenied from 'foremanReact/components/PermissionDenied';
@@ -91,7 +91,7 @@ const Finish = ({ onClose }) => {
91
91
  </Button>
92
92
  );
93
93
  const errorComponent =
94
- statusCode === 403 ? (
94
+ statusCode === HTTP_STATUS_CODES.FORBIDDEN ? (
95
95
  <PermissionDenied
96
96
  missingPermissions={data?.error?.missing_permissions}
97
97
  primaryButton={closeBtn}
@@ -24,7 +24,11 @@ import {
24
24
  import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
25
25
  import WizardHeader from '../WizardHeader';
26
26
  import ViewSelectedHostsLink from '../ViewSelectedHostsLink';
27
- import { FAIL_RULE_SEARCH } from '../constants';
27
+ import {
28
+ FAIL_RULE_SEARCH,
29
+ TOOLTIP_COPIED_EXIT_DELAY_MS,
30
+ TOOLTIP_DEFAULT_EXIT_DELAY_MS,
31
+ } from '../constants';
28
32
  import { findFixBySnippet } from '../helpers';
29
33
 
30
34
  import './ReviewRemediation.scss';
@@ -78,7 +82,11 @@ const ReviewRemediation = () => {
78
82
  textId="code-content"
79
83
  aria-label="Copy to clipboard"
80
84
  onClick={e => onCopyClick(e, snippetText)}
81
- exitDelay={copied ? 1500 : 600}
85
+ exitDelay={
86
+ copied
87
+ ? TOOLTIP_COPIED_EXIT_DELAY_MS
88
+ : TOOLTIP_DEFAULT_EXIT_DELAY_MS
89
+ }
82
90
  maxWidth="110px"
83
91
  variant="plain"
84
92
  onTooltipHidden={() => setCopied(false)}
@@ -18,6 +18,8 @@ import WizardHeader from '../WizardHeader';
18
18
  import EmptyState from '../../EmptyState';
19
19
  import { errorMsg, supportedRemediationSnippets } from '../helpers';
20
20
 
21
+ const URN_TAIL_SEGMENTS = -2;
22
+
21
23
  const SnippetSelect = () => {
22
24
  const {
23
25
  fixes,
@@ -44,7 +46,7 @@ const SnippetSelect = () => {
44
46
  if (mapped) return mapped;
45
47
 
46
48
  return join(
47
- map(slice(split(system, ':'), -2), n => capitalize(n)),
49
+ map(slice(split(system, ':'), URN_TAIL_SEGMENTS), n => capitalize(n)),
48
50
  ' '
49
51
  );
50
52
  };
@@ -2,9 +2,11 @@ import React from 'react';
2
2
  import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
3
3
  import HostKebabItems from './components/HostExtentions/HostKebabItems';
4
4
 
5
+ const OPENSCAP_KEBAB_WEIGHT = 400;
6
+
5
7
  addGlobalFill(
6
8
  'host-details-kebab',
7
9
  `openscap-kebab-items`,
8
10
  <HostKebabItems key="openscap-host-kebab" />,
9
- 400
11
+ OPENSCAP_KEBAB_WEIGHT
10
12
  );
data/webpack/index.js CHANGED
@@ -2,10 +2,12 @@ import componentRegistry from 'foremanReact/components/componentRegistry';
2
2
 
3
3
  import RuleSeverity from './components/RuleSeverity';
4
4
  import OpenscapRemediationWizard from './components/OpenscapRemediationWizard';
5
+ import LineChart from './components/LineChart';
5
6
 
6
7
  const components = [
7
8
  { name: 'RuleSeverity', type: RuleSeverity },
8
9
  { name: 'OpenscapRemediationWizard', type: OpenscapRemediationWizard },
10
+ { name: 'OpenscapLineChart', type: LineChart },
9
11
  ];
10
12
 
11
13
  components.forEach(component => {
@@ -0,0 +1 @@
1
+ import 'foremanJSTestSetup';
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_openscap
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.1.2
4
+ version: 13.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - slukasik@redhat.com
@@ -40,7 +40,6 @@ files:
40
40
  - app/assets/javascripts/foreman_openscap/policy_dashboard.js
41
41
  - app/assets/javascripts/foreman_openscap/policy_edit.js
42
42
  - app/assets/javascripts/foreman_openscap/reports.js
43
- - app/assets/javascripts/foreman_openscap/scap_hosts_show.js
44
43
  - app/assets/stylesheets/foreman_openscap/policy.css
45
44
  - app/assets/stylesheets/foreman_openscap/policy_dashboard.css
46
45
  - app/assets/stylesheets/foreman_openscap/reports.css
@@ -363,15 +362,14 @@ files:
363
362
  - test/unit/services/lookup_key_overrider_test.rb
364
363
  - test/unit/services/report_dashboard/data_test.rb
365
364
  - test/unit/tailoring_file_test.rb
366
- - webpack/components/ConfirmModal.js
367
- - webpack/components/ConfirmModal.scss
368
365
  - webpack/components/EmptyState.js
369
366
  - webpack/components/HostExtentions/HostKebabItems.js
370
- - webpack/components/IndexLayout.js
371
367
  - webpack/components/IndexLayout.scss
372
- - webpack/components/IndexTable/IndexTableHelper.js
373
- - webpack/components/IndexTable/index.js
374
- - webpack/components/LinkButton.js
368
+ - webpack/components/LineChart/LineChart.fixtures.js
369
+ - webpack/components/LineChart/LineChart.scss
370
+ - webpack/components/LineChart/LineChart.test.js
371
+ - webpack/components/LineChart/LineChartHelpers.js
372
+ - webpack/components/LineChart/index.js
375
373
  - webpack/components/OpenscapRemediationWizard/Footer.js
376
374
  - webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js
377
375
  - webpack/components/OpenscapRemediationWizard/OpenscapRemediationWizardContext.js
@@ -395,18 +393,9 @@ files:
395
393
  - webpack/components/RuleSeverity/i_severity-med.svg
396
394
  - webpack/components/RuleSeverity/i_unknown.svg
397
395
  - webpack/components/RuleSeverity/index.js
398
- - webpack/components/withDeleteModal.js
399
- - webpack/components/withLoading.js
400
396
  - webpack/global_index.js
401
- - webpack/helpers/commonHelper.js
402
- - webpack/helpers/globalIdHelper.js
403
- - webpack/helpers/mutationHelper.js
404
- - webpack/helpers/pageParamsHelper.js
405
- - webpack/helpers/permissionsHelper.js
406
- - webpack/helpers/tableHelper.js
407
- - webpack/helpers/toastHelper.js
408
397
  - webpack/index.js
409
- - webpack/testHelper.js
398
+ - webpack/test_setup.js
410
399
  homepage: https://github.com/theforeman/foreman_openscap
411
400
  licenses:
412
401
  - GPL-3.0
@@ -425,7 +414,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
425
414
  - !ruby/object:Gem::Version
426
415
  version: '0'
427
416
  requirements: []
428
- rubygems_version: 4.0.10
417
+ rubygems_version: 4.0.16
429
418
  specification_version: 4
430
419
  summary: Foreman plug-in for displaying OpenSCAP audit reports
431
420
  test_files:
@@ -1,4 +0,0 @@
1
- $(function() {
2
- // Not sure about this ugly hack.
3
- $('.col-md-4 .stats-well').height($('.col-md-8 .stats-well').height());
4
- });
@@ -1,66 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { Modal, Button, ModalVariant, Spinner } from '@patternfly/react-core';
4
-
5
- import { translate as __ } from 'foremanReact/common/I18n';
6
-
7
- import './ConfirmModal.scss';
8
-
9
- const ConfirmModal = props => {
10
- const [callMutation, { loading }] = props.prepareMutation();
11
-
12
- const actions = [
13
- <Button
14
- ouiaId={`oscap-conf-modal-${props.record?.id}-confirm`}
15
- key="confirm"
16
- variant="primary"
17
- onClick={() => props.onConfirm(callMutation, props.record.id)}
18
- isDisabled={loading}
19
- >
20
- {__('Confirm')}
21
- </Button>,
22
- <Button
23
- ouiaId={`oscap-conf-modal-${props.record?.id}-cancel`}
24
- key="cancel"
25
- variant="link"
26
- onClick={event => props.onClose()}
27
- isDisabled={loading}
28
- >
29
- {__('Cancel')}
30
- </Button>,
31
- ];
32
-
33
- if (loading) {
34
- actions.push(<Spinner key="spinner" size="lg" />);
35
- }
36
-
37
- return (
38
- <Modal
39
- ouiaId={`oscap-conf-modal-${props.record?.id}`}
40
- variant={ModalVariant.medium}
41
- title={props.title}
42
- isOpen={props.isOpen}
43
- className="foreman-modal"
44
- showClose={false}
45
- actions={actions}
46
- >
47
- {props.text}
48
- </Modal>
49
- );
50
- };
51
-
52
- ConfirmModal.propTypes = {
53
- prepareMutation: PropTypes.func.isRequired,
54
- onConfirm: PropTypes.func.isRequired,
55
- record: PropTypes.object,
56
- onClose: PropTypes.func.isRequired,
57
- title: PropTypes.string.isRequired,
58
- isOpen: PropTypes.bool.isRequired,
59
- text: PropTypes.string.isRequired,
60
- };
61
-
62
- ConfirmModal.defaultProps = {
63
- record: null,
64
- };
65
-
66
- export default ConfirmModal;
@@ -1,3 +0,0 @@
1
- .pf-v5-c-backdrop {
2
- z-index: 1040;
3
- }
@@ -1,44 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { Helmet } from 'react-helmet';
4
- import ToastsList from 'foremanReact/components/ToastsList';
5
- import {
6
- Grid,
7
- GridItem,
8
- TextContent,
9
- Text,
10
- TextVariants,
11
- } from '@patternfly/react-core';
12
-
13
- import './IndexLayout.scss';
14
-
15
- const IndexLayout = ({ pageTitle, children, contentWidthSpan }) => (
16
- <React.Fragment>
17
- <Helmet>
18
- <title>{pageTitle}</title>
19
- </Helmet>
20
- <ToastsList />
21
- <Grid className="scap-page-grid">
22
- <GridItem span={12} className="pf-v5-u-pb-xl">
23
- <TextContent>
24
- <Text ouiaId="oscap-index-title" component={TextVariants.h1}>
25
- {pageTitle}
26
- </Text>
27
- </TextContent>
28
- </GridItem>
29
- <GridItem span={contentWidthSpan}>{children}</GridItem>
30
- </Grid>
31
- </React.Fragment>
32
- );
33
-
34
- IndexLayout.propTypes = {
35
- pageTitle: PropTypes.string.isRequired,
36
- children: PropTypes.oneOfType([PropTypes.node, PropTypes.object]).isRequired,
37
- contentWidthSpan: PropTypes.number,
38
- };
39
-
40
- IndexLayout.defaultProps = {
41
- contentWidthSpan: 12,
42
- };
43
-
44
- export default IndexLayout;
@@ -1,6 +0,0 @@
1
- import { addSearch } from '../../helpers/pageParamsHelper';
2
-
3
- export const refreshPage = (history, params = {}) => {
4
- const url = addSearch(history.location.pathname, params);
5
- history.push(url);
6
- };
@@ -1,73 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import {
4
- Table,
5
- TableHeader,
6
- TableBody,
7
- } from '@patternfly/react-table/deprecated';
8
- import { Flex, FlexItem } from '@patternfly/react-core';
9
- import Pagination from 'foremanReact/components/Pagination';
10
- import { refreshPage } from './IndexTableHelper';
11
-
12
- const IndexTable = ({
13
- history,
14
- pagination,
15
- totalCount,
16
- toolbarBtns,
17
- ariaTableLabel,
18
- ouiaTableId,
19
- columns,
20
- ...rest
21
- }) => {
22
- const handlePerPageSelected = perPage => {
23
- refreshPage(history, { page: 1, perPage });
24
- };
25
-
26
- const handlePageSelected = page => {
27
- refreshPage(history, { ...pagination, page });
28
- };
29
-
30
- return (
31
- <React.Fragment>
32
- <Flex className="pf-v5-u-pt-md">
33
- <FlexItem>{toolbarBtns}</FlexItem>
34
- <FlexItem align={{ default: 'alignRight' }}>
35
- <Pagination
36
- itemCount={totalCount}
37
- page={pagination.page}
38
- perPage={pagination.perPage}
39
- onSetPage={handlePageSelected}
40
- onPerPageSelect={handlePerPageSelected}
41
- variant="top"
42
- />
43
- </FlexItem>
44
- </Flex>
45
- <Table
46
- ouiaId={ouiaTableId}
47
- aria-label={ariaTableLabel}
48
- cells={columns}
49
- {...rest}
50
- variant="compact"
51
- >
52
- <TableHeader />
53
- <TableBody />
54
- </Table>
55
- </React.Fragment>
56
- );
57
- };
58
-
59
- IndexTable.propTypes = {
60
- history: PropTypes.object.isRequired,
61
- pagination: PropTypes.object.isRequired,
62
- toolbarBtns: PropTypes.node,
63
- totalCount: PropTypes.number.isRequired,
64
- ariaTableLabel: PropTypes.string.isRequired,
65
- ouiaTableId: PropTypes.string.isRequired,
66
- columns: PropTypes.array.isRequired,
67
- };
68
-
69
- IndexTable.defaultProps = {
70
- toolbarBtns: null,
71
- };
72
-
73
- export default IndexTable;