Skip to content

Commit b75c7ee

Browse files
pusch-etjuanitas
andauthored
feat(content-explorer): include subfolders toggle (#3242)
* feat(content-explorer): include subfolders toggle * feat(content explorer include subfolders): remove obsolete css * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle * feat(content-explorer): include subfolders toggle --------- Co-authored-by: Trevor <7311041+tjuanitas@users.noreply.github.com>
1 parent 24dcba2 commit b75c7ee

File tree

13 files changed

+258
-46
lines changed

13 files changed

+258
-46
lines changed

i18n/en-US.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,8 @@ boxui.contentExplorer.emptySearch = Sorry, we couldn't find what you're looking
904904
boxui.contentExplorer.filepath = File path
905905
# Text shown for the current folder and its number of items next to the folder tree breadcrumbs
906906
boxui.contentExplorer.folderTreeBreadcrumbsText = {folderName} ({totalItems})
907+
# Label text shown next to the Include Subfolders toggle
908+
boxui.contentExplorer.includeSubfolders = Include Subfolders
907909
# Text shown on button used to move an item to a different folder
908910
boxui.contentExplorer.move = Move
909911
# Text shown as the header for a column of item names in the list

src/features/content-explorer/content-explorer-modal-container/ContentExplorerModalContainer.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class ContentExplorerModalContainer extends Component {
6262
createFolderError: PropTypes.string,
6363
/** Configures the content explorer based on the user's intended action (ex. select file or move/copy) */
6464
contentExplorerMode: ContentExplorerModePropType.isRequired,
65+
/** Props for the include subfolders toggle */
66+
includeSubfoldersProps: PropTypes.object,
6567
/** Initial path of folders. The last folder in the array is the current folder. */
6668
initialFoldersPath: FoldersPathPropType.isRequired,
6769
/** Initial items that will show up as selected */
@@ -73,6 +75,16 @@ class ContentExplorerModalContainer extends Component {
7375
* @param {Array} newFoldersPath
7476
*/
7577
onEnterFolder: PropTypes.func.isRequired,
78+
/** Called when the folders path is updated
79+
*
80+
* @param {Array} newFoldersPath
81+
*/
82+
onFoldersPathUpdate: PropTypes.func,
83+
/** Called whenever the selected items list changes
84+
*
85+
* @param {Object} selectedItems
86+
*/
87+
onSelectedItemsUpdate: PropTypes.func,
7688
/**
7789
* Called when items are chosen.
7890
*

src/features/content-explorer/content-explorer-modal/ContentExplorerModal.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@ type Props = {
1717
customInput?: React.ComponentType<any>,
1818
description?: string,
1919
hasFolderTreeBreadcrumbs: boolean,
20+
includeSubfoldersProps?: Object,
2021
isNoSelectionAllowed?: boolean,
2122
isOpen?: boolean,
2223
isResponsive?: boolean,
24+
itemButtonRenderer: Function,
2325
itemRowHeight?: number,
2426
itemRowRenderer?: Function,
2527
listHeaderHeight?: number,
2628
listHeaderRenderer?: Function,
29+
onFoldersPathUpdate?: Function,
2730
onRequestClose?: Function,
2831
onSelectItem?: (item: Object, index: number) => void,
2932
onSelectedClick?: () => void,
33+
onSelectedItemsUpdate?: Function,
3034
title?: string,
3135
};
3236

src/features/content-explorer/content-explorer/ContentExplorer.js

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ContentExplorerHeaderActions from './ContentExplorerHeaderActions';
77
import ContentExplorerEmptyState from './ContentExplorerEmptyState';
88
import ContentExplorerActionButtons from './ContentExplorerActionButtons';
99
import ContentExplorerSelectAll from './ContentExplorerSelectAll';
10+
import ContentExplorerIncludeSubfolders from './ContentExplorerIncludeSubfolders';
1011

1112
import ItemList from '../item-list';
1213
import { ContentExplorerModePropType, FoldersPathPropType, ItemsPropType } from '../prop-types';
@@ -45,6 +46,8 @@ class ContentExplorer extends Component {
4546
hasFolderTreeBreadcrumbs: PropTypes.bool,
4647
/** Any extra items in the header to the right of the search input (and new folder button) */
4748
headerActionsAccessory: PropTypes.node,
49+
/** Props for the include subfolders toggle */
50+
includeSubfoldersProps: PropTypes.object,
4851
/** Initial path of folders. The last folder in the array is the current folder. */
4952
initialFoldersPath: FoldersPathPropType.isRequired,
5053
/** Initial items that will show up as selected */
@@ -58,6 +61,16 @@ class ContentExplorer extends Component {
5861
* @param {Array} newFoldersPath
5962
*/
6063
onEnterFolder: PropTypes.func.isRequired,
64+
/** Called when the folders path is updated
65+
*
66+
* @param {Array} newFoldersPath
67+
*/
68+
onFoldersPathUpdate: PropTypes.func,
69+
/** Called whenever the selected items list changes
70+
*
71+
* @param {Object} selectedItems
72+
*/
73+
onSelectedItemsUpdate: PropTypes.func,
6174
/**
6275
* Called when an item is selected
6376
*
@@ -169,9 +182,7 @@ class ContentExplorer extends Component {
169182
const { initialFoldersPath } = this.props;
170183

171184
if (prevInitialFoldersPath !== initialFoldersPath) {
172-
this.setState({
173-
foldersPath: initialFoldersPath,
174-
});
185+
this.handleFoldersPathUpdated(initialFoldersPath);
175186
}
176187
}
177188

@@ -231,13 +242,17 @@ class ContentExplorer extends Component {
231242
};
232243

233244
deselectItems() {
245+
const { onSelectedItemsUpdate } = this.props;
234246
this.setState({
235247
selectedItems: {},
236248
});
249+
if (onSelectedItemsUpdate) {
250+
onSelectedItemsUpdate({});
251+
}
237252
}
238253

239254
enterFolder = enteredFolder => {
240-
const { contentExplorerMode, onEnterFolder } = this.props;
255+
const { contentExplorerMode, onEnterFolder, onFoldersPathUpdate } = this.props;
241256
const { foldersPath } = this.state;
242257

243258
const folderIndex = foldersPath.findIndex(folder => folder.id === enteredFolder.id);
@@ -261,14 +276,22 @@ class ContentExplorer extends Component {
261276
}
262277

263278
this.setState(newState);
279+
if (onFoldersPathUpdate) {
280+
onFoldersPathUpdate(newFoldersPath);
281+
}
264282

265283
onEnterFolder(enteredFolder, newFoldersPath);
266284
};
267285

268286
handleFoldersPathUpdated = newFoldersPath => {
287+
const { onFoldersPathUpdate } = this.props;
288+
269289
this.setState({
270290
foldersPath: newFoldersPath,
271291
});
292+
if (onFoldersPathUpdate) {
293+
onFoldersPathUpdate(newFoldersPath);
294+
}
272295
};
273296

274297
handleSearchSubmit = searchQuery => {
@@ -286,11 +309,11 @@ class ContentExplorer extends Component {
286309
};
287310

288311
handleItemClick = ({ event, index }) => {
289-
const { contentExplorerMode, items, onSelectItem } = this.props;
312+
const { contentExplorerMode, items, onSelectItem, onSelectedItemsUpdate } = this.props;
290313
const { selectedItems } = this.state;
291314
const item = items[index];
292315

293-
if (item.isDisabled || item.isLoading) {
316+
if (item.isDisabled || item.isLoading || item.isActionDisabled) {
294317
return;
295318
}
296319

@@ -306,6 +329,10 @@ class ContentExplorer extends Component {
306329

307330
this.setState({ selectedItems: newSelectedItems, isSelectAllChecked: false });
308331

332+
if (onSelectedItemsUpdate) {
333+
onSelectedItemsUpdate(newSelectedItems);
334+
}
335+
309336
if (onSelectItem) {
310337
onSelectItem(item, index);
311338
}
@@ -321,7 +348,7 @@ class ContentExplorer extends Component {
321348

322349
if (item.type === TYPE_FOLDER) {
323350
this.enterFolder(item);
324-
} else {
351+
} else if (!item.isActionDisabled) {
325352
onChooseItems([item]);
326353
}
327354
this.setState({ isSelectAllChecked: false });
@@ -385,14 +412,16 @@ class ContentExplorer extends Component {
385412
};
386413

387414
handleSelectAllClick = async () => {
388-
// check if the items list is still loading
389-
const { items } = this.props;
415+
const { items, onSelectedItemsUpdate } = this.props;
390416
if (items && items[0] && items[0].isLoading) {
391417
return;
392418
}
393419
const { isSelectAllChecked } = this.state;
394420
const newSelectedItems = isSelectAllChecked ? this.unselectAll() : this.selectAll();
395421
this.setState({ selectedItems: newSelectedItems, isSelectAllChecked: !isSelectAllChecked });
422+
if (onSelectedItemsUpdate) {
423+
onSelectedItemsUpdate(newSelectedItems);
424+
}
396425
};
397426

398427
renderItemListEmptyState = () => {
@@ -433,6 +462,7 @@ class ContentExplorer extends Component {
433462
numItemsPerPage,
434463
numTotalItems,
435464
onLoadMoreItems,
465+
includeSubfoldersProps,
436466
itemIconRenderer,
437467
itemNameLinkRenderer,
438468
itemButtonRenderer,
@@ -513,13 +543,17 @@ class ContentExplorer extends Component {
513543
>
514544
{headerActionsAccessory}
515545
</ContentExplorerHeaderActions>
516-
{isSelectAllAllowed && (
517-
<ContentExplorerSelectAll
518-
numTotalItems={numTotalItems}
519-
isSelectAllChecked={isSelectAllChecked}
520-
handleSelectAllClick={this.handleSelectAllClick}
521-
/>
522-
)}
546+
<div className="bdl-ContentExplorer-subheader">
547+
{includeSubfoldersProps && <ContentExplorerIncludeSubfolders {...includeSubfoldersProps} />}
548+
{isSelectAllAllowed && (
549+
<ContentExplorerSelectAll
550+
handleSelectAllClick={this.handleSelectAllClick}
551+
isLabelHidden={!!includeSubfoldersProps}
552+
isSelectAllChecked={isSelectAllChecked}
553+
numTotalItems={numTotalItems}
554+
/>
555+
)}
556+
</div>
523557
<ItemList
524558
additionalColumns={additionalColumns}
525559
contentExplorerMode={contentExplorerMode}

src/features/content-explorer/content-explorer/ContentExplorer.scss

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
align-items: center;
5757
height: 30px;
5858
margin-top: 15px;
59-
margin-bottom: 15px;
59+
margin-bottom: 5px;
6060

6161
.bdl-ContentExplorerFolderTreeBreadcrumbs-button {
6262
margin: 0 10px 0 0;
@@ -72,22 +72,52 @@
7272
}
7373
}
7474

75+
.bdl-ContentExplorer-subheader {
76+
display: flex;
77+
align-items: center;
78+
height: 10 * $bdl-grid-unit;
79+
80+
.checkbox-container,
81+
.toggle-container {
82+
margin: 0;
83+
}
84+
85+
.toggle-simple-label {
86+
display: flex;
87+
align-items: center;
88+
min-width: auto;
89+
}
90+
91+
.content-explorer-select-all-checkbox {
92+
margin-right: 18px;
93+
94+
&:first-child {
95+
margin-left: auto;
96+
}
97+
}
98+
}
99+
75100
.content-explorer-select-all-container {
76-
display: block;
77-
height: 8 * $bdl-grid-unit;
101+
display: flex;
102+
flex-grow: 1;
103+
align-items: center;
104+
height: 10 * $bdl-grid-unit;
78105

79106
.content-explorer-select-all-items-counter {
80107
margin-left: 2px;
81108
}
82109

83110
.content-explorer-select-all-checkbox-label {
84-
float: right;
85-
margin-right: 42px;
111+
margin-right: 10px;
112+
margin-left: auto;
86113
}
114+
}
87115

88-
.content-explorer-select-all-checkbox {
89-
float: right;
90-
margin-right: -21 * $bdl-grid-unit;
116+
.bdl-ContentExplorerIncludeSubfolders-icon {
117+
margin-left: 4px;
118+
119+
path {
120+
fill: $bdl-box-blue;
91121
}
92122
}
93123

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { injectIntl, FormattedMessage } from 'react-intl';
4+
import InfoBadge16 from '../../../icon/fill/InfoBadge16';
5+
import Toggle from '../../../components/toggle';
6+
import Tooltip from '../../../components/tooltip';
7+
import messages from '../messages';
8+
9+
const ContentExplorerIncludeSubfolders = ({ isDisabled, onChange, tooltipMessage }) => {
10+
const label = (
11+
<>
12+
<FormattedMessage {...messages.includeSubfolders} />
13+
{tooltipMessage && (
14+
<Tooltip text={<FormattedMessage {...tooltipMessage} />}>
15+
<InfoBadge16 className="bdl-ContentExplorerIncludeSubfolders-icon" fill="blue" />
16+
</Tooltip>
17+
)}
18+
</>
19+
);
20+
return <Toggle label={label} isDisabled={isDisabled} onChange={onChange} />;
21+
};
22+
23+
ContentExplorerIncludeSubfolders.propTypes = {
24+
isDisabled: PropTypes.bool,
25+
onChange: PropTypes.func,
26+
tooltipMessage: PropTypes.object,
27+
};
28+
29+
export { ContentExplorerIncludeSubfolders as ContentExplorerIncludeSubfoldersBase };
30+
export default injectIntl(ContentExplorerIncludeSubfolders);

src/features/content-explorer/content-explorer/ContentExplorerSelectAll.js

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,49 @@ import PropTypes from 'prop-types';
22
import React from 'react';
33
import { injectIntl, FormattedMessage } from 'react-intl';
44
import Checkbox from '../../../components/checkbox/Checkbox';
5-
5+
import Tooltip from '../../../components/tooltip';
66
import messages from '../messages';
77

8-
const ContentExplorerSelectAll = ({ intl, numTotalItems = 0, isSelectAllChecked, handleSelectAllClick }) => (
8+
const ContentExplorerSelectAll = ({
9+
handleSelectAllClick,
10+
intl,
11+
isLabelHidden,
12+
isSelectAllChecked,
13+
numTotalItems = 0,
14+
}) => (
915
<div className="content-explorer-select-all-container">
10-
<label className="content-explorer-select-all-items-counter">
11-
{numTotalItems === 1 ? (
12-
<FormattedMessage {...messages.result} values={{ itemsCount: intl.formatNumber(numTotalItems) }} />
13-
) : (
14-
<FormattedMessage {...messages.results} values={{ itemsCount: intl.formatNumber(numTotalItems) }} />
15-
)}
16-
</label>
17-
<label className="content-explorer-select-all-checkbox-label">
18-
<FormattedMessage {...messages.selectAll} />
19-
</label>
20-
<Checkbox
21-
hideLabel
22-
className="content-explorer-select-all-checkbox"
23-
onChange={handleSelectAllClick}
24-
isChecked={isSelectAllChecked}
25-
/>
16+
{!isLabelHidden && (
17+
<label className="content-explorer-select-all-items-counter">
18+
{numTotalItems === 1 ? (
19+
<FormattedMessage {...messages.result} values={{ itemsCount: intl.formatNumber(numTotalItems) }} />
20+
) : (
21+
<FormattedMessage {...messages.results} values={{ itemsCount: intl.formatNumber(numTotalItems) }} />
22+
)}
23+
</label>
24+
)}
25+
{!isLabelHidden && (
26+
<label className="content-explorer-select-all-checkbox-label">
27+
<FormattedMessage {...messages.selectAll} />
28+
</label>
29+
)}
30+
<Tooltip isShown={isLabelHidden ? undefined : false} text={<FormattedMessage {...messages.selectAll} />}>
31+
<Checkbox
32+
hideLabel
33+
className="content-explorer-select-all-checkbox"
34+
onChange={handleSelectAllClick}
35+
isChecked={isSelectAllChecked}
36+
/>
37+
</Tooltip>
2638
</div>
2739
);
2840

2941
ContentExplorerSelectAll.propTypes = {
42+
handleSelectAllClick: PropTypes.func,
3043
intl: PropTypes.any,
31-
numTotalItems: PropTypes.number,
3244
isSelectAllChecked: PropTypes.bool,
33-
handleSelectAllClick: PropTypes.func,
45+
isLabelHidden: PropTypes.bool,
46+
numTotalItems: PropTypes.number,
3447
};
48+
3549
export { ContentExplorerSelectAll as ContentExplorerSelectAllBase };
3650
export default injectIntl(ContentExplorerSelectAll);

0 commit comments

Comments
 (0)