Skip to content

Commit

Permalink
feat: add labels customization in CytoViz NodeData component
Browse files Browse the repository at this point in the history
Tables header and attributes labels can now be provided with
the 'labels' prop in NodeData
  • Loading branch information
csm-thu committed Mar 2, 2022
1 parent a6174ec commit 99730df
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 24 deletions.
49 changes: 33 additions & 16 deletions src/charts/CytoViz/CytoViz.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export const CytoViz = (props) => {
const { cytoscapeStylesheet, defaultSettings, elements, extraLayouts, labels, loading, getNodeDetails, bubblesets } =
props;

let getNodeDetailsCallback = getNodeDetails;
if (!getNodeDetailsCallback) {
// eslint-disable-next-line react/display-name
getNodeDetailsCallback = (node) => <NodeData data={node.data()} labels={labels.nodeData} />;
getNodeDetailsCallback.displayName = 'NodeData';
}

// Layout
const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
const [currentDrawerTab, setCurrentDrawerTab] = React.useState(0);
Expand Down Expand Up @@ -76,7 +83,7 @@ export const CytoViz = (props) => {
// Init node selection behavior
cytoscapeRef.on('select', 'node', function (e) {
const selectedNode = e.target;
setCurrentNodeDetails(getNodeDetails(selectedNode));
setCurrentNodeDetails(getNodeDetailsCallback(selectedNode));
});
cytoscapeRef.on('unselect', 'node', function (e) {
setCurrentNodeDetails(null);
Expand All @@ -86,7 +93,7 @@ export const CytoViz = (props) => {
const selectedNode = e.target;
setCurrentDrawerTab(0);
setIsDrawerOpen(true);
setCurrentNodeDetails(getNodeDetails(selectedNode));
setCurrentNodeDetails(getNodeDetailsCallback(selectedNode));
});

// Init bubblesets
Expand Down Expand Up @@ -287,16 +294,38 @@ CytoViz.propTypes = {
spacingFactor: 'string',
zoomLimits: 'string',
}
nodeData: {
dictKey: 'string',
dictValue: 'string',
}
}
* </pre>
*/
labels: PropTypes.object,
/**
* Array of cytoscape elements to display
* Boolean defining whether or not the data are loading.
While loading is true, a spinner is displayed instead of the cytoscape component.
*/
loading: PropTypes.bool,
};

const DEFAULT_LABELS = {
elementDetails: 'Details',
loading: 'Loading...',
noSelectedNode: 'Select a node to view its data',
settings: {
compactMode: 'Compact layout',
layout: 'Layout',
title: 'Settings',
spacingFactor: 'Spacing factor',
zoomLimits: 'Min & max zoom',
},
nodeData: {
dictKey: 'Key',
dictValue: 'Value',
},
};

CytoViz.defaultProps = {
cytoscapeStylesheet: [],
defaultSettings: {
Expand All @@ -307,19 +336,7 @@ CytoViz.defaultProps = {
spacingFactor: 1,
},
extraLayouts: {},
getNodeDetails: (node) => <NodeData data={node.data()} />,
groups: {},
labels: {
elementDetails: 'Details',
loading: 'Loading...',
noSelectedNode: 'Select a node to view its data',
settings: {
compactMode: 'Compact layout',
layout: 'Layout',
title: 'Settings',
spacingFactor: 'Spacing factor',
zoomLimits: 'Min & max zoom',
},
},
labels: DEFAULT_LABELS,
loading: false,
};
27 changes: 19 additions & 8 deletions src/charts/CytoViz/components/NodeData/NodeData.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@ import React from 'react';
import PropTypes from 'prop-types';
import useStyles from './style';

const _generateAttributeDetails = (classes, attributeName, attributeValue) => {
const attributesToIgnore = ['label', 'Label', 'parent'];
const _generateAttributeDetails = (classes, labels, attributeName, attributeValue) => {
const attributeLabel = labels?.attributes?.[attributeName] || attributeName;
const attributesToIgnore = ['label', 'Label', 'parent', 'source', 'target'];
if (attributesToIgnore.indexOf(attributeName) !== -1) {
return null;
}
if (typeof attributeValue === 'object' && 0 in attributeValue) {
// List represented as an object
return (
<div key={attributeName} className={classes.attributeColumnContainer}>
<div className={classes.attributeLabel}>{attributeName}:</div>
<div className={classes.attributeLabel}>{attributeLabel}:</div>
<div className={classes.tableContainer}>
<table className={classes.table}>
<thead>
<tr>
<th className={classes.th}>Iteration</th>
<th className={classes.th}>Value</th>
<th className={classes.th}>{labels.dictKey}</th>
<th className={classes.th}>{labels.dictValue}</th>
</tr>
</thead>
<tbody>
Expand All @@ -39,7 +40,7 @@ const _generateAttributeDetails = (classes, attributeName, attributeValue) => {
// List represented as an object
return (
<div key={attributeName} className={classes.attributeRowContainer}>
<span className={classes.attributeLabel}>{attributeName}:</span>
<span className={classes.attributeLabel}>{attributeLabel}:</span>
<span className={classes.attributeValue}>{JSON.stringify(attributeValue)}</span>
</div>
);
Expand All @@ -48,20 +49,30 @@ const _generateAttributeDetails = (classes, attributeName, attributeValue) => {

const NodeData = (props) => {
const classes = useStyles();
const { data } = props;
const { data, labels } = props;
if (!data) {
return 'No data to display for this node.';
}

return (
<div className={classes.nodeDetailsContainer}>
{Object.keys(data).map((key) => _generateAttributeDetails(classes, key, data[key]))}
{Object.keys(data).map((key) => _generateAttributeDetails(classes, labels, key, data[key]))}
</div>
);
};

NodeData.propTypes = {
data: PropTypes.object,
labels: PropTypes.object,
};

NodeData.defaultProps = {
data: PropTypes.object,
labels: {
dictKey: 'Key',
dictValue: 'Value',
attributes: {},
},
};

export default NodeData;

0 comments on commit 99730df

Please sign in to comment.