@jetbrains/ring-ui 5.0.138 → 5.0.139

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.
Files changed (37) hide show
  1. package/components/auth/auth__core.js +10 -10
  2. package/components/header/profile.d.ts +2 -53
  3. package/components/header/profile.js +8 -8
  4. package/components/i18n/README.md +43 -0
  5. package/components/i18n/i18n-context.d.ts +12 -53
  6. package/components/i18n/i18n-context.js +14 -3
  7. package/components/i18n/i18n.d.ts +6 -3
  8. package/components/i18n/i18n.js +18 -0
  9. package/components/input/input.js +2 -2
  10. package/components/message/message.js +5 -3
  11. package/components/pager/pager.d.ts +2 -53
  12. package/components/pager/pager.js +9 -8
  13. package/components/query-assist/query-assist.js +5 -5
  14. package/components/select/select.js +3 -3
  15. package/components/select/select__filter.js +1 -1
  16. package/components/user-agreement/user-agreement.js +8 -8
  17. package/components/user-card/card.d.ts +2 -53
  18. package/components/user-card/card.js +13 -11
  19. package/dist/_helpers/card.js +15 -11
  20. package/dist/_helpers/select__filter.js +5 -2
  21. package/dist/auth/auth__core.js +10 -11
  22. package/dist/header/profile.d.ts +2 -53
  23. package/dist/header/profile.js +13 -8
  24. package/dist/i18n/i18n-context.d.ts +12 -53
  25. package/dist/i18n/i18n-context.js +22 -4
  26. package/dist/i18n/i18n.d.ts +6 -3
  27. package/dist/i18n/i18n.js +23 -1
  28. package/dist/input/input.js +32 -26
  29. package/dist/link/link.js +1 -1
  30. package/dist/message/message.js +39 -31
  31. package/dist/pager/pager.d.ts +2 -53
  32. package/dist/pager/pager.js +14 -8
  33. package/dist/query-assist/query-assist.js +90 -81
  34. package/dist/select/select.js +8 -5
  35. package/dist/user-agreement/user-agreement.js +39 -33
  36. package/dist/user-card/card.d.ts +2 -53
  37. package/package.json +14 -14
@@ -2,7 +2,7 @@ import { fixUrl, getAbsoluteBaseURL } from '../global/url';
2
2
  import Listeners from '../global/listeners';
3
3
  import HTTP from '../http/http';
4
4
  import promiseWithTimeout from '../global/promise-with-timeout';
5
- import { getTranslations } from '../i18n/i18n';
5
+ import { getTranslations, getTranslationsWithFallback, translate } from '../i18n/i18n';
6
6
  import AuthStorage from './storage';
7
7
  import AuthResponseParser from './response-parser';
8
8
  import AuthRequestBuilder from './request-builder';
@@ -120,7 +120,7 @@ class Auth {
120
120
  }
121
121
  this._backgroundFlow = new BackgroundFlow(this._requestBuilder, this._storage, backgroundRefreshTimeout);
122
122
  if (this.config.EmbeddedLoginFlow) {
123
- this._embeddedFlow = new this.config.EmbeddedLoginFlow(this._requestBuilder, this._storage, this.config.translations ?? getTranslations());
123
+ this._embeddedFlow = new this.config.EmbeddedLoginFlow(this._requestBuilder, this._storage, this.config.translations ?? getTranslationsWithFallback());
124
124
  }
125
125
  const API_BASE = this.config.serverUri + Auth.API_PATH;
126
126
  const fetchConfig = config.fetchCredentials
@@ -540,7 +540,6 @@ class Auth {
540
540
  }
541
541
  _showUserChangedDialog({ newUser, onApply, onPostpone }) {
542
542
  const { translations } = this.config;
543
- const actualTranslations = translations ?? getTranslations();
544
543
  this._createInitDeferred();
545
544
  const done = () => {
546
545
  this._initDeferred?.resolve?.();
@@ -549,14 +548,14 @@ class Auth {
549
548
  };
550
549
  const hide = this._authDialogService?.({
551
550
  ...this._service,
552
- title: actualTranslations.youHaveLoggedInAs.
551
+ title: translations?.youHaveLoggedInAs ?? translate('youHaveLoggedInAs').
553
552
  replace('%userName%', newUser.name ?? '').
554
553
  replace('{{userName}}', newUser.name ?? ''),
555
- loginCaption: actualTranslations.login,
556
- loginToCaption: actualTranslations.loginTo,
557
- confirmLabel: actualTranslations.applyChange,
558
- tryAgainLabel: actualTranslations.tryAgainLabel,
559
- cancelLabel: actualTranslations.postpone,
554
+ loginCaption: translations?.login ?? translate('login'),
555
+ loginToCaption: translations?.loginTo ?? translate('loginTo'),
556
+ confirmLabel: translations?.applyChange ?? translate('applyChange'),
557
+ tryAgainLabel: translations?.tryAgainLabel ?? translate('tryAgainLabel'),
558
+ cancelLabel: translations?.postpone ?? translate('postpone'),
560
559
  onConfirm: () => {
561
560
  done();
562
561
  onApply();
@@ -617,7 +616,8 @@ class Auth {
617
616
  reject(new Error('Auth(@jetbrains/ring-ui): postponed by user'));
618
617
  };
619
618
  const hide = onBackendDown({
620
- onCheckAgain, onPostpone, backendError, translations: translations ?? getTranslations()
619
+ onCheckAgain, onPostpone, backendError,
620
+ translations: translations ?? getTranslationsWithFallback()
621
621
  });
622
622
  window.addEventListener('online', onCheckAgain);
623
623
  function networkWatchdog() {
@@ -67,59 +67,8 @@ export default class Profile extends PureComponent<ProfileProps> {
67
67
  renderGuest: PropTypes.Requireable<(...args: any[]) => any>;
68
68
  };
69
69
  static defaultProps: ProfileProps;
70
- static contextType: React.Context<{
71
- login: string;
72
- logout: string;
73
- loginTo: string;
74
- ok: string;
75
- cancel: string;
76
- tryAgainLabel: string;
77
- postpone: string;
78
- youHaveLoggedInAs: string;
79
- applyChange: string;
80
- backendIsNotAvailable: string;
81
- checkAgain: string;
82
- nothingHappensLink: string;
83
- errorMessage: string;
84
- applyChangedUser: string;
85
- profile: string;
86
- switchUser: string;
87
- addFirstDate: string;
88
- addSecondDate: string;
89
- addTime: string;
90
- selectName: string;
91
- setDate: string;
92
- setDateTime: string;
93
- setPeriod: string;
94
- clear: string;
95
- gotIt: string;
96
- dismiss: string;
97
- perPage: string;
98
- firstPage: string;
99
- lastPage: string;
100
- nextPage: string;
101
- previousPage: string;
102
- searchTitle: string;
103
- clearTitle: string;
104
- userAgreement: string;
105
- accept: string;
106
- decline: string;
107
- close: string;
108
- scrollToAccept: string;
109
- remindLater: string;
110
- filterItems: string;
111
- selectOption: string;
112
- progress: string;
113
- loading: string;
114
- noOptionsFound: string;
115
- banned: string;
116
- online: string;
117
- offline: string;
118
- copyToClipboard: string;
119
- copiedToClipboard: string;
120
- copingToClipboardError: string;
121
- unverified: string;
122
- }>;
70
+ static contextType: React.Context<import("../i18n/i18n-context").I18nContextProps>;
71
+ context: React.ContextType<typeof Profile.contextType>;
123
72
  static Size: typeof Size;
124
73
  render(): string | number | boolean | JSX.Element | React.ReactFragment | null | undefined;
125
74
  }
@@ -51,9 +51,9 @@ class Profile extends PureComponent {
51
51
  renderPopupItems: items => items,
52
52
  size: Size.Size32,
53
53
  renderGuest: ({ loading, onLogin, className, translations }) => (<I18nContext.Consumer>
54
- {messages => (<div className={classNames(styles.profileEmpty, className)}>
54
+ {({ translate }) => (<div className={classNames(styles.profileEmpty, className)}>
55
55
  <Button primary data-test="ring-header-login-button" disabled={loading} loader={loading} onClick={onLogin}>
56
- {translations?.login ?? messages.login}
56
+ {translations?.login ?? translate('login')}
57
57
  </Button>
58
58
  </div>)}
59
59
  </I18nContext.Consumer>)
@@ -62,7 +62,7 @@ class Profile extends PureComponent {
62
62
  static Size = Size;
63
63
  render() {
64
64
  const { className, closeOnSelect, hasUpdates, onLogout, user, profileUrl, LinkComponent, onSwitchUser, renderPopupItems, onRevertPostponement, showApplyChangedUser, showLogIn, showLogOut, showSwitchUser, renderGuest, translations, size, round, loading, onLogin, ...props } = this.props;
65
- const messages = this.context;
65
+ const { translate } = this.context;
66
66
  if (!user) {
67
67
  return (<div {...props} className={classNames(styles.profileEmpty, className)}>
68
68
  <Avatar size={size} round={round}/>
@@ -80,32 +80,32 @@ class Profile extends PureComponent {
80
80
  const items = [
81
81
  showApplyChangedUser && {
82
82
  rgItemType,
83
- label: translations?.applyChangedUser ?? messages.applyChangedUser,
83
+ label: translations?.applyChangedUser ?? translate('applyChangedUser'),
84
84
  className: styles.profileMenuItem,
85
85
  onClick: onRevertPostponement
86
86
  },
87
87
  showLogIn && {
88
88
  rgItemType,
89
- label: translations?.login ?? messages.login,
89
+ label: translations?.login ?? translate('login'),
90
90
  className: styles.profileMenuItem,
91
91
  onClick: onRevertPostponement
92
92
  },
93
93
  {
94
94
  rgItemType: PopupMenu.ListProps.Type.LINK,
95
- label: translations?.profile ?? messages.profile,
95
+ label: translations?.profile ?? translate('profile'),
96
96
  target: '_self',
97
97
  href: profileUrl,
98
98
  LinkComponent
99
99
  },
100
100
  showSwitchUser && {
101
101
  rgItemType,
102
- label: translations?.switchUser ?? messages.switchUser,
102
+ label: translations?.switchUser ?? translate('switchUser'),
103
103
  className: styles.profileMenuItem,
104
104
  onClick: onSwitchUser
105
105
  },
106
106
  showLogOut && {
107
107
  rgItemType,
108
- label: translations?.logout ?? messages.logout,
108
+ label: translations?.logout ?? translate('logout'),
109
109
  onClick: onLogout
110
110
  }
111
111
  ].filter(isTruthy);
@@ -1,3 +1,46 @@
1
1
  This is localisation layer, that allows to translate the app into different languages.
2
2
 
3
3
  All messages should be stored in a messages.json, using that format https://www.i18next.com/misc/json-format#i18next-json-v4
4
+
5
+ ### How to use
6
+
7
+ 1. Using your project i18n tooling, construct an object with localised messages, like:
8
+ ```
9
+ // You can use https://ttag.js.org/ for example:
10
+ const localised = {
11
+ login: t`Log in`,
12
+ ...
13
+ }
14
+ ```
15
+
16
+ 2. Pass it to i18n singleton, using `setTranslations`. That would provide localisation to non-react components, like Auth.
17
+
18
+ ```
19
+ // Your messages object should have localised strings at that moment
20
+ const localised = {
21
+ login: 'Inloggen',
22
+ ...
23
+ }
24
+
25
+ setTranslations(localised);
26
+ ```
27
+
28
+ 3. Add I18nContextHolder into your root React tree. If you replace "localised" object with another one, consumers would be updated automatically.
29
+
30
+ ```
31
+ <I18nContextHolder messages={localised}>
32
+ <App />
33
+ </I18nContextHolder>
34
+ ```
35
+
36
+
37
+
38
+ ### Utilities
39
+
40
+ There is some ways to automate strings extraction from messages.json. You can do:
41
+
42
+ ```
43
+ npx i18next-conv -s messages.json -t messages.pot -l en --ctxSeparator "|" --compatibilityJSON v4
44
+ ```
45
+
46
+ And get a .pot file, which you can merge with your other sources
@@ -1,54 +1,13 @@
1
1
  import React from 'react';
2
- export declare const I18nContext: React.Context<{
3
- login: string;
4
- logout: string;
5
- loginTo: string;
6
- ok: string;
7
- cancel: string;
8
- tryAgainLabel: string;
9
- postpone: string;
10
- youHaveLoggedInAs: string;
11
- applyChange: string;
12
- backendIsNotAvailable: string;
13
- checkAgain: string;
14
- nothingHappensLink: string;
15
- errorMessage: string;
16
- applyChangedUser: string;
17
- profile: string;
18
- switchUser: string;
19
- addFirstDate: string;
20
- addSecondDate: string;
21
- addTime: string;
22
- selectName: string;
23
- setDate: string;
24
- setDateTime: string;
25
- setPeriod: string;
26
- clear: string;
27
- gotIt: string;
28
- dismiss: string;
29
- perPage: string;
30
- firstPage: string;
31
- lastPage: string;
32
- nextPage: string;
33
- previousPage: string;
34
- searchTitle: string;
35
- clearTitle: string;
36
- userAgreement: string;
37
- accept: string;
38
- decline: string;
39
- close: string;
40
- scrollToAccept: string;
41
- remindLater: string;
42
- filterItems: string;
43
- selectOption: string;
44
- progress: string;
45
- loading: string;
46
- noOptionsFound: string;
47
- banned: string;
48
- online: string;
49
- offline: string;
50
- copyToClipboard: string;
51
- copiedToClipboard: string;
52
- copingToClipboardError: string;
53
- unverified: string;
54
- }>;
2
+ import { type Messages } from './i18n';
3
+ export interface I18nContextProps {
4
+ translate(key: keyof Messages): string;
5
+ messages: Messages;
6
+ }
7
+ export declare const I18nContext: React.Context<I18nContextProps>;
8
+ interface I18nContextHolderProps {
9
+ messages: Messages;
10
+ children?: React.ReactNode;
11
+ }
12
+ export declare const I18nContextHolder: React.FC<I18nContextHolderProps>;
13
+ export {};
@@ -1,3 +1,14 @@
1
- import React from 'react';
2
- import { getTranslations } from './i18n';
3
- export const I18nContext = React.createContext(getTranslations());
1
+ import React, { useEffect } from 'react';
2
+ import { getTranslations, setTranslations, translate } from './i18n';
3
+ export const I18nContext = React.createContext({
4
+ messages: getTranslations(),
5
+ translate
6
+ });
7
+ export const I18nContextHolder = ({ children, messages }) => {
8
+ useEffect(() => {
9
+ setTranslations(messages);
10
+ }, [messages]);
11
+ return (<I18nContext.Provider value={{ messages, translate }}>
12
+ {children}
13
+ </I18nContext.Provider>);
14
+ };
@@ -1,7 +1,8 @@
1
1
  import defaultMessages from './messages.json';
2
- export type Messages = typeof defaultMessages;
2
+ export type MessagesStrict = typeof defaultMessages;
3
+ export type Messages = Partial<MessagesStrict>;
3
4
  export declare function setTranslations(newMessages: Messages): void;
4
- export declare function getTranslations(): {
5
+ export declare function getTranslations(): Partial<{
5
6
  login: string;
6
7
  logout: string;
7
8
  loginTo: string;
@@ -53,4 +54,6 @@ export declare function getTranslations(): {
53
54
  copiedToClipboard: string;
54
55
  copingToClipboardError: string;
55
56
  unverified: string;
56
- };
57
+ }>;
58
+ export declare function getTranslationsWithFallback(): MessagesStrict;
59
+ export declare function translate(key: keyof MessagesStrict): string;
@@ -1,8 +1,26 @@
1
1
  import defaultMessages from './messages.json';
2
2
  let messages = defaultMessages;
3
+ const warned = new Set();
4
+ function warnMissedKeyOnce(key) {
5
+ if (warned.has(key)) {
6
+ return;
7
+ }
8
+ warned.add(key);
9
+ // eslint-disable-next-line no-console
10
+ console.warn(`Missing localisation for key "${key}"`);
11
+ }
3
12
  export function setTranslations(newMessages) {
4
13
  messages = newMessages;
5
14
  }
6
15
  export function getTranslations() {
7
16
  return messages;
8
17
  }
18
+ export function getTranslationsWithFallback() {
19
+ return { ...defaultMessages, ...messages };
20
+ }
21
+ export function translate(key) {
22
+ if (!(key in messages)) {
23
+ warnMissedKeyOnce(key);
24
+ }
25
+ return messages[key] ?? defaultMessages[key];
26
+ }
@@ -119,14 +119,14 @@ class Input extends PureComponent {
119
119
  'data-enabled-shortcuts': Array.isArray(enableShortcuts) ? enableShortcuts.join(',') : null
120
120
  };
121
121
  return (<I18nContext.Consumer>
122
- {messages => (<div className={classes} data-test="ring-input">
122
+ {({ translate }) => (<div className={classes} data-test="ring-input">
123
123
  {label && (<InputLabel htmlFor={this.getId()} disabled={disabled} label={label}/>)}
124
124
  <div className={styles.container}>
125
125
  {icon && <Icon glyph={icon} className={styles.icon}/>}
126
126
  {multiline
127
127
  ? (<textarea onChange={this.handleTextareaChange} rows={1} {...commonProps} {...restProps}/>)
128
128
  : (<input onChange={this.handleInputChange} {...commonProps} {...restProps}/>)}
129
- {clearable && !disabled && (<Button title={(translations ?? messages).clear} data-test="ring-input-clear" className={styles.clear} icon={closeIcon} onClick={this.clear}/>)}
129
+ {clearable && !disabled && (<Button title={translations?.clear ?? translate('clear')} data-test="ring-input-clear" className={styles.clear} icon={closeIcon} onClick={this.clear}/>)}
130
130
  {afterInput}
131
131
  </div>
132
132
  {error && <div className={styles.errorText}>{error}</div>}
@@ -94,7 +94,7 @@ class Message extends Component {
94
94
  : this.props.directions;
95
95
  const { direction } = this.state;
96
96
  return (<I18nContext.Consumer>
97
- {messages => (<WithThemeClasses theme={theme}>
97
+ {({ translate }) => (<WithThemeClasses theme={theme}>
98
98
  {themeClasses => (<Popup ref={this.popupRef} hidden={false} directions={popupDirections} className={classNames(classes, themeClasses)} offset={UNIT * 2} onDirectionChange={this._onDirectionChange} {...popupProps}>
99
99
  <ThemeProvider theme={theme} passToPopups>
100
100
  {direction && (<div className={tailClasses} style={getTailOffsets(this.getTailOffset())[direction]}/>)}
@@ -102,8 +102,10 @@ class Message extends Component {
102
102
  {icon && <Icon className={styles.icon} glyph={icon}/>}
103
103
  {title && <h1 data-test="rgMessageTitle" className={styles.title}>{title}</h1>}
104
104
  {children && <div className={styles.description}>{children}</div>}
105
- {(onClose || buttonProps) && (<Button className={styles.button} onClick={onClose} primary {...buttonProps}>{(translations ?? messages).gotIt}</Button>)}
106
- {onDismiss && (<Button onClick={onDismiss} text>{(translations ?? messages).dismiss}</Button>)}
105
+ {(onClose || buttonProps) && (<Button className={styles.button} onClick={onClose} primary {...buttonProps}>{translations?.gotIt ?? translate('gotIt')}</Button>)}
106
+ {onDismiss && (<Button onClick={onDismiss} text>
107
+ {translations?.dismiss ?? translate('dismiss')}
108
+ </Button>)}
107
109
  </ThemeProvider>
108
110
  </Popup>)}
109
111
  </WithThemeClasses>)}
@@ -45,59 +45,8 @@ export default class Pager extends PureComponent<PagerProps> {
45
45
  onPageSizeChange: () => void;
46
46
  onLoadPage: () => void;
47
47
  };
48
- static contextType: React.Context<{
49
- login: string;
50
- logout: string;
51
- loginTo: string;
52
- ok: string;
53
- cancel: string;
54
- tryAgainLabel: string;
55
- postpone: string;
56
- youHaveLoggedInAs: string;
57
- applyChange: string;
58
- backendIsNotAvailable: string;
59
- checkAgain: string;
60
- nothingHappensLink: string;
61
- errorMessage: string;
62
- applyChangedUser: string;
63
- profile: string;
64
- switchUser: string;
65
- addFirstDate: string;
66
- addSecondDate: string;
67
- addTime: string;
68
- selectName: string;
69
- setDate: string;
70
- setDateTime: string;
71
- setPeriod: string;
72
- clear: string;
73
- gotIt: string;
74
- dismiss: string;
75
- perPage: string;
76
- firstPage: string;
77
- lastPage: string;
78
- nextPage: string;
79
- previousPage: string;
80
- searchTitle: string;
81
- clearTitle: string;
82
- userAgreement: string;
83
- accept: string;
84
- decline: string;
85
- close: string;
86
- scrollToAccept: string;
87
- remindLater: string;
88
- filterItems: string;
89
- selectOption: string;
90
- progress: string;
91
- loading: string;
92
- noOptionsFound: string;
93
- banned: string;
94
- online: string;
95
- offline: string;
96
- copyToClipboard: string;
97
- copiedToClipboard: string;
98
- copingToClipboardError: string;
99
- unverified: string;
100
- }>;
48
+ static contextType: React.Context<import("../i18n/i18n-context").I18nContextProps>;
49
+ context: React.ContextType<typeof Pager.contextType>;
101
50
  getSelectOptions(): {
102
51
  selected: SelectItem<PagerSizeItem> | undefined;
103
52
  data: SelectItem<PagerSizeItem>[];
@@ -33,10 +33,10 @@ class Pager extends PureComponent {
33
33
  static contextType = I18nContext;
34
34
  getSelectOptions() {
35
35
  const { pageSize, pageSizes } = this.props;
36
- const messages = this.context;
36
+ const { translate } = this.context;
37
37
  const data = pageSizes.map(size => ({
38
38
  key: size,
39
- label: `${size} ${(this.props.translations ?? messages).perPage}`
39
+ label: `${size} ${this.props.translations?.perPage ?? translate('perPage')}`
40
40
  }));
41
41
  const selected = data.find(it => it.key === pageSize);
42
42
  return { selected, data };
@@ -99,14 +99,14 @@ class Pager extends PureComponent {
99
99
  </div>);
100
100
  }
101
101
  getPagerLinks() {
102
- const messages = this.context;
102
+ const { translate } = this.context;
103
103
  const prevLinkAvailable = this.props.currentPage !== 1;
104
104
  const nextLinkAvailable = this.props.openTotal ||
105
105
  this.props.currentPage !== this.getTotalPages();
106
106
  const nextIcon = (<Icon glyph={chevronRightIcon} key="icon"/>);
107
107
  const prevIcon = (<Icon glyph={chevronLeftIcon} key="icon"/>);
108
- const prevText = (this.props.translations ?? messages).previousPage;
109
- const nextText = (this.props.translations ?? messages).nextPage;
108
+ const prevText = this.props.translations?.previousPage ?? translate('previousPage');
109
+ const nextText = this.props.translations?.nextPage ?? translate('nextPage');
110
110
  const nextLinkContent = (WrapText) => [
111
111
  <span key="text"><WrapText>{nextText}</WrapText></span>,
112
112
  nextIcon
@@ -145,7 +145,7 @@ class Pager extends PureComponent {
145
145
  getPagerContent() {
146
146
  const { currentPage, visiblePagesLimit } = this.props;
147
147
  const totalPages = this.getTotalPages();
148
- const messages = this.context;
148
+ const { translate } = this.context;
149
149
  if (totalPages < this.props.currentPage) {
150
150
  this.props.onPageChange?.(totalPages);
151
151
  }
@@ -180,7 +180,8 @@ class Pager extends PureComponent {
180
180
  {this.getPagerLinks()}
181
181
 
182
182
  <ButtonToolbar>
183
- {start > 1 && this.getButton(1, (this.props.translations ?? messages).firstPage)}
183
+ {start > 1 &&
184
+ this.getButton(1, this.props.translations?.firstPage ?? translate('firstPage'))}
184
185
 
185
186
  <ButtonGroup>
186
187
  {start > 1 && this.getButton(start - 1, '...')}
@@ -192,7 +193,7 @@ class Pager extends PureComponent {
192
193
  {end === totalPages && this.props.openTotal && (<Button href={this.generateHref(end + 1)} disabled={this.props.loader} {...this.getClickProps(this.handleLoadMore(end + 1))}>{'...'}</Button>)}
193
194
  </ButtonGroup>
194
195
 
195
- {lastPageButtonAvailable && this.getButton(this.props.openTotal ? -1 : totalPages, (this.props.translations ?? messages).lastPage)}
196
+ {lastPageButtonAvailable && this.getButton(this.props.openTotal ? -1 : totalPages, this.props.translations?.lastPage ?? translate('lastPage'))}
196
197
  </ButtonToolbar>
197
198
 
198
199
  {this.getPageSizeSelector()}
@@ -773,7 +773,7 @@ class QueryAssist extends Component {
773
773
  const renderClear = this.props.clear && !!this.state.query;
774
774
  if (renderClear) {
775
775
  actions.push(<I18nContext.Consumer key={'clearAction'}>
776
- {messages => (<Button icon={closeIcon} className={styles.clear} title={(this.props.translations ?? messages).clearTitle} ref={this.clearRef} onClick={this.clearQuery} data-test="query-assist-clear-icon"/>)}
776
+ {({ translate }) => (<Button icon={closeIcon} className={styles.clear} title={this.props.translations?.clearTitle ?? translate('clearTitle')} ref={this.clearRef} onClick={this.clearQuery} data-test="query-assist-clear-icon"/>)}
777
777
  </I18nContext.Consumer>);
778
778
  }
779
779
  return actions;
@@ -803,10 +803,10 @@ class QueryAssist extends Component {
803
803
  });
804
804
  return (<ControlsHeightContext.Provider value={ControlsHeight.M}>
805
805
  <I18nContext.Consumer>
806
- {messages => (<div data-test={dataTests('ring-query-assist', dataTest)} className={containerClasses} role="presentation" ref={this.nodeRef}>
806
+ {({ translate }) => (<div data-test={dataTests('ring-query-assist', dataTest)} className={containerClasses} role="presentation" ref={this.nodeRef}>
807
807
  {this.state.shortcuts && (<Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>)}
808
808
 
809
- {renderGlass && !huge && (<Icon glyph={searchIcon} className={styles.icon} title={(translations ?? messages).searchTitle} ref={this.glassRef} data-test="query-assist-search-icon"/>)}
809
+ {renderGlass && !huge && (<Icon glyph={searchIcon} className={styles.icon} title={translations?.searchTitle ?? translate('searchTitle')} ref={this.glassRef} data-test="query-assist-search-icon"/>)}
810
810
 
811
811
  {renderLoader && (<div className={classNames(styles.icon, styles.loader, {
812
812
  [styles.loaderOnTheRight]: !glass && !huge,
@@ -815,7 +815,7 @@ class QueryAssist extends Component {
815
815
  <LoaderInline />
816
816
  </div>)}
817
817
 
818
- <ContentEditable aria-label={(translations ?? messages).searchTitle} className={inputClasses} data-test="ring-query-assist-input" inputRef={this.inputRef} disabled={this.props.disabled} onComponentUpdate={() => this.setCaretPosition({ fromContentEditable: true })} onBlur={this.handleFocusChange} onClick={this.handleCaretMove} onCompositionStart={this.trackCompositionState} onCompositionEnd={this.trackCompositionState} onFocus={this.handleFocusChange} onInput={this.handleInput} // To support IE use the same method
818
+ <ContentEditable aria-label={translations?.searchTitle ?? translate('searchTitle')} className={inputClasses} data-test="ring-query-assist-input" inputRef={this.inputRef} disabled={this.props.disabled} onComponentUpdate={() => this.setCaretPosition({ fromContentEditable: true })} onBlur={this.handleFocusChange} onClick={this.handleCaretMove} onCompositionStart={this.trackCompositionState} onCompositionEnd={this.trackCompositionState} onFocus={this.handleFocusChange} onInput={this.handleInput} // To support IE use the same method
819
819
  onKeyUp={this.handleInput} // to handle input and key up
820
820
  onKeyDown={this.handleEnter} onPaste={this.handlePaste} spellCheck="false">{this.state.query && <span>{this.renderQuery()}</span>}</ContentEditable>
821
821
 
@@ -830,7 +830,7 @@ class QueryAssist extends Component {
830
830
  <PopupMenu hidden={!this.state.showPopup} onCloseAttempt={this.closePopup} ref={this.popupRef} anchorElement={this.node} keepMounted attached className={this.props.popupClassName} directions={[PopupMenu.PopupProps.Directions.BOTTOM_RIGHT]} data={useCustomItemRender ? this.state.suggestions : this.renderSuggestions()} data-test="ring-query-assist-popup" hint={this.props.hint} hintOnSelection={this.props.hintOnSelection} left={this.getPopupOffset(this.state.suggestions)} maxHeight={PopupMenu.PopupProps.MaxHeight.SCREEN} onMouseDown={this.trackPopupMouseState} onMouseUp={this.trackPopupMouseState} onSelect={item => this.handleComplete(item)}/>
831
831
 
832
832
  {glass && huge && (<div className={styles.rightSearchButton} data-test="query-assist-search-button">
833
- <Icon glyph={searchIcon} className={styles.rightSearchIcon} title={(translations ?? messages).searchTitle} onClick={this.handleApply} ref={this.glassRef} data-test="query-assist-search-icon"/>
833
+ <Icon glyph={searchIcon} className={styles.rightSearchIcon} title={translations?.searchTitle ?? translate('searchTitle')} onClick={this.handleApply} ref={this.glassRef} data-test="query-assist-search-icon"/>
834
834
  </div>)}
835
835
  </div>)}
836
836
  </I18nContext.Consumer>
@@ -423,13 +423,13 @@ class Select extends Component {
423
423
  const { showPopup, shownData } = this.state;
424
424
  const _shownData = this._prependResetOption(shownData);
425
425
  return (<I18nContext.Consumer>
426
- {messages => {
426
+ {({ translate }) => {
427
427
  let message;
428
428
  if (this.props.loading) {
429
- message = this.props.loadingMessage ?? messages.loading;
429
+ message = this.props.loadingMessage ?? translate('loading');
430
430
  }
431
431
  else if (!shownData.length) {
432
- message = this.props.notFoundMessage ?? messages.noOptionsFound;
432
+ message = this.props.notFoundMessage ?? translate('noOptionsFound');
433
433
  }
434
434
  return (<SelectPopup data={_shownData} message={message} toolbar={showPopup && this.getToolbar()} loading={this.props.loading} activeIndex={this.state.selectedIndex} hidden={!showPopup} ref={this.popupRef} maxHeight={this.props.maxHeight} minWidth={this.props.minWidth} directions={this.props.directions} className={this.props.popupClassName} style={this.props.popupStyle} top={this.props.top} left={this.props.left} filter={this.isInputMode() ? false : this.props.filter} // disable popup filter in INPUT mode
435
435
  multiple={this.props.multiple} filterValue={this.state.filterValue} anchorElement={anchorElement} onCloseAttempt={this._onCloseAttempt} onSelect={this._listSelectHandler} onSelectAll={this._listSelectAllHandler} onFilter={this._filterChangeHandler} onClear={this.clearFilter} onLoadMore={this.props.onLoadMore} isInputMode={this.isInputMode()} selected={this.state.selected} tags={this.props.tags} compact={this.props.compact} renderOptimization={this.props.renderOptimization} ringPopupTarget={this.props.ringPopupTarget} disableMoveOverflow={this.props.disableMoveOverflow} disableScrollToActive={this.props.disableScrollToActive} dir={this.props.dir} onEmptyPopupEnter={this.onEmptyPopupEnter} listId={this.listId}/>);
@@ -35,7 +35,7 @@ class SelectFilter extends Component {
35
35
  const classes = classNames(styles.filter, className);
36
36
  return (<ActiveItemContext.ValueContext.Consumer>
37
37
  {activeItemId => (<I18nContext.Consumer>
38
- {messages => (<Input {...restProps} placeholder={restProps.placeholder ?? messages.filterItems} aria-owns={listId} aria-activedescendant={activeItemId} autoComplete="off" autoFocus borderless inputRef={this.inputRef} className={classes}/>)}
38
+ {({ translate }) => (<Input {...restProps} placeholder={restProps.placeholder ?? translate('filterItems')} aria-owns={listId} aria-activedescendant={activeItemId} autoComplete="off" autoFocus borderless inputRef={this.inputRef} className={classes}/>)}
39
39
  </I18nContext.Consumer>)}
40
40
  </ActiveItemContext.ValueContext.Consumer>);
41
41
  }
@@ -48,32 +48,32 @@ class UserAgreement extends PureComponent {
48
48
  const { scrolledDown } = this.state;
49
49
  const { translations, onAccept, onDecline, onClose, onRemindLater, text, show, preview, className } = this.props;
50
50
  return (<I18nContext.Consumer>
51
- {messages => (<Dialog label={(translations ?? messages).userAgreement} show={show} className={classNames(style.agreementDialog, className)} contentClassName={style.dialogContent} trapFocus autoFocusFirst={false} data-test="user-agreement">
52
- <Header>{(translations ?? messages).userAgreement}</Header>
51
+ {({ translate }) => (<Dialog label={translations?.userAgreement ?? translate('userAgreement')} show={show} className={classNames(style.agreementDialog, className)} contentClassName={style.dialogContent} trapFocus autoFocusFirst={false} data-test="user-agreement">
52
+ <Header>{translations?.userAgreement ?? translate('userAgreement')}</Header>
53
53
  <Content fade onScrollToBottom={this.onScrollToBottom}>
54
54
  <Markdown>{text}</Markdown>
55
55
  </Content>
56
56
  {!preview && (<Panel>
57
57
  {onRemindLater && !scrolledDown && (<div className={style.suggestion}>
58
- {(translations ?? messages).scrollToAccept}
58
+ {translations?.scrollToAccept ?? translate('scrollToAccept')}
59
59
  </div>)}
60
60
  <Button primary disabled={!scrolledDown} onClick={onAccept} data-test="accept">
61
- {(translations ?? messages).accept}
61
+ {translations?.accept ?? translate('accept')}
62
62
  </Button>
63
63
  <Button onClick={onDecline} autoFocus data-test="decline">
64
- {(translations ?? messages).decline}
64
+ {translations?.decline ?? translate('decline')}
65
65
  </Button>
66
66
 
67
67
  {!onRemindLater && !scrolledDown && (<span className={style.suggestion}>
68
- {(translations ?? messages).scrollToAccept}
68
+ {translations?.scrollToAccept ?? translate('scrollToAccept')}
69
69
  </span>)}
70
70
  {onRemindLater && (<Button className={style.remindLaterButton} onClick={onRemindLater} data-test="later">
71
- {(translations ?? messages).remindLater}
71
+ {translations?.remindLater ?? translate('remindLater')}
72
72
  </Button>)}
73
73
  </Panel>)}
74
74
  {preview && (<Panel>
75
75
  <Button onClick={onClose} autoFocus data-test="close">
76
- {(translations ?? messages).close}
76
+ {translations?.close ?? translate('close')}
77
77
  </Button>
78
78
  </Panel>)}
79
79
  </Dialog>)}