Skip to content

Commit

Permalink
Client - Uniform items object configuration for all menus (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
luorlandini authored Apr 26, 2021
1 parent 17f1f21 commit a629631
Show file tree
Hide file tree
Showing 16 changed files with 712 additions and 356 deletions.
22 changes: 16 additions & 6 deletions geonode_mapstore_client/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,34 @@ Menu item object contains properties for a list item rendered in a menu. Configu
{
"labelId": "gnhome.register", // label message id
"type": "link", // one of 'link', 'dropdown', 'divider' or 'filter'
"href": "/account/signup/?next=/", // link href
"authenticated": false, // true shows the item only when user authenticated while false only for anonymous user, if undefined the item is always visible
"badge": "${layersTotalCount}", // (menu configuration only) counter badge. variable available: layersTotalCount, mapsTotalCount, documentsTotalCount, geoappsTotalCount or geostoriesTotalCount
"style": {} // (menu configuration only) css style object for a specific menu item
"href": "/account/signup/?next=/",
"badge": "{state.layersTotalCount}", // (menu configuration only) counter badge. variable available: layersTotalCount, mapsTotalCount,
"authenticated": true, // true shows the item only when user authenticated while false only for anonymous user, if undefined the item is always visible
"permissions": [], // the value in array define the access to the contents
"allowedRoles": [] // user roles to access to the contents
}
```

- type dropdown
```js
{
"labelId": "gnhome.register", // label message id
"image": "", //the toogle image
"type": "dropdown", // one of 'link', 'dropdown', 'divider' or 'filter'
"authenticated": true, // true shows the item only when user authenticated while false only for anonymous user, if undefined the item is always visible
"items": [] // menu items of type link or divider
"permissions": [], // the value in array define the access to the contents
"allowedRoles": [], // user roles to access to the contents
"items": [] // menu items of type link or divider
}
```

- type divider
```js
{
"type": "divider" // one of 'link', 'dropdown', 'divider' or 'filter'
"type": "divider", // one of 'link', 'dropdown', 'divider' or 'filter'
"authenticated": true, // true shows the item only when user authenticated while false only for anonymous user, if undefined the item is always visible
"permissions": [], // the value in array define the access to the contents
"allowedRoles": [], // user roles to access to the contents
}
```

Expand All @@ -163,6 +170,9 @@ Menu item object contains properties for a list item rendered in a menu. Configu
"type": "filter", // one of 'link', 'dropdown', 'divider' or 'filter'
"id": "landuse", // unique id of the filter
"labelId": "gnhome.customFilterExample", // label message id
"authenticated": true, // true shows the item only when user authenticated while false only for anonymous user, if undefined the item is always visible
"permissions": [], // the value in array define the access to the contents
"allowedRoles": [], // user roles to access to the contents
"query": { // query filter to use
"filter{regions.name.in}": [
"Global"
Expand Down
132 changes: 132 additions & 0 deletions geonode_mapstore_client/client/js/components/Menu/DropdownList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright 2021, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/


import React from 'react';
import PropTypes from 'prop-types';
import Message from '@mapstore/framework/components/I18N/Message';
import { Dropdown, Badge } from 'react-bootstrap-v1';
import isNil from 'lodash/isNil';
import { createPortal } from 'react-dom';

const isValidBadgeValue = value => !!(value !== '' && !isNil(value));
/**
* DropdownList component
* @name DropdownList
* @memberof components.Menu.DropdownList
* @prop {number} id to apply to toogle
* @prop {array} items list od items of Dropdown
* @prop {string} label label to apply to toogle
* @prop {string} labelId alternative to label
* @prop {string} labelId alternative to labe
* @prop {object} toogleStyle inline style to apply to toogle comp
* @prop {string} toogleImage image to apply to toogle comp
* @prop {string} dropdownClass the css class to apply to the comp
* @prop {number} tabIndex define navigation order
* @prop {number} badgeValue to apply the value to the item in list
* @prop {node} containerNode the node to append the child element into a DOM
* @example
* <DropdownList
* id={id}
* items={items}
* label={label}
* labelId={labelId}
* toogleStyle={style}
* toogleImage={image}
* state={state}
* dropdownClass={classItem}
* tabIndex={tabIndex}
* badgeValue={badgeValue}
* containerNode={containerNode}
* />
*
*/


const DropdownList = ({
id,
items,
label,
labelId,
toogleStyle,
toogleImage,
dropdownClass,
tabIndex,
badgeValue,
containerNode
}) => {


const dropdownItems = items
.map((itm, idx) => {
if (itm.type === 'divider') {
return <Dropdown.Divider key={idx} />;
}
return (
<Dropdown.Item
key={idx}
href={itm.href}
style={itm.style}
>
{itm.labelId && <Message msgId={itm.labelId} /> || itm.label}
{isValidBadgeValue(itm.badge) && <Badge>{itm.badge}</Badge>}
</Dropdown.Item>
);
});

const DropdownToogle = (
<Dropdown.Toggle
id={'gn-toggle-dropdown-' + id}
variant="default"
tabIndex={tabIndex}
style={toogleStyle}
>
{toogleImage
? <img src={toogleImage} />
: null
}
{labelId && <Message msgId={labelId} /> || label}
{isValidBadgeValue(badgeValue) && <Badge>{badgeValue}</Badge>}
</Dropdown.Toggle>

);


return (
<Dropdown
className={`${dropdownClass}`}
>
{DropdownToogle}
{containerNode
? createPortal(<Dropdown.Menu>
{dropdownItems}
</Dropdown.Menu>, containerNode.parentNode)
: <Dropdown.Menu>
{dropdownItems}
</Dropdown.Menu>}
</Dropdown>
);

};

DropdownList.propTypes = {
id: PropTypes.number,
items: PropTypes.array.isRequired,
label: PropTypes.string,
labelId: PropTypes.string,
toogleStyle: PropTypes.object,
toogleImage: PropTypes.string,
state: PropTypes.object,
dropdownClass: PropTypes.string,
tabIndex: PropTypes.number,
badgeValue: PropTypes.number,
containerNode: PropTypes.element

};

export default DropdownList;
74 changes: 74 additions & 0 deletions geonode_mapstore_client/client/js/components/Menu/Menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2021, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import PropTypes from 'prop-types';
import MenuItem from './MenuItem';

/**
* Menu component
* @name Menu
* @memberof components.Menu
* @prop {array} items list of menu item
* @prop {string} containerClass css class of list container
* @prop {string} childrenClass css class of item in list
* @prop {string} query string to build the query url in case of link item
* @prop {function} formatHref function to format the href in case of link item
* @example
* <Menu items={items} user={user} />
*
*/


const Menu = ({
items,
containerClass,
childrenClass,
query,
formatHref
}) => {

return (
<ul className={`${containerClass}`}>
{items
.map((item, idx) => {
return (
<li key={idx}>
<MenuItem
item={{ ...item, id: item.id || idx }}
menuItemsProps={{
query,
formatHref
}}
classItem={childrenClass}
/>
</li>
);
})}
</ul>
);
};

Menu.propTypes = {
items: PropTypes.array.isRequired,
containerClass: PropTypes.string,
childrenClass: PropTypes.string,
query: PropTypes.string,
formatHref: PropTypes.func

};

Menu.defaultProps = {
items: [],
query: {},
user: undefined,
formatHref: () => '#'
};


export default Menu;
119 changes: 119 additions & 0 deletions geonode_mapstore_client/client/js/components/Menu/MenuItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright 2021, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/


import React from 'react';
import PropTypes from 'prop-types';
import castArray from 'lodash/castArray';
import isNil from 'lodash/isNil';
import Tag from '@js/components/home/Tag';
import { Badge, Nav } from 'react-bootstrap-v1';
import Message from '@mapstore/framework/components/I18N/Message';
import DropdownList from './DropdownList';
const isValidBadgeValue = value => !!(value !== '' && !isNil(value));
/**
* Menu item component
* @name MenuItem
* @memberof components.Menu.MenuItem
* @prop {object} item the item menu
* @prop {object} menuItemsProps contains pros to apply to items, to manage single permissions, build href and query url
* @prop {node} containerNode the node to append the child element into a DOM
* @prop {number} tabIndex define navigation order
* @prop {boolean} draggable is element is draggable
* @prop {function} classItem class to apply to the Item
* @example
* <MenuItem
* tabIndex={tabindex}
* item={{ ...item, id: item.id || idx }}
* draggable={false}
* menuItemsProps={menuItemsProps}
* containerNode={containerNode.current}
* />
*
*/

const MenuItem = ({ item, menuItemsProps, containerNode, tabIndex, draggable, classItem }) => {

const { formatHref, query } = menuItemsProps;
const { id, type, label, labelId = '', items = [], href, style, badge = '', image, subType } = item;

const badgeValue = badge;
if (type === 'dropdown') {
return (<DropdownList
id={id}
items={items}
label={label}
labelId={labelId}
toogleStyle={style}
toogleImage={image}
dropdownClass={classItem}
tabIndex={tabIndex}
badgeValue={badgeValue}
containerNode={containerNode}
/>);
}

if (type === 'link') {
if (subType === 'tag') {
return (
<Tag
tabIndex={tabIndex}
draggable={draggable}
href={href}
style={style}

>
{labelId && <Message msgId={labelId} /> || label}
{isValidBadgeValue(badgeValue) && <Badge>{badgeValue}</Badge>}
</Tag>
);
}

return (
<Nav.Link href={href}>{labelId && <Message msgId={labelId} /> || label}</Nav.Link>
);

}

if (type === 'divider') {
return <div className="gn-menu-index-divider" style={style}></div>;
}
if (type === 'filter') {
const active = castArray(query.f || []).find(value => value === item.id);
return (
<Tag
tabIndex={tabIndex}
draggable={draggable}
active={active}
style={style}
href={formatHref({
query: { f: item.id },
replaceQuery: active ? false : true
})}
>
{labelId && <Message msgId={labelId} /> || label}
{isValidBadgeValue(badgeValue) && <Badge>{badgeValue}</Badge>}
</Tag>
);
}
return null;


};

MenuItem.propTypes = {
item: PropTypes.object.isRequired,
menuItemsProps: PropTypes.object.isRequired,
containerNode: PropTypes.element,
tabIndex: PropTypes.number,
draggable: PropTypes.bool,
classItem: PropTypes.string

};

export default MenuItem;
Loading

0 comments on commit a629631

Please sign in to comment.