Skip to content

Commit d876589

Browse files
mareklibrapriley86
authored andcommitted
feat(ConsoleSelector): introduce ConsoleSelector component (#606)
* feat(ConsoleSelector): introduce ConsoleSelector component affects: @patternfly/react-console The component unifies composition of various console components for server/virtual machine remote access (recently via serial console or VNC). * update paddings * fix(VncConsole): sync .scss and .less and minor fixes Fixes (as top-level Toolbar is gone): - unit tests - padding of VncConsole actions * feat(AccessConsoles): Rename ConsoleSelector to AccessConsoles Based on design-review. Former `ConsoleSelector` component has not been released yet.
1 parent f1d591c commit d876589

File tree

17 files changed

+1900
-21
lines changed

17 files changed

+1900
-21
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.console-selector-pf {
2+
margin-bottom: 0px;
3+
}
4+
5+
.console-selector-pf-disconnect-switch {
6+
margin-left: 15px;
7+
}

packages/patternfly-3/react-console/less/console.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
*/
44
@import 'serial-console';
55
@import 'vnc-console';
6+
@import 'console-selector';
Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
.vnc-console {
2-
padding-right: 0px;
3-
padding-left: 0px;
4-
2+
.toolbar-pf-results {
3+
padding: 15px 0;
4+
border-top: none;
5+
margin-top: 0px;
6+
}
57
.vnc-console-connecting {
68
background-color: @color-pf-green;
79
}
10+
811
.vnc-console-disconnected {
912
background-color: @color-pf-red;
1013
}
11-
.toolbar-pf {
12-
border-bottom: none;
13-
}
14-
.toolbar-pf-results {
15-
padding-top: 10px;
16-
}
17-
.toolbar-pf-action-right .dropdown-menu {
18-
min-width: 102px; /* avoid overflow if DropdownButton is used under Toolbar.RightContent */
14+
15+
.toolbar-pf-action-right {
16+
padding-bottom: 5px;
17+
18+
.dropdown-menu {
19+
min-width: 102px;
20+
}
1921
}
2022
}

packages/patternfly-3/react-console/package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,19 @@
4040
"dependencies": {
4141
"@novnc/novnc": "^1.0.0",
4242
"classnames": "^2.2.5",
43-
"patternfly": "^3.52.1",
44-
"patternfly-react": "^2.21.5",
4543
"xterm": "^3.3.0"
4644
},
45+
"devDependencies": {
46+
"patternfly": "^3.52.1",
47+
"patternfly-react": "^2.21.5"
48+
},
4749
"peerDependencies": {
4850
"prop-types": "^15.6.1",
4951
"react": "^16.3.2",
50-
"react-dom": "^16.3.2"
52+
"react-dom": "^16.3.2",
53+
"patternfly": "^3.52.1",
54+
"patternfly-react": "^2.21.5",
55+
"react-bootstrap": "^0.32.1"
5156
}
5257
}
58+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.console-selector-pf {
2+
margin-bottom: 0;
3+
}
4+
5+
.console-selector-pf-disconnect-switch {
6+
margin-left: 15px;
7+
}
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
.vnc-console {
2+
.toolbar-pf-results {
3+
padding: 15px 0;
4+
border-top: none;
5+
margin-top: 0px;
6+
}
27
.vnc-console-connecting {
38
background-color: $color-pf-green;
49
}
@@ -7,7 +12,11 @@
712
background-color: $color-pf-red;
813
}
914

10-
.toolbar-pf-action-right .dropdown-menu {
11-
min-width: 102px;
15+
.toolbar-pf-action-right {
16+
padding-bottom: 5px;
17+
18+
.dropdown-menu {
19+
min-width: 102px;
20+
}
1221
}
1322
}

packages/patternfly-3/react-console/sass/console.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// Console StyleSheets
1414
@import "serial-console";
1515
@import "vnc-console";
16+
@import "console-selector";
1617

1718
/**
1819
Styling shared by both VncConsole and SerialConsole.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { Grid, Row, Col, Form, FormGroup, Dropdown, MenuItem, Checkbox } from 'patternfly-react';
5+
6+
import { NONE_TYPE, SERIAL_CONSOLE_TYPE, VNC_CONSOLE_TYPE } from '../common/constants';
7+
8+
class AccessConsoles extends React.Component {
9+
state = {
10+
type: NONE_TYPE,
11+
disconnectByChange: this.props.disconnectByChange,
12+
keptConnection: {} // no connection exists when mounted
13+
};
14+
15+
onTypeChange(type) {
16+
this.setState(prevState => {
17+
const keptConnection = prevState.disconnectByChange
18+
? { [type]: true }
19+
: { ...prevState.keptConnection, [type]: true };
20+
21+
return {
22+
type,
23+
keptConnection
24+
};
25+
});
26+
}
27+
28+
onChangeDisconnectBySwitchClick(target) {
29+
this.setState(prevState => ({
30+
disconnectByChange: target.checked,
31+
keptConnection: target.checked ? { [prevState.type]: true } : prevState.keptConnection
32+
}));
33+
}
34+
35+
getSelectedConsole() {
36+
return this.getConsoleForType(this.state.type);
37+
}
38+
39+
getConsoleForType(type) {
40+
if (!this.props.children) {
41+
return null;
42+
}
43+
44+
const getChildTypeName = child => (child.props.type ? child.props.type : (child.type && child.type.name) || null);
45+
const isChildOfType = child => getChildTypeName(child) === type;
46+
47+
// To keep connection, render all consoles but hide those unused
48+
return React.Children.map(
49+
this.props.children,
50+
child =>
51+
this.state.keptConnection[getChildTypeName(child)] ? (
52+
<div key={getChildTypeName(child)} hidden={!isChildOfType(child)}>
53+
{child}
54+
</div>
55+
) : null
56+
);
57+
}
58+
59+
render() {
60+
const items = {
61+
[NONE_TYPE]: this.props.textSelectConsoleType,
62+
[SERIAL_CONSOLE_TYPE]: this.props.textSerialConsole,
63+
[VNC_CONSOLE_TYPE]: this.props.textVncConsole
64+
};
65+
66+
return (
67+
<Grid fluid>
68+
<Form horizontal>
69+
<FormGroup controlId="console-type" className="console-selector-pf">
70+
<Col>
71+
<Dropdown id="console-type-selector" disabled={!this.props.children}>
72+
<Dropdown.Toggle>
73+
{this.props.children ? items[this.state.type] : this.props.textEmptyConsoleList}
74+
</Dropdown.Toggle>
75+
<Dropdown.Menu>
76+
{this.getConsoleForType(SERIAL_CONSOLE_TYPE) && (
77+
<MenuItem eventKey="1" onClick={() => this.onTypeChange(SERIAL_CONSOLE_TYPE)}>
78+
{items[SERIAL_CONSOLE_TYPE]}
79+
</MenuItem>
80+
)}
81+
{this.getConsoleForType(VNC_CONSOLE_TYPE) && (
82+
<MenuItem eventKey="2" onClick={() => this.onTypeChange(VNC_CONSOLE_TYPE)}>
83+
{items[VNC_CONSOLE_TYPE]}
84+
</MenuItem>
85+
)}
86+
</Dropdown.Menu>
87+
</Dropdown>
88+
{this.state.type !== NONE_TYPE && (
89+
<Checkbox
90+
className="console-selector-pf-disconnect-switch"
91+
inline
92+
defaultChecked={this.props.disconnectByChange}
93+
onChange={e => this.onChangeDisconnectBySwitchClick(e.target)}
94+
>
95+
{this.props.textDisconnectByChange}
96+
</Checkbox>
97+
)}
98+
</Col>
99+
</FormGroup>
100+
</Form>
101+
<Row>
102+
<Col>{this.getSelectedConsole()}</Col>
103+
</Row>
104+
</Grid>
105+
);
106+
}
107+
}
108+
109+
const validChildrenTypes = [SERIAL_CONSOLE_TYPE, VNC_CONSOLE_TYPE];
110+
const childElementValidator = propValue => {
111+
if (propValue) {
112+
const children = Array.isArray(propValue) ? propValue : [propValue];
113+
if (
114+
!children.every(
115+
child =>
116+
(child.type && validChildrenTypes.indexOf(child.type.name) >= 0) ||
117+
(child.props && validChildrenTypes.indexOf(child.props.type) >= 0)
118+
)
119+
) {
120+
return new Error('AccessConsoles child validation failed');
121+
}
122+
}
123+
return true;
124+
};
125+
126+
AccessConsoles.propTypes = {
127+
/**
128+
* Child element can be either
129+
* - <SerialConsole> or <VncConsole>
130+
* - or has a property "type" of value either SERIAL_CONSOLE_TYPE or VNC_CONSOLE_TYPE (useful when wrapping (composing) basic console components
131+
*/
132+
children: PropTypes.oneOfType([PropTypes.objectOf(childElementValidator), PropTypes.arrayOf(childElementValidator)]),
133+
134+
textSelectConsoleType: PropTypes.string /** Internationalization */,
135+
textSerialConsole: PropTypes.string /** Internationalization */,
136+
textVncConsole: PropTypes.string /** Internationalization */,
137+
textDisconnectByChange: PropTypes.string /** Internationalization */,
138+
textEmptyConsoleList: PropTypes.string /** Internationalization */,
139+
140+
disconnectByChange:
141+
PropTypes.bool /** Initial value of "Disconnect before switching" checkbox, "false" to disconnect when console type changed */
142+
};
143+
144+
AccessConsoles.defaultProps = {
145+
children: null,
146+
147+
textSelectConsoleType: 'Select Console Type',
148+
textSerialConsole: 'Serial Console',
149+
textVncConsole: 'VNC Console',
150+
textDisconnectByChange: 'Disconnect before switching',
151+
textEmptyConsoleList: 'No console available',
152+
153+
disconnectByChange: true /** By default, console is unmounted (disconnected) when switching to other type */
154+
};
155+
156+
export default AccessConsoles;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import { storiesOf } from '@storybook/react';
3+
import { withInfo } from '@storybook/addon-info';
4+
import { inlineTemplate } from 'storybook/decorators/storyTemplates';
5+
import { name } from '../../package.json';
6+
import { storybookPackageName } from 'storybook/constants/siteConstants';
7+
8+
import { noop } from 'patternfly-react';
9+
import { AccessConsoles, VncConsole } from '../index';
10+
import { SerialConsoleConnector } from '../SerialConsole/SerialConsole.stories'; // contains mock backend
11+
import { DISCONNECTED } from '../SerialConsole/constants';
12+
import { SERIAL_CONSOLE_TYPE } from '../common/constants';
13+
14+
const stories = storiesOf(`${storybookPackageName(name)}/AccessConsoles`, module);
15+
16+
stories.add(
17+
'AccessConsoles',
18+
withInfo()(() => {
19+
const story = (
20+
<AccessConsoles>
21+
<SerialConsoleConnector onConnect={noop} onDisconnect={noop} status={DISCONNECTED} type={SERIAL_CONSOLE_TYPE} />
22+
<VncConsole
23+
host="foo.bar.host"
24+
textDisconnected="Disconnected as expected - VncConsole component is not connected to a real backend"
25+
/>
26+
</AccessConsoles>
27+
);
28+
return inlineTemplate({
29+
story,
30+
title: 'AccessConsoles'
31+
});
32+
})
33+
);
34+
35+
stories.add(
36+
'AccessConsoles - empty',
37+
withInfo()(() => {
38+
const story = <AccessConsoles />;
39+
return inlineTemplate({
40+
story,
41+
title: 'AccessConsoles - empty'
42+
});
43+
})
44+
);

0 commit comments

Comments
 (0)