Skip to content

Commit 22fc463

Browse files
authored
[jsx/Panel.js] Improved Panel component with optional multiple views (#8299)
Updated the Panel component to handle views if given. Also refactored the code to be a react functional component instead of a class.
1 parent a903575 commit 22fc463

File tree

8 files changed

+133
-108
lines changed

8 files changed

+133
-108
lines changed

htdocs/bootstrap/css/custom-css.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,16 @@ a.btn.btn-primary:hover:not(.download, .split-nav) {
760760
box-shadow: 2px 3px 4px 1px rgba(0,0,0,0.175);
761761
}
762762

763+
.panel-heading {
764+
display: flex;
765+
align-items: center;
766+
}
767+
768+
.panel-title {
769+
width: 100%;
770+
padding-right: 10px;
771+
}
772+
763773
.panel-default {
764774
border-color: #C3D5DB;
765775
}

jsx/Panel.js

Lines changed: 109 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,140 @@
1+
import React, {useEffect, useState} from 'react';
2+
import PropTypes from 'prop-types';
3+
14
/**
2-
* This file contains React component for Panel
5+
* Panel - a collapsible panel component with optional multiple views.
36
*
47
* @author Alex I.
5-
* @version 1.0.0
8+
* @version 2.0.0
9+
* @param {object} props
10+
* @return {JSX.Element}
611
*/
12+
const Panel = (props) => {
13+
const [collapsed, setCollapsed] = useState(false);
14+
const [activeView, setActiveView] = useState(0);
715

8-
import React, {Component} from 'react';
9-
import PropTypes from 'prop-types';
10-
11-
/**
12-
* Panel component
13-
* Wraps children in a collapsible bootstrap panel
14-
*/
15-
class Panel extends Component {
1616
/**
17-
* @constructor
18-
* @param {object} props - React Component properties
17+
* Similar to componentDidMount and componentDidUpdate.
1918
*/
20-
constructor(props) {
21-
super(props);
22-
23-
this.state = {
24-
collapsed: this.props.initCollapsed,
25-
};
26-
27-
// Initialize panel class based on collapsed status
28-
this.panelClass = (
29-
this.props.initCollapsed ?
30-
'panel-collapse collapse' :
31-
'panel-collapse collapse in'
32-
);
33-
34-
this.toggleCollapsed = this.toggleCollapsed.bind(this);
35-
}
19+
useEffect(() => {
20+
setCollapsed(props.initCollapsed);
21+
}, []);
3622

3723
/**
38-
* Toggle whether this Panel is displayed as collapsed
24+
* Toggle whether panel is displayed as collapsed
3925
*/
40-
toggleCollapsed() {
41-
if (this.props.collapsing) {
42-
this.setState({collapsed: !this.state.collapsed});
26+
const toggleCollapsed = () => {
27+
if (props.collapsing) {
28+
setCollapsed(!collapsed);
4329
}
44-
}
30+
};
4531

4632
/**
47-
* Render the React component
33+
* User clicked a view to display.
4834
*
49-
* @return {object}
35+
* @param {number} index
5036
*/
51-
render() {
52-
// Change arrow direction based on collapse status
53-
let glyphClass = (
54-
this.state.collapsed ?
55-
'glyphicon pull-right glyphicon-chevron-down' :
56-
'glyphicon pull-right glyphicon-chevron-up'
57-
);
37+
const viewClicked = (index) => {
38+
setActiveView(index);
39+
};
5840

59-
const title = this.props.bold ? (
60-
<h3 className={'panel-title'}>
61-
{this.props.title}
62-
</h3>
63-
) : this.props.title;
64-
65-
// Add panel header, if title is set
66-
const panelHeading = this.props.title ? (
67-
<div
68-
className="panel-heading"
69-
onClick={this.toggleCollapsed}
70-
data-toggle={this.props.collapsing ? 'collapse' : null}
71-
data-target={'#' + this.props.id}
72-
data-parent={this.props.parentId ?
73-
'#'+this.props.parentId :
74-
false
75-
}
76-
style={{
77-
cursor: this.props.collapsing ? 'pointer' : 'default',
78-
height: '3em',
79-
fontWeight: 'bold',
80-
}}
81-
>
82-
{title}
83-
{this.props.collapsing ? <span className={glyphClass}/> : ''}
84-
</div>
85-
) : '';
86-
87-
return (
88-
<div className={'panel ' + this.props.class}
89-
style={{height: this.props.panelSize}}
90-
>
91-
{panelHeading}
92-
<div id={this.props.id}
93-
className={this.panelClass}
94-
role='tabpanel'
95-
style={this.props.collapsing ? {} : {height: 'calc(100% - 3em)'}}
96-
>
97-
<div className="panel-body"
98-
style={{...this.props.style, height: this.props.height}}>
99-
{this.props.children}
100-
</div>
41+
// Panel Views (START)
42+
let views = [];
43+
let content = [];
44+
let panelViews;
45+
if (props.views) {
46+
for (const [index, view] of props.views.entries()) {
47+
views.push(
48+
<li key={index}
49+
onClick={() => viewClicked(index)}
50+
className={index === activeView ? 'active' : null}>
51+
<a data-target={`${index}_panel_content`}>
52+
{view['title']}
53+
</a>
54+
</li>
55+
);
56+
content.push(
57+
<div key={index}
58+
id={`${index}_panel_content_${props.id}`}
59+
className={index === activeView ?
60+
`${index}_panel_content` : `${index}_panel_content hidden`}>
61+
{view['content']}
10162
</div>
63+
);
64+
}
65+
panelViews = (
66+
<div className='btn-group views'>
67+
<button type='button'
68+
className='btn btn-default btn-xs dropdown-toggle'
69+
data-toggle='dropdown'>
70+
Views<span className='caret'/>
71+
</button>
72+
<ul className='dropdown-menu pull-right'
73+
role='menu'>
74+
{views}
75+
</ul>
10276
</div>
10377
);
10478
}
105-
}
79+
// Panel Views (END)
10680

81+
// Add panel header, if title is set
82+
const panelHeading = props.title || props.views ? (
83+
<div className='panel-heading'
84+
data-parent={props.parentId
85+
? `#${props.parentId}`
86+
: null}>
87+
<h3 className='panel-title'>
88+
{props.views && props.views[activeView]['title']
89+
? props.views[activeView]['title']
90+
: props.title}
91+
</h3>
92+
{panelViews}
93+
{props.collapsing
94+
? <span className={collapsed ?
95+
'glyphicon glyphicon-chevron-down' :
96+
'glyphicon glyphicon-chevron-up'}
97+
onClick={toggleCollapsed}
98+
data-toggle='collapse'
99+
data-target={`#${props.id}`}
100+
style={{cursor: 'pointer'}}/>
101+
: null}
102+
</div>
103+
) : '';
104+
105+
/**
106+
* Renders the React component.
107+
*
108+
* @return {JSX.Element} - React markup for component.
109+
*/
110+
return (
111+
<div className={`panel ${props.class}`}
112+
style={{height: props.panelSize}}>
113+
{panelHeading}
114+
<div id={props.id}
115+
className={props.collapsed ?
116+
'panel-collapse collapse' :
117+
'panel-collapse collapse in'}
118+
role='tabpanel'
119+
style={{height: 'calc(100% - 3em)'}}>
120+
<div className='panel-body'
121+
style={{...props.style, height: props.height}}>
122+
{content.length > 0 ? content : props.children}
123+
</div>
124+
</div>
125+
</div>
126+
);
127+
};
107128
Panel.propTypes = {
108129
initCollapsed: PropTypes.bool,
130+
collapsed: PropTypes.bool,
109131
parentId: PropTypes.string,
110132
id: PropTypes.string,
111133
height: PropTypes.string,
112134
title: PropTypes.string,
113135
class: PropTypes.string,
136+
children: PropTypes.node,
137+
views: PropTypes.array,
114138
collapsing: PropTypes.bool,
115139
bold: PropTypes.bool,
116140
panelSize: PropTypes.string,
@@ -124,8 +148,6 @@ Panel.defaultProps = {
124148
height: '100%',
125149
class: 'panel-primary',
126150
collapsing: true,
127-
bold: false,
128-
title: '',
129151
};
130152

131153
export default Panel;

modules/dashboard/css/dashboard.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151

5252
.views {
5353
margin-right: 10px;
54-
margin-top: -10px;
5554
}
5655

5756
.welcome {

modules/dashboard/templates/panel.tpl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
{if $title}
33
<div class="panel-heading">
44
<h3 class="panel-title">{$title}</h3>
5-
<span class="pull-right clickable glyphicon glyphicon-chevron-up"></span>
65
{if !empty($menus)}
7-
<div class="pull-right">
86
<div class="btn-group views">
97
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">Views<span class="caret"></span></button>
108
<ul class="dropdown-menu pull-right" role="menu">
@@ -13,8 +11,8 @@
1311
{/foreach}
1412
</ul>
1513
</div>
16-
</div>
1714
{/if}
15+
<span class="clickable glyphicon glyphicon-chevron-up"></span>
1816
</div>
1917
{/if}
2018
<div class="panel-body">

modules/dashboard/test/DashboardTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,21 +358,21 @@ public function testDashboardRecruitmentView()
358358
$views = $this->safeFindElement(
359359
WebDriverBy::Xpath(
360360
"//*[@id='lorisworkspace']/div[1]".
361-
"/div[2]/div[1]/div/div/button"
361+
"/div[2]/div[1]/div/button"
362362
)
363363
);
364364
$views->click();
365365

366366
$assertText1 = $this->safeFindElement(
367367
WebDriverBy::XPath(
368368
"//*[@id='lorisworkspace']/div[1]".
369-
"/div[2]/div[1]/div/div/ul/li[1]/a"
369+
"/div[2]/div[1]/div/ul/li[1]/a"
370370
)
371371
)->getText();
372372
$assertText2 = $this->safeFindElement(
373373
WebDriverBy::XPath(
374374
"//*[@id='lorisworkspace']/div[1]".
375-
"/div[2]/div[1]/div/div/ul/li[2]/a"
375+
"/div[2]/div[1]/div/ul/li[2]/a"
376376
)
377377
)->getText();
378378
$this->assertStringContainsString("View overall recruitment", $assertText1);

modules/imaging_browser/css/imaging_browser.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ h3 {
7575

7676
.views {
7777
margin-right: 10px;
78-
margin-top: -2px;
7978
}
8079

8180
.mri-second-row-panel {

modules/imaging_browser/jsx/ImagePanel.js

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,15 @@ class ImagePanelHeader extends Component {
5959
</span>;
6060
}
6161
let headerButton = (
62-
<div className="pull-right">
63-
<div className="btn-group views">
64-
<button
65-
type="button"
66-
className="btn btn-default btn-xs dropdown-toggle"
67-
onClick={this.props.onToggleHeaders}
68-
aria-expanded={this.props.HeadersExpanded}>
69-
Header Info
70-
</button>
71-
<span className="caret"></span>
72-
</div>
62+
<div className="btn-group views">
63+
<button
64+
type="button"
65+
className="btn btn-default btn-xs dropdown-toggle"
66+
onClick={this.props.onToggleHeaders}
67+
aria-expanded={this.props.HeadersExpanded}>
68+
Header Info
69+
</button>
70+
<span className="caret"></span>
7371
</div>
7472
);
7573
return (
@@ -84,8 +82,8 @@ class ImagePanelHeader extends Component {
8482
{this.props.Filename}
8583
</h3>
8684
{QCStatusLabel}
87-
{arrow}
8885
{headerButton}
86+
{arrow}
8987
</div>
9088
);
9189
}

modules/issue_tracker/css/issue_tracker.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ h3 {
5454

5555
.views {
5656
margin-right: 10px;
57-
margin-top: -2px;
5857
}
5958

6059
.btn-volume-viewer {

0 commit comments

Comments
 (0)