diff --git a/src/components/InventoryGroups/Modals/RemoveHostsFromGroupModal.js b/src/components/InventoryGroups/Modals/RemoveHostsFromGroupModal.js
index 5f59a6aaa..17ccf827f 100644
--- a/src/components/InventoryGroups/Modals/RemoveHostsFromGroupModal.js
+++ b/src/components/InventoryGroups/Modals/RemoveHostsFromGroupModal.js
@@ -7,27 +7,56 @@ import { removeHostsFromGroup } from '../utils/api';
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import { Text } from '@patternfly/react-core';
-const schema = (groupName, hosts) => ({
- fields: [
- {
- component: componentTypes.PLAIN_TEXT,
- name: 'warning-message',
- label:
- hosts.length === 1 ? (
-
- {hosts[0].display_name} will no longer be part of{' '}
- {groupName} and its configuration will be impacted.
-
- ) : (
-
- {hosts.length} systems will no longer be part of{' '}
- {groupName} and their configuration will be
- impacted.
-
- ),
- },
- ],
-});
+const schema = (hosts) => {
+ const hostsInGroup = hosts.filter(({ groups }) => groups.length > 0); // selection can contain ungroupped hosts
+ const groupName = hostsInGroup[0].groups[0].name;
+
+ return {
+ fields: [
+ {
+ component: componentTypes.PLAIN_TEXT,
+ name: 'warning-message',
+ label:
+ hostsInGroup.length === 1 ? (
+
+ {hostsInGroup[0].display_name} will no longer be
+ part of {groupName} and its configuration will be
+ impacted.
+
+ ) : (
+
+ {hostsInGroup.length} systems will no longer be
+ part of {groupName} and their configuration will
+ be impacted.
+
+ ),
+ },
+ ],
+ };
+};
+
+const statusMessages = (hosts) => {
+ const hostsInGroup = hosts.filter(({ groups }) => groups.length > 0);
+ const groupName = hostsInGroup[0].groups[0].name;
+
+ return hostsInGroup.length === 1
+ ? {
+ onSuccess: {
+ title: `1 system removed from ${groupName}`,
+ },
+ onError: {
+ title: `Failed to remove 1 system from ${groupName}`,
+ },
+ }
+ : {
+ onSuccess: {
+ title: `${hostsInGroup.length} systems removed from ${groupName}`,
+ },
+ onError: {
+ title: `Failed to remove ${hostsInGroup.length} systems from ${groupName}`,
+ },
+ };
+};
const RemoveHostsFromGroupModal = ({
isModalOpen,
@@ -37,23 +66,9 @@ const RemoveHostsFromGroupModal = ({
reloadTimeout,
}) => {
const dispatch = useDispatch();
- // the current iteration of groups feature a host can be in at maximum one group
- const { name: groupName, id: groupId } = hosts[0].groups[0];
-
- const handleRemoveHosts = () => {
- const statusMessages = {
- onSuccess: {
- title: `${hosts.length} ${
- hosts.length > 1 ? 'systems' : 'system'
- } removed from ${groupName}`,
- },
- onError: {
- title: `Failed to remove ${hosts.length} ${
- hosts.length > 1 ? 'systems' : 'system'
- } from ${groupName}`,
- },
- };
+ const groupId = hosts.find(({ groups }) => groups.length > 0).groups[0].id;
+ const handleRemoveHosts = () =>
apiWithToast(
dispatch,
async () =>
@@ -61,9 +76,8 @@ const RemoveHostsFromGroupModal = ({
groupId,
hosts.map(({ id }) => id)
),
- statusMessages
+ statusMessages(hosts)
);
- };
return (
{
.to.be.true;
});
- it('the fourth host is not in a group', () => {
+ it('the fourth and fifth hosts are not in a group', () => {
expect(hostsFixtures.results[3].groups.length === 0).to.be.true;
+ expect(hostsFixtures.results[4].groups.length === 0).to.be.true;
});
});
@@ -387,8 +388,57 @@ describe('inventory table', () => {
.contains('Remove from group')
.shouldHaveAriaDisabled();
});
- });
- // TODO: test group bulk actions once granular RBAC is implemented there too
+ it('can bulk remove from the permitted group', () => {
+ cy.get(ROW).find('[type="checkbox"]').eq(0).click();
+ // TODO: implement ouia selector for this component
+ cy.get(
+ '.ins-c-primary-toolbar__actions [aria-label="Actions"]'
+ ).click();
+ cy.get(DROPDOWN_ITEM)
+ .contains('Remove from group')
+ .shouldHaveAriaEnabled();
+ cy.get(ROW).find('[type="checkbox"]').eq(1).click();
+ // TODO: implement ouia selector for this component
+ cy.get(
+ '.ins-c-primary-toolbar__actions [aria-label="Actions"]'
+ ).click();
+ cy.get(DROPDOWN_ITEM)
+ .contains('Remove from group')
+ .shouldHaveAriaEnabled();
+ });
+
+ it('can bulk remove from group together with ungroupped hosts', () => {
+ cy.get(ROW).find('[type="checkbox"]').eq(0).click();
+ cy.get(ROW).find('[type="checkbox"]').eq(3).click();
+ // TODO: implement ouia selector for this component
+ cy.get(
+ '.ins-c-primary-toolbar__actions [aria-label="Actions"]'
+ ).click();
+ cy.get(DROPDOWN_ITEM)
+ .contains('Remove from group')
+ .shouldHaveAriaEnabled();
+ });
+
+ it('can bulk add hosts to the permitted group', () => {
+ cy.get(ROW).find('[type="checkbox"]').eq(3).click();
+ cy.get(ROW).find('[type="checkbox"]').eq(4).click();
+ // TODO: implement ouia selector for this component
+ cy.get(
+ '.ins-c-primary-toolbar__actions [aria-label="Actions"]'
+ ).click();
+ cy.get(DROPDOWN_ITEM).contains('Add to group').shouldHaveAriaEnabled();
+ });
+
+ it('cannot bulk add to group if groupped hosts selected', () => {
+ cy.get(ROW).find('[type="checkbox"]').eq(0).click();
+ cy.get(ROW).find('[type="checkbox"]').eq(3).click();
+ // TODO: implement ouia selector for this component
+ cy.get(
+ '.ins-c-primary-toolbar__actions [aria-label="Actions"]'
+ ).click();
+ cy.get(DROPDOWN_ITEM).contains('Add to group').shouldHaveAriaDisabled();
+ });
+ });
});
});
diff --git a/src/routes/InventoryTable.js b/src/routes/InventoryTable.js
index 2b7acbab6..ed4c80207 100644
--- a/src/routes/InventoryTable.js
+++ b/src/routes/InventoryTable.js
@@ -50,6 +50,7 @@ import {
ActionButton,
ActionDropdownItem,
} from '../components/InventoryTable/ActionWithRBAC';
+import uniq from 'lodash/uniq';
const mapTags = ({ category, values }) =>
values.map(
@@ -272,17 +273,16 @@ const Inventory = ({
const calculateSelected = () => (selected ? selected.size : 0);
const isBulkRemoveFromGroupsEnabled = () => {
- if (calculateSelected() > 0) {
- const selectedHosts = Array.from(selected.values());
-
- return selectedHosts.every(
- ({ groups }) =>
- groups.length !== 0 &&
- groups[0].name === selectedHosts[0].groups[0].name
- );
- }
-
- return false;
+ return (
+ calculateSelected() > 0 &&
+ Array.from(selected.values()).some(({ groups }) => groups.length > 0) &&
+ uniq(
+ // can remove from at maximum one group at a time
+ Array.from(selected.values())
+ .filter(({ groups }) => groups.length > 0)
+ .map(({ groups }) => groups[0].name)
+ ).length === 1
+ );
};
const isBulkAddHostsToGroupsEnabled = () => {
@@ -448,6 +448,7 @@ const Inventory = ({
setCurrentSystem(Array.from(selected.values()));
setAddHostGroupModalOpen(true);
}}
+ ignoreResourceDefinitions
>
Add to group
@@ -462,15 +463,29 @@ const Inventory = ({
label: (
+ groups?.[0]?.id !== undefined
+ ? REQUIRED_PERMISSIONS_TO_MODIFY_GROUP(
+ groups[0].id
+ )
+ : null
+ )
+ .filter(Boolean) // don't check ungroupped hosts
+ : []
+ }
isAriaDisabled={!isBulkRemoveFromGroupsEnabled()}
noAccessTooltip={NO_MODIFY_GROUPS_TOOLTIP_MESSAGE}
onClick={() => {
setCurrentSystem(Array.from(selected.values()));
setRemoveHostsFromGroupModalOpen(true);
}}
+ {...(selected === undefined // when nothing is selected, no access must be checked
+ ? { override: true }
+ : {})}
+ checkAll
>
Remove from group