Skip to content

Commit

Permalink
feat: add custom treeView to remove hidden items
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-Torrent committed Jan 12, 2022
1 parent a6e1cbf commit 93a7c30
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 39 deletions.
18 changes: 12 additions & 6 deletions src/components/common/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ import {
buildFolderButtonId,
FOLDER_NAME_TITLE_CLASS,
} from '../../config/selectors';
import { API_HOST, HIDDEN_ITEM_TAG_ID, SCREEN_MAX_HEIGHT } from '../../config/constants';
import {
API_HOST,
HIDDEN_ITEM_TAG_ID,
SCREEN_MAX_HEIGHT,
} from '../../config/constants';

const { useItem, useChildren, useFileContent, useCurrentMember, useItemTags } = hooks;
const { useItem, useChildren, useFileContent, useCurrentMember, useItemTags } =
hooks;

const useStyles = makeStyles((theme) => ({
container: {
Expand All @@ -30,7 +35,7 @@ const Item = ({ id, isChildren, showPinnedOnly }) => {
const { t } = useTranslation();
const classes = useStyles();
const { data: item, isLoading, isError } = useItem(id);
const { data: x, isLoading: isTagsLoading, /* isError: z */ } = useItemTags(id);
const { data: itemTags, isLoading: isTagsLoading } = useItemTags(id);
const { data: user, isLoading: isMemberLoading } = useCurrentMember();
// fetch children if item is folder
const isFolder = Boolean(item?.get('type') === ITEM_TYPES.FOLDER);
Expand All @@ -50,12 +55,13 @@ const Item = ({ id, isChildren, showPinnedOnly }) => {
return <Loader />;
}

const isHidden = x.filter(({ tagId }) => tagId === HIDDEN_ITEM_TAG_ID).size > 0;
if(isHidden && isChildren){
const isHidden =
itemTags.filter(({ tagId }) => tagId === HIDDEN_ITEM_TAG_ID).size > 0;
if (isHidden && isChildren) {
return null;
}

if(isHidden){
if (isHidden) {
return <Alert severity="error">{t('You cannnot access this item')}</Alert>;
}

Expand Down
16 changes: 6 additions & 10 deletions src/components/common/MainMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import Alert from '@material-ui/lab/Alert';
import { useTranslation } from 'react-i18next';
import {
MainMenu as GraaspMainMenu,
DynamicTreeView,
Loader,
} from '@graasp/ui';
import { buildTreeItemClass, MAIN_MENU_ID } from '../../config/selectors';
import { MAIN_MENU_ID } from '../../config/selectors';
import { hooks } from '../../config/queryClient';
import { ITEM_TYPES } from '../../enums';
import { ItemContext } from '../context/ItemContext';
import DynamicTreeView from './Tree/Tree';
import { HIDDEN_ITEM_TAG_ID } from '../../config/constants';

const { useItem, useChildren } = hooks;
const { useItem, useChildren, useItemTags } = hooks;

const MainMenu = () => {
const { rootId } = useParams();
Expand Down Expand Up @@ -56,19 +57,14 @@ const MainMenu = () => {
rootLabel={rootItem.get('name')}
rootId={rootId}
useItem={useItem}
useTags={useItemTags}
useChildren={useChildren}
buildTreeItemClass={(nodeId) => buildTreeItemClass(nodeId)}
initialExpendedItems={[rootId]}
showCheckbox={false}
showItemFilter={() => true}
showItemFilter={(item, tags) => tags.filter(({ tagId }) => tagId === HIDDEN_ITEM_TAG_ID).size <= 0}
selectedId={focusedItemId}
onTreeItemSelect={(payload) => {
setFocusedItemId(payload);
}}
shouldFetchChildrenForItem={(item) =>
item.get('type') === ITEM_TYPES.FOLDER
}
isTreeItemDisabled={() => false}
items={children && !children.isEmpty() ? children : []}
/>
</GraaspMainMenu>
Expand Down
102 changes: 102 additions & 0 deletions src/components/common/Tree/CustomTreeItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* eslint-disable react/forbid-prop-types */
import React from 'react';
import Skeleton from '@material-ui/lab/Skeleton';
import TreeItem from '@material-ui/lab/TreeItem';
import PropTypes from 'prop-types';
import { buildTreeItemClass } from '../../../config/selectors';
import { ITEM_TYPES } from '../../../enums';

const LoadingTreeItem = <Skeleton variant="text" />;

const CustomTreeItem = ({
itemId,
expandedItems = [],
selectedId,
useChildren,
useItem,
useTags,
showItemFilter,
}) => {
const { data: item, isLoading, isError } = useItem(itemId);
const { data: tags, isLoading: isTagLoading } = useTags(itemId);

const showItem = item && tags && showItemFilter?.(item, tags);
const isExpanded = expandedItems?.includes(itemId);

const { data: children, isLoading: childrenIsLoading } = useChildren(itemId, {
enabled: Boolean(
item && showItem && item.get('type') === ITEM_TYPES.FOLDER && isExpanded,
),
});

if (isLoading || isTagLoading) {
return (
<TreeItem
nodeId={`loading-${itemId}`}
key={itemId}
label={LoadingTreeItem}
/>
);
}

// display only folders
if (!showItem || !item || isError) {
return null;
}

const renderChildrenItems = () => {
if (childrenIsLoading) {
return LoadingTreeItem;
}

const filteredChildren = children?.filter((child) =>
showItemFilter?.(child),
);

if (!filteredChildren?.size) {
return null;
}

return filteredChildren.map(({ id: childId }) => (
<CustomTreeItem
key={childId}
itemId={childId}
expandedItems={expandedItems}
selectedId={selectedId}
useChildren={useChildren}
useItem={useItem}
useTags={useTags}
showItemFilter={showItemFilter}
/>
));
};

const childrenTreeItems = renderChildrenItems();

// render child with checkbox
const content = childrenIsLoading ? LoadingTreeItem : <>{item.get('name')}</>;

// recursive display of children
return (
<TreeItem
key={itemId}
nodeId={itemId}
label={content}
className={buildTreeItemClass(itemId)}
>
{childrenTreeItems}
</TreeItem>
);
};

CustomTreeItem.propTypes = {
itemId: PropTypes.string.isRequired,
expandedItems: PropTypes.arrayOf(PropTypes.string).isRequired,
selectedId: PropTypes.string.isRequired,
useChildren: PropTypes.any.isRequired,
useItem: PropTypes.any.isRequired,
useTags: PropTypes.any.isRequired,
showItemFilter: PropTypes.any.isRequired,
};

export default CustomTreeItem;
72 changes: 72 additions & 0 deletions src/components/common/Tree/Tree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable react/forbid-prop-types */
import React, { useState } from 'react';
import TreeView from '@material-ui/lab/TreeView';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import TreeItem from '@material-ui/lab/TreeItem';
import PropTypes from 'prop-types';
import CustomTreeItem from './CustomTreeItem';

const DynamicTreeView = ({
id,
rootLabel,
rootId,
useItem,
useTags,
useChildren,
initialExpendedItems = [],
items,
showItemFilter = () => true,
selectedId,
onTreeItemSelect,
}) => {
const [expandedItems, setExpandedItems] = useState(initialExpendedItems);

// types based on TreeView types
const onSelect = (event, value) => onTreeItemSelect?.(value);

// types based on TreeView types
const onToggle = (event, nodeIds) => setExpandedItems(nodeIds);

return (
<TreeView
id={id}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
onNodeSelect={onSelect}
onNodeToggle={onToggle}
expanded={expandedItems}
>
<TreeItem nodeId={rootId} label={rootLabel}>
{items.map(({ id: itemId }) => (
<CustomTreeItem
key={itemId}
itemId={itemId}
expandedItems={expandedItems}
selectedId={selectedId}
useItem={useItem}
useChildren={useChildren}
useTags={useTags}
showItemFilter={showItemFilter}
/>
))}
</TreeItem>
</TreeView>
);
};

DynamicTreeView.propTypes = {
id: PropTypes.string.isRequired,
rootLabel: PropTypes.string.isRequired,
rootId: PropTypes.string.isRequired,
items: PropTypes.any.isRequired,
initialExpendedItems: PropTypes.arrayOf(PropTypes.string).isRequired,
selectedId: PropTypes.string.isRequired,
onTreeItemSelect: PropTypes.any.isRequired,
useItem: PropTypes.any.isRequired,
useTags: PropTypes.any.isRequired,
useChildren: PropTypes.any.isRequired,
showItemFilter: PropTypes.any.isRequired,
};

export default DynamicTreeView;
Loading

0 comments on commit 93a7c30

Please sign in to comment.