@jetbrains/ring-ui 4.1.0-beta.3 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +17 -15
- package/babel.config.js +3 -2
- package/components/alert/alert.js +9 -3
- package/components/alert/alert.test.js +21 -48
- package/components/alert/container.css +1 -1
- package/components/alert/container.test.js +3 -13
- package/components/alert-service/alert-service.examples.css +18 -0
- package/components/alert-service/alert-service.examples.js +21 -0
- package/components/alert-service/alert-service.js +10 -3
- package/components/analytics/analytics__fus-plugin.js +3 -3
- package/components/analytics/analytics__ga-plugin.js +2 -2
- package/components/auth/auth.test.js +14 -7
- package/components/auth/auth__core.js +64 -33
- package/components/auth-dialog/auth-dialog.css +2 -3
- package/components/auth-dialog/auth-dialog.js +4 -1
- package/components/auth-dialog/auth-dialog.test.js +3 -19
- package/components/avatar/avatar.css +4 -1
- package/components/avatar/avatar.examples.js +3 -2
- package/components/avatar/avatar.js +34 -6
- package/components/avatar/avatar.test.js +20 -17
- package/components/avatar/fallback-avatar.js +136 -0
- package/components/avatar-editor-ng/avatar-editor-ng.css +2 -2
- package/components/avatar-editor-ng/avatar-editor-ng.js +2 -1
- package/components/avatar-editor-ng/{avatar-editor-ng.html → avatar-editor-ng__template.js} +2 -2
- package/components/badge/badge.test.js +13 -20
- package/components/button/button.css +2 -2
- package/components/button/button.js +4 -1
- package/components/button/button.test.js +32 -33
- package/components/button-group/button-group.js +1 -1
- package/components/button-group/caption.js +1 -1
- package/components/button-ng/button-ng.js +1 -1
- package/components/button-set-ng/button-set-ng.js +3 -1
- package/components/checkbox/checkbox.css +1 -1
- package/components/code/code.js +2 -5
- package/components/confirm/confirm.js +1 -0
- package/components/confirm-service/confirm-service.js +5 -5
- package/components/content-layout/content-layout.css +1 -1
- package/components/data-list/data-list.css +1 -1
- package/components/date-picker/date-input.js +5 -4
- package/components/date-picker/date-picker.css +34 -22
- package/components/date-picker/date-picker.js +16 -14
- package/components/date-picker/date-popup.js +22 -7
- package/components/date-picker/month-names.js +8 -5
- package/components/date-picker/month.js +6 -2
- package/components/date-picker/weekdays.js +10 -2
- package/components/dialog/dialog.examples.js +3 -1
- package/components/dialog/dialog.js +10 -5
- package/components/dialog/dialog.test.js +1 -1
- package/components/dialog/dialog__body-scroll-preventer.js +51 -31
- package/components/dialog-ng/dialog-ng.js +10 -10
- package/components/dialog-ng/{dialog-ng.html → dialog-ng__template.js} +2 -2
- package/components/dropdown/dropdown.examples.js +36 -1
- package/components/dropdown/dropdown.test.js +2 -2
- package/components/dropdown-menu/dropdown-menu.examples.js +47 -0
- package/components/dropdown-menu/dropdown-menu.js +117 -0
- package/components/dropdown-menu/dropdown-menu.test.js +76 -0
- package/components/error-bubble/error-bubble-legacy.css +1 -1
- package/components/error-bubble/error-bubble.css +1 -1
- package/components/error-bubble/error-bubble.examples.js +1 -1
- package/components/error-page/error-page.css +2 -2
- package/components/footer-ng/footer-ng.js +13 -3
- package/components/form/form.css +2 -2
- package/components/form-ng/form-ng.js +3 -1
- package/components/global/global.css +1 -1
- package/components/global/theme.js +1 -1
- package/components/global/variables.css +8 -1
- package/components/grid/grid.css +10 -9
- package/components/header/header.css +1 -1
- package/components/header/header.examples.js +7 -8
- package/components/header/profile.js +10 -11
- package/components/http/http.js +1 -1
- package/components/icon/icon.css +5 -4
- package/components/input/input-legacy.css +7 -7
- package/components/island/header.js +2 -2
- package/components/island/island.css +8 -3
- package/components/island-legacy/island-legacy.css +3 -1
- package/components/list/list.js +6 -1
- package/components/list/list__custom.js +9 -3
- package/components/list/list__item.js +8 -2
- package/components/list/list__link.js +2 -1
- package/components/loader-inline/loader-inline.css +1 -1
- package/components/loader-screen/loader-screen.css +1 -1
- package/components/message/message.css +1 -1
- package/components/message/message.examples.js +8 -7
- package/components/pager/pager.js +5 -3
- package/components/permissions/permissions.js +1 -1
- package/components/popup/popup.js +1 -1
- package/components/popup/popup.test.js +15 -13
- package/components/progress-bar/progress-bar.css +1 -1
- package/components/progress-bar/progress-bar.examples.js +3 -3
- package/components/progress-bar/progress-bar.js +5 -2
- package/components/progress-bar/progress-bar.test.js +12 -13
- package/components/progress-bar-ng/progress-bar-ng.examples.js +3 -3
- package/components/query-assist/query-assist.css +13 -3
- package/components/query-assist/query-assist.examples.js +3 -4
- package/components/query-assist/query-assist.js +56 -12
- package/components/query-assist/query-assist.test.js +37 -5
- package/components/save-field-ng/save-field-ng.css +0 -3
- package/components/save-field-ng/save-field-ng.js +3 -1
- package/components/save-field-ng/{save-field-ng.html → save-field-ng__template.js} +2 -2
- package/components/select/select.css +12 -7
- package/components/select/select.examples.js +13 -0
- package/components/select/select.js +30 -43
- package/components/select/select.test.js +4 -5
- package/components/select/select__popup.js +1 -0
- package/components/shortcuts-hint-ng/shortcuts-hint-ng.css +1 -1
- package/components/shortcuts-hint-ng/shortcuts-hint-ng.js +1 -1
- package/components/shortcuts-hint-ng/{shortcuts-hint-ng.html → shortcuts-hint-ng__template.js} +2 -2
- package/components/sidebar/sidebar.css +1 -0
- package/components/sidebar-ng/sidebar-ng.js +6 -2
- package/components/sidebar-ng/{sidebar-ng__button.html → sidebar-ng__button-template.js} +2 -2
- package/components/sidebar-ng/{sidebar-ng.html → sidebar-ng__template.js} +2 -2
- package/components/table/header.js +9 -1
- package/components/table/row.js +2 -1
- package/components/table/table.css +2 -1
- package/components/table-legacy/table-legacy.css +2 -2
- package/components/table-legacy/table-legacy__toolbar.css +2 -2
- package/components/table-legacy-ng/table-legacy-ng.js +38 -5
- package/components/table-legacy-ng/table-legacy-ng__pager.js +7 -1
- package/components/tabs/collapsible-tab.js +2 -2
- package/components/tabs/collapsible-tabs.js +5 -9
- package/components/tabs/tab-link.js +4 -2
- package/components/tabs/tabs.css +32 -5
- package/components/tabs-ng/tabs-ng.js +4 -2
- package/components/tabs-ng/{tabs-ng.html → tabs-ng__template.js} +6 -2
- package/components/tag/tag.css +5 -2
- package/components/tag/tag.examples.js +3 -0
- package/components/tag/tag.js +19 -16
- package/components/tags-input/tag-input.examples.js +1 -1
- package/components/tags-input/tags-input.js +5 -2
- package/components/template-ng/template-ng.js +1 -1
- package/components/tooltip/tooltip.js +7 -2
- package/components/user-agreement/user-agreement.css +1 -5
- package/components/user-agreement/user-agreement.examples.js +7 -6
- package/components/user-agreement/user-agreement.js +11 -3
- package/package.json +85 -83
- package/webpack.config.js +14 -10
- package/components/button-set-ng/button-set-ng.html +0 -1
- package/components/footer-ng/footer-ng.html +0 -13
- package/components/form-ng/form-ng__error-bubble.html +0 -3
- package/components/table-legacy-ng/table-legacy-ng.html +0 -4
- package/components/table-legacy-ng/table-legacy-ng__column.html +0 -12
- package/components/table-legacy-ng/table-legacy-ng__header.html +0 -4
- package/components/table-legacy-ng/table-legacy-ng__pager.html +0 -7
- package/components/table-legacy-ng/table-legacy-ng__row.html +0 -12
- package/components/table-legacy-ng/table-legacy-ng__title.html +0 -9
- package/dist/_helpers/_rollupPluginBabelHelpers.js +0 -123
- package/dist/_helpers/background-flow.js +0 -232
- package/dist/_helpers/badge.js +0 -3
- package/dist/_helpers/button.js +0 -145
- package/dist/_helpers/clickableLink.js +0 -65
- package/dist/_helpers/data-tests.js +0 -15
- package/dist/_helpers/disable-hover-hoc.js +0 -407
- package/dist/_helpers/dom.js +0 -101
- package/dist/_helpers/get-uid.js +0 -15
- package/dist/_helpers/linear-function.js +0 -17
- package/dist/_helpers/list.js +0 -1327
- package/dist/_helpers/logo.js +0 -36
- package/dist/_helpers/memoize.js +0 -17
- package/dist/_helpers/popup.js +0 -691
- package/dist/_helpers/popup.target.js +0 -27
- package/dist/_helpers/rerender-hoc.js +0 -49
- package/dist/_helpers/schedule-raf.js +0 -31
- package/dist/_helpers/sniffer.js +0 -6
- package/dist/_helpers/theme.js +0 -40
- package/dist/_helpers/url.js +0 -125
- package/dist/alert-service.js +0 -149
- package/dist/alert.js +0 -284
- package/dist/analytics.js +0 -116
- package/dist/auth-dialog-service.js +0 -56
- package/dist/auth-dialog.js +0 -122
- package/dist/auth.js +0 -1744
- package/dist/avatar.js +0 -152
- package/dist/badge.js +0 -52
- package/dist/button-group.js +0 -48
- package/dist/button-set.js +0 -27
- package/dist/button-toolbar.js +0 -30
- package/dist/button.js +0 -12
- package/dist/caret.js +0 -262
- package/dist/checkbox.js +0 -108
- package/dist/confirm-service.js +0 -102
- package/dist/confirm.js +0 -113
- package/dist/content-layout.js +0 -184
- package/dist/contenteditable.js +0 -81
- package/dist/data-list.js +0 -466
- package/dist/date-picker.js +0 -1398
- package/dist/dialog.js +0 -223
- package/dist/dropdown.js +0 -250
- package/dist/error-bubble.js +0 -56
- package/dist/error-message.js +0 -53
- package/dist/footer.js +0 -124
- package/dist/grid.js +0 -148
- package/dist/group.js +0 -34
- package/dist/header.js +0 -658
- package/dist/heading.js +0 -76
- package/dist/http.js +0 -207
- package/dist/hub-source.js +0 -130
- package/dist/icon.js +0 -211
- package/dist/input.js +0 -228
- package/dist/island.js +0 -314
- package/dist/link.js +0 -117
- package/dist/list.js +0 -29
- package/dist/loader-inline.js +0 -165
- package/dist/loader-screen.js +0 -45
- package/dist/loader.js +0 -338
- package/dist/login-dialog.js +0 -173
- package/dist/logo.js +0 -8
- package/dist/message.js +0 -226
- package/dist/old-browsers-message.js +0 -129
- package/dist/pager.js +0 -325
- package/dist/panel.js +0 -34
- package/dist/permissions.js +0 -466
- package/dist/popup-menu.js +0 -93
- package/dist/popup.js +0 -16
- package/dist/progress-bar.js +0 -111
- package/dist/proxy-attrs.js +0 -19
- package/dist/query-assist.js +0 -1081
- package/dist/radio.js +0 -112
- package/dist/select.js +0 -1920
- package/dist/selection.js +0 -213
- package/dist/shortcuts.js +0 -307
- package/dist/storage.js +0 -373
- package/dist/style.css +0 -1
- package/dist/tab-trap.js +0 -174
- package/dist/table.js +0 -903
- package/dist/tabs.js +0 -721
- package/dist/tag.js +0 -187
- package/dist/tags-input.js +0 -440
- package/dist/tags-list.js +0 -91
- package/dist/text.js +0 -38
- package/dist/toggle.js +0 -80
- package/dist/tooltip.js +0 -202
- package/dist/user-card.js +0 -218
|
@@ -6,6 +6,8 @@ import {Content} from '../island/island';
|
|
|
6
6
|
import Dialog from '../dialog/dialog';
|
|
7
7
|
import Button from '../button/button';
|
|
8
8
|
|
|
9
|
+
import {H2} from '../heading/heading';
|
|
10
|
+
|
|
9
11
|
import styles from './auth-dialog.css';
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -68,6 +70,7 @@ export default class AuthDialog extends Component {
|
|
|
68
70
|
|
|
69
71
|
return (
|
|
70
72
|
<Dialog
|
|
73
|
+
label={title}
|
|
71
74
|
data-test="ring-auth-dialog"
|
|
72
75
|
className={className}
|
|
73
76
|
contentClassName={classNames(className, styles.dialog)}
|
|
@@ -84,7 +87,7 @@ export default class AuthDialog extends Component {
|
|
|
84
87
|
src={serviceImage}
|
|
85
88
|
/>
|
|
86
89
|
)}
|
|
87
|
-
<
|
|
90
|
+
<H2 className={styles.title}>{title}</H2>
|
|
88
91
|
{errorMessage && (
|
|
89
92
|
<div className={styles.error}>{errorMessage}</div>
|
|
90
93
|
)}
|
|
@@ -1,27 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import styles from './auth-dialog.css';
|
|
2
|
+
import {render, screen} from '@testing-library/react';
|
|
5
3
|
|
|
6
4
|
import AuthDialog from './auth-dialog';
|
|
7
5
|
|
|
8
6
|
describe('AuthDialog', () => {
|
|
9
|
-
const defaultProps = {show: true, text: 'Foo'};
|
|
10
|
-
let wrapper;
|
|
11
|
-
const mountAuthDialog = props => {
|
|
12
|
-
wrapper = mount(<AuthDialog {...props}/>);
|
|
13
|
-
return wrapper;
|
|
14
|
-
};
|
|
15
|
-
afterEach(() => wrapper.unmount());
|
|
16
|
-
|
|
17
|
-
const getContainer = () => document.querySelector('*[data-test~="ring-auth-dialog"]');
|
|
18
|
-
|
|
19
|
-
it('should create component', () => {
|
|
20
|
-
mountAuthDialog(defaultProps).should.have.type(AuthDialog);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
7
|
it('should render confirm', () => {
|
|
24
|
-
|
|
25
|
-
|
|
8
|
+
render(<AuthDialog show title="Foo"/>);
|
|
9
|
+
screen.getByRole('heading', {text: 'Foo'}).should.exist;
|
|
26
10
|
});
|
|
27
11
|
});
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
@import "../global/variables.css";
|
|
2
2
|
|
|
3
3
|
.avatar {
|
|
4
|
+
display: inline-block;
|
|
4
5
|
object-fit: cover;
|
|
5
6
|
object-position: center;
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
/* This is a "graceful degradation" fallback, while the real value is controlled by JS */
|
|
9
|
+
|
|
10
|
+
border-radius: var(--ring-border-radius);
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
.subavatar {
|
|
@@ -21,7 +21,8 @@ export const avatar = () => {
|
|
|
21
21
|
{Object.keys(Size).map(size => (
|
|
22
22
|
<div className="avatar-demo" key={size}>
|
|
23
23
|
<Avatar size={Size[size]} url={avatarDataUri}/>
|
|
24
|
-
<Avatar size={Size[size]}
|
|
24
|
+
<Avatar size={Size[size]} username="Jet Brains"/>
|
|
25
|
+
<Avatar size={Size[size]} username="Jet Brains" round/>
|
|
25
26
|
<Avatar size={Size[size]}/>
|
|
26
27
|
</div>
|
|
27
28
|
))}
|
|
@@ -38,7 +39,7 @@ avatar.parameters = {
|
|
|
38
39
|
.avatar-demo {
|
|
39
40
|
display: flex;
|
|
40
41
|
justify-content: space-between;
|
|
41
|
-
width:
|
|
42
|
+
width: 240px;
|
|
42
43
|
margin-bottom: 16px;
|
|
43
44
|
}
|
|
44
45
|
</style>`
|
|
@@ -6,6 +6,7 @@ import {encodeURL, isDataURI, parseQueryString} from '../global/url';
|
|
|
6
6
|
import {getPixelRatio} from '../global/dom';
|
|
7
7
|
|
|
8
8
|
import styles from './avatar.css';
|
|
9
|
+
import FallbackAvatar from './fallback-avatar';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @name Avatar
|
|
@@ -15,6 +16,7 @@ export const Size = {
|
|
|
15
16
|
Size18: 18,
|
|
16
17
|
Size20: 20,
|
|
17
18
|
Size24: 24,
|
|
19
|
+
Size28: 28,
|
|
18
20
|
Size32: 32,
|
|
19
21
|
Size40: 40,
|
|
20
22
|
Size48: 48,
|
|
@@ -30,7 +32,9 @@ export default class Avatar extends PureComponent {
|
|
|
30
32
|
url: PropTypes.string,
|
|
31
33
|
round: PropTypes.bool,
|
|
32
34
|
subavatar: PropTypes.string,
|
|
33
|
-
subavatarSize: PropTypes.number
|
|
35
|
+
subavatarSize: PropTypes.number,
|
|
36
|
+
username: PropTypes.string,
|
|
37
|
+
skipParams: PropTypes.bool
|
|
34
38
|
};
|
|
35
39
|
|
|
36
40
|
static defaultProps = {
|
|
@@ -53,7 +57,18 @@ export default class Avatar extends PureComponent {
|
|
|
53
57
|
};
|
|
54
58
|
|
|
55
59
|
render() {
|
|
56
|
-
const {
|
|
60
|
+
const {
|
|
61
|
+
size,
|
|
62
|
+
url,
|
|
63
|
+
dpr,
|
|
64
|
+
style,
|
|
65
|
+
round,
|
|
66
|
+
subavatar,
|
|
67
|
+
subavatarSize,
|
|
68
|
+
username,
|
|
69
|
+
skipParams,
|
|
70
|
+
...restProps
|
|
71
|
+
} = this.props;
|
|
57
72
|
const sizeString = `${size}px`;
|
|
58
73
|
const subavatarSizeString = `${subavatarSize}px`;
|
|
59
74
|
const borderRadius = size <= Size.Size18 ? 'var(--ring-border-radius-small)' : 'var(--ring-border-radius)';
|
|
@@ -75,14 +90,25 @@ export default class Avatar extends PureComponent {
|
|
|
75
90
|
return (
|
|
76
91
|
<span
|
|
77
92
|
{...restProps}
|
|
78
|
-
|
|
93
|
+
data-test="avatar"
|
|
94
|
+
className={
|
|
95
|
+
classNames(styles.avatar, this.props.className, {[styles.empty]: username == null})
|
|
96
|
+
}
|
|
79
97
|
style={styleObj}
|
|
80
|
-
|
|
98
|
+
>{
|
|
99
|
+
username != null && (
|
|
100
|
+
<FallbackAvatar
|
|
101
|
+
size={size}
|
|
102
|
+
round={round}
|
|
103
|
+
username={username}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
}</span>
|
|
81
107
|
);
|
|
82
108
|
}
|
|
83
109
|
|
|
84
110
|
let src = url;
|
|
85
|
-
if (!isDataURI(url)) {
|
|
111
|
+
if (!skipParams && !isDataURI(url)) {
|
|
86
112
|
const [urlStart, query] = url.split('?');
|
|
87
113
|
const queryParams = {
|
|
88
114
|
...parseQueryString(query),
|
|
@@ -101,7 +127,7 @@ export default class Avatar extends PureComponent {
|
|
|
101
127
|
subavatarSizeString
|
|
102
128
|
};
|
|
103
129
|
|
|
104
|
-
subavatarSrc = encodeURL(urlStart, queryParams);
|
|
130
|
+
subavatarSrc = skipParams ? subavatar : encodeURL(urlStart, queryParams);
|
|
105
131
|
return (
|
|
106
132
|
<div>
|
|
107
133
|
<img
|
|
@@ -115,6 +141,7 @@ export default class Avatar extends PureComponent {
|
|
|
115
141
|
/>
|
|
116
142
|
<img
|
|
117
143
|
{...restProps}
|
|
144
|
+
data-test="avatar"
|
|
118
145
|
onError={this.handleError}
|
|
119
146
|
onLoad={this.handleSuccess}
|
|
120
147
|
className={classNames(styles.subavatar)}
|
|
@@ -128,6 +155,7 @@ export default class Avatar extends PureComponent {
|
|
|
128
155
|
return (
|
|
129
156
|
<img
|
|
130
157
|
{...restProps}
|
|
158
|
+
data-test="avatar"
|
|
131
159
|
onError={this.handleError}
|
|
132
160
|
onLoad={this.handleSuccess}
|
|
133
161
|
className={classNames(styles.avatar, this.props.className)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {render, screen} from '@testing-library/react';
|
|
3
3
|
|
|
4
4
|
import {getPixelRatio} from '../global/dom';
|
|
5
5
|
|
|
@@ -12,45 +12,48 @@ const dataURI = `data:image/svg+xml,${encodeURIComponent(`
|
|
|
12
12
|
)}`;
|
|
13
13
|
|
|
14
14
|
describe('Avatar', () => {
|
|
15
|
-
const shallowAvatar = props => shallow(<Avatar {...props}/>);
|
|
16
|
-
const mountAvatar = props => mount(<Avatar {...props}/>);
|
|
17
|
-
|
|
18
15
|
it('should create component', () => {
|
|
19
|
-
|
|
16
|
+
render(<Avatar/>);
|
|
17
|
+
screen.getByTestId('avatar').should.exist;
|
|
20
18
|
});
|
|
21
19
|
|
|
22
20
|
it('should use passed className', () => {
|
|
23
|
-
|
|
21
|
+
render(<Avatar className="test-class"/>);
|
|
22
|
+
screen.getByTestId('avatar').should.have.class('test-class');
|
|
24
23
|
});
|
|
25
24
|
|
|
26
25
|
it('should use passed className when url is passed', () => {
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
render(<Avatar className="test-class" url={dataURI}/>);
|
|
27
|
+
screen.getByAltText('User avatar').should.have.class('test-class');
|
|
29
28
|
});
|
|
30
29
|
|
|
31
30
|
it('should render span when no url is passed', () => {
|
|
32
|
-
|
|
31
|
+
render(<Avatar/>);
|
|
32
|
+
screen.getByTestId('avatar').should.have.tagName('span');
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it('should render image when url is passed', () => {
|
|
36
|
-
|
|
36
|
+
render(<Avatar url={dataURI}/>);
|
|
37
|
+
screen.getByAltText('User avatar').should.exist;
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
it('should not append params when data:uri is passed', () => {
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
render(<Avatar url={dataURI}/>);
|
|
42
|
+
screen.getByAltText('User avatar').src.should.not.match(/dpr=|size=/);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
it('should append params when
|
|
45
|
-
|
|
45
|
+
it('should append params when http:uri is passed', () => {
|
|
46
|
+
render(<Avatar url="http://"/>);
|
|
47
|
+
screen.getByAltText('User avatar').src.should.match(/dpr=|size=/);
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
it('should set size 20 as default', () => {
|
|
49
|
-
|
|
51
|
+
render(<Avatar url="http://"/>);
|
|
52
|
+
screen.getByAltText('User avatar').src.should.match(/size=20/);
|
|
50
53
|
});
|
|
51
54
|
|
|
52
55
|
it('should set proper dpr', () => {
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
render(<Avatar url="http://"/>);
|
|
57
|
+
screen.getByAltText('User avatar').src.should.match(new RegExp(`dpr=${getPixelRatio()}`));
|
|
55
58
|
});
|
|
56
59
|
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, {useMemo} from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import getUID from '../global/get-uid';
|
|
5
|
+
|
|
6
|
+
const colorPairs = [
|
|
7
|
+
['#60A800', '#D5CA00'],
|
|
8
|
+
['#21D370', '#03E9E1'],
|
|
9
|
+
['#3BA1FF', '#36E97D'],
|
|
10
|
+
['#00C243', '#00FFFF'],
|
|
11
|
+
['#4BE098', '#627FFF'],
|
|
12
|
+
['#168BFA', '#26F7C7'],
|
|
13
|
+
['#9D4CFF', '#39D3C3'],
|
|
14
|
+
['#0A81F6', '#0ACFF6'],
|
|
15
|
+
['#765AF8', '#5A91F8'],
|
|
16
|
+
['#9E54FF', '#0ACFF6'],
|
|
17
|
+
['#B345F1', '#669DFF'],
|
|
18
|
+
['#765AF8', '#C059EE'],
|
|
19
|
+
['#9039D0', '#C239D0'],
|
|
20
|
+
['#9F2AFF', '#FD56FD'],
|
|
21
|
+
['#AB3AF2', '#E40568'],
|
|
22
|
+
['#9F2AFF', '#E9A80B'],
|
|
23
|
+
['#D50F6B', '#E73AE8'],
|
|
24
|
+
['#ED5502', '#E73AE8'],
|
|
25
|
+
['#ED358C', '#DBED18'],
|
|
26
|
+
['#ED358C', '#F9902E'],
|
|
27
|
+
['#FF7500', '#FFCA00']
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const Sizes = {
|
|
31
|
+
18: {
|
|
32
|
+
radius: 2,
|
|
33
|
+
text: {x: 9, y: 13},
|
|
34
|
+
fontSize: '11px',
|
|
35
|
+
textAnchor: 'middle'
|
|
36
|
+
},
|
|
37
|
+
24: {
|
|
38
|
+
radius: 3,
|
|
39
|
+
text: {x: 2, y: 13},
|
|
40
|
+
fontSize: '11px',
|
|
41
|
+
underscore: {x: 3, y: 17}
|
|
42
|
+
},
|
|
43
|
+
32: {
|
|
44
|
+
radius: 3,
|
|
45
|
+
text: {x: 3, y: 17},
|
|
46
|
+
fontSize: '13px',
|
|
47
|
+
letterSpacing: 1,
|
|
48
|
+
underscore: {x: 4, y: 22}
|
|
49
|
+
},
|
|
50
|
+
40: {
|
|
51
|
+
radius: 3,
|
|
52
|
+
text: {x: 5, y: 19},
|
|
53
|
+
fontSize: '15px',
|
|
54
|
+
letterSpacing: 1,
|
|
55
|
+
underscore: {x: 6, y: 28}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const sizeKeys = Object.keys(Sizes).map(Number);
|
|
60
|
+
|
|
61
|
+
function extractLetters(name) {
|
|
62
|
+
const names = name.split(/[\s._]+/).filter(Boolean);
|
|
63
|
+
if (names.length >= 2) {
|
|
64
|
+
return names[0][0].toUpperCase() + names[1][0].toUpperCase();
|
|
65
|
+
} else if (names.length === 1) {
|
|
66
|
+
if (names[0].length >= 2) {
|
|
67
|
+
return names[0].slice(0, 2).toUpperCase();
|
|
68
|
+
} else {
|
|
69
|
+
return `${names[0][0].toUpperCase()}X`;
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
return 'XX';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0#gistcomment-2775538
|
|
77
|
+
const BASE = 32;
|
|
78
|
+
function hashCode(s) {
|
|
79
|
+
let h = 0;
|
|
80
|
+
for (let i = 0; i < s.length; i++) {
|
|
81
|
+
h = Math.imul(BASE - 1, h) + s.charCodeAt(i) | 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return h;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default function FallbackAvatar({username, size, round}) {
|
|
88
|
+
const hash = Math.abs(hashCode(username.toLowerCase()));
|
|
89
|
+
const [fromColor, toColor] = colorPairs[hash % colorPairs.length];
|
|
90
|
+
const possibleSizeKeys = sizeKeys.filter(key => key >= size);
|
|
91
|
+
const sizeKey = possibleSizeKeys.length > 0
|
|
92
|
+
? Math.min(...possibleSizeKeys)
|
|
93
|
+
: Math.max(...sizeKeys);
|
|
94
|
+
const sizes = Sizes[sizeKey];
|
|
95
|
+
const radius = round ? '50%' : sizes.radius;
|
|
96
|
+
const gradientId = useMemo(() => getUID('gradient-'), []);
|
|
97
|
+
return (
|
|
98
|
+
<svg viewBox={`0 0 ${sizeKey} ${sizeKey}`} xmlns="http://www.w3.org/2000/svg">
|
|
99
|
+
<defs>
|
|
100
|
+
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
|
|
101
|
+
<stop stopColor={fromColor} offset="0"/>
|
|
102
|
+
<stop stopColor={toColor} offset="1"/>
|
|
103
|
+
</linearGradient>
|
|
104
|
+
</defs>
|
|
105
|
+
<g>
|
|
106
|
+
<rect
|
|
107
|
+
fill={`url(#${gradientId})`}
|
|
108
|
+
x="0"
|
|
109
|
+
y="0"
|
|
110
|
+
width={sizeKey}
|
|
111
|
+
height={sizeKey}
|
|
112
|
+
rx={radius}
|
|
113
|
+
ry={radius}
|
|
114
|
+
/>
|
|
115
|
+
<text
|
|
116
|
+
x={sizes.text.x}
|
|
117
|
+
y={sizes.text.y}
|
|
118
|
+
fontFamily="Arial, Helvetica, sans-serif"
|
|
119
|
+
fontSize={sizes.fontSize}
|
|
120
|
+
letterSpacing={sizes.letterSpacing}
|
|
121
|
+
fill="#FFFFFF"
|
|
122
|
+
textAnchor={sizes.textAnchor}
|
|
123
|
+
>
|
|
124
|
+
<tspan>{extractLetters(username)}</tspan>
|
|
125
|
+
{sizes.underscore && <tspan x={sizes.underscore.x} y={sizes.underscore.y}>{'_'}</tspan>}
|
|
126
|
+
</text>
|
|
127
|
+
</g>
|
|
128
|
+
</svg>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
FallbackAvatar.propTypes = {
|
|
133
|
+
username: PropTypes.string.isRequired,
|
|
134
|
+
size: PropTypes.number.isRequired,
|
|
135
|
+
round: PropTypes.bool
|
|
136
|
+
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
width: 0;
|
|
20
20
|
height: calc(unit * 5);
|
|
21
21
|
|
|
22
|
-
content:
|
|
22
|
+
content: "";
|
|
23
23
|
vertical-align: middle;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
width: 100%;
|
|
50
50
|
height: 100%;
|
|
51
51
|
|
|
52
|
-
content:
|
|
52
|
+
content: "";
|
|
53
53
|
|
|
54
54
|
background-color: rgba(0, 0, 0, 0.8);
|
|
55
55
|
}
|
|
@@ -6,6 +6,7 @@ import messageBundleNg from '../message-bundle-ng/message-bundle-ng';
|
|
|
6
6
|
import alertService from '../alert-service/alert-service';
|
|
7
7
|
import IconNG from '../icon-ng/icon-ng';
|
|
8
8
|
|
|
9
|
+
import template from './avatar-editor-ng__template';
|
|
9
10
|
import './avatar-editor-ng.css';
|
|
10
11
|
|
|
11
12
|
const angularModule = angular.module('Ring.avatar-editor', [messageBundleNg, IconNG]);
|
|
@@ -26,7 +27,7 @@ function rgAvatarEditor() {
|
|
|
26
27
|
deleteLabel: '@',
|
|
27
28
|
addMessage: '@'
|
|
28
29
|
},
|
|
29
|
-
template
|
|
30
|
+
template,
|
|
30
31
|
transclude: true,
|
|
31
32
|
controller: [
|
|
32
33
|
'$scope', '$attrs', 'RingMessageBundle',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export default `<div>
|
|
2
2
|
<div ng-click="!ngDisabled && controls.select()" class="ring-avatar-editor__frame" ng-class="{
|
|
3
3
|
'ring-avatar-editor_controlled': controlled,
|
|
4
4
|
'ring-avatar-editor': !ngDisabled
|
|
@@ -25,4 +25,4 @@
|
|
|
25
25
|
>
|
|
26
26
|
<span translate>{{ getDeleteLabel() }}</span>
|
|
27
27
|
</rg-button>
|
|
28
|
-
</div
|
|
28
|
+
</div>`;
|
|
@@ -1,41 +1,34 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {render, screen} from '@testing-library/react';
|
|
3
3
|
|
|
4
4
|
import Badge from './badge';
|
|
5
5
|
import style from './badge.css';
|
|
6
6
|
|
|
7
7
|
describe('Badge', () => {
|
|
8
|
-
const shallowBadge = (params, content) => shallow(<Badge {...params}>{content}</Badge>);
|
|
9
|
-
const mountBadge = (params, content) => mount(<Badge {...params}>{content}</Badge>);
|
|
10
|
-
const renderBadge = (params, content) => render(<Badge {...params}>{content}</Badge>);
|
|
11
|
-
|
|
12
|
-
it('should create component', () => {
|
|
13
|
-
mountBadge().should.have.type(Badge);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
8
|
it('should render span with badge class', () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
render(<Badge/>);
|
|
10
|
+
const element = screen.getByTestId('ring-badge');
|
|
11
|
+
element.should.have.tagName('span');
|
|
12
|
+
element.should.have.class(style.badge);
|
|
20
13
|
});
|
|
21
14
|
|
|
22
15
|
it('should use passed className', () => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
it('should have default data-test', () => {
|
|
27
|
-
shallowBadge({}).should.have.attr('data-test', 'ring-badge');
|
|
16
|
+
render(<Badge className="test-class"/>);
|
|
17
|
+
screen.getByTestId('ring-badge').should.have.class('test-class');
|
|
28
18
|
});
|
|
29
19
|
|
|
30
20
|
it('should use passed data-test', () => {
|
|
31
|
-
|
|
21
|
+
render(<Badge data-test="foo"/>);
|
|
22
|
+
screen.getByTestId('foo', {exact: false}).should.exist;
|
|
32
23
|
});
|
|
33
24
|
|
|
34
25
|
it('should render children', () => {
|
|
35
|
-
|
|
26
|
+
render(<Badge>{'foo'}</Badge>);
|
|
27
|
+
screen.getByText('foo').should.exist;
|
|
36
28
|
});
|
|
37
29
|
|
|
38
30
|
it('should render valid badge', () => {
|
|
39
|
-
|
|
31
|
+
render(<Badge valid/>);
|
|
32
|
+
screen.getByTestId('ring-badge').should.have.class(style.valid);
|
|
40
33
|
});
|
|
41
34
|
});
|
|
@@ -451,7 +451,7 @@
|
|
|
451
451
|
width: calc(100% + loaderWidth);
|
|
452
452
|
height: 100%;
|
|
453
453
|
|
|
454
|
-
content:
|
|
454
|
+
content: "";
|
|
455
455
|
animation: progress 1s linear infinite;
|
|
456
456
|
|
|
457
457
|
background-repeat: repeat;
|
|
@@ -460,7 +460,7 @@
|
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
.delayed .content::after {
|
|
463
|
-
content:
|
|
463
|
+
content: "…";
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
.short {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'focus-visible';
|
|
2
|
-
import React, {PureComponent} from 'react';
|
|
2
|
+
import React, {createRef, PureComponent} from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
5
|
import chevronDown from '@jetbrains/icons/chevron-10px';
|
|
@@ -47,6 +47,8 @@ class Button extends PureComponent {
|
|
|
47
47
|
static IconSize = Size;
|
|
48
48
|
static Theme = Theme;
|
|
49
49
|
|
|
50
|
+
buttonRef = createRef();
|
|
51
|
+
|
|
50
52
|
render() {
|
|
51
53
|
const {
|
|
52
54
|
// Modifiers
|
|
@@ -102,6 +104,7 @@ class Button extends PureComponent {
|
|
|
102
104
|
const Tag = isLink ? ClickableLink : 'button';
|
|
103
105
|
return (
|
|
104
106
|
<Tag
|
|
107
|
+
ref={this.buttonRef}
|
|
105
108
|
tabIndex={loader ? -1 : 0}
|
|
106
109
|
type={isLink ? null : 'button'}
|
|
107
110
|
{...props}
|
|
@@ -1,65 +1,64 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {render, screen} from '@testing-library/react';
|
|
3
|
+
|
|
3
4
|
import caretDownSVG from '@jetbrains/icons/caret-down-10px';
|
|
4
5
|
|
|
5
6
|
import Button from './button';
|
|
6
7
|
import styles from './button.css';
|
|
7
8
|
|
|
8
9
|
describe('Button', () => {
|
|
9
|
-
const mountButton = props => mount(<Button {...props}/>);
|
|
10
|
-
const getButtonOutput = props => mountButton(props).find('button');
|
|
11
|
-
const renderButton = props => render(<Button {...props}/>);
|
|
12
|
-
|
|
13
10
|
it('should create component', () => {
|
|
14
|
-
|
|
11
|
+
render(<Button/>);
|
|
12
|
+
screen.getByRole('button').should.exist;
|
|
15
13
|
});
|
|
16
14
|
|
|
17
15
|
it('should set _default modifier', () => {
|
|
18
|
-
|
|
16
|
+
render(<Button/>);
|
|
17
|
+
screen.getByRole('button').className.should.include(styles.button);
|
|
19
18
|
});
|
|
20
19
|
|
|
21
20
|
it('should set modifiers', () => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
render(
|
|
22
|
+
<Button
|
|
23
|
+
active
|
|
24
|
+
danger
|
|
25
|
+
delayed
|
|
26
|
+
loader
|
|
27
|
+
primary
|
|
28
|
+
short
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const className = screen.getByRole('button').className;
|
|
33
|
+
className.should.include(styles.active);
|
|
34
|
+
className.should.include(styles.danger);
|
|
35
|
+
className.should.include(styles.delayed);
|
|
36
|
+
className.should.include(styles.loader);
|
|
37
|
+
className.should.include(styles.primary);
|
|
38
|
+
className.should.include(styles.short);
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
it('should add icon', () => {
|
|
40
|
-
|
|
41
|
-
icon: caretDownSVG
|
|
42
|
-
});
|
|
42
|
+
render(<Button icon={caretDownSVG}/>);
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const element = screen.getByRole('button');
|
|
45
|
+
element.should.have.class(styles.withIcon);
|
|
45
46
|
caretDownSVG.
|
|
46
47
|
replace('/>', '></path>').
|
|
47
|
-
should.include(
|
|
48
|
+
should.include(element.querySelector('svg').innerHTML);
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
it('should set custom class', () => {
|
|
51
52
|
const CUSTOM_CLASS = 'test';
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
className: CUSTOM_CLASS
|
|
55
|
-
});
|
|
54
|
+
render(<Button className={CUSTOM_CLASS}/>);
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
screen.getByRole('button').should.have.class(CUSTOM_CLASS);
|
|
58
57
|
});
|
|
59
58
|
|
|
60
59
|
it('should render link instead of button if href specified', () => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
render(<Button href="http://www.jetbrains.com"/>);
|
|
61
|
+
|
|
62
|
+
screen.getByRole('link').should.have.attr('href', 'http://www.jetbrains.com');
|
|
64
63
|
});
|
|
65
64
|
});
|