Skip to content

Commit

Permalink
UI improvements to cards and preview panel (#620)
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidQuartz authored Nov 25, 2021
1 parent 7f57ae2 commit 486d8d9
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 104 deletions.
19 changes: 19 additions & 0 deletions geonode_mapstore_client/client/js/components/ALink/ALink.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';

function ALink({ href, readOnly, children }) {
return readOnly ? children : <a href={href}>{children}</a>;
}

ALink.PropTypes = {
href: PropTypes.string,
readOnly: PropTypes.bool.isRequired,
children: PropTypes.any
};

ALink.defaultProps = {
href: '',
readOnly: false
};

export default ALink;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ALink';
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getUserName } from '@js/utils/SearchUtils';
import ALink from '@js/components/ALink';

function AuthorInfo({ resource, readOnly, formatHref, ...props}) {

return (<p className="card-text gn-card-user" {...props}>
{resource.owner?.avatar &&
<img src={resource.owner.avatar} alt={getUserName(resource.owner)} className="gn-card-author-image" />}
<ALink readOnly={readOnly} href={formatHref({
query: {
'filter{owner.username.in}': resource.owner?.username
}
})}>{resource.owner && getUserName(resource.owner)}</ALink>
</p>);
}

AuthorInfo.PropTypes = {
resource: PropTypes.object,
readOnly: PropTypes.bool,
formatHref: PropTypes.func,
props: PropTypes.any
};

AuthorInfo.defaultProps = {
resource: {},
readOnly: false,
formatHref: () => '#'
};

export default AuthorInfo;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './AuthorInfo';
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import Spinner from '@js/components/Spinner';
import Message from '@mapstore/framework/components/I18N/Message';
import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
import moment from 'moment';
import { getUserName } from '@js/utils/SearchUtils';
import { getResourceTypesInfo, getMetadataDetailUrl } from '@js/utils/ResourceUtils';
import debounce from 'lodash/debounce';
import CopyToClipboardCmp from 'react-copy-to-clipboard';
import { TextEditable, ThumbnailEditable } from '@js/components/ContentsEditable/';
import ResourceStatus from '@js/components/ResourceStatus/';
import AuthorInfo from '@js/components/AuthorInfo/AuthorInfo';

const CopyToClipboard = tooltip(CopyToClipboardCmp);

Expand Down Expand Up @@ -118,6 +118,14 @@ const DefinitionListMoreItem = ({itemslist, extraItemsList}) => {
);
};

const extractResourceString = (res) => {
const resourceFirstLetter = res?.charAt(0).toUpperCase();
const restOfResourceLetters = res?.slice(1);
const resourceTypeString = resourceFirstLetter + restOfResourceLetters;
return resourceTypeString;

};

function DetailsPanel({
resource,
formatHref,
Expand Down Expand Up @@ -464,7 +472,7 @@ function DetailsPanel({
</CopyToClipboard>
}
{detailUrl && !editThumbnail && <Button
variant="default"
variant="primary"
href={(resourceCanPreviewed) ? detailUrl : metadataDetailUrl}
rel="noopener noreferrer">
<Message msgId={`gnhome.view${((resourceCanPreviewed) ? name : 'Metadata')}`} />
Expand All @@ -475,19 +483,20 @@ function DetailsPanel({

</div>
<ResourceStatus resource={resource} />
{<p>
{resource?.owner && <><a href={formatHref({
pathname: editTitle && '/search/filter/',
query: {
'filter{owner.username.in}': resource.owner.username
}
})}>{getUserName(resource.owner)}</a></>}
{<p className="gn-details-panel-meta-text">
{resource?.owner && <AuthorInfo resource={resource} formatHref={formatHref} style={{margin: 0}} />}
{(resource?.date_type && resource?.date)
&& <>{' '}/{' '}{moment(resource.date).format('MMMM Do YYYY')}</>}
&& <div className="gn-details-panel-meta-date">{' '}/{' '}{moment(resource.date).format('MMMM Do YYYY')}</div>}
</p>
}

<EditAbstract disabled={!activeEditMode} tagName="span" abstract={resource?.abstract} onEdit={editAbstract} />
<EditAbstract disabled={!activeEditMode} tagName="span" abstract={resource?.abstract} onEdit={editAbstract} />
<p className="gn-details-panel-type"><Message msgId="gnhome.reasourceType" />: <a href={formatHref({
query: {
'filter{resource_type.in}': resource.resource_type
}
})} title="Search all similar resources">{extractResourceString(resource.resource_type)}</a>
</p>
<p>
{resource?.category?.identifier && <div>
<Message msgId="gnhome.category" />:{' '}
Expand All @@ -496,7 +505,7 @@ function DetailsPanel({
query: {
'filter{category.identifier.in}': resource.category.identifier
}
})}>{resource.category.identifier}</a>
})}>{extractResourceString(resource.category.identifier)}</a>
</div>}
</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import FaIcon from '@js/components/FaIcon';
import Dropdown from '@js/components/Dropdown';
import Button from '@js/components/Button';
import Spinner from '@js/components/Spinner';
import { getUserName } from '@js/utils/SearchUtils';
import { getResourceTypesInfo, getMetadataDetailUrl } from '@js/utils/ResourceUtils';
import ResourceStatus from '@js/components/ResourceStatus';
import ALink from '@js/components/ALink';
import AuthorInfo from '../AuthorInfo/AuthorInfo';

function ALink({ href, readOnly, children }) {
return readOnly ? children : <a href={href}>{children}</a>;
}

const ResourceCard = forwardRef(({
data,
Expand Down Expand Up @@ -60,94 +58,92 @@ const ResourceCard = forwardRef(({
className={`${(layoutCardsStyle === 'list') ? 'card-img-left' : 'card-img-top'}`}
src={res.thumbnail_url}
/>
<div className="card-body">
<div className="card-title">
{(icon && !loading) &&
<>
<ALink
readOnly={readOnly}
href={formatHref({
query: {
'filter{resource_type.in}': res.resource_type
}
})}>
<FaIcon name={icon} />
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
<div className="card-body">
<div className="card-title">
{(icon && !loading) &&
<>
<ALink
readOnly={readOnly}
href={formatHref({
query: {
'filter{resource_type.in}': res.resource_type
}
})}>
<FaIcon name={icon} />
</ALink>
</>}
{loading && <Spinner />}
<ALink readOnly={readOnly} href={formatHref({
pathname: `/detail/${res.resource_type}/${res.pk}`
})}>
{res.title}
</ALink>
</>}
{loading && <Spinner />}
<ALink readOnly={readOnly} href={formatHref({
pathname: `/detail/${res.resource_type}/${res.pk}`
})}>
{res.title}
</ALink>
</div>
<ResourceStatus resource={res}/>
<p
className="card-text gn-card-description"
>
{res.raw_abstract ? res.raw_abstract : '...'}
</p>
<p
className="card-text gn-card-user"
>
<Message msgId="gnhome.author"/>: <ALink readOnly={readOnly} href={formatHref({
query: {
'filter{owner.username.in}': res?.owner?.username
}
})}>{getUserName(res.owner)}</ALink>
</p>
</div>
<div className="gn-card-actions" >
{!readOnly && (detailUrl || metadataDetailUrl) &&
<div className={`${(options && options.length === 0) ? 'gn-card-view-editor-right' : 'gn-card-view-editor' }`}>
<Button
variant="default"
href={(resourceCanPreviewed) ? detailUrl : metadataDetailUrl}
rel="noopener noreferrer"><FaIcon name={'edit'} />
<Message msgId={`gnhome.view`} />

</Button>
</div>
}
{(!readOnly && options && options.length > 0) && <Dropdown
className="gn-card-options"
pullRight
>
<Dropdown.Toggle
id={`gn-card-options-${res.pk}`}
variant="default"
size="sm"
noCaret
</div>
<ResourceStatus resource={res} />
<p
className="card-text gn-card-description"
>
<FaIcon name="ellipsis-h" />
</Dropdown.Toggle>
<Dropdown.Menu className={`gn-card-dropdown`} >
{options
.map((opt) => {
if (opt.type === 'button' && actions[opt.action]) {
return (
<Dropdown.Item
key={opt.action}
onClick={() => onAction(actions[opt.action], [res])}
>
<FaIcon name={opt.icon} /> <Message msgId={opt.labelId}/>
</Dropdown.Item>
);
}
{res.raw_abstract ? res.raw_abstract : '...'}
</p>
</div>
<div className="gn-footer-wrapper">
<div className="gn-card-footer" style={{ padding: (options && options.length === 0) ? '0 0.25rem 0 0.5rem' : '0 0.5rem' }}>
<AuthorInfo resource={res} readOnly={readOnly} formatHref={formatHref} />
<div className="gn-card-actions" >
{
!readOnly && (detailUrl || metadataDetailUrl) &&
<div className={`${(options && options.length === 0) ? 'gn-card-view-editor-right' : 'gn-card-view-editor'}`}>
<Button
variant="primary"
href={(resourceCanPreviewed) ? detailUrl : metadataDetailUrl}
rel="noopener noreferrer" >
<Message msgId={`gnhome.view`} />
</Button>
</div>
}
{(!readOnly && options && options.length > 0) && <Dropdown
className="gn-card-options"
pullRight
>
<Dropdown.Toggle
id={`gn-card-options-${res.pk}`}
variant="default"
size="sm"
noCaret
>
<FaIcon name="ellipsis-h" />
</Dropdown.Toggle>
<Dropdown.Menu className={`gn-card-dropdown`} >
{options
.map((opt) => {
if (opt.type === 'button' && actions[opt.action]) {
return (
<Dropdown.Item
key={opt.action}
onClick={() => onAction(actions[opt.action], [res])}
>
<FaIcon name={opt.icon} /> <Message msgId={opt.labelId} />
</Dropdown.Item>
);
}

return (
<Dropdown.Item
key={opt.href}
href={buildHrefByTemplate(res, opt.href)}
>
<FaIcon name={opt.icon} /> <Message msgId={opt.labelId}/>
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>
return (
<Dropdown.Item
key={opt.href}
href={buildHrefByTemplate(res, opt.href)}
>
<FaIcon name={opt.icon} /> <Message msgId={opt.labelId} />
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>

}
}
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2016, 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 ReactDOM from 'react-dom';
import expect from 'expect';
import ALink from '../ALink';

describe('ALink test', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('render ALink', () => {
ReactDOM.render(<ALink />, document.getElementById("container"));
const aLink = document.querySelector('a');
expect(aLink).toExist();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2016, 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 ReactDOM from 'react-dom';
import expect from 'expect';
import AuthorInfo from '../AuthorInfo';

describe('AuthorInfo test', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('render AuthorInfo', () => {
ReactDOM.render(<AuthorInfo />, document.getElementById("container"));
const authorInfo = document.querySelector('.gn-card-user');
expect(authorInfo).toExist();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"clearFilters": "Filter löschen",
"categoriesPlaceholder": "Wählen sie kategorien aus",
"resourceTypes": "Ressourcentypen",
"reasourceType": "Hulpbron type",
"resourceTypesPlaceholder": "Ressourcentypen auswählen",
"keywords": "Schlüsselwörter",
"keywordsPlaceholder": "Schlüsselwörter auswählen",
Expand Down
Loading

0 comments on commit 486d8d9

Please sign in to comment.