Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #10785 Review of footer plugin #10822

Merged
merged 5 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/developer-guide/mapstore-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,37 @@ This is a list of things to check if you want to update from a previous version

## Migration from 2024.02.00 to 2025.01.00

### Footer plugin configuration changes

The Footer plugin has been refactored and some properties have been removed:

- `cfg.logo` is not available anymore in favor of translation html snippet
- translation message identifier `home.footerDescription` is not used anymore in the footer by default

It is possible to replicate the old footer structure for existing project that want to keep the homepage footer information as before with the following configurations:

1. configure the new Footer plugin in `localConfig.json` as follow:

```js
{
"name": "Footer",
"cfg": {
"hideMenuItems": true,
"customFooter": true,
"customFooterMessageId": "home.footerDescription" // by default is using home.footerCustomHTML
}
}
```

2. update the `home.footerDescription` translation by adding the desired html structure, eg:

```js
{
"home": {
"footerDescription": "<footer class=\"ms-flex-box _flex _flex-center-h _padding-md\"><div> ...my previous message </div></footer>"
}
}

### HomeDescription plugin configuration changes

The HomeDescription plugin has been refactored and a property has been removed:
Expand Down
5 changes: 3 additions & 2 deletions web/client/configs/localConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,7 @@
}
},
{ "name": "Footer"},
{ "name": "About" },
{
"name": "Cookie",
"cfg": {
Expand Down Expand Up @@ -1094,7 +1095,7 @@
}
}
],
"manager": ["Header", "Redirect", "Manager", "Home", "UserManager", "GroupManager", "Footer"],
"context-manager": ["Header", "Redirect", "Home", "ContextManager", "Footer"]
"manager": ["Header", "Redirect", "Manager", "Home", "UserManager", "GroupManager", "Footer", { "name": "About" }],
"context-manager": ["Header", "Redirect", "Home", "ContextManager", "Footer", { "name": "About" }]
}
}
202 changes: 192 additions & 10 deletions web/client/plugins/ResourcesCatalog/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,207 @@
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import { createPlugin } from "../../utils/PluginsUtils";
import Menu from './components/Menu';
import Button from './components/Button';
import Spinner from './components/Spinner';
import Icon from './components/Icon';
import HTML from '../../components/I18N/HTML';
import Text from './components/Text';
import Message from '../../components/I18N/Message';
import FlexBox from './components/FlexBox';
import usePluginItems from '../../hooks/usePluginItems';
import { withResizeDetector } from 'react-resize-detector';
function FooterMenuItem({
className,
loading,
glyph,
iconType,
labelId,
onClick,
label
}) {
return (
<li>
<Button
onClick={onClick}
className={className}
>
{loading ? <Spinner /> : <Icon glyph={glyph} type={iconType} />}
{' '}
{labelId ? <Message msgId={labelId} /> : label}
</Button>
</li>
);
}

function Footer({
FooterMenuItem.propTypes = {
className: PropTypes.string,
loading: PropTypes.bool,
glyph: PropTypes.string,
iconType: PropTypes.string,
labelId: PropTypes.string,
onClick: PropTypes.func
};

}) {
FooterMenuItem.defaultProps = {
iconType: 'glyphicon',
onClick: () => { }
};

/**
* This plugin shows the footer
* @memberof plugins
* @class
* @name Footer
* @prop {boolean} cfg.customFooter params that can be used to render a custom html to be used instead of the default one
* @prop {string} cfg.customFooterMessageId replace custom footer translations message identifier
* @prop {object[]} cfg.menuItems list of menu items objects
* @prop {boolean} cfg.hideMenuItems hide menu items menu
* @prop {object[]} items this property contains the items injected from the other plugins,
* using the `containers` option in the plugin that want to inject new menu items.
* ```javascript
* const MyMenuItemComponent = connect(selector, { onActivateTool })(({
* component, // default component that provides a consistent UI (see BrandNavbarMenuItem in BrandNavbar plugin for props)
* variant, // one of style variant (primary, success, danger or warning)
* size, // button size
* className, // custom class name provided by configuration
* onActivateTool, // example of a custom connected action
* }) => {
* const ItemComponent = component;
* return (
* <ItemComponent
* className="my-class-name"
* loading={false}
* glyph="heart"
* iconType="glyphicon"
* labelId="myMessageId"
* onClick={() => onActivateTool()}
* />
* );
* });
* createPlugin(
* 'MyPlugin',
* {
* containers: {
* Footer: {
* name: "TOOLNAME", // a name for the current tool.
* target: 'menu',
* Component: MyMenuItemComponent
* },
* // ...
* ```
* @example
* {
* "name": "Footer",
* "cfg": {
* "menuItems": [
* {
* "type": "link",
* "href": "/my-link",
* "target": "blank",
* "glyph": "heart",
* "labelId": "myMessageId",
* "variant": "default"
* },
* {
* "type": "logo",
* "href": "/my-link",
* "target": "blank",
* "src": "/my-image.jpg",
* "style": {}
* },
* {
* "type": "button",
* "href": "/my-link",
* "target": "blank",
* "glyph": "heart",
* "iconType": "glyphicon",
* "tooltipId": "myMessageId",
* "variant": "default",
* "square": true
* },
* {
* "type": "divider"
* },
* {
* "type": "message",
* "labelId": "myTranslationMessageId"
* }
* ]
* }
* }
*/
function Footer({
menuItems: menuItemsProp,
hideMenuItems,
items,
customFooter,
customFooterMessageId
}, context) {
const { loadedPlugins } = context;
const ref = useRef();
const configuredItems = usePluginItems({ items, loadedPlugins });
const pluginMenuItems = configuredItems.filter(({ target }) => target === 'menu').map(item => ({ ...item, type: 'plugin' }));
const menuItems = [
...menuItemsProp.map((menuItem, idx) => ({ ...menuItem, position: idx + 1 })),
...pluginMenuItems
].sort((a, b) => a.position - b.position);
return (
<div className="ms-footer _padding-tb-lg _padding-lr-md">
<Text textAlign="center">
<HTML msgId="home.footerDescription"/>
</Text>
</div>
<>
{customFooter ? <HTML msgId={customFooterMessageId} /> : null}
{!hideMenuItems || menuItems.length === 0 ? <>
<div style={{ height: ref?.current?.clientHeight }}></div>
<FlexBox ref={ref} component="footer" id="ms-footer" className="ms-footer _padding-xs" centerChildren>
<Menu
centerChildrenVertically
gap="md"
alignRight
wrap
menuItemComponent={FooterMenuItem}
items={menuItems}
/>
</FlexBox>
</> : false}
</>
);
}

Footer.propTypes = {
menuItems: PropTypes.array,
hideMenuItems: PropTypes.bool,
items: PropTypes.array,
customFooter: PropTypes.bool,
customFooterMessageId: PropTypes.string
};

Footer.contextTypes = {
loadedPlugins: PropTypes.object
};

Footer.defaultProps = {
menuItems: [
{
type: 'link',
href: "https://docs.mapstore.geosolutionsgroup.com/",
target: 'blank',
glyph: 'book',
labelId: 'resourcesCatalog.documentation'
},
{
type: 'link',
href: 'https://github.com/geosolutions-it/MapStore2',
target: 'blank',
label: 'GitHub',
glyph: 'github'
}
],
customFooter: false,
customFooterMessageId: 'home.footerCustomHTML'
};


export default createPlugin('Footer', {
component: Footer
component: withResizeDetector(Footer)
});
2 changes: 1 addition & 1 deletion web/client/plugins/ResourcesCatalog/ResourceDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function ResourceDetails({
targetSelector,
headerNodeSelector = '#ms-brand-navbar',
navbarNodeSelector = '',
footerNodeSelector = '',
footerNodeSelector = '#ms-footer',
width,
height,
show,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function ResourcesFiltersForm({
targetSelector,
headerNodeSelector = '#ms-brand-navbar',
navbarNodeSelector = '',
footerNodeSelector = '',
footerNodeSelector = '#ms-footer',
width,
height,
user
Expand Down
51 changes: 14 additions & 37 deletions web/client/plugins/ResourcesCatalog/components/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,21 @@ import PropTypes from 'prop-types';
import MenuItem from './MenuItem';
import FlexBox from './FlexBox';

/**
* @module components/Menu
*/

/**
* Menu component
* @name 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} />
*
* @prop {object[]} items list of menu item
* @prop {string} className custom class name
* @prop {string} size button size, one of `xs`, `sm`, `md` or `xl`
* @prop {bool} alignRight align the dropdown menu to the right
* @prop {string} variant style for the button, one of `undefined`, `default` or `primary`
* @prop {any} menuItemComponent a default component to be passed as a prop to a custom `item.Component`
*/
const Menu = forwardRef(({
items,
containerClass,
childrenClass,
query,
formatHref,
size,
alignRight,
variant,
resourceName,
className,
menuItemComponent,
...props
Expand All @@ -50,23 +39,16 @@ const Menu = forwardRef(({
component="ul"
ref={ref}
className={className}
classNames={[containerClass]}
>
{items
.map((item, idx) => {
return (
<MenuItem
key={idx}
variant={item.variant || variant}
item={{ ...item, id: item.id || idx }}
item={{ ...item, id: item.id !== undefined ? item.id : idx }}
size={item.size || size}
alignRight={alignRight}
menuItemsProps={{
query,
formatHref
}}
classItem={childrenClass}
resourceName={resourceName}
menuItemComponent={menuItemComponent}
/>
);
Expand All @@ -76,21 +58,16 @@ const Menu = forwardRef(({
});

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

items: PropTypes.array,
size: PropTypes.string,
alignRight: PropTypes.bool,
variant: PropTypes.string,
className: PropTypes.string,
menuItemComponent: PropTypes.any
};

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


export default Menu;
Loading