diff --git a/packages/patternfly-3/patternfly-react/less/dual-list-selector.less b/packages/patternfly-3/patternfly-react/less/dual-list-selector.less
index b0338a93b5b..d13a6a08ae5 100644
--- a/packages/patternfly-3/patternfly-react/less/dual-list-selector.less
+++ b/packages/patternfly-3/patternfly-react/less/dual-list-selector.less
@@ -117,12 +117,22 @@
color: white;
}
+ &.disabled {
+ cursor: not-allowed;
+ background: @color-pf-black-150;
+ color: @color-pf-black-500;
+
+ input[type='checkbox'] {
+ cursor: not-allowed;
+ }
+ }
+
&.child {
padding-left: 22px;
}
&:hover {
- &:not(.selected) {
+ &:not(.selected):not(.disabled) {
background-color: @color-pf-blue-100;
color: inherit;
}
diff --git a/packages/patternfly-3/patternfly-react/sass/patternfly-react/_dual-list-selector.scss b/packages/patternfly-3/patternfly-react/sass/patternfly-react/_dual-list-selector.scss
index 04970f8754d..aeeb3657188 100644
--- a/packages/patternfly-3/patternfly-react/sass/patternfly-react/_dual-list-selector.scss
+++ b/packages/patternfly-3/patternfly-react/sass/patternfly-react/_dual-list-selector.scss
@@ -117,12 +117,22 @@ $dual-list-scroll-bar-length: 12px;
color: white;
}
+ &.disabled {
+ cursor: not-allowed;
+ background: $color-pf-black-150;
+ color: $color-pf-black-500;
+
+ input[type='checkbox'] {
+ cursor: not-allowed;
+ }
+ }
+
&.child {
padding-left: 22px;
}
&:hover {
- &:not(.selected) {
+ &:not(.selected):not(.disabled) {
background-color: $color-pf-blue-100;
color: inherit;
}
diff --git a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.js b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.js
index 00483a172bb..5aaca2735ac 100644
--- a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.js
+++ b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.js
@@ -16,12 +16,11 @@ import {
isAllItemsChecked,
isItemExistOnList,
filterByHiding,
- getItemsLength,
- toggleFilterredItems,
getFilterredItemsLength,
makeAllItemsVisible,
getSelectedFilterredItemsLength,
- isItemSelected
+ isItemSelected,
+ getFilterredItems
} from './helpers';
class DualList extends React.Component {
@@ -81,11 +80,12 @@ class DualList extends React.Component {
const items = cloneDeep(originalItems);
let selectCount = originalSelectCount;
if (filterTerm) {
- toggleFilterredItems(items, checked);
- selectCount += getFilterredItemsLength(items) * (checked ? 1 : -1);
+ const filterredItems = getFilterredItems(items);
+ const toggledAmount = toggleAllItems(filterredItems, checked);
+ selectCount += toggledAmount * (checked ? 1 : -1);
} else {
- toggleAllItems(items, checked);
- selectCount = checked ? getItemsLength(items) : 0;
+ const toggledAmount = toggleAllItems(items, checked);
+ selectCount = checked ? selectCount + toggledAmount : 0;
}
this.props.onMainCheckboxChange({
side,
diff --git a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.test.js b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.test.js
index 6c3012bf830..35249381438 100644
--- a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.test.js
+++ b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualList.test.js
@@ -28,7 +28,9 @@ const getProps = () => ({
{ value: 'Heather Davis', label: 'Heather Davis' }
]
},
- right: { items: [{ value: 'Donald Trump', label: 'Donald Trump' }] }
+ right: {
+ items: [{ value: 'Donald Trump', label: 'Donald Trump' }]
+ }
});
test('dual-list render properly ', () => {
@@ -170,3 +172,16 @@ test('sorting works ! ', () => {
sortingIcon.simulate('click', mockedEvent);
expect(component.state().left.items[0]).toBe(originalList[originalList.length - 1]);
});
+
+test('item is disabled and tooltip exists', () => {
+ const props = getProps();
+ props.right.items.push({
+ value: 'Barack Obama',
+ label: 'Barack Obama',
+ disabled: true,
+ tooltipText: 'Barack Obama'
+ });
+ const component = mount();
+ expect(component.exists('DualListItemTooltip')).toBeTruthy();
+ expect(component.exists('.disabled')).toBeTruthy();
+});
diff --git a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualListMocks.js b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualListMocks.js
index cafb4dceced..8aa88f69fff 100644
--- a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualListMocks.js
+++ b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/DualListMocks.js
@@ -3,7 +3,7 @@ import { MenuItem } from '../../index';
export const items = {
left: [
- { value: 'Ann Little', label: 'Ann Little' },
+ { value: 'Ann Little', label: 'Ann Little', disabled: true, tooltipText: 'Permission Denied' },
{ value: 'Daniel Nguyen', label: 'Daniel Nguyen' },
{ value: 'Heather Davis', label: 'Heather Davis' },
{
diff --git a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItem.js b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItem.js
index ce031a1aaa0..4153320ed0e 100644
--- a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItem.js
+++ b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItem.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import DualListItemTooltip from './DualListItemTooltip';
import { TypeAheadSelect } from '../../../components/TypeAheadSelect';
import { noop } from '../../../common/helpers';
@@ -16,10 +17,18 @@ const DualListItem = ({
filterTerm,
onChange,
side,
- hidden
+ hidden,
+ disabled,
+ tooltipID,
+ tooltipText
}) => {
- const cx = classNames('dual-list-pf-item', className, checked && ' selected');
- return (
+ const cx = classNames('dual-list-pf-item', className, checked && 'selected', disabled && 'disabled');
+ const itemLabel = (
+
+ {label}
+
+ );
+ const item = (
);
+ const getTooltipID = () => {
+ let uniqueTooltipID = `dual-list-item-tooltip-${side}`;
+ if (parentPosition) {
+ uniqueTooltipID += `-${parentPosition}`;
+ }
+ uniqueTooltipID += `-${position}`;
+ return uniqueTooltipID;
+ };
+ return tooltipText ? (
+
+ {item}
+
+ ) : (
+ item
+ );
};
DualListItem.propTypes = {
@@ -57,7 +80,13 @@ DualListItem.propTypes = {
/** The side of the selector. */
side: PropTypes.string,
/** Sets the item visibillity when filtering. */
- hidden: PropTypes.bool
+ hidden: PropTypes.bool,
+ /** Disable the item to move between lists */
+ disabled: PropTypes.bool,
+ /** unique tooltip ID */
+ tooltipID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ /** text to be shown on the tooltip */
+ tooltipText: PropTypes.string
};
DualListItem.defaultProps = {
@@ -70,7 +99,10 @@ DualListItem.defaultProps = {
filterTerm: null,
onChange: noop,
side: null,
- hidden: false
+ hidden: false,
+ disabled: false,
+ tooltipID: null,
+ tooltipText: null
};
export default DualListItem;
diff --git a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItemTooltip.js b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItemTooltip.js
new file mode 100644
index 00000000000..904e137f68a
--- /dev/null
+++ b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/components/DualListItemTooltip.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { OverlayTrigger, Tooltip } from '../../../index';
+
+const DualListItemTooltip = ({ id, text, children }) => {
+ const tooltip = {text};
+ return (
+
+ {children}
+
+ );
+};
+
+DualListItemTooltip.propTypes = {
+ /** unique tooltip ID */
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ /** text to be shown on the tooltip */
+ text: PropTypes.string,
+ /** children nodes */
+ children: PropTypes.node
+};
+
+DualListItemTooltip.defaultProps = {
+ id: null,
+ text: null,
+ children: null
+};
+
+export default DualListItemTooltip;
diff --git a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/helpers.js b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/helpers.js
index 45136e8de49..0043848eb4e 100644
--- a/packages/patternfly-3/patternfly-react/src/components/DualListSelector/helpers.js
+++ b/packages/patternfly-3/patternfly-react/src/components/DualListSelector/helpers.js
@@ -155,15 +155,29 @@ export const itemHasChildren = item => item.children !== undefined;
export const getItemPosition = (array, position, isSortAsc) => (isSortAsc ? position : array.length - position - 1);
export const toggleAllItems = (list, checked) => {
+ let toggleCount = 0;
list.forEach(item => {
- item.checked = checked;
+ if (item.disabled) {
+ return;
+ }
+ if (item.checked !== checked) {
+ item.checked = checked;
+ toggleCount += 1;
+ }
if (itemHasChildren(item)) {
+ let childrenToggleCount = 0;
item.children.forEach(childItem => {
- childItem.checked = checked;
+ if (childItem.checked !== checked) {
+ childItem.checked = checked;
+ childrenToggleCount += 1;
+ }
});
+ if (childrenToggleCount > 0) {
+ toggleCount += childrenToggleCount - 1;
+ }
}
- return item;
});
+ return toggleCount;
};
export const isAllItemsChecked = (items, selectCount) => selectCount > 0 && selectCount === getItemsLength(items);
@@ -179,24 +193,7 @@ export const isItemExistOnList = (list, itemLabel) => {
return { isParentExist: parentIndex !== null, parentIndex };
};
-export const toggleFilterredItems = (list, checked) => {
- list.forEach(item => {
- if (!isItemHidden(item)) {
- item.checked = checked;
- if (itemHasChildren(item)) {
- toggleAllItems(item.children, checked);
- }
- } else if (itemHasChildren(item)) {
- item.children.forEach(childItem => {
- if (!isItemHidden(childItem)) {
- item.checked = checked;
- }
- });
- }
- });
-};
-
-export const getFilteredItems = list => {
+export const getFilterredItems = list => {
const filteredItems = [];
list.forEach(item => {
if (!isItemHidden(item)) {
@@ -217,10 +214,10 @@ export const getFilteredItems = list => {
return filteredItems;
};
-export const getFilterredItemsLength = list => getItemsLength(getFilteredItems(list));
+export const getFilterredItemsLength = list => getItemsLength(getFilterredItems(list));
export const getSelectedFilterredItemsLength = list => {
- const filteredItems = getFilteredItems(list);
+ const filteredItems = getFilterredItems(list);
let selectedAmount = 0;
filteredItems.forEach(item => {
if (isItemSelected(item)) {
@@ -244,3 +241,5 @@ export const getSelectedFilterredItemsLength = list => {
export const isItemSelected = item => item.checked;
export const isItemHidden = item => item.hidden;
+
+export const isItemDisabled = item => item.disabled;