bookbrainz/bookbrainz-site

View on GitHub
src/client/entity-editor/relationship-editor/relationship.tsx

Summary

Maintainability
A
35 mins
Test Coverage
/*
 * Copyright (C) 2018  Ben Ockmore
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

import * as React from 'react';

import type {Attribute, RelationshipType, Entity as _Entity} from './types';
import {OverlayTrigger, Tooltip} from 'react-bootstrap';
import Entity from '../common/entity';
import RelationshipAttribute from './relationship-attribute';
import _ from 'lodash';
import {getEntityLink} from '../../../common/helpers/utils';


function getEntityObjectForDisplay(entity: _Entity, makeLink: boolean) {
    const link = makeLink && entity.bbid &&
        getEntityLink({bbid: entity.bbid, type: entity.type});
    let disambiguation = _.get(entity, ['disambiguation']);
    if (_.has(disambiguation, 'comment')) {
        disambiguation = disambiguation.comment;
    }
    return {
        disambiguation,
        link,
        text: _.get(entity, ['defaultAlias', 'name']),
        type: entity.type,
        unnamedText: entity.bbid ? '(unnamed)' : 'New Entity'
    };
}

type Relationship = {
    link: boolean, // eslint-disable-line react/require-default-props
    contextEntity: _Entity | null | undefined, // eslint-disable-line react/require-default-props
    sourceEntity: _Entity,
    targetEntity: _Entity,
    attributes?: Array<Attribute>,
    showAttributes?: boolean,
    relationshipType: RelationshipType
};
export {Relationship as RelationshipType};
type RelationshipProps = Relationship & {Parent?:React.FunctionComponent<any>};

function Relationship({Parent, ...props}: RelationshipProps) {
    const {contextEntity, link, relationshipType, sourceEntity, attributes, showAttributes, targetEntity, ...rest} = props;
    const {depth, description, id, linkPhrase, reverseLinkPhrase} = relationshipType;

    const reversed = contextEntity &&
        (_.get(contextEntity, 'bbid') === _.get(targetEntity, 'bbid'));

    const sourceObject = getEntityObjectForDisplay(
        reversed ? targetEntity : sourceEntity, link
    );
    const targetObject = getEntityObjectForDisplay(
        reversed ? sourceEntity : targetEntity, link
    );

    const usedLinkPhrase = reversed ? reverseLinkPhrase : linkPhrase;

    // If there is a depth structure, indent accordingly by setting a margin on the left
    let indentationClass = '';
    if (typeof depth !== 'undefined') {
        indentationClass = `margin-left-d${8 * depth}`;
    }
    return (
        <OverlayTrigger
            delay={50}
            overlay={<Tooltip id={`tooltip-${id}`}>{description}</Tooltip>}
            placement="bottom"
        >
            <Parent {...rest}>
                <div aria-label={description} className={indentationClass}>
                    {depth > 0 && <div className="hierarchy-arrow"/>}
                    <Entity {...sourceObject}/>
                    {` ${usedLinkPhrase} `}
                    <Entity {...targetObject}/>
                    {' '}
                    <RelationshipAttribute attributes={attributes} showAttributes={showAttributes}/>
                </div>
            </Parent>
        </OverlayTrigger>
    );
}
Relationship.defaultProps =
{
    Parent: React.Fragment,
    attributes: [],
    showAttributes: false
};
Relationship.displayName = 'Relationship';
export default Relationship;