@jetbrains/ring-ui 4.1.8 → 4.1.12

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.
@@ -516,19 +516,17 @@ describe('Auth', () => {
516
516
  }
517
517
  });
518
518
 
519
- it('should show login overlay if token refresh fails and window login enabled', async () => {
519
+ it('should show login overlay if token refresh fails and window login enabled', done => {
520
520
  auth._backgroundFlow._timeout = 100;
521
521
  sandbox.stub(BackgroundFlow.prototype, '_redirectFrame');
522
522
  sandbox.stub(Auth.prototype, '_showAuthDialog');
523
523
 
524
- try {
525
- await auth.requestToken();
526
- } catch (reject) {
527
- Auth.prototype._showAuthDialog.should.have.been.calledWith({
528
- nonInteractive: true,
529
- error: reject
530
- });
531
- }
524
+ auth.requestToken();
525
+
526
+ setTimeout(() => {
527
+ Auth.prototype._showAuthDialog.should.have.been.called;
528
+ done();
529
+ }, auth._backgroundFlow._timeout * 2);
532
530
  });
533
531
  });
534
532
 
@@ -52,6 +52,7 @@ const DEFAULT_CONFIG = {
52
52
  login: 'Log in',
53
53
  loginTo: 'Log in to %serviceName%',
54
54
  cancel: 'Cancel',
55
+ tryAgainLabel: 'Try again',
55
56
  postpone: 'Postpone',
56
57
  youHaveLoggedInAs: 'You have logged in as another user: %userName%',
57
58
  applyChange: 'Apply change',
@@ -405,9 +406,27 @@ export default class Auth {
405
406
  try {
406
407
  return await this._backgroundFlow.authorize();
407
408
  } catch (error) {
408
-
409
409
  if (this._canShowDialogs()) {
410
- return this._showAuthDialog({nonInteractive: true, error});
410
+ return new Promise(resolve => {
411
+ const onTryAgain = async () => {
412
+ try {
413
+ const result = await this._backgroundFlow.authorize();
414
+ resolve(result);
415
+ } catch (retryError) {
416
+ this._showAuthDialog({
417
+ nonInteractive: true,
418
+ error: retryError,
419
+ onTryAgain
420
+ });
421
+ throw retryError;
422
+ }
423
+ };
424
+ this._showAuthDialog({
425
+ nonInteractive: true,
426
+ error,
427
+ onTryAgain
428
+ });
429
+ });
411
430
  } else {
412
431
  const authRequest = await this._requestBuilder.prepareAuthRequest();
413
432
  this._redirectCurrentPage(authRequest.url);
@@ -516,7 +535,7 @@ export default class Auth {
516
535
  this.logout();
517
536
  }
518
537
 
519
- _showAuthDialog({nonInteractive, error, canCancel} = {}) {
538
+ _showAuthDialog({nonInteractive, error, canCancel, onTryAgain} = {}) {
520
539
  const {embeddedLogin, onPostponeLogout, translations} = this.config;
521
540
  const cancelable = this.user.guest || canCancel;
522
541
 
@@ -557,15 +576,22 @@ export default class Auth {
557
576
  }
558
577
  };
559
578
 
579
+ const onTryAgainClick = async () => {
580
+ await onTryAgain();
581
+ closeDialog();
582
+ };
583
+
560
584
  const hide = this._authDialogService({
561
585
  ...this._service,
562
586
  loginCaption: translations.login,
563
587
  loginToCaption: translations.loginTo,
564
588
  confirmLabel: translations.login,
589
+ tryAgainLabel: translations.tryAgainLabel,
565
590
  cancelLabel: cancelable ? translations.cancel : translations.postpone,
566
591
  errorMessage: this._extractErrorMessage(error, true),
567
592
  onConfirm,
568
- onCancel
593
+ onCancel,
594
+ onTryAgain: onTryAgain ? onTryAgainClick : null
569
595
  });
570
596
 
571
597
  const stopTokenListening = this._storage.onTokenChange(token => {
@@ -28,9 +28,11 @@ export default class AuthDialog extends Component {
28
28
  cancelOnEsc: PropTypes.bool,
29
29
  confirmLabel: PropTypes.string,
30
30
  cancelLabel: PropTypes.string,
31
+ tryAgainLabel: PropTypes.string,
31
32
 
32
33
  onConfirm: PropTypes.func,
33
- onCancel: PropTypes.func
34
+ onCancel: PropTypes.func,
35
+ onTryAgain: PropTypes.func
34
36
  };
35
37
 
36
38
  static defaultProps = {
@@ -44,12 +46,38 @@ export default class AuthDialog extends Component {
44
46
  onCancel: () => {}
45
47
  };
46
48
 
49
+ state = {
50
+ retrying: false
51
+ };
52
+
53
+ componentDidMount() {
54
+ window.addEventListener('online', this.onRetryPress);
55
+ }
56
+
57
+ componentWillUnmount() {
58
+ window.removeEventListener('online', this.onRetryPress);
59
+ }
60
+
47
61
  onEscPress = () => {
48
62
  if (this.props.cancelOnEsc) {
49
63
  this.props.onCancel();
50
64
  }
51
65
  };
52
66
 
67
+ onRetryPress = async () => {
68
+ if (!this.props.onTryAgain || this.state.retrying) {
69
+ return;
70
+ }
71
+ this.setState({retrying: true});
72
+ try {
73
+ await this.props.onTryAgain();
74
+ } catch (e) {
75
+ // do nothing, error is handled in onTryAgain
76
+ } finally {
77
+ this.setState({retrying: false});
78
+ }
79
+ };
80
+
53
81
  render() {
54
82
  const {
55
83
  show,
@@ -61,10 +89,14 @@ export default class AuthDialog extends Component {
61
89
  loginToCaption,
62
90
  confirmLabel,
63
91
  cancelLabel,
92
+ tryAgainLabel,
64
93
  onConfirm,
65
- onCancel
94
+ onCancel,
95
+ onTryAgain
66
96
  } = this.props;
67
97
 
98
+ const {retrying} = this.state;
99
+
68
100
  const defaultTitle = serviceName ? loginToCaption : loginCaption;
69
101
  const title = (this.props.title || defaultTitle).replace('%serviceName%', serviceName);
70
102
 
@@ -99,6 +131,17 @@ export default class AuthDialog extends Component {
99
131
  >
100
132
  {confirmLabel}
101
133
  </Button>
134
+ {onTryAgain && (
135
+ <Button
136
+ className={styles.button}
137
+ data-test="auth-dialog-retry-button"
138
+ onClick={() => this.onRetryPress()}
139
+ loader={retrying}
140
+ disabled={retrying}
141
+ >
142
+ {tryAgainLabel}
143
+ </Button>
144
+ )}
102
145
  <Button
103
146
  className={styles.button}
104
147
  data-test="auth-dialog-cancel-button"
@@ -38,11 +38,11 @@ class SelectLazy {
38
38
  }
39
39
 
40
40
  attachEvents() {
41
- this.container.addEventListener('click', this.onClick);
41
+ this.container.addEventListener('click', this.onClick, {capture: true});
42
42
  }
43
43
 
44
44
  detachEvents() {
45
- this.container.removeEventListener('click', this.onClick);
45
+ this.container.removeEventListener('click', this.onClick, {capture: true});
46
46
  }
47
47
 
48
48
  render(props) {
@@ -59,10 +59,10 @@ class SelectLazy {
59
59
  this.detachEvents();
60
60
  if (this.type === 'dropdown') {
61
61
  this.ctrl.selectInstance = render(this.reactSelect, this.container);
62
+ this.ctrl.selectInstance._openPopupIfClosed();
62
63
  } else {
63
64
  this.ctrl.selectInstance = hydrate(this.reactSelect, this.container);
64
65
  }
65
- this.ctrl.selectInstance._openPopupIfClosed();
66
66
  }
67
67
  }
68
68
 
@@ -86,6 +86,7 @@ const DEFAULT_CONFIG = {
86
86
  login: 'Log in',
87
87
  loginTo: 'Log in to %serviceName%',
88
88
  cancel: 'Cancel',
89
+ tryAgainLabel: 'Try again',
89
90
  postpone: 'Postpone',
90
91
  youHaveLoggedInAs: 'You have logged in as another user: %userName%',
91
92
  applyChange: 'Apply change',
@@ -475,9 +476,27 @@ class Auth {
475
476
  return await this._backgroundFlow.authorize();
476
477
  } catch (error) {
477
478
  if (this._canShowDialogs()) {
478
- return this._showAuthDialog({
479
- nonInteractive: true,
480
- error
479
+ return new Promise(resolve => {
480
+ const onTryAgain = async () => {
481
+ try {
482
+ const result = await this._backgroundFlow.authorize();
483
+ resolve(result);
484
+ } catch (retryError) {
485
+ this._showAuthDialog({
486
+ nonInteractive: true,
487
+ error: retryError,
488
+ onTryAgain
489
+ });
490
+
491
+ throw retryError;
492
+ }
493
+ };
494
+
495
+ this._showAuthDialog({
496
+ nonInteractive: true,
497
+ error,
498
+ onTryAgain
499
+ });
481
500
  });
482
501
  } else {
483
502
  const authRequest = await this._requestBuilder.prepareAuthRequest();
@@ -603,7 +622,8 @@ class Auth {
603
622
  let {
604
623
  nonInteractive,
605
624
  error,
606
- canCancel
625
+ canCancel,
626
+ onTryAgain
607
627
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
608
628
  const {
609
629
  embeddedLogin,
@@ -654,14 +674,21 @@ class Auth {
654
674
  }
655
675
  };
656
676
 
677
+ const onTryAgainClick = async () => {
678
+ await onTryAgain();
679
+ closeDialog();
680
+ };
681
+
657
682
  const hide = this._authDialogService({ ...this._service,
658
683
  loginCaption: translations.login,
659
684
  loginToCaption: translations.loginTo,
660
685
  confirmLabel: translations.login,
686
+ tryAgainLabel: translations.tryAgainLabel,
661
687
  cancelLabel: cancelable ? translations.cancel : translations.postpone,
662
688
  errorMessage: this._extractErrorMessage(error, true),
663
689
  onConfirm,
664
- onCancel
690
+ onCancel,
691
+ onTryAgain: onTryAgain ? onTryAgainClick : null
665
692
  });
666
693
 
667
694
  const stopTokenListening = this._storage.onTokenChange(token => {
@@ -49,11 +49,42 @@ class AuthDialog extends Component {
49
49
  constructor() {
50
50
  super(...arguments);
51
51
 
52
+ _defineProperty(this, "state", {
53
+ retrying: false
54
+ });
55
+
52
56
  _defineProperty(this, "onEscPress", () => {
53
57
  if (this.props.cancelOnEsc) {
54
58
  this.props.onCancel();
55
59
  }
56
60
  });
61
+
62
+ _defineProperty(this, "onRetryPress", async () => {
63
+ if (!this.props.onTryAgain || this.state.retrying) {
64
+ return;
65
+ }
66
+
67
+ this.setState({
68
+ retrying: true
69
+ });
70
+
71
+ try {
72
+ await this.props.onTryAgain();
73
+ } catch (e) {// do nothing, error is handled in onTryAgain
74
+ } finally {
75
+ this.setState({
76
+ retrying: false
77
+ });
78
+ }
79
+ });
80
+ }
81
+
82
+ componentDidMount() {
83
+ window.addEventListener('online', this.onRetryPress);
84
+ }
85
+
86
+ componentWillUnmount() {
87
+ window.removeEventListener('online', this.onRetryPress);
57
88
  }
58
89
 
59
90
  render() {
@@ -67,9 +98,14 @@ class AuthDialog extends Component {
67
98
  loginToCaption,
68
99
  confirmLabel,
69
100
  cancelLabel,
101
+ tryAgainLabel,
70
102
  onConfirm,
71
- onCancel
103
+ onCancel,
104
+ onTryAgain
72
105
  } = this.props;
106
+ const {
107
+ retrying
108
+ } = this.state;
73
109
  const defaultTitle = serviceName ? loginToCaption : loginCaption;
74
110
  const title = (this.props.title || defaultTitle).replace('%serviceName%', serviceName);
75
111
  return /*#__PURE__*/React.createElement(Dialog, {
@@ -95,7 +131,13 @@ class AuthDialog extends Component {
95
131
  className: modules_ae521deb.firstButton,
96
132
  "data-test": "auth-dialog-confirm-button",
97
133
  onClick: onConfirm
98
- }, confirmLabel), /*#__PURE__*/React.createElement(Button, {
134
+ }, confirmLabel), onTryAgain && /*#__PURE__*/React.createElement(Button, {
135
+ className: modules_ae521deb.button,
136
+ "data-test": "auth-dialog-retry-button",
137
+ onClick: () => this.onRetryPress(),
138
+ loader: retrying,
139
+ disabled: retrying
140
+ }, tryAgainLabel), /*#__PURE__*/React.createElement(Button, {
99
141
  className: modules_ae521deb.button,
100
142
  "data-test": "auth-dialog-cancel-button",
101
143
  onClick: onCancel
@@ -116,8 +158,10 @@ _defineProperty(AuthDialog, "propTypes", {
116
158
  cancelOnEsc: PropTypes.bool,
117
159
  confirmLabel: PropTypes.string,
118
160
  cancelLabel: PropTypes.string,
161
+ tryAgainLabel: PropTypes.string,
119
162
  onConfirm: PropTypes.func,
120
- onCancel: PropTypes.func
163
+ onCancel: PropTypes.func,
164
+ onTryAgain: PropTypes.func
121
165
  });
122
166
 
123
167
  _defineProperty(AuthDialog, "defaultProps", {
@@ -115,11 +115,15 @@ class SelectLazy {
115
115
  }
116
116
 
117
117
  attachEvents() {
118
- this.container.addEventListener('click', this.onClick);
118
+ this.container.addEventListener('click', this.onClick, {
119
+ capture: true
120
+ });
119
121
  }
120
122
 
121
123
  detachEvents() {
122
- this.container.removeEventListener('click', this.onClick);
124
+ this.container.removeEventListener('click', this.onClick, {
125
+ capture: true
126
+ });
123
127
  }
124
128
 
125
129
  render(props) {
@@ -138,11 +142,11 @@ class SelectLazy {
138
142
 
139
143
  if (this.type === 'dropdown') {
140
144
  this.ctrl.selectInstance = render(this.reactSelect, this.container);
145
+
146
+ this.ctrl.selectInstance._openPopupIfClosed();
141
147
  } else {
142
148
  this.ctrl.selectInstance = hydrate(this.reactSelect, this.container);
143
149
  }
144
-
145
- this.ctrl.selectInstance._openPopupIfClosed();
146
150
  }
147
151
 
148
152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "4.1.8",
3
+ "version": "4.1.12",
4
4
  "description": "JetBrains UI library",
5
5
  "author": "JetBrains",
6
6
  "license": "Apache-2.0",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "readmeFilename": "README.md",
69
69
  "devDependencies": {
70
- "@babel/cli": "^7.16.7",
70
+ "@babel/cli": "^7.16.8",
71
71
  "@babel/eslint-parser": "^7.16.5",
72
72
  "@jetbrains/eslint-config": "^5.3.1",
73
73
  "@jetbrains/generator-ring-ui": "^4.1.4",
@@ -75,20 +75,20 @@
75
75
  "@primer/octicons": "^16.2.0",
76
76
  "@rollup/plugin-babel": "^5.3.0",
77
77
  "@rollup/plugin-replace": "^3.0.1",
78
- "@storybook/addon-a11y": "6.4.9",
79
- "@storybook/addon-docs": "6.4.9",
80
- "@storybook/addon-essentials": "6.4.9",
78
+ "@storybook/addon-a11y": "6.4.12",
79
+ "@storybook/addon-docs": "6.4.10",
80
+ "@storybook/addon-essentials": "6.4.12",
81
81
  "@storybook/addon-storyshots": "6.4.3",
82
82
  "@storybook/addon-storyshots-puppeteer": "6.4.3",
83
83
  "@storybook/addon-storysource": "6.4.9",
84
84
  "@storybook/addons": "6.4.9",
85
- "@storybook/builder-webpack5": "6.4.9",
86
- "@storybook/client-api": "6.4.9",
85
+ "@storybook/builder-webpack5": "6.4.10",
86
+ "@storybook/client-api": "6.4.12",
87
87
  "@storybook/core": "6.4.9",
88
- "@storybook/html": "6.4.9",
89
- "@storybook/manager-webpack5": "^6.4.9",
90
- "@storybook/source-loader": "6.4.9",
91
- "@storybook/theming": "6.4.9",
88
+ "@storybook/html": "6.4.10",
89
+ "@storybook/manager-webpack5": "^6.4.10",
90
+ "@storybook/source-loader": "6.4.10",
91
+ "@storybook/theming": "6.4.12",
92
92
  "@testing-library/react": "^12.1.2",
93
93
  "@testing-library/user-event": "^13.5.0",
94
94
  "@wojtekmaj/enzyme-adapter-react-17": "^0.6.6",
@@ -126,7 +126,7 @@
126
126
  "karma-teamcity-reporter": "^2.0.0",
127
127
  "karma-webpack": "^5.0.0",
128
128
  "lerna": "^4.0.0",
129
- "lint-staged": "^12.1.5",
129
+ "lint-staged": "^12.1.7",
130
130
  "merge-options": "^3.0.4",
131
131
  "mocha": "^9.1.3",
132
132
  "pinst": "^2.1.6",
@@ -188,10 +188,10 @@
188
188
  "extricate-loader": "^3.0.0",
189
189
  "fastdom": "^1.0.10",
190
190
  "file-loader": "^6.2.0",
191
- "focus-trap": "^6.7.1",
191
+ "focus-trap": "^6.7.2",
192
192
  "focus-visible": "^5.2.0",
193
193
  "highlight.js": "^10.7.2",
194
- "html-loader": "^3.0.1",
194
+ "html-loader": "^3.1.0",
195
195
  "interpolate-loader": "^2.0.1",
196
196
  "just-debounce-it": "^3.0.1",
197
197
  "memoize-one": "^6.0.0",
@@ -221,5 +221,5 @@
221
221
  "node": ">=7.4",
222
222
  "npm": ">=6.0.0"
223
223
  },
224
- "gitHead": "dca1856fa248b940b4375685e61b9e8687317b4d"
224
+ "gitHead": "48dc9eeda100d347581148a0453ea2d8aa6b88e7"
225
225
  }