foreman_ansible 17.0.9 → 18.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd2541b943e7880fe2ba5e0d324201353404cd66c4c5a290ea9674998587114b
4
- data.tar.gz: 50c6e9be97dce5a43db62fb490c2acaa4919c4ed0696beb6d24fa0b99bfdff5e
3
+ metadata.gz: '087d3ebdcf70f8891bf6de236413f46c971950589ecc7a9b32aa781b747f0d64'
4
+ data.tar.gz: 11d10eeff06402af8baaeda250d021c44db0fc21bd86450a1123f47d19083353
5
5
  SHA512:
6
- metadata.gz: af34bb1ff7a20d37a84ceb972efcd4f7ff4468e979a1f5fbe992b43c88c426e18e2696aa54b828212ca6ceecdbadf8ff7f1b3b0b4e16c09ce8dc23766e36bdb7
7
- data.tar.gz: 7d354aad703f47f77404adb10effe366c42dc141d4dffb8dd4018a429f18410dc0e842dd655bf6cdf430fee8c366b66963c68ac6702764695b45370535bda7f5
6
+ metadata.gz: 550e5075d68631d646f73b56b370d7816dc2f59a784cc1d828c04b453fde61871d14293aba62fbdca70aa063eec4d74cbe77d5b8d2b1eedd9bafd12c0f24d610
7
+ data.tar.gz: 4b459dcf5991a72248fe82f8bc348a28f10593834fb5c3b352fbebc55a22020efee2af4fe593cd2a67d161f7b197ef4fd0e96682b0e0f5a5de93184a9f0149da
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Foreman::Plugin.register :foreman_ansible do
4
- requires_foreman '>= 3.16.0'
4
+ requires_foreman '>= 5.0'
5
5
  register_gettext
6
6
 
7
7
  settings do
@@ -4,5 +4,5 @@
4
4
  # This way other parts of Foreman can just call ForemanAnsible::VERSION
5
5
  # and detect what version the plugin is running.
6
6
  module ForemanAnsible
7
- VERSION = '17.0.9'
7
+ VERSION = '18.0.0'
8
8
  end
@@ -9,6 +9,8 @@ import {
9
9
  ANSIBLE_ROLES_INDEX,
10
10
  } from './AnsibleRolesAndVariablesConstants';
11
11
 
12
+ const REDIRECT_DELAY_MS = 500;
13
+
12
14
  export const foremanUrl = path => `${window.URL_PREFIX}${path}`;
13
15
 
14
16
  export const onSubmit = (rows, proxy) => dispatch => {
@@ -19,7 +21,10 @@ export const onSubmit = (rows, proxy) => dispatch => {
19
21
  url: ANSIBLE_ROLE_CONFIRM_IMPORT_PATH,
20
22
  params: { changed: params, proxy },
21
23
  handleSuccess: () => {
22
- setTimeout(() => dispatch(push(ANSIBLE_ROLES_INDEX)), 500);
24
+ setTimeout(
25
+ () => dispatch(push(ANSIBLE_ROLES_INDEX)),
26
+ REDIRECT_DELAY_MS
27
+ );
23
28
  },
24
29
  successToast: response => (
25
30
  <span>
@@ -6,19 +6,12 @@
6
6
  }
7
7
 
8
8
  .ansible-inherited-roles {
9
- --pf-v5-c-dual-list-selector__title-text--FontWeight: var(
10
- --pf-v5-global--FontWeight--bold
11
- );
12
- --pf-v5-c-dual-list-selector__header--MarginBottom: var(
13
- --pf-v5-global--spacer--sm
14
- );
15
-
16
- .pf-v5-c-dual-list-selector__header {
17
- margin-block-end: var(--pf-v5-c-dual-list-selector__header--MarginBottom);
18
- }
19
-
20
- .pf-v5-c-dual-list-selector__title-text {
21
- font-weight: var(--pf-v5-c-dual-list-selector__title-text--FontWeight);
9
+ .ansible-inherited-roles-title {
10
+ margin: 0 0 var(--pf-v5-global--spacer--sm);
11
+ font-family: var(--pf-v5-global--FontFamily--text);
12
+ font-size: var(--pf-v5-global--FontSize--sm);
13
+ font-weight: var(--pf-v5-global--FontWeight--bold);
14
+ line-height: var(--pf-v5-global--LineHeight--md);
22
15
  }
23
16
 
24
17
  ul {
@@ -34,6 +34,7 @@ const ansibleRoles = (state = initialState, action) => {
34
34
  return state.merge({ error: payload.error, loading: false });
35
35
  case ANSIBLE_ROLES_DUAL_LIST_CHANGE: {
36
36
  const { chosenNames } = payload;
37
+ const chosenNamesUnique = [...new Set(chosenNames)];
37
38
  const inherited = state.assignedRoles.filter(role =>
38
39
  state.inheritedRoleIds.includes(role.id)
39
40
  );
@@ -42,20 +43,24 @@ const ansibleRoles = (state = initialState, action) => {
42
43
  );
43
44
  const roleByName = name =>
44
45
  currentOwn.find(role => role.name === name) ||
45
- state.results.find(role => role.name === name) ||
46
- state.toDestroyRoles.find(role => role.name === name);
46
+ state.toDestroyRoles.find(role => role.name === name) ||
47
+ state.results.find(role => role.name === name);
47
48
 
48
49
  const removed = currentOwn.filter(
49
- role => !chosenNames.includes(role.name)
50
+ role => !chosenNamesUnique.includes(role.name)
50
51
  );
51
- const newOwn = chosenNames
52
+ const newOwn = chosenNamesUnique
52
53
  .map(name => roleByName(name))
53
- .filter(role => role && !state.inheritedRoleIds.includes(role.id));
54
+ .filter(role => role && !state.inheritedRoleIds.includes(role.id))
55
+ .map(role => ({ ...role, destroy: false }));
56
+
57
+ const isSameRole = (left, right) =>
58
+ left.name === right.name || String(left.id) === String(right.id);
54
59
 
55
60
  return state.merge({
56
61
  assignedRoles: inherited.concat(newOwn),
57
62
  toDestroyRoles: state.toDestroyRoles
58
- .filter(item => !newOwn.some(role => role.id === item.id))
63
+ .filter(item => !newOwn.some(role => isSameRole(role, item)))
59
64
  .concat(removed.map(role => ({ ...role, destroy: true }))),
60
65
  });
61
66
  }
@@ -72,4 +72,45 @@ describe('AnsibleRolesSwitcherReducer', () => {
72
72
  expect(state.inheritedRoleIds).toEqual([4]);
73
73
  expect(state.results).toEqual(ansibleRolesLong);
74
74
  });
75
+
76
+ it('restores removed roles without duplicating destroy entries', () => {
77
+ const ownRole = {
78
+ ...ansibleRolesLong[4],
79
+ host_ansible_role_id: 42,
80
+ };
81
+ const stateAfterRemoveAll = reducer(
82
+ successState.set('assignedRoles', [
83
+ ansibleRolesLong[3],
84
+ ownRole,
85
+ ansibleRolesLong[5],
86
+ ]),
87
+ {
88
+ type: ANSIBLE_ROLES_DUAL_LIST_CHANGE,
89
+ payload: { chosenNames: [] },
90
+ }
91
+ );
92
+
93
+ expect(stateAfterRemoveAll.toDestroyRoles).toEqual([
94
+ { ...ownRole, destroy: true },
95
+ { ...ansibleRolesLong[5], destroy: true },
96
+ ]);
97
+
98
+ const stateAfterReAdd = reducer(stateAfterRemoveAll, {
99
+ type: ANSIBLE_ROLES_DUAL_LIST_CHANGE,
100
+ payload: {
101
+ chosenNames: [ownRole.name, ansibleRolesLong[5].name],
102
+ },
103
+ });
104
+
105
+ expect(stateAfterReAdd.assignedRoles.map(role => role.name)).toEqual([
106
+ ansibleRolesLong[3].name,
107
+ ownRole.name,
108
+ ansibleRolesLong[5].name,
109
+ ]);
110
+ expect(stateAfterReAdd.assignedRoles[1]).toEqual({
111
+ ...ownRole,
112
+ destroy: false,
113
+ });
114
+ expect(stateAfterReAdd.toDestroyRoles).toEqual([]);
115
+ });
75
116
  });
@@ -1,10 +1,8 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Tooltip } from '@patternfly/react-core';
3
+ import { Title, Tooltip } from '@patternfly/react-core';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
5
 
6
- import ListHeader from '../../DualList/ListHeader';
7
-
8
6
  const InheritedRolesList = ({ roles, resourceName }) => {
9
7
  if (roles.length === 0) {
10
8
  return null;
@@ -17,7 +15,14 @@ const InheritedRolesList = ({ roles, resourceName }) => {
17
15
 
18
16
  return (
19
17
  <div className="ansible-inherited-roles">
20
- <ListHeader title={__('Inherited Ansible Roles')} />
18
+ <Title
19
+ headingLevel="h2"
20
+ size="md"
21
+ className="ansible-inherited-roles-title"
22
+ ouiaId="inherited-ansible-roles-title"
23
+ >
24
+ {__('Inherited Ansible Roles')}
25
+ </Title>
21
26
  <ul>
22
27
  {roles.map(role => (
23
28
  <li key={role.id}>
@@ -0,0 +1,101 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import '@testing-library/jest-dom';
5
+
6
+ import DualList from './index';
7
+
8
+ const defaultProps = {
9
+ availableOptions: ['role.a', 'role.b'],
10
+ chosenOptions: ['role.c'],
11
+ onListChange: jest.fn(),
12
+ };
13
+
14
+ describe('DualList', () => {
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+
19
+ it('renders available and chosen options', () => {
20
+ render(<DualList {...defaultProps} />);
21
+
22
+ expect(screen.getByText('Available Ansible roles')).toBeInTheDocument();
23
+ expect(screen.getByText('Assigned Ansible roles')).toBeInTheDocument();
24
+ expect(screen.getByText('role.a')).toBeInTheDocument();
25
+ expect(screen.getByText('role.b')).toBeInTheDocument();
26
+ expect(screen.getByText('role.c')).toBeInTheDocument();
27
+ });
28
+
29
+ it('moves selected available options to chosen', () => {
30
+ render(<DualList {...defaultProps} />);
31
+
32
+ fireEvent.click(screen.getByRole('option', { name: 'role.a' }));
33
+ userEvent.click(screen.getByRole('button', { name: 'Add selected' }));
34
+
35
+ expect(defaultProps.onListChange).toHaveBeenCalledWith(
36
+ ['role.b'],
37
+ ['role.c', 'role.a']
38
+ );
39
+ });
40
+
41
+ it('moves selected chosen options to available', () => {
42
+ render(<DualList {...defaultProps} />);
43
+
44
+ fireEvent.click(screen.getByRole('option', { name: 'role.c' }));
45
+ userEvent.click(screen.getByRole('button', { name: 'Remove selected' }));
46
+
47
+ expect(defaultProps.onListChange).toHaveBeenCalledWith(
48
+ ['role.a', 'role.b', 'role.c'],
49
+ []
50
+ );
51
+ });
52
+
53
+ it('moves all available options to chosen', () => {
54
+ render(<DualList {...defaultProps} />);
55
+
56
+ userEvent.click(screen.getByRole('button', { name: 'Add all' }));
57
+
58
+ expect(defaultProps.onListChange).toHaveBeenCalledWith(
59
+ [],
60
+ ['role.c', 'role.a', 'role.b']
61
+ );
62
+ });
63
+
64
+ it('moves all chosen options to available', () => {
65
+ render(<DualList {...defaultProps} />);
66
+
67
+ userEvent.click(screen.getByRole('button', { name: 'Remove all' }));
68
+
69
+ expect(defaultProps.onListChange).toHaveBeenCalledWith(
70
+ ['role.a', 'role.b', 'role.c'],
71
+ []
72
+ );
73
+ });
74
+
75
+ it('disables move controls when nothing is selected or lists are empty', () => {
76
+ render(
77
+ <DualList
78
+ availableOptions={[]}
79
+ chosenOptions={[]}
80
+ onListChange={jest.fn()}
81
+ />
82
+ );
83
+
84
+ expect(screen.getByRole('button', { name: 'Add selected' })).toBeDisabled();
85
+ expect(screen.getByRole('button', { name: 'Add all' })).toBeDisabled();
86
+ expect(screen.getByRole('button', { name: 'Remove all' })).toBeDisabled();
87
+ expect(
88
+ screen.getByRole('button', { name: 'Remove selected' })
89
+ ).toBeDisabled();
90
+ });
91
+
92
+ it('shows selection status for each pane', () => {
93
+ render(<DualList {...defaultProps} />);
94
+
95
+ fireEvent.click(screen.getByRole('option', { name: 'role.a' }));
96
+ fireEvent.click(screen.getByRole('option', { name: 'role.c' }));
97
+
98
+ expect(screen.getByText('1 of 2 items selected')).toBeInTheDocument();
99
+ expect(screen.getByText('1 of 1 items selected')).toBeInTheDocument();
100
+ });
101
+ });
@@ -1,103 +1,47 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import OrderableDualListSelector from 'foremanReact/components/common/OrderableDualListSelector/OrderableDualListSelector';
3
4
  import { translate as __ } from 'foremanReact/common/I18n';
4
5
 
5
- import ListControls from './ListControls';
6
- import ListPane from './ListPane';
7
- import './DualList.scss';
8
-
9
- const DualList = props => {
10
- const defaultSelectState = { availableSelected: [], chosenSelected: [] };
11
- const [selectState, setSelectState] = useState(defaultSelectState);
12
-
13
- const onItemClick = stateName => itemName => (...args) => {
14
- if (isItemSelected(stateName)(itemName)) {
15
- setSelectState({
16
- ...selectState,
17
- [stateName]: selectState[stateName].filter(item => item !== itemName),
18
- });
19
- } else {
20
- setSelectState({
21
- ...selectState,
22
- [stateName]: [...selectState[stateName], itemName],
23
- });
24
- }
25
- };
26
-
27
- const isItemSelected = stateName => item =>
28
- selectState[stateName].includes(item);
29
-
30
- const onAddAll = () =>
31
- props.onListChange([], [...props.chosenOptions, ...props.availableOptions]);
32
- const onRemoveAll = () =>
33
- props.onListChange([...props.availableOptions, ...props.chosenOptions], []);
34
-
35
- const onAddSelected = () => {
36
- const [newAvailable, newChosen] = moveItems(
37
- props.availableOptions,
38
- props.chosenOptions,
39
- selectState.availableSelected
40
- );
41
- setSelectState(defaultSelectState);
42
- props.onListChange(newAvailable, newChosen);
43
- };
44
-
45
- const onRemoveSelected = () => {
46
- const [newChosen, newAvailable] = moveItems(
47
- props.chosenOptions,
48
- props.availableOptions,
49
- selectState.chosenSelected
50
- );
51
- setSelectState(defaultSelectState);
52
- props.onListChange(newAvailable, newChosen);
53
- };
54
-
55
- const moveItems = (removeFrom, addTo, selected) => {
56
- const newRemoveFrom = removeFrom.filter(item => !selected.includes(item));
57
- return [newRemoveFrom, [...addTo, ...selected]];
58
- };
59
-
60
- const onChosenSetOrder = items =>
61
- props.onListChange(props.availableOptions, items);
62
-
63
- return (
64
- <div className="pf-v5-c-dual-list-selector">
65
- <ListPane
66
- title={__('Available Ansible roles')}
67
- items={props.availableOptions}
68
- paneClass="pf-m-available"
69
- onItemClick={onItemClick('availableSelected')}
70
- selected={selectState.availableSelected}
71
- isItemSelected={isItemSelected('availableSelected')}
72
- />
73
- <ListControls
74
- onAddSelected={onAddSelected}
75
- onRemoveSelected={onRemoveSelected}
76
- onRemoveAll={onRemoveAll}
77
- onAddAll={onAddAll}
78
- addAllDisabled={props.availableOptions.length === 0}
79
- removeAllDisabled={props.chosenOptions.length === 0}
80
- addSelectedDisabled={selectState.availableSelected.length === 0}
81
- removeSelectedDisabled={selectState.chosenSelected.length === 0}
82
- />
83
- <ListPane
84
- title={__('Assigned Ansible roles')}
85
- items={props.chosenOptions}
86
- paneClass="pf-m-chosen"
87
- draggable
88
- onItemClick={onItemClick('chosenSelected')}
89
- selected={selectState.chosenSelected}
90
- isItemSelected={isItemSelected('chosenSelected')}
91
- onSetOrderedItems={onChosenSetOrder}
92
- />
93
- </div>
94
- );
95
- };
6
+ const DualList = ({
7
+ availableOptions,
8
+ chosenOptions,
9
+ onListChange,
10
+ id,
11
+ availableOptionsTitle,
12
+ chosenOptionsTitle,
13
+ isDisabled,
14
+ showTooltips,
15
+ }) => (
16
+ <OrderableDualListSelector
17
+ id={id}
18
+ availableOptions={availableOptions}
19
+ chosenOptions={chosenOptions}
20
+ onListChange={onListChange}
21
+ availableOptionsTitle={availableOptionsTitle}
22
+ chosenOptionsTitle={chosenOptionsTitle}
23
+ isDisabled={isDisabled}
24
+ showTooltips={showTooltips}
25
+ />
26
+ );
96
27
 
97
28
  DualList.propTypes = {
98
29
  onListChange: PropTypes.func.isRequired,
99
30
  chosenOptions: PropTypes.array.isRequired,
100
31
  availableOptions: PropTypes.array.isRequired,
32
+ id: PropTypes.string,
33
+ availableOptionsTitle: PropTypes.string,
34
+ chosenOptionsTitle: PropTypes.string,
35
+ isDisabled: PropTypes.bool,
36
+ showTooltips: PropTypes.bool,
37
+ };
38
+
39
+ DualList.defaultProps = {
40
+ id: 'ansible-roles-dual-list',
41
+ availableOptionsTitle: __('Available Ansible roles'),
42
+ chosenOptionsTitle: __('Assigned Ansible roles'),
43
+ isDisabled: false,
44
+ showTooltips: true,
101
45
  };
102
46
 
103
47
  export default DualList;
@@ -8,11 +8,13 @@ import AnsibleHostDetail from './components/AnsibleHostDetail';
8
8
 
9
9
  import { ANSIBLE_KEY } from './components/AnsibleHostDetail/constants';
10
10
 
11
+ const ANSIBLE_TAB_WEIGHT = 500;
12
+
11
13
  addGlobalFill(
12
14
  'host-details-page-tabs',
13
15
  ANSIBLE_KEY,
14
16
  <AnsibleHostDetail key="ansible-host-detail" />,
15
- 500
17
+ ANSIBLE_TAB_WEIGHT
16
18
  );
17
19
 
18
20
  registerRoutes('foreman_ansible', routes);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_ansible
3
3
  version: !ruby/object:Gem::Version
4
- version: 17.0.9
4
+ version: 18.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Lobato Garcia
@@ -416,12 +416,7 @@ files:
416
416
  - webpack/components/AnsibleRolesSwitcher/components/InheritedRolesList.test.js
417
417
  - webpack/components/AnsibleRolesSwitcher/components/withProtectedView.js
418
418
  - webpack/components/AnsibleRolesSwitcher/index.js
419
- - webpack/components/DualList/DualList.scss
420
- - webpack/components/DualList/ListControls.js
421
- - webpack/components/DualList/ListHeader.js
422
- - webpack/components/DualList/ListItem.js
423
- - webpack/components/DualList/ListPane.js
424
- - webpack/components/DualList/SelectedStatus.js
419
+ - webpack/components/DualList/DualList.test.js
425
420
  - webpack/components/DualList/index.js
426
421
  - webpack/components/ErrorState.js
427
422
  - webpack/components/ReportJsonViewer.js
@@ -470,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
470
465
  - !ruby/object:Gem::Version
471
466
  version: '0'
472
467
  requirements: []
473
- rubygems_version: 4.0.10
468
+ rubygems_version: 4.0.16
474
469
  specification_version: 4
475
470
  summary: Ansible integration with Foreman (theforeman.org)
476
471
  test_files:
@@ -1,3 +0,0 @@
1
- .foreman-dual-list-order {
2
- padding-right: var(--pf-global--spacer--sm);
3
- }
@@ -1,65 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { translate as __ } from 'foremanReact/common/I18n';
4
-
5
- const ListControls = props => (
6
- <div className="pf-v5-c-dual-list-selector__controls">
7
- <div className="pf-v5-c-dual-list-selector__controls-item">
8
- <button
9
- className="pf-v5-c-button pf-m-plain"
10
- type="button"
11
- disabled={props.addSelectedDisabled}
12
- onClick={props.onAddSelected}
13
- aria-label={__('Add selected')}
14
- >
15
- <i className="fas fa-fw fa-angle-right" />
16
- </button>
17
- </div>
18
- <div className="pf-v5-c-dual-list-selector__controls-item">
19
- <button
20
- className="pf-v5-c-button pf-m-plain"
21
- type="button"
22
- disabled={props.addAllDisabled}
23
- onClick={props.onAddAll}
24
- aria-label={__('Add all')}
25
- >
26
- <i className="fas fa-fw fa-angle-double-right" />
27
- </button>
28
- </div>
29
- <div className="pf-v5-c-dual-list-selector__controls-item">
30
- <button
31
- className="pf-v5-c-button pf-m-plain"
32
- type="button"
33
- disabled={props.removeAllDisabled}
34
- onClick={props.onRemoveAll}
35
- aria-label={__('Remove all')}
36
- >
37
- <i className="fas fa-fw fa-angle-double-left" />
38
- </button>
39
- </div>
40
- <div className="pf-v5-c-dual-list-selector__controls-item">
41
- <button
42
- className="pf-v5-c-button pf-m-plain"
43
- type="button"
44
- disabled={props.removeSelectedDisabled}
45
- onClick={props.onRemoveSelected}
46
- aria-label={__('Remove selected')}
47
- >
48
- <i className="fas fa-fw fa-angle-left" />
49
- </button>
50
- </div>
51
- </div>
52
- );
53
-
54
- ListControls.propTypes = {
55
- addSelectedDisabled: PropTypes.bool.isRequired,
56
- onAddSelected: PropTypes.func.isRequired,
57
- addAllDisabled: PropTypes.bool.isRequired,
58
- onAddAll: PropTypes.func.isRequired,
59
- removeAllDisabled: PropTypes.bool.isRequired,
60
- onRemoveAll: PropTypes.func.isRequired,
61
- removeSelectedDisabled: PropTypes.bool.isRequired,
62
- onRemoveSelected: PropTypes.func.isRequired,
63
- };
64
-
65
- export default ListControls;
@@ -1,18 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
-
4
- const ListHeader = props => (
5
- <div className="pf-v5-c-dual-list-selector__header">
6
- <div className="pf-v5-c-dual-list-selector__title">
7
- <div className="pf-v5-c-dual-list-selector__title-text">
8
- {props.title}
9
- </div>
10
- </div>
11
- </div>
12
- );
13
-
14
- ListHeader.propTypes = {
15
- title: PropTypes.string.isRequired,
16
- };
17
-
18
- export default ListHeader;
@@ -1,69 +0,0 @@
1
- import React from 'react';
2
- import classNames from 'classnames';
3
- import PropTypes from 'prop-types';
4
-
5
- const ListItem = props => {
6
- const draggableBtn = (
7
- <div className="pf-v5-c-dual-list-selector__draggable">
8
- <button
9
- className="pf-v5-c-button pf-m-plain"
10
- type="button"
11
- aria-pressed="false"
12
- aria-label="Reorder"
13
- id="draggable-list-item-2-draggable-button"
14
- aria-labelledby="draggable-list-item-2-draggable-button draggable-list-item-2-item-text"
15
- aria-describedby="draggable-help"
16
- >
17
- <i className="fas fa-grip-vertical" aria-hidden="true" />
18
- </button>
19
- </div>
20
- );
21
-
22
- const orderBtn = (
23
- <span className="foreman-dual-list-order">{`${props.index + 1}. `}</span>
24
- );
25
-
26
- return (
27
- <li className="pf-v5-c-dual-list-selector__list-item">
28
- <div
29
- className={classNames('pf-v5-c-dual-list-selector__list-item-row ', {
30
- 'pf-m-selected': props.selected,
31
- 'pf-m-ghost-row': props.dragging,
32
- })}
33
- >
34
- {props.draggable && draggableBtn}
35
- <button
36
- className="pf-v5-c-dual-list-selector__item"
37
- type="button"
38
- onClick={props.onClick}
39
- >
40
- <span className="pf-v5-c-dual-list-selector__item-main">
41
- <span className="pf-v5-c-dual-list-selector__item-text">
42
- {props.draggable && orderBtn}
43
- <span>{props.name}</span>
44
- </span>
45
- </span>
46
- <span className="pf-v5-c-dual-list-selector__item-count">
47
- <span className="pf-v5-c-badge pf-m-read" />
48
- </span>
49
- </button>
50
- </div>
51
- </li>
52
- );
53
- };
54
-
55
- ListItem.propTypes = {
56
- selected: PropTypes.bool.isRequired,
57
- dragging: PropTypes.bool,
58
- draggable: PropTypes.bool,
59
- onClick: PropTypes.func.isRequired,
60
- name: PropTypes.string.isRequired,
61
- index: PropTypes.number.isRequired,
62
- };
63
-
64
- ListItem.defaultProps = {
65
- draggable: false,
66
- dragging: false,
67
- };
68
-
69
- export default ListItem;
@@ -1,95 +0,0 @@
1
- import React from 'react';
2
- import { DndProvider } from 'react-dnd';
3
- import { HTML5Backend } from 'react-dnd-html5-backend';
4
- import PropTypes from 'prop-types';
5
-
6
- import {
7
- orderable,
8
- orderDragged,
9
- } from 'foremanReact/components/common/forms/OrderableSelect/helpers';
10
-
11
- import ListItem from './ListItem';
12
- import ListHeader from './ListHeader';
13
- import SelectedStatus from './SelectedStatus';
14
-
15
- const OrderableListItem = orderable(ListItem, {
16
- type: 'orderableItem',
17
- getItem: props => ({ id: props.name }),
18
- direction: 'vertical',
19
- });
20
-
21
- const ListPane = ({
22
- items,
23
- isItemSelected,
24
- draggable,
25
- onItemClick,
26
- paneClass,
27
- title,
28
- selected,
29
- onSetOrderedItems,
30
- }) => {
31
- const moveValue = (dragIndex, hoverIndex) => {
32
- onSetOrderedItems(orderDragged(items, dragIndex, hoverIndex));
33
- };
34
-
35
- const renderOrderableItem = (item, idx) => (
36
- <OrderableListItem
37
- key={item}
38
- name={item}
39
- index={idx}
40
- moveValue={moveValue}
41
- onClick={onItemClick(item)}
42
- selected={isItemSelected(item)}
43
- draggable={draggable}
44
- />
45
- );
46
-
47
- const renderListItem = (item, idx) => (
48
- <ListItem
49
- key={item}
50
- name={item}
51
- index={idx}
52
- onClick={onItemClick(item)}
53
- selected={isItemSelected(item)}
54
- />
55
- );
56
-
57
- const renderItem = draggable ? renderOrderableItem : renderListItem;
58
-
59
- return (
60
- <div
61
- className={`pf-v5-c-dual-list-selector__pane pf-m-available ${paneClass}`}
62
- >
63
- <ListHeader title={title} />
64
- <SelectedStatus
65
- selectedCount={selected.length}
66
- totalCount={items.length}
67
- />
68
- <div className="pf-v5-c-dual-list-selector__menu">
69
- <DndProvider backend={HTML5Backend}>
70
- <ul className="pf-v5-c-dual-list-selector__list">
71
- {items.map(renderItem)}
72
- </ul>
73
- </DndProvider>
74
- </div>
75
- </div>
76
- );
77
- };
78
-
79
- ListPane.propTypes = {
80
- items: PropTypes.array.isRequired,
81
- isItemSelected: PropTypes.func.isRequired,
82
- draggable: PropTypes.bool,
83
- onItemClick: PropTypes.func.isRequired,
84
- paneClass: PropTypes.string.isRequired,
85
- title: PropTypes.string.isRequired,
86
- selected: PropTypes.array.isRequired,
87
- onSetOrderedItems: PropTypes.func,
88
- };
89
-
90
- ListPane.defaultProps = {
91
- draggable: false,
92
- onSetOrderedItems: () => {},
93
- };
94
-
95
- export default ListPane;
@@ -1,21 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { translate as __, sprintf } from 'foremanReact/common/I18n';
4
-
5
- const SelectedStatus = ({ selectedCount, totalCount }) => (
6
- <div className="pf-v5-c-dual-list-selector__status">
7
- <span className="pf-v5-c-dual-list-selector__status-text">
8
- {sprintf(__('%(selectedCount)s of %(totalCount)s items selected'), {
9
- selectedCount,
10
- totalCount,
11
- })}
12
- </span>
13
- </div>
14
- );
15
-
16
- SelectedStatus.propTypes = {
17
- selectedCount: PropTypes.number.isRequired,
18
- totalCount: PropTypes.number.isRequired,
19
- };
20
-
21
- export default SelectedStatus;