Skip to content

Commit

Permalink
Add data layers (#129)
Browse files Browse the repository at this point in the history
- Separate graph nodes vertically (by layer)
- Add layer bands, and highlight them on hover
  • Loading branch information
richardwestenra authored Apr 7, 2020
1 parent d152b0e commit 4e597dd
Show file tree
Hide file tree
Showing 41 changed files with 1,627 additions and 299 deletions.
14 changes: 9 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
},
"dependencies": {
"@quantumblack/kedro-ui": "^1.1.2",
"batching-toposort": "^1.2.0",
"classnames": "^2.2.6",
"d3-fetch": "^1.1.2",
"d3-interpolate-path": "^2.1.0",
Expand All @@ -38,7 +39,7 @@
"d3-shape": "^1.3.5",
"d3-transition": "^1.2.0",
"d3-zoom": "^1.7.3",
"dagre": "^0.8.5",
"dagre": "git+https://github.com/richardwestenra/dagre.git#manual-ranking",
"konami-code": "^0.2.1",
"react-custom-scrollbars": "^4.2.1",
"react-flip-toolkit": "^7.0.7",
Expand Down
22 changes: 22 additions & 0 deletions src/actions/actions.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import animals from '../utils/data/animals.mock';
import {
RESET_DATA,
TOGGLE_LAYERS,
TOGGLE_SIDEBAR,
TOGGLE_TEXT_LABELS,
TOGGLE_THEME,
UPDATE_CHART_SIZE,
UPDATE_FONT_LOADED,
resetData,
toggleLayers,
toggleSidebar,
toggleTextLabels,
toggleTheme,
updateChartSize,
Expand Down Expand Up @@ -36,6 +40,24 @@ describe('actions', () => {
expect(resetData(animals)).toEqual(expectedAction);
});

it('should create an action to toggle whether to show layers', () => {
const visible = false;
const expectedAction = {
type: TOGGLE_LAYERS,
visible
};
expect(toggleLayers(visible)).toEqual(expectedAction);
});

it('should create an action to toggle whether the sidebar is open', () => {
const visible = false;
const expectedAction = {
type: TOGGLE_SIDEBAR,
visible
};
expect(toggleSidebar(visible)).toEqual(expectedAction);
});

it('should create an action to toggle whether a node has been clicked', () => {
const nodeClicked = '12367890';
const expectedAction = {
Expand Down
13 changes: 13 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ export function resetData(data) {
};
}

export const TOGGLE_LAYERS = 'TOGGLE_LAYERS';

/**
* Toggle whether to show layers on/off
* @param {Boolean} layers True if text labels are to be shown
*/
export function toggleLayers(visible) {
return {
type: TOGGLE_LAYERS,
visible
};
}

export const TOGGLE_TEXT_LABELS = 'TOGGLE_TEXT_LABELS';

/**
Expand Down
6 changes: 5 additions & 1 deletion src/components/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class App extends React.Component {

App.propTypes = {
data: PropTypes.oneOfType([
PropTypes.oneOf(['random', 'lorem', 'animals', 'demo', 'json']),
PropTypes.oneOf(['random', 'lorem', 'animals', 'demo', 'json', 'layers']),
PropTypes.shape({
schema_id: PropTypes.string,
edges: PropTypes.array.isRequired,
Expand All @@ -84,6 +84,10 @@ App.propTypes = {
theme: PropTypes.oneOf(['dark', 'light']),
visible: PropTypes.shape({
labelBtn: PropTypes.bool,
layerBtn: PropTypes.bool,
layers: PropTypes.bool,
exportBtn: PropTypes.bool,
sidebar: PropTypes.bool,
themeBtn: PropTypes.bool
})
};
Expand Down
69 changes: 69 additions & 0 deletions src/components/flowchart/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,73 @@ import { select } from 'd3-selection';
import { curveBasis, line } from 'd3-shape';
import icon from './icon';

/**
* Render layer bands
*/
const drawLayers = function() {
const { layers, visibleLayers } = this.props;

this.el.layers = this.el.layerGroup
.selectAll('.layer')
.data(visibleLayers ? layers : [], layer => layer.id);

const enterLayers = this.el.layers
.enter()
.append('rect')
.attr('class', 'layer');

this.el.layers.exit().remove();

this.el.layers = this.el.layers.merge(enterLayers);

this.el.layers
.attr('x', d => d.x)
.attr('y', d => d.y)
.attr('height', d => d.height)
.attr('width', d => d.width);
};

/**
* Render layer name labels
*/
const drawLayerNames = function() {
const {
chartSize: { sidebarWidth = 0 },
layers,
visibleLayers
} = this.props;

this.el.layerNameGroup
.transition('layer-names-sidebar-width')
.duration(this.DURATION)
.style('transform', `translateX(${sidebarWidth}px)`);

this.el.layerNames = this.el.layerNameGroup
.selectAll('.layer-name')
.data(visibleLayers ? layers : [], layer => layer.id);

const enterLayerNames = this.el.layerNames
.enter()
.append('li')
.attr('class', 'layer-name')
.style('opacity', 0)
.transition('enter-layer-names')
.duration(this.DURATION)
.style('opacity', 1);

this.el.layerNames
.exit()
.style('opacity', 1)
.transition('exit-layer-names')
.duration(this.DURATION)
.style('opacity', 0)
.remove();

this.el.layerNames = this.el.layerNames.merge(enterLayerNames);

this.el.layerNames.text(d => d.name).attr('dy', 5);
};

/**
* Render node icons and name labels
*/
Expand Down Expand Up @@ -150,6 +217,8 @@ const drawEdges = function() {
* Render chart to the DOM with D3
*/
const draw = function() {
drawLayers.call(this);
drawLayerNames.call(this);
drawEdges.call(this);
drawNodes.call(this);
};
Expand Down
2 changes: 2 additions & 0 deletions src/components/flowchart/flowchart.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ describe('FlowChart', () => {
chartSize: expect.any(Object),
edges: expect.any(Array),
graphSize: expect.any(Object),
layers: expect.any(Array),
linkedNodes: expect.any(Object),
nodes: expect.any(Array),
textLabels: expect.any(Boolean),
visibleLayers: expect.any(Boolean),
visibleSidebar: expect.any(Boolean),
zoom: expect.any(Object)
};
Expand Down
26 changes: 23 additions & 3 deletions src/components/flowchart/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getLayoutEdges,
getZoomPosition
} from '../../selectors/layout';
import { getLayers } from '../../selectors/layers';
import { getCentralNode, getLinkedNodes } from '../../selectors/linked-nodes';
import draw from './draw';
import './styles/flowchart.css';
Expand All @@ -39,6 +40,8 @@ export class FlowChart extends Component {
this.wrapperRef = React.createRef();
this.edgesRef = React.createRef();
this.nodesRef = React.createRef();
this.layersRef = React.createRef();
this.layerNamesRef = React.createRef();
}

componentDidMount() {
Expand Down Expand Up @@ -72,7 +75,9 @@ export class FlowChart extends Component {
svg: select(this.svgRef.current),
wrapper: select(this.wrapperRef.current),
edgeGroup: select(this.edgesRef.current),
nodeGroup: select(this.nodesRef.current)
nodeGroup: select(this.nodesRef.current),
layerGroup: select(this.layersRef.current),
layerNameGroup: select(this.layerNamesRef.current)
};
}

Expand Down Expand Up @@ -124,7 +129,7 @@ export class FlowChart extends Component {
*/
initZoomBehaviour() {
this.zoomBehaviour = zoom().on('zoom', () => {
const { k: scale } = event.transform;
const { k: scale, y } = event.transform;
const { sidebarWidth } = this.props.chartSize;
const { width, height } = this.props.graphSize;

Expand All @@ -140,6 +145,14 @@ export class FlowChart extends Component {
// Transform the <g> that wraps the chart
this.el.wrapper.attr('transform', event.transform);

// Update layer label y positions
this.el.layerNames
.style('height', d => `${d.height * scale}px`)
.style('transform', d => {
const ty = y + d.y * scale;
return `translateY(${ty}px)`;
});

// Hide the tooltip so it doesn't get misaligned to its node
this.hideTooltip();
});
Expand Down Expand Up @@ -255,7 +268,7 @@ export class FlowChart extends Component {
* Render React elements
*/
render() {
const { outerWidth, outerHeight } = this.props.chartSize;
const { outerWidth = 0, outerHeight = 0 } = this.props.chartSize;
const {
tooltipVisible,
tooltipIsRight,
Expand Down Expand Up @@ -290,6 +303,7 @@ export class FlowChart extends Component {
</marker>
</defs>
<g id="zoom-wrapper" ref={this.wrapperRef}>
<g className="pipeline-flowchart__layers" ref={this.layersRef} />
<g className="pipeline-flowchart__edges" ref={this.edgesRef} />
<g
id="nodes"
Expand All @@ -298,6 +312,10 @@ export class FlowChart extends Component {
/>
</g>
</svg>
<ul
className="pipeline-flowchart__layer-names"
ref={this.layerNamesRef}
/>
<div
className={classnames('pipeline-flowchart__tooltip kedro', {
'tooltip--visible': tooltipVisible,
Expand All @@ -316,9 +334,11 @@ export const mapStateToProps = state => ({
chartSize: getChartSize(state),
edges: getLayoutEdges(state),
graphSize: getGraphSize(state),
layers: getLayers(state),
linkedNodes: getLinkedNodes(state),
nodes: getLayoutNodes(state),
textLabels: state.textLabels,
visibleLayers: state.visible.layers,
visibleSidebar: state.visible.sidebar,
zoom: getZoomPosition(state)
});
Expand Down
1 change: 1 addition & 0 deletions src/components/flowchart/styles/_edges.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import './variables';

.edge path {
pointer-events: none;
fill: none;
stroke-width: 1.5px;

Expand Down
48 changes: 48 additions & 0 deletions src/components/flowchart/styles/_layers.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@import './variables';

.layer {
opacity: 0;
transition: opacity ease 0.5s;

.kui-theme--dark & {
fill: rgba(white, 0.05);
}

.kui-theme--light & {
fill: rgba(black, 0.05);
}

&:hover {
opacity: 1;
}
}

.pipeline-flowchart__layer-names {
position: absolute;
top: 0;
left: 0;
margin: 0;
padding: 0;
list-style: none;
pointer-events: none;
}

.layer-name {
position: absolute;
top: 0;
display: flex;
align-items: center;
width: 130px;
padding-left: 15px;
font-weight: bold;
font-size: 1.6em;
white-space: nowrap;

.kui-theme--dark & {
background: linear-gradient(to right, $color-bg-dark, transparent);
}

.kui-theme--light & {
background: linear-gradient(to right, $color-bg-light, transparent);
}
}
1 change: 1 addition & 0 deletions src/components/flowchart/styles/flowchart.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import './node';
@import './edges';
@import './layers';
@import './tooltip';

.pipeline-flowchart {
Expand Down
Loading

0 comments on commit 4e597dd

Please sign in to comment.