@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
|
@@ -13,7 +13,7 @@ import Button from '../button/button';
|
|
|
13
13
|
|
|
14
14
|
import {PopupTarget} from '../popup/popup.target';
|
|
15
15
|
|
|
16
|
-
import
|
|
16
|
+
import {preventerFactory as scrollPreventerFactory} from './dialog__body-scroll-preventer';
|
|
17
17
|
import styles from './dialog.css';
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -24,6 +24,7 @@ function noop() {}
|
|
|
24
24
|
|
|
25
25
|
export default class Dialog extends PureComponent {
|
|
26
26
|
static propTypes = {
|
|
27
|
+
label: PropTypes.string,
|
|
27
28
|
className: PropTypes.string,
|
|
28
29
|
contentClassName: PropTypes.string,
|
|
29
30
|
children: PropTypes.oneOfType([
|
|
@@ -47,6 +48,7 @@ export default class Dialog extends PureComponent {
|
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
static defaultProps = {
|
|
51
|
+
label: 'Dialog',
|
|
50
52
|
onOverlayClick: noop,
|
|
51
53
|
onEscPress: noop,
|
|
52
54
|
onCloseClick: noop,
|
|
@@ -72,16 +74,18 @@ export default class Dialog extends PureComponent {
|
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
componentWillUnmount() {
|
|
75
|
-
|
|
77
|
+
this.scrollPreventer.reset();
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
scrollPreventer = scrollPreventerFactory(getUID('preventer-'));
|
|
81
|
+
|
|
78
82
|
uid = getUID('dialog-');
|
|
79
83
|
|
|
80
84
|
toggleScrollPreventer() {
|
|
81
85
|
if (this.props.show) {
|
|
82
|
-
|
|
86
|
+
this.scrollPreventer.prevent();
|
|
83
87
|
} else {
|
|
84
|
-
|
|
88
|
+
this.scrollPreventer.reset();
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -115,7 +119,7 @@ export default class Dialog extends PureComponent {
|
|
|
115
119
|
render() {
|
|
116
120
|
const {show, showCloseButton, onOverlayClick, onCloseAttempt, onEscPress, onCloseClick,
|
|
117
121
|
children, className, contentClassName, trapFocus, 'data-test': dataTest, closeButtonInside,
|
|
118
|
-
portalTarget, ...restProps} = this.props;
|
|
122
|
+
portalTarget, label, ...restProps} = this.props;
|
|
119
123
|
const classes = classNames(styles.container, className);
|
|
120
124
|
const shortcutsMap = this.getShortcutsMap();
|
|
121
125
|
|
|
@@ -148,6 +152,7 @@ export default class Dialog extends PureComponent {
|
|
|
148
152
|
className={classNames(styles.content, contentClassName)}
|
|
149
153
|
data-test="ring-dialog"
|
|
150
154
|
role="dialog"
|
|
155
|
+
aria-label={label}
|
|
151
156
|
>
|
|
152
157
|
{children}
|
|
153
158
|
{showCloseButton &&
|
|
@@ -6,7 +6,7 @@ import styles from './dialog.css';
|
|
|
6
6
|
|
|
7
7
|
describe('Dialog', () => {
|
|
8
8
|
const children = <div/>;
|
|
9
|
-
const mountDialog = props => mount(<Dialog {...props} trapFocus={false}/>);
|
|
9
|
+
const mountDialog = props => mount(<Dialog label="Dialog" {...props} trapFocus={false}/>);
|
|
10
10
|
|
|
11
11
|
it('should create component', () => {
|
|
12
12
|
mountDialog({show: true, children}).should.have.type(Dialog);
|
|
@@ -2,38 +2,58 @@ import scrollbarWidth from 'scrollbar-width';
|
|
|
2
2
|
|
|
3
3
|
import styles from './dialog.css';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const isPrevented = new Set();
|
|
6
6
|
let previousDocumentWidth = null;
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
8
|
+
const prevent = key => {
|
|
9
|
+
if (isPrevented.has(key)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
isPrevented.add(key);
|
|
14
|
+
|
|
15
|
+
if (isPrevented.size > 1) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const documentHasScroll = document.documentElement.scrollHeight > window.innerHeight ||
|
|
20
|
+
getComputedStyle(document.documentElement).overflowY === 'scroll';
|
|
21
|
+
document.documentElement.classList.add(styles.documentWithoutScroll);
|
|
22
|
+
|
|
23
|
+
const scrollWidth = scrollbarWidth();
|
|
24
|
+
|
|
25
|
+
if (documentHasScroll && scrollWidth > 0) {
|
|
26
|
+
previousDocumentWidth = document.documentElement.style.width;
|
|
27
|
+
document.documentElement.style.width = `calc(100% - ${scrollWidth}px)`;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const reset = key => {
|
|
32
|
+
if (isPrevented.size === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
isPrevented.delete(key);
|
|
37
|
+
|
|
38
|
+
if (isPrevented.size > 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
document.documentElement.classList.remove(styles.documentWithoutScroll);
|
|
43
|
+
|
|
44
|
+
if (previousDocumentWidth !== null) {
|
|
45
|
+
document.documentElement.style.width = previousDocumentWidth;
|
|
46
|
+
previousDocumentWidth = null;
|
|
38
47
|
}
|
|
39
48
|
};
|
|
49
|
+
|
|
50
|
+
export const preventerFactory = key => {
|
|
51
|
+
const preventerKey = key || Math.random();
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
prevent: () => prevent(preventerKey),
|
|
55
|
+
reset: () => reset(preventerKey)
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default preventerFactory('default-preventer');
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import angular from 'angular';
|
|
2
2
|
|
|
3
|
-
import angularSanitize from 'angular-sanitize';
|
|
4
|
-
|
|
5
3
|
import {createFocusTrap} from 'focus-trap';
|
|
6
4
|
|
|
7
5
|
import {getRect, getStyles} from '../global/dom';
|
|
@@ -10,12 +8,13 @@ import shortcuts from '../shortcuts/core';
|
|
|
10
8
|
import RingButton from '../button-ng/button-ng';
|
|
11
9
|
import PromisedClickNg from '../promised-click-ng/promised-click-ng';
|
|
12
10
|
import rgCompilerModuleName from '../compiler-ng/compiler-ng';
|
|
13
|
-
import
|
|
11
|
+
import {preventerFactory as scrollPreventerFactory} from '../dialog/dialog__body-scroll-preventer';
|
|
14
12
|
import '../form/form.css';
|
|
15
13
|
import dialogStyles from '../dialog/dialog.css';
|
|
16
14
|
import islandStyles from '../island/island.css';
|
|
17
15
|
|
|
18
16
|
import styles from './dialog-ng.css';
|
|
17
|
+
import dialogTemplate from './dialog-ng__template';
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* @name Dialog Ng
|
|
@@ -23,12 +22,12 @@ import styles from './dialog-ng.css';
|
|
|
23
22
|
|
|
24
23
|
const angularModule = angular.module(
|
|
25
24
|
'Ring.dialog',
|
|
26
|
-
[
|
|
25
|
+
[RingButton, PromisedClickNg, rgCompilerModuleName]
|
|
27
26
|
);
|
|
28
27
|
|
|
29
28
|
class DialogController extends RingAngularComponent {
|
|
30
29
|
static $inject = ['$scope', '$q', 'dialog', '$element', 'dialogInSidebar', '$compile',
|
|
31
|
-
'$injector', '$controller', 'rgCompiler'];
|
|
30
|
+
'$injector', '$controller', 'rgCompiler', '$sce'];
|
|
32
31
|
|
|
33
32
|
constructor(...args) {
|
|
34
33
|
super(...args);
|
|
@@ -36,6 +35,7 @@ class DialogController extends RingAngularComponent {
|
|
|
36
35
|
this.styles = styles;
|
|
37
36
|
this.dialogStyles = dialogStyles;
|
|
38
37
|
this.islandStyles = islandStyles;
|
|
38
|
+
this.scrollPreventer = scrollPreventerFactory();
|
|
39
39
|
|
|
40
40
|
this.$inject.$q((resolve, reject) => {
|
|
41
41
|
this.resolve = resolve;
|
|
@@ -134,7 +134,7 @@ class DialogController extends RingAngularComponent {
|
|
|
134
134
|
const {$q, $scope} = this.$inject;
|
|
135
135
|
|
|
136
136
|
if (!this.inSidebar) {
|
|
137
|
-
|
|
137
|
+
this.scrollPreventer.prevent();
|
|
138
138
|
if (config && config.trapFocus === true) {
|
|
139
139
|
this.focusTrap.activate();
|
|
140
140
|
}
|
|
@@ -195,7 +195,7 @@ class DialogController extends RingAngularComponent {
|
|
|
195
195
|
|
|
196
196
|
hide = () => {
|
|
197
197
|
if (!this.inSidebar) {
|
|
198
|
-
|
|
198
|
+
this.scrollPreventer.reset();
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
this.active = false;
|
|
@@ -272,7 +272,7 @@ class DialogController extends RingAngularComponent {
|
|
|
272
272
|
}];
|
|
273
273
|
this.serverErrorFields.push(errorField);
|
|
274
274
|
} else {
|
|
275
|
-
this.error = this.getErrorMessage(errorResponse);
|
|
275
|
+
this.error = this.$inject.$sce.trustAsHtml(this.getErrorMessage(errorResponse));
|
|
276
276
|
}
|
|
277
277
|
};
|
|
278
278
|
|
|
@@ -364,7 +364,7 @@ class DialogService extends RingAngularComponent {
|
|
|
364
364
|
|
|
365
365
|
unregister = () => {
|
|
366
366
|
Reflect.deleteProperty(this, 'ctrl');
|
|
367
|
-
}
|
|
367
|
+
};
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
class DialogInSidebarService extends DialogService {
|
|
@@ -490,7 +490,7 @@ function rgDialogDirective($timeout) {
|
|
|
490
490
|
active: '=?'
|
|
491
491
|
},
|
|
492
492
|
replace: true,
|
|
493
|
-
template:
|
|
493
|
+
template: dialogTemplate,
|
|
494
494
|
controllerAs: 'dialog',
|
|
495
495
|
link
|
|
496
496
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export default `<div
|
|
2
2
|
ng-show="dialog.active"
|
|
3
3
|
ng-class="[!dialog.inSidebar && dialog.dialogStyles.container]"
|
|
4
4
|
ng-click="dialog.handleClick($event)"
|
|
@@ -66,4 +66,4 @@
|
|
|
66
66
|
<div tabindex="-1" ng-show="false" data-anchor="focus-trap-fallback"></div>
|
|
67
67
|
</div>
|
|
68
68
|
</div>
|
|
69
|
-
</div
|
|
69
|
+
</div>`;
|
|
@@ -3,12 +3,14 @@ import chevronDownIcon from '@jetbrains/icons/chevron-down';
|
|
|
3
3
|
|
|
4
4
|
import reactDecorator from '../../.storybook/react-decorator';
|
|
5
5
|
|
|
6
|
+
import {ActiveItemContext} from '../list/list';
|
|
7
|
+
|
|
6
8
|
import Popup from '@jetbrains/ring-ui/components/popup/popup';
|
|
7
9
|
import PopupMenu from '@jetbrains/ring-ui/components/popup-menu/popup-menu';
|
|
8
10
|
import Button from '@jetbrains/ring-ui/components/button/button';
|
|
9
11
|
import Link from '@jetbrains/ring-ui/components/link/link';
|
|
10
12
|
import {Input} from '@jetbrains/ring-ui/components/input/input';
|
|
11
|
-
|
|
13
|
+
import getUID from '@jetbrains/ring-ui/components/global/get-uid';
|
|
12
14
|
import Dropdown from '@jetbrains/ring-ui/components/dropdown/dropdown';
|
|
13
15
|
|
|
14
16
|
export default {
|
|
@@ -45,6 +47,39 @@ export const withCustomAnchorAndPopup = () => (
|
|
|
45
47
|
</Dropdown>
|
|
46
48
|
);
|
|
47
49
|
|
|
50
|
+
export const withCustomAnchorAndPopupAndContentAccessibilityHandling = () => {
|
|
51
|
+
const listId = getUID('popup-menu-list-id');
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<ActiveItemContext.Provider>
|
|
55
|
+
<Dropdown anchor={({active}) => (
|
|
56
|
+
<ActiveItemContext.ValueContext.Consumer>
|
|
57
|
+
{activeItemId => {
|
|
58
|
+
const anchorAriaProps = active && activeItemId
|
|
59
|
+
? {'aria-owns': listId, 'aria-activedescendant': activeItemId}
|
|
60
|
+
: {};
|
|
61
|
+
return (
|
|
62
|
+
<Button
|
|
63
|
+
{...anchorAriaProps}
|
|
64
|
+
delayed
|
|
65
|
+
>Edit</Button>
|
|
66
|
+
);
|
|
67
|
+
}}
|
|
68
|
+
</ActiveItemContext.ValueContext.Consumer>
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
<PopupMenu
|
|
72
|
+
id={listId}
|
|
73
|
+
ariaLabel="My options menu"
|
|
74
|
+
closeOnSelect
|
|
75
|
+
activateFirstItem
|
|
76
|
+
data={['Cut', 'Copy', 'Paste'].map(label => ({label, key: label.toLowerCase()}))}
|
|
77
|
+
/>
|
|
78
|
+
</Dropdown>
|
|
79
|
+
</ActiveItemContext.Provider>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
48
83
|
withCustomAnchorAndPopup.storyName = 'with custom anchor and popup';
|
|
49
84
|
|
|
50
85
|
export const withActiveClassName = () => (
|
|
@@ -76,11 +76,11 @@ describe('Dropdown', () => {
|
|
|
76
76
|
popup.isVisible().should.be.false;
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
it('should hide popup on outside
|
|
79
|
+
it('should hide popup on outside pointer down event', done => {
|
|
80
80
|
mountDropdown();
|
|
81
81
|
Simulate.click(anchor);
|
|
82
82
|
setTimeout(() => {
|
|
83
|
-
document.dispatchEvent(new
|
|
83
|
+
document.dispatchEvent(new PointerEvent('pointerdown'));
|
|
84
84
|
popup.isVisible().should.be.false;
|
|
85
85
|
done();
|
|
86
86
|
}, 0);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import reactDecorator from '../../.storybook/react-decorator';
|
|
4
|
+
|
|
5
|
+
import DropdownMenu from '@jetbrains/ring-ui/components/dropdown-menu/dropdown-menu';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Components/DropdownMenu',
|
|
9
|
+
decorators: [reactDecorator()],
|
|
10
|
+
|
|
11
|
+
parameters: {
|
|
12
|
+
notes: 'Displays a menu in a dropdown.',
|
|
13
|
+
hermione: {
|
|
14
|
+
actions: [
|
|
15
|
+
{type: 'click', selector: '[data-test~=ring-dropdown]'},
|
|
16
|
+
{
|
|
17
|
+
type: 'capture',
|
|
18
|
+
name: 'dropdown',
|
|
19
|
+
selector: ['[data-test~=ring-dropdown]', '[data-test~=ring-popup]']
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
a11y: {element: '*[data-test~=ring-dropdown]'}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const basic = () => {
|
|
28
|
+
const data = [
|
|
29
|
+
{label: 'Item'},
|
|
30
|
+
{label: 'Link to jetbrains.com', href: 'http://www.jetbrains.com'},
|
|
31
|
+
{rgItemType: DropdownMenu.ListProps.Type.SEPARATOR},
|
|
32
|
+
{rgItemType: DropdownMenu.ListProps.Type.LINK, label: 'Link Item'},
|
|
33
|
+
{
|
|
34
|
+
rgItemType: DropdownMenu.ListProps.Type.LINK,
|
|
35
|
+
label: 'Link Item With Additional Class',
|
|
36
|
+
className: 'test'
|
|
37
|
+
},
|
|
38
|
+
{rgItemType: DropdownMenu.ListProps.Type.SEPARATOR, description: 'Separator With Description'},
|
|
39
|
+
{rgItemType: DropdownMenu.ListProps.Type.TITLE, label: 'Title'},
|
|
40
|
+
{rgItemType: DropdownMenu.ListProps.Type.ITEM, label: '1 Element in group'},
|
|
41
|
+
{rgItemType: DropdownMenu.ListProps.Type.ITEM, label: '2 Element in group'}
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
return <DropdownMenu data={data} anchor={'Click me!'}/>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
basic.storyName = 'DropdownMenu';
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, {useMemo, cloneElement} from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import List, {ActiveItemContext} from '../list/list';
|
|
5
|
+
import Dropdown from '../dropdown/dropdown';
|
|
6
|
+
import PopupMenu from '../popup-menu/popup-menu';
|
|
7
|
+
import getUID from '../global/get-uid';
|
|
8
|
+
import Anchor from '../dropdown/anchor';
|
|
9
|
+
|
|
10
|
+
const {children, ...dropdownPropTypes} = Dropdown.propTypes || {};
|
|
11
|
+
const {
|
|
12
|
+
id: idPropType,
|
|
13
|
+
data: dataPropType,
|
|
14
|
+
ariaLabel: ariaLabelPropType,
|
|
15
|
+
onSelect: onSelectPropType
|
|
16
|
+
} = PopupMenu.propTypes || {};
|
|
17
|
+
|
|
18
|
+
const defaultAriaLabel = 'Dropdown menu';
|
|
19
|
+
|
|
20
|
+
function DropdownAnchorWrapper({anchor, pinned, active, activeListItemId, listId, ...restProps}) {
|
|
21
|
+
const anchorAriaProps = useMemo(() => ({
|
|
22
|
+
...(listId ? {'aria-haspopup': 'true'} : {}),
|
|
23
|
+
...(activeListItemId ? {'aria-activedescendant': activeListItemId, 'aria-owns': listId} : {}),
|
|
24
|
+
...(active ? {'aria-expanded': 'true'} : {})
|
|
25
|
+
}), [active, activeListItemId, listId]);
|
|
26
|
+
|
|
27
|
+
const anchorProps = useMemo(
|
|
28
|
+
() => ({active, pinned, ...restProps, ...anchorAriaProps}),
|
|
29
|
+
[pinned, active, restProps, anchorAriaProps]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const anchorComponentProps = useMemo(
|
|
33
|
+
() => ({...anchorProps, pinned: `${anchorProps.pinned}`}),
|
|
34
|
+
[anchorProps]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (typeof anchor === 'string') {
|
|
38
|
+
return (
|
|
39
|
+
<Anchor
|
|
40
|
+
{...anchorComponentProps}
|
|
41
|
+
>{anchor}</Anchor>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (typeof anchor === 'function') {
|
|
45
|
+
return anchor(({active, pinned, ...restProps}), anchorAriaProps);
|
|
46
|
+
}
|
|
47
|
+
if (!Array.isArray(anchor)) {
|
|
48
|
+
return cloneElement(
|
|
49
|
+
anchor,
|
|
50
|
+
typeof anchor.type === 'string' ? anchorAriaProps : anchorComponentProps
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return (
|
|
54
|
+
<div {...anchorAriaProps}>{anchor}</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
DropdownAnchorWrapper.propTypes = {
|
|
59
|
+
anchor: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.func]).isRequired,
|
|
60
|
+
pinned: PropTypes.bool,
|
|
61
|
+
active: PropTypes.bool,
|
|
62
|
+
activeListItemId: PropTypes.string,
|
|
63
|
+
listId: PropTypes.string
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const DropdownMenu = React.forwardRef(function DropdownMenu(
|
|
67
|
+
{id, anchor, ariaLabel, data, onSelect, menuProps, ...restDropdownProps},
|
|
68
|
+
forwardedRef
|
|
69
|
+
) {
|
|
70
|
+
const listId = useMemo(() => id || getUID('dropdown-menu-list'), [id]);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<ActiveItemContext.Provider>
|
|
74
|
+
<Dropdown
|
|
75
|
+
anchor={({pinned, active, ...restAnchorProps}) => (
|
|
76
|
+
<ActiveItemContext.ValueContext.Consumer>
|
|
77
|
+
{activeItemId => (
|
|
78
|
+
<DropdownAnchorWrapper
|
|
79
|
+
anchor={anchor}
|
|
80
|
+
pinned={pinned}
|
|
81
|
+
active={active}
|
|
82
|
+
activeListItemId={activeItemId}
|
|
83
|
+
listId={listId}
|
|
84
|
+
{...restAnchorProps}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
87
|
+
</ActiveItemContext.ValueContext.Consumer>
|
|
88
|
+
)}
|
|
89
|
+
{...restDropdownProps}
|
|
90
|
+
>
|
|
91
|
+
<PopupMenu
|
|
92
|
+
ref={forwardedRef}
|
|
93
|
+
id={listId}
|
|
94
|
+
ariaLabel={ariaLabel || defaultAriaLabel}
|
|
95
|
+
closeOnSelect
|
|
96
|
+
activateFirstItem
|
|
97
|
+
data={data}
|
|
98
|
+
onSelect={onSelect}
|
|
99
|
+
{...menuProps}
|
|
100
|
+
/>
|
|
101
|
+
</Dropdown>
|
|
102
|
+
</ActiveItemContext.Provider>
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
DropdownMenu.propTypes = {
|
|
107
|
+
id: idPropType,
|
|
108
|
+
data: dataPropType,
|
|
109
|
+
ariaLabel: ariaLabelPropType,
|
|
110
|
+
onSelect: onSelectPropType,
|
|
111
|
+
menuProps: PropTypes.object,
|
|
112
|
+
...dropdownPropTypes
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
DropdownMenu.ListProps = List.ListProps;
|
|
116
|
+
|
|
117
|
+
export default DropdownMenu;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {shallow, mount} from 'enzyme';
|
|
3
|
+
|
|
4
|
+
import PopupMenu from '../popup-menu/popup-menu';
|
|
5
|
+
import Anchor from '../dropdown/anchor';
|
|
6
|
+
|
|
7
|
+
import DropdownMenu from './dropdown-menu';
|
|
8
|
+
|
|
9
|
+
const waitForCondition = (condition, rejectMessage) => new Promise((resolve, reject) => {
|
|
10
|
+
const interval = 10;
|
|
11
|
+
const maxWaitingTime = 2000;
|
|
12
|
+
let remainingTime = maxWaitingTime;
|
|
13
|
+
|
|
14
|
+
const intervalId = setInterval(() => {
|
|
15
|
+
if (condition()) {
|
|
16
|
+
clearInterval(intervalId);
|
|
17
|
+
resolve();
|
|
18
|
+
} else if (remainingTime < 0) {
|
|
19
|
+
clearInterval(intervalId);
|
|
20
|
+
reject(new Error(rejectMessage));
|
|
21
|
+
} else {
|
|
22
|
+
remainingTime -= interval;
|
|
23
|
+
}
|
|
24
|
+
}, interval);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('Dropdown Menu', () => {
|
|
28
|
+
const shallowDropdownMenu = props => shallow(<DropdownMenu id="test-list-id" {...props}/>);
|
|
29
|
+
const mountDropdownMenu = props => mount(<DropdownMenu id="test-list-id" {...props}/>);
|
|
30
|
+
|
|
31
|
+
const mountAndWaitForMenuContent = async props => {
|
|
32
|
+
const wrapper = mountDropdownMenu(props);
|
|
33
|
+
|
|
34
|
+
wrapper.find('button').getDOMNode().click();
|
|
35
|
+
await waitForCondition(
|
|
36
|
+
() => !!wrapper.find(PopupMenu).length,
|
|
37
|
+
'List was not rendered in a dropdown menu'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return wrapper;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
it('should create component', () => {
|
|
44
|
+
shallowDropdownMenu({anchor: 'Anchor text'}).should.exist;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should open List', async () => {
|
|
48
|
+
const wrapper = await mountAndWaitForMenuContent({anchor: 'Anchor text'});
|
|
49
|
+
|
|
50
|
+
const list = wrapper.find(PopupMenu).instance().list;
|
|
51
|
+
list.should.exist;
|
|
52
|
+
|
|
53
|
+
//We need it to maintain compatibility between Dropdown Menu and List
|
|
54
|
+
list.props.data.length.should.equal(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should pass params to List', async () => {
|
|
58
|
+
const wrapper = await mountAndWaitForMenuContent({
|
|
59
|
+
anchor: 'Anchor text',
|
|
60
|
+
data: [{key: 'key1'}]
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
shallow(wrapper.find(PopupMenu).instance().list.renderItem({index: 1})).should.exist;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should add accessibility attributes to anchor', async () => {
|
|
67
|
+
const wrapper = await mountAndWaitForMenuContent({
|
|
68
|
+
anchor: 'Anchor text',
|
|
69
|
+
data: [{key: 'key1'}, {key: 'key2'}]
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const anchorProps = wrapper.update().find(Anchor).props();
|
|
73
|
+
anchorProps['aria-owns'].should.equal('test-list-id');
|
|
74
|
+
anchorProps['aria-activedescendant'].should.contain(':key1');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -4,8 +4,6 @@ import RingAngularComponent from '../global/ring-angular-component';
|
|
|
4
4
|
import styles from '../footer/footer.css';
|
|
5
5
|
import {copyright} from '../footer/footer';
|
|
6
6
|
|
|
7
|
-
import template from './footer-ng.html';
|
|
8
|
-
|
|
9
7
|
/**
|
|
10
8
|
* @name Footer Ng
|
|
11
9
|
*/
|
|
@@ -22,7 +20,19 @@ class rgFooterComponent extends RingAngularComponent {
|
|
|
22
20
|
right: '?rgFooterRight'
|
|
23
21
|
};
|
|
24
22
|
|
|
25
|
-
static template =
|
|
23
|
+
static template = `<footer ng-class=":: $ctrl.styles.footer" data-test="ring-footer">
|
|
24
|
+
<div ng-class=":: $ctrl.styles.columnLeft">
|
|
25
|
+
<div ng-transclude="left" ng-class=":: $ctrl.styles.columnItem"></div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div ng-class=":: $ctrl.styles.columnCenter">
|
|
29
|
+
<div ng-transclude="center" ng-class=":: $ctrl.styles.columnItem"></div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div ng-class=":: $ctrl.styles.columnRight">
|
|
33
|
+
<div ng-transclude="right" ng-class=":: $ctrl.styles.columnItem"></div>
|
|
34
|
+
</div>
|
|
35
|
+
</footer>`;
|
|
26
36
|
}
|
|
27
37
|
|
|
28
38
|
class rgFooterLineComponent extends RingAngularComponent {
|
package/components/form/form.css
CHANGED
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
display: block;
|
|
97
97
|
clear: both;
|
|
98
98
|
|
|
99
|
-
content:
|
|
99
|
+
content: "";
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
:global(.ring-form__group_united) {
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
|
|
168
168
|
height: calc(unit * 3);
|
|
169
169
|
|
|
170
|
-
content:
|
|
170
|
+
content: "";
|
|
171
171
|
vertical-align: middle;
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -54,7 +54,9 @@ angularModule.directive('rgErrorBubble', function rgErrorBubbleDirective(getForm
|
|
|
54
54
|
},
|
|
55
55
|
|
|
56
56
|
replace: true,
|
|
57
|
-
template:
|
|
57
|
+
template: `<div class="ring-error-bubble" ng-class="{ active: active, 'ring-error-bubble_material': material }" ng-style="style">
|
|
58
|
+
<div ng-repeat="errorMessage in getFormErrorMessages(errorBubble().$error)">{{ errorMessage }}</div>
|
|
59
|
+
</div>`,
|
|
58
60
|
|
|
59
61
|
link: function link(scope, iElement, iAttrs) {
|
|
60
62
|
scope.style = {};
|