Skip to content

Commit 561fca1

Browse files
authored
feat(VSCODE-197): Add radio box group to update connect form select options (#202)
1 parent ff7efe6 commit 561fca1

File tree

9 files changed

+216
-24
lines changed

9 files changed

+216
-24
lines changed

src/test/suite/views/webview-app/components/connect-form/general-tab/authentication/authentication.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as React from 'react';
33
import { shallow } from 'enzyme';
44

55
import { Authentication } from '../../../../../../../../views/webview-app/components/connect-form/general-tab/authentication/authentication';
6-
import FormItemSelect from '../../../../../../../../views/webview-app/components/form/form-item-select';
6+
import RadioBoxGroup from '../../../../../../../../views/webview-app/components/form/radio-box-group/radio-box-group';
77
import MongodbAuthentication from '../../../../../../../../views/webview-app/components/connect-form/general-tab/authentication/mongodb-authentication';
88
import AUTH_STRATEGIES from '../../../../../../../../views/webview-app/connection-model/constants/auth-strategies';
99
import ScramSha256 from '../../../../../../../../views/webview-app/components/connect-form/general-tab/authentication/scram-sha-256';
@@ -16,7 +16,7 @@ describe('Authentication Component Test Suite', () => {
1616
kerberosCanonicalizeHostname
1717
onAuthStrategyChanged={(): void => {}}
1818
/>);
19-
assert(wrapper.find(FormItemSelect).exists());
19+
assert(wrapper.find(RadioBoxGroup).exists());
2020
assert(!wrapper.find(MongodbAuthentication).exists());
2121
assert(!wrapper.find(ScramSha256).exists());
2222
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import assert from 'assert';
2+
import * as React from 'react';
3+
import { mount } from 'enzyme';
4+
5+
import RadioBoxGroup from '../../../../../../../views/webview-app/components/form/radio-box-group/radio-box-group';
6+
7+
describe('Radio Box Group Component Test Suite', () => {
8+
describe('when rendered', () => {
9+
let didCallChangeHandler = false;
10+
let changeHandlerCalledValue;
11+
const changeHandler = (evt: React.ChangeEvent<HTMLInputElement>): void => {
12+
didCallChangeHandler = true;
13+
changeHandlerCalledValue = evt.target.value;
14+
};
15+
16+
const wrapper = mount(<RadioBoxGroup
17+
name=""
18+
label="Box form label"
19+
options={[{
20+
value: 'pineapple',
21+
label: 'Pineapple!!'
22+
}, {
23+
value: 'watermelon',
24+
label: 'Watermelon!!'
25+
}]}
26+
onChange={changeHandler}
27+
value={'watermelon'}
28+
/>);
29+
30+
test('it shows an input for each option', () => {
31+
assert(wrapper.find('input').length === 2);
32+
});
33+
34+
test('it shows the group label', () => {
35+
assert(wrapper.find('label').at(0).text() === 'Box form label');
36+
});
37+
38+
test('when an option is clicked it calls the onChange prop with that value', () => {
39+
assert(!didCallChangeHandler);
40+
wrapper.find('input').at(0).simulate('change');
41+
assert(didCallChangeHandler);
42+
assert(changeHandlerCalledValue === 'pineapple');
43+
});
44+
});
45+
});

src/views/webview-app/components/connect-form/advanced-tab/read-preference-select.tsx

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,37 @@ import { connect } from 'react-redux';
33

44
import { AppState } from '../../../store/store';
55
import { ActionTypes, ReadPreferenceChangedAction } from '../../../store/actions';
6-
import FormItemSelect from '../../form/form-item-select';
6+
import RadioBoxGroup from '../../form/radio-box-group/radio-box-group';
77
import READ_PREFERENCES from '../../../connection-model/constants/read-preferences';
88

9+
type ReadPreferenceOption = {
10+
label: string;
11+
value: READ_PREFERENCES;
12+
};
13+
14+
const ReadPreferencesOptions: ReadPreferenceOption[] = [
15+
{
16+
label: 'Primary',
17+
value: READ_PREFERENCES.PRIMARY
18+
},
19+
{
20+
label: 'Primary Preferred',
21+
value: READ_PREFERENCES.PRIMARY_PREFERRED
22+
},
23+
{
24+
label: 'Secondary',
25+
value: READ_PREFERENCES.SECONDARY
26+
},
27+
{
28+
label: 'Secondary Preferred',
29+
value: READ_PREFERENCES.SECONDARY_PREFERRED
30+
},
31+
{
32+
label: 'Nearest',
33+
value: READ_PREFERENCES.NEAREST
34+
}
35+
];
36+
937
type StateProps = {
1038
readPreference: READ_PREFERENCES;
1139
};
@@ -20,25 +48,19 @@ class ReadPreferenceSelect extends React.PureComponent<StateProps & DispatchProp
2048
*
2149
* @param {Object} evt - evt.
2250
*/
23-
onReadPreferenceChanged(evt): void {
51+
onReadPreferenceChanged = (evt): void => {
2452
this.props.onReadPreferenceChanged(evt.target.value);
25-
}
53+
};
2654

2755
render(): React.ReactNode {
2856
const { readPreference } = this.props;
2957

3058
return (
31-
<FormItemSelect
59+
<RadioBoxGroup
3260
label="Read Preference"
3361
name="readPreference"
34-
options={[
35-
{ [READ_PREFERENCES.PRIMARY]: 'Primary' },
36-
{ [READ_PREFERENCES.PRIMARY_PREFERRED]: 'Primary Preferred' },
37-
{ [READ_PREFERENCES.SECONDARY]: 'Secondary' },
38-
{ [READ_PREFERENCES.SECONDARY_PREFERRED]: 'Secondary Preferred' },
39-
{ [READ_PREFERENCES.NEAREST]: 'Nearest' }
40-
]}
41-
changeHandler={this.onReadPreferenceChanged.bind(this)}
62+
options={ReadPreferencesOptions}
63+
onChange={this.onReadPreferenceChanged}
4264
value={readPreference}
4365
/>
4466
);

src/views/webview-app/components/connect-form/general-tab/authentication/authentication.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import AUTH_STRATEGIES, {
66
AuthStrategies
77
} from '../../../../connection-model/constants/auth-strategies';
88
import FormGroup from '../../../form/form-group';
9-
import FormItemSelect from '../../../form/form-item-select';
109
import Kerberos from './kerberos';
1110
import LDAP from './ldap';
1211
import MongoDBAuth from './mongodb-authentication';
1312
import ScramSha256 from './scram-sha-256';
1413
import X509 from './x509';
1514
import { AppState } from '../../../../store/store';
15+
import RadioBoxGroup from '../../../form/radio-box-group/radio-box-group';
1616

1717
type StateProps = {
1818
authStrategy: AUTH_STRATEGIES;
@@ -123,13 +123,14 @@ export class Authentication extends React.Component<StateProps & DispatchProps>
123123

124124
return (
125125
<FormGroup id="authStrategy" separator>
126-
<FormItemSelect
126+
<RadioBoxGroup
127127
label="Authentication"
128128
name="authStrategy"
129129
options={AuthStrategies.map((authStrat) => ({
130-
[`${authStrat.id}`]: authStrat.title
130+
label: authStrat.title,
131+
value: authStrat.id
131132
}))}
132-
changeHandler={this.onAuthStrategyChanged}
133+
onChange={this.onAuthStrategyChanged}
133134
value={authStrategy}
134135
/>
135136
{this.renderAuthStrategy()}

src/views/webview-app/components/connect-form/ssh-tab/ssh-tunnel-tab.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import SSH_TUNNEL_TYPES, {
66
SSHTunnelOptions
77
} from '../../../connection-model/constants/ssh-tunnel-types';
88
import FormGroup from '../../form/form-group';
9-
import FormItemSelect from '../../form/form-item-select';
9+
import RadioBoxGroup from '../../form/radio-box-group/radio-box-group';
1010
import SSHTunnelPasswordValidation from './ssh-tunnel-password-validation';
1111
import SSHTunnelIdentityFileValidation from './ssh-tunnel-identity-file-validation';
1212
import { AppState } from '../../../store/store';
@@ -51,13 +51,14 @@ class SSHTunnelTab extends React.Component<StateProps & DispatchProps> {
5151

5252
return (
5353
<FormGroup id="ssh-tunnel" separator>
54-
<FormItemSelect
54+
<RadioBoxGroup
5555
label="SSH Tunnel"
5656
name="sshTunnel"
5757
options={SSHTunnelOptions.map((sshTunnelOption) => ({
58-
[`${sshTunnelOption.id}`]: sshTunnelOption.title
58+
label: sshTunnelOption.title,
59+
value: sshTunnelOption.id
5960
}))}
60-
changeHandler={this.onSSHTunnelChanged}
61+
onChange={this.onSSHTunnelChanged}
6162
value={sshTunnel}
6263
/>
6364
{this.renderSSHTunnel()}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
.radio-box-item {
3+
display: inline-block;
4+
margin: 3px;
5+
border: 1px solid var(--vscode-searchEditor-textInputBorder, black);
6+
padding: 8px 14px;
7+
border-radius: 3px;
8+
transition: all 150ms ease-in-out;
9+
color: var(--vscode-editor-foreground);
10+
11+
&:hover {
12+
cursor: pointer;
13+
}
14+
15+
&:focus-within {
16+
box-shadow: 0 0 5px rgba(81, 203, 238, 1);
17+
}
18+
}
19+
20+
.radio-box-item-selected {
21+
background: #C3E7CA;
22+
border-color: transparent;
23+
color: #1E1E1E;
24+
}
25+
26+
.radio-box-item-input {
27+
// Hidden radio button styles.
28+
opacity: 0;
29+
position: absolute;
30+
pointer-events: none;
31+
}
32+
33+
.radio-box-item-label {
34+
font-size: 14px;
35+
font-weight: normal;
36+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import classnames from 'classnames';
2+
import * as React from 'react';
3+
4+
const styles = require('./radio-box-group.less');
5+
const formStyles = require('../form.less');
6+
7+
type props = {
8+
label: string;
9+
name: string;
10+
options: {
11+
label: string;
12+
value: string;
13+
}[];
14+
onChange: (evt: React.ChangeEvent<HTMLInputElement>) => void;
15+
value: string;
16+
};
17+
18+
class RadioBoxGroup extends React.Component<props> {
19+
handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
20+
const { onChange, value } = this.props;
21+
22+
// Stopped propagation to prevent event from bubbling with new target, and thus value coming back as undefined
23+
e.stopPropagation();
24+
25+
onChange(e);
26+
27+
if (!value) {
28+
this.setState({ value: e.target.value });
29+
}
30+
};
31+
32+
renderOptions(): React.ReactNode {
33+
const {
34+
options,
35+
value
36+
} = this.props;
37+
38+
return options.map((option, i) => {
39+
const checked = value === option.value;
40+
41+
return (
42+
<label
43+
className={classnames(styles['radio-box-item'], {
44+
[styles['radio-box-item-selected']]: checked
45+
})}
46+
key={i}
47+
htmlFor={`radio-box-group-${option.value}`}
48+
>
49+
<input
50+
className={styles['radio-box-item-input']}
51+
checked={checked}
52+
aria-selected={checked}
53+
type="radio"
54+
id={`radio-box-group-${option.value}`}
55+
name={`radio-box-group-${option.value}`}
56+
onChange={this.handleChange}
57+
value={option.value}
58+
/>
59+
<div
60+
className={styles['radio-box-item-label']}
61+
>{option.label}</div>
62+
</label>
63+
);
64+
});
65+
}
66+
67+
render(): React.ReactNode {
68+
const { label, name } = this.props;
69+
70+
return (
71+
<div className={formStyles['form-item']}>
72+
<label>
73+
<span>{label}</span>
74+
</label>
75+
<div
76+
className={styles['radio-box-group']}
77+
aria-label={name}
78+
role="group"
79+
>
80+
{this.renderOptions()}
81+
</div>
82+
</div>
83+
);
84+
}
85+
}
86+
87+
export default RadioBoxGroup;

src/views/webview-app/connection-model/constants/ssh-tunnel-types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Allowed values for the `sshTunnel` field.
2-
enum SSH_TUNNEL_TYPES {
2+
export enum SSH_TUNNEL_TYPES {
33
/**
44
* Do not use SSH tunneling.
55
*/

src/views/webview-app/connection-model/constants/ssl-methods.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Allowed values for the `sslMethod` field.
2-
enum SSL_METHODS {
2+
export enum SSL_METHODS {
33
/**
44
* Do not use SSL for anything.
55
*/

0 commit comments

Comments
 (0)