@dr.pogodin/react-utils 1.41.17 → 1.42.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.
Files changed (230) hide show
  1. package/babel.config.js +3 -0
  2. package/bin/build.js +5 -8
  3. package/bin/setup.js +2 -1
  4. package/build/development/client/getInj.js +7 -2
  5. package/build/development/client/getInj.js.map +1 -1
  6. package/build/development/client/index.js +1 -2
  7. package/build/development/client/index.js.map +1 -1
  8. package/build/development/client/init.js +16 -13
  9. package/build/development/client/init.js.map +1 -1
  10. package/build/development/index.js +4 -1
  11. package/build/development/index.js.map +1 -1
  12. package/build/development/server/Cache.js +5 -9
  13. package/build/development/server/Cache.js.map +1 -1
  14. package/build/development/server/index.js +14 -11
  15. package/build/development/server/index.js.map +1 -1
  16. package/build/development/server/renderer.js +36 -41
  17. package/build/development/server/renderer.js.map +1 -1
  18. package/build/development/server/server.js +22 -13
  19. package/build/development/server/server.js.map +1 -1
  20. package/build/development/server/utils/errors.js.map +1 -1
  21. package/build/development/shared/components/Button/index.js +2 -3
  22. package/build/development/shared/components/Button/index.js.map +1 -1
  23. package/build/development/shared/components/Checkbox/index.js +3 -1
  24. package/build/development/shared/components/Checkbox/index.js.map +1 -1
  25. package/build/development/shared/components/GenericLink/index.js +13 -6
  26. package/build/development/shared/components/GenericLink/index.js.map +1 -1
  27. package/build/development/shared/components/Input/index.js +5 -1
  28. package/build/development/shared/components/Input/index.js.map +1 -1
  29. package/build/development/shared/components/Link.js +5 -6
  30. package/build/development/shared/components/Link.js.map +1 -1
  31. package/build/development/shared/components/MetaTags.js +31 -24
  32. package/build/development/shared/components/MetaTags.js.map +1 -1
  33. package/build/development/shared/components/Modal/index.js +13 -6
  34. package/build/development/shared/components/Modal/index.js.map +1 -1
  35. package/build/development/shared/components/NavLink.js +6 -6
  36. package/build/development/shared/components/NavLink.js.map +1 -1
  37. package/build/development/shared/components/TextArea/index.js +6 -3
  38. package/build/development/shared/components/TextArea/index.js.map +1 -1
  39. package/build/development/shared/components/WithTooltip/Tooltip.js +35 -39
  40. package/build/development/shared/components/WithTooltip/Tooltip.js.map +1 -1
  41. package/build/development/shared/components/WithTooltip/index.js +27 -21
  42. package/build/development/shared/components/WithTooltip/index.js.map +1 -1
  43. package/build/development/shared/components/YouTubeVideo/index.js +4 -3
  44. package/build/development/shared/components/YouTubeVideo/index.js.map +1 -1
  45. package/build/development/shared/components/selectors/CustomDropdown/Options/index.js +4 -5
  46. package/build/development/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
  47. package/build/development/shared/components/selectors/CustomDropdown/index.js +7 -9
  48. package/build/development/shared/components/selectors/CustomDropdown/index.js.map +1 -1
  49. package/build/development/shared/components/selectors/NativeDropdown/index.js +3 -5
  50. package/build/development/shared/components/selectors/NativeDropdown/index.js.map +1 -1
  51. package/build/development/shared/components/selectors/Switch/index.js +21 -20
  52. package/build/development/shared/components/selectors/Switch/index.js.map +1 -1
  53. package/build/development/shared/utils/config.js +6 -3
  54. package/build/development/shared/utils/config.js.map +1 -1
  55. package/build/development/shared/utils/globalState.js +6 -0
  56. package/build/development/shared/utils/globalState.js.map +1 -1
  57. package/build/development/shared/utils/isomorphy/buildInfo.js.map +1 -1
  58. package/build/development/shared/utils/isomorphy/environment-check.js +6 -1
  59. package/build/development/shared/utils/isomorphy/environment-check.js.map +1 -1
  60. package/build/development/shared/utils/isomorphy/index.js +1 -3
  61. package/build/development/shared/utils/isomorphy/index.js.map +1 -1
  62. package/build/development/shared/utils/jest/E2eSsrEnv.js +26 -17
  63. package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  64. package/build/development/shared/utils/jest/global.js +0 -7
  65. package/build/development/shared/utils/jest/global.js.map +1 -1
  66. package/build/development/shared/utils/jest/index.js +15 -4
  67. package/build/development/shared/utils/jest/index.js.map +1 -1
  68. package/build/development/shared/utils/splitComponent.js +37 -19
  69. package/build/development/shared/utils/splitComponent.js.map +1 -1
  70. package/build/development/shared/utils/time.js +3 -3
  71. package/build/development/shared/utils/time.js.map +1 -1
  72. package/build/development/shared/utils/webpack.js +11 -11
  73. package/build/development/shared/utils/webpack.js.map +1 -1
  74. package/build/development/web.bundle.js +31 -31
  75. package/build/production/client/getInj.js +4 -2
  76. package/build/production/client/getInj.js.map +1 -1
  77. package/build/production/client/index.js +2 -2
  78. package/build/production/client/index.js.map +1 -1
  79. package/build/production/client/init.js +4 -2
  80. package/build/production/client/init.js.map +1 -1
  81. package/build/production/index.js +2 -1
  82. package/build/production/index.js.map +1 -1
  83. package/build/production/server/Cache.js +1 -2
  84. package/build/production/server/Cache.js.map +1 -1
  85. package/build/production/server/index.js +9 -5
  86. package/build/production/server/index.js.map +1 -1
  87. package/build/production/server/renderer.js +24 -21
  88. package/build/production/server/renderer.js.map +1 -1
  89. package/build/production/server/server.js +14 -9
  90. package/build/production/server/server.js.map +1 -1
  91. package/build/production/server/utils/errors.js.map +1 -1
  92. package/build/production/shared/components/Button/index.js +1 -1
  93. package/build/production/shared/components/Button/index.js.map +1 -1
  94. package/build/production/shared/components/Checkbox/index.js +1 -1
  95. package/build/production/shared/components/Checkbox/index.js.map +1 -1
  96. package/build/production/shared/components/GenericLink/index.js +6 -4
  97. package/build/production/shared/components/GenericLink/index.js.map +1 -1
  98. package/build/production/shared/components/Input/index.js +3 -1
  99. package/build/production/shared/components/Input/index.js.map +1 -1
  100. package/build/production/shared/components/Link.js +3 -1
  101. package/build/production/shared/components/Link.js.map +1 -1
  102. package/build/production/shared/components/MetaTags.js +5 -2
  103. package/build/production/shared/components/MetaTags.js.map +1 -1
  104. package/build/production/shared/components/Modal/index.js +6 -2
  105. package/build/production/shared/components/Modal/index.js.map +1 -1
  106. package/build/production/shared/components/NavLink.js +4 -1
  107. package/build/production/shared/components/NavLink.js.map +1 -1
  108. package/build/production/shared/components/TextArea/index.js +4 -4
  109. package/build/production/shared/components/TextArea/index.js.map +1 -1
  110. package/build/production/shared/components/WithTooltip/Tooltip.js +37 -38
  111. package/build/production/shared/components/WithTooltip/Tooltip.js.map +1 -1
  112. package/build/production/shared/components/WithTooltip/index.js +4 -4
  113. package/build/production/shared/components/WithTooltip/index.js.map +1 -1
  114. package/build/production/shared/components/YouTubeVideo/index.js +3 -2
  115. package/build/production/shared/components/YouTubeVideo/index.js.map +1 -1
  116. package/build/production/shared/components/selectors/CustomDropdown/Options/index.js +2 -2
  117. package/build/production/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
  118. package/build/production/shared/components/selectors/CustomDropdown/index.js +2 -2
  119. package/build/production/shared/components/selectors/CustomDropdown/index.js.map +1 -1
  120. package/build/production/shared/components/selectors/NativeDropdown/index.js +2 -2
  121. package/build/production/shared/components/selectors/NativeDropdown/index.js.map +1 -1
  122. package/build/production/shared/components/selectors/Switch/index.js +1 -1
  123. package/build/production/shared/components/selectors/Switch/index.js.map +1 -1
  124. package/build/production/shared/utils/config.js +6 -4
  125. package/build/production/shared/utils/config.js.map +1 -1
  126. package/build/production/shared/utils/globalState.js +4 -1
  127. package/build/production/shared/utils/globalState.js.map +1 -1
  128. package/build/production/shared/utils/isomorphy/buildInfo.js.map +1 -1
  129. package/build/production/shared/utils/isomorphy/environment-check.js +5 -1
  130. package/build/production/shared/utils/isomorphy/environment-check.js.map +1 -1
  131. package/build/production/shared/utils/isomorphy/index.js +1 -3
  132. package/build/production/shared/utils/isomorphy/index.js.map +1 -1
  133. package/build/production/shared/utils/jest/E2eSsrEnv.js +15 -8
  134. package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  135. package/build/production/shared/utils/jest/global.js +1 -1
  136. package/build/production/shared/utils/jest/global.js.map +1 -1
  137. package/build/production/shared/utils/jest/index.js +13 -5
  138. package/build/production/shared/utils/jest/index.js.map +1 -1
  139. package/build/production/shared/utils/splitComponent.js +16 -8
  140. package/build/production/shared/utils/splitComponent.js.map +1 -1
  141. package/build/production/shared/utils/time.js +2 -2
  142. package/build/production/shared/utils/time.js.map +1 -1
  143. package/build/production/shared/utils/webpack.js +5 -2
  144. package/build/production/shared/utils/webpack.js.map +1 -1
  145. package/build/production/web.bundle.js +1 -1
  146. package/build/production/web.bundle.js.map +1 -1
  147. package/build/types-code/client/getInj.d.ts +1 -1
  148. package/build/types-code/client/index.d.ts +2 -2
  149. package/build/types-code/client/init.d.ts +1 -1
  150. package/build/types-code/index.d.ts +6 -5
  151. package/build/types-code/server/Cache.d.ts +1 -2
  152. package/build/types-code/server/index.d.ts +4 -5
  153. package/build/types-code/server/renderer.d.ts +9 -11
  154. package/build/types-code/server/server.d.ts +3 -5
  155. package/build/types-code/server/utils/errors.d.ts +1 -1
  156. package/build/types-code/shared/components/Button/index.d.ts +2 -2
  157. package/build/types-code/shared/components/TextArea/index.d.ts +3 -3
  158. package/build/types-code/shared/components/WithTooltip/Tooltip.d.ts +7 -1
  159. package/build/types-code/shared/components/index.d.ts +12 -12
  160. package/build/types-code/shared/utils/config.d.ts +1 -1
  161. package/build/types-code/shared/utils/globalState.d.ts +3 -6
  162. package/build/types-code/shared/utils/isomorphy/index.d.ts +1 -3
  163. package/build/types-code/shared/utils/jest/E2eSsrEnv.d.ts +1 -1
  164. package/build/types-code/shared/utils/jest/global.d.ts +4 -4
  165. package/build/types-code/shared/utils/jest/index.d.ts +2 -2
  166. package/build/types-code/shared/utils/webpack.d.ts +1 -1
  167. package/config/babel/node-ssr.d.ts +3 -2
  168. package/config/babel/node-ssr.js +5 -7
  169. package/config/babel/webpack.d.ts +3 -11
  170. package/config/babel/webpack.js +15 -15
  171. package/config/eslint/default.mjs +32 -0
  172. package/config/jest/default.js +10 -6
  173. package/config/jest/resolver.js +2 -0
  174. package/config/jest/setup.js +2 -2
  175. package/config/stylelint/default.js +3 -0
  176. package/config/webpack/app-base.d.ts +0 -6
  177. package/config/webpack/app-base.js +64 -70
  178. package/config/webpack/app-development.d.ts +2 -2
  179. package/config/webpack/app-development.js +8 -12
  180. package/config/webpack/app-production.js +1 -0
  181. package/config/webpack/lib-base.js +20 -18
  182. package/config/webpack/lib-development.js +1 -0
  183. package/config/webpack/lib-production.js +1 -0
  184. package/config/workbox/default.js +2 -5
  185. package/dev-styles.js +1 -0
  186. package/eslint.config.mjs +13 -0
  187. package/node-entry.js +7 -2
  188. package/null.js +1 -0
  189. package/package.json +27 -35
  190. package/prod-styles.js +1 -0
  191. package/src/client/getInj.ts +8 -3
  192. package/src/client/index.tsx +4 -4
  193. package/src/client/init.ts +18 -15
  194. package/src/index.ts +8 -3
  195. package/src/server/Cache.ts +7 -15
  196. package/src/server/index.ts +30 -21
  197. package/src/server/renderer.tsx +77 -67
  198. package/src/server/server.ts +41 -21
  199. package/src/server/utils/errors.ts +6 -3
  200. package/src/shared/components/Button/index.tsx +4 -5
  201. package/src/shared/components/Checkbox/index.tsx +10 -7
  202. package/src/shared/components/GenericLink/index.tsx +14 -7
  203. package/src/shared/components/Input/index.tsx +4 -1
  204. package/src/shared/components/Link.tsx +9 -5
  205. package/src/shared/components/MetaTags.tsx +21 -15
  206. package/src/shared/components/Modal/index.tsx +12 -11
  207. package/src/shared/components/NavLink.tsx +10 -5
  208. package/src/shared/components/TextArea/index.tsx +17 -9
  209. package/src/shared/components/WithTooltip/{Tooltip.tsx → Tooltip.ts} +35 -39
  210. package/src/shared/components/WithTooltip/index.tsx +36 -30
  211. package/src/shared/components/YouTubeVideo/index.tsx +10 -6
  212. package/src/shared/components/selectors/CustomDropdown/Options/index.tsx +5 -6
  213. package/src/shared/components/selectors/CustomDropdown/index.tsx +10 -14
  214. package/src/shared/components/selectors/NativeDropdown/index.tsx +5 -7
  215. package/src/shared/components/selectors/Switch/index.tsx +19 -17
  216. package/src/shared/utils/config.ts +12 -5
  217. package/src/shared/utils/globalState.ts +8 -5
  218. package/src/shared/utils/isomorphy/buildInfo.ts +1 -1
  219. package/src/shared/utils/isomorphy/environment-check.ts +5 -1
  220. package/src/shared/utils/isomorphy/index.ts +4 -6
  221. package/src/shared/utils/jest/E2eSsrEnv.ts +64 -39
  222. package/src/shared/utils/jest/global.ts +6 -8
  223. package/src/shared/utils/jest/{index.tsx → index.ts} +25 -12
  224. package/src/shared/utils/splitComponent.tsx +44 -25
  225. package/src/shared/utils/time.ts +16 -9
  226. package/src/shared/utils/webpack.ts +19 -14
  227. package/webpack.config.ts +36 -10
  228. package/config/eslint/default.json +0 -30
  229. package/config/eslint/jest.json +0 -19
  230. package/config/eslint/typescript.js +0 -46
@@ -18,6 +18,7 @@ import csrf from '@dr.pogodin/csurf';
18
18
  import express, {
19
19
  type Express,
20
20
  type NextFunction,
21
+ type RequestHandler,
21
22
  type Request,
22
23
  type Response,
23
24
  } from 'express';
@@ -27,7 +28,8 @@ import helmet, { type HelmetOptions } from 'helmet';
27
28
  import loggerMiddleware from 'morgan';
28
29
  import requestIp from 'request-ip';
29
30
  import { v4 as uuid } from 'uuid';
30
- import { type Configuration } from 'webpack';
31
+
32
+ import type { Compiler, Configuration } from 'webpack';
31
33
 
32
34
  import rendererFactory, {
33
35
  type LoggerI,
@@ -44,6 +46,7 @@ import {
44
46
  export type CspOptionsT =
45
47
  Exclude<HelmetOptions['contentSecurityPolicy'], boolean | undefined>;
46
48
 
49
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
47
50
  interface RequestT extends Request {
48
51
  cspNonce: string;
49
52
  nonce: string;
@@ -98,7 +101,9 @@ delete defaultCspSettings.directives['upgrade-insecure-requests'];
98
101
  * with the exception of `nonce-xxx` clause in `script-src` directive,
99
102
  * which is added dynamically for each request.
100
103
  */
101
- export function getDefaultCspSettings() {
104
+ export function getDefaultCspSettings(): {
105
+ directives: Record<string, string[]>;
106
+ } {
102
107
  return cloneDeep(defaultCspSettings);
103
108
  }
104
109
 
@@ -107,7 +112,9 @@ export type ServerT = Express & {
107
112
  };
108
113
 
109
114
  export type OptionsT = RendererOptionsT & {
110
- beforeExpressJsError?: (server: ServerT) => boolean | Promise<boolean | void> | void;
115
+ beforeExpressJsError?:
116
+ (server: ServerT) => boolean | Promise<boolean>;
117
+
111
118
  beforeExpressJsSetup?: (server: ServerT) => Promise<void> | void;
112
119
  cspSettingsHook?: (
113
120
  defaultOptions: CspOptionsT,
@@ -121,7 +128,7 @@ export type OptionsT = RendererOptionsT & {
121
128
  export default async function factory(
122
129
  webpackConfig: Configuration,
123
130
  options: OptionsT,
124
- ) {
131
+ ): Promise<ServerT> {
125
132
  const rendererOps: RendererOptionsT = pick(options, [
126
133
  'Application',
127
134
  'beforeRender',
@@ -150,9 +157,10 @@ export default async function factory(
150
157
  if (schema === 'http') {
151
158
  let url = `https://${req.headers.host}`;
152
159
  if (req.originalUrl !== '/') url += req.originalUrl;
153
- return res.redirect(url);
160
+ res.redirect(url);
161
+ return;
154
162
  }
155
- return next();
163
+ next();
156
164
  });
157
165
  }
158
166
 
@@ -193,7 +201,9 @@ export default async function factory(
193
201
  server.use(favicon(options.favicon));
194
202
  }
195
203
 
196
- server.use('/robots.txt', (req, res) => res.send('User-agent: *\nDisallow:'));
204
+ server.use('/robots.txt', (req, res) => {
205
+ res.send('User-agent: *\nDisallow:');
206
+ });
197
207
 
198
208
  server.use(express.json({ limit: '300kb' }));
199
209
  server.use(express.urlencoded({ extended: false }));
@@ -220,7 +230,7 @@ export default async function factory(
220
230
  // Thus, this setup to serve it. Probably, need some more configuration
221
231
  // for special cases, but this will do for now.
222
232
  server.get('/__service-worker.js', express.static(
223
- webpackConfig.output?.path || '',
233
+ webpackConfig.output?.path ?? '',
224
234
  {
225
235
  setHeaders: (res) => res.set('Cache-Control', 'no-cache'),
226
236
  },
@@ -229,23 +239,31 @@ export default async function factory(
229
239
  /* Setup of Hot Module Reloading for development environment.
230
240
  * These dependencies are not used, nor installed for production use,
231
241
  * hence we should violate some import-related lint rules. */
232
- /* eslint-disable global-require */
233
242
  /* eslint-disable import/no-extraneous-dependencies */
234
- /* eslint-disable import/no-unresolved */
235
243
  if (options.devMode) {
236
244
  // This is a workaround for SASS bug:
237
245
  // https://github.com/dart-lang/sdk/issues/27979
238
246
  // which manifests itself sometimes when webpack dev middleware is used
239
247
  // (in dev mode), and app modules are imported in some unfortunate ways.
248
+ // TODO: Double-check, what is going on here.
249
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
240
250
  if (!global.location) {
241
251
  global.location = {
242
252
  href: `${pathToFileURL(process.cwd()).href}${sep}`,
243
253
  } as Location;
244
254
  }
245
255
 
246
- const webpack = require('webpack');
247
- const webpackDevMiddleware = require('webpack-dev-middleware');
248
- const webpackHotMiddleware = require('webpack-hot-middleware');
256
+ /* eslint-disable @typescript-eslint/no-require-imports */
257
+ const webpack = require('webpack') as (ops: Configuration) => Compiler;
258
+
259
+ // TODO: Figure out the exact type for options, don't wanna waste time on it
260
+ // right now.
261
+ const webpackDevMiddleware = require('webpack-dev-middleware') as
262
+ (c: Compiler, ops: unknown) => RequestHandler;
263
+
264
+ const webpackHotMiddleware = require('webpack-hot-middleware') as
265
+ (c: Compiler) => RequestHandler;
266
+
249
267
  const compiler = webpack(webpackConfig);
250
268
  server.use(webpackDevMiddleware(compiler, {
251
269
  publicPath,
@@ -253,9 +271,7 @@ export default async function factory(
253
271
  }));
254
272
  server.use(webpackHotMiddleware(compiler));
255
273
  }
256
- /* eslint-enable global-require */
257
274
  /* eslint-enable import/no-extraneous-dependencies */
258
- /* eslint-enable import/no-unresolved */
259
275
 
260
276
  server.use(publicPath as string, express.static(webpackConfig.output!.path!));
261
277
 
@@ -285,20 +301,25 @@ export default async function factory(
285
301
  // prevents to do it without some extra refactoring. Should be done sometime
286
302
  // though.
287
303
  server.use((
288
- error: any,
304
+ error: Error & {
305
+ status?: number;
306
+ },
289
307
  req: Request,
290
308
  res: Response,
291
309
  next: NextFunction,
292
310
  ) => {
293
311
  // TODO: This is needed to correctly handled any errors thrown after
294
312
  // sending initial response to the client.
295
- if (res.headersSent) return next(error);
313
+ if (res.headersSent) {
314
+ next(error);
315
+ return;
316
+ }
296
317
 
297
- const status = error.status || CODES.INTERNAL_SERVER_ERROR;
298
- const serverSide = status >= CODES.INTERNAL_SERVER_ERROR;
318
+ const status = error.status ?? CODES.INTERNAL_SERVER_ERROR;
319
+ const serverSide = status >= (CODES.INTERNAL_SERVER_ERROR as number);
299
320
 
300
321
  // Log server-side errors always, client-side at debug level only.
301
- options.logger!.log(serverSide ? 'error' : 'debug', error);
322
+ options.logger!.log(serverSide ? 'error' : 'debug', error.toString());
302
323
 
303
324
  let message = error.message || getErrorForCode(status);
304
325
  if (serverSide && process.env.NODE_ENV === 'production') {
@@ -306,7 +327,6 @@ export default async function factory(
306
327
  }
307
328
 
308
329
  res.status(status).send(message);
309
- return undefined;
310
330
  });
311
331
  }
312
332
 
@@ -89,7 +89,10 @@ class ErrorWithStatus extends Error {
89
89
  * Server Error).
90
90
  * @return {Error}
91
91
  */
92
- export function newError(message: string, statusCode = CODES.INTERNAL_SERVER_ERROR) {
92
+ export function newError(
93
+ message: string,
94
+ statusCode = CODES.INTERNAL_SERVER_ERROR,
95
+ ): ErrorWithStatus {
93
96
  const error = new ErrorWithStatus(message);
94
97
  error.status = statusCode;
95
98
  return error;
@@ -119,11 +122,11 @@ export function fail(
119
122
  * Request).
120
123
  */
121
124
  export function assert(
122
- value: any,
125
+ value: unknown,
123
126
  schema: joi.AnySchema,
124
127
  message = '',
125
128
  statusCode = CODES.BAD_REQUEST,
126
- ) {
129
+ ): void {
127
130
  const { error } = schema.validate(value, { abortEarly: false });
128
131
  if (error) {
129
132
  fail(message.concat(message ? '\n' : '', error.message), statusCode);
@@ -1,6 +1,6 @@
1
1
  // The <Button> component implements a standard button / button-like link.
2
2
 
3
- import type { PointerEventHandler, ReactNode } from 'react';
3
+ import type { FunctionComponent, PointerEventHandler, ReactNode } from 'react';
4
4
 
5
5
  import Link from 'components/Link';
6
6
 
@@ -24,8 +24,7 @@ type PropsT = {
24
24
  to?: object | string;
25
25
  };
26
26
 
27
- /* eslint-disable react/function-component-definition */
28
- export const BaseButton: React.FunctionComponent<PropsT> = ({
27
+ export const BaseButton: FunctionComponent<PropsT> = ({
29
28
  active,
30
29
  children,
31
30
  disabled,
@@ -75,9 +74,9 @@ export const BaseButton: React.FunctionComponent<PropsT> = ({
75
74
  className={className}
76
75
  data-testid={process.env.NODE_ENV === 'production' ? undefined : testId}
77
76
  onClick={onClick}
78
- onKeyDown={onClick && ((e) => {
77
+ onKeyDown={onClick ? (e) => {
79
78
  if (e.key === 'Enter') onClick(e);
80
- })}
79
+ } : undefined}
81
80
  onMouseDown={onMouseDown}
82
81
  onPointerDown={onPointerDown}
83
82
  role="button"
@@ -9,11 +9,11 @@ type PropT<ValueT> = {
9
9
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
10
10
  testId?: string;
11
11
  theme: Theme<
12
- | 'checkbox'
13
- | 'container'
14
- | 'disabled'
15
- | 'indeterminate'
16
- | 'label'
12
+ | 'checkbox'
13
+ | 'container'
14
+ | 'disabled'
15
+ | 'indeterminate'
16
+ | 'label'
17
17
  >;
18
18
  };
19
19
 
@@ -33,14 +33,17 @@ const Checkbox = <ValueT extends boolean | 'indeterminate' = boolean>({
33
33
 
34
34
  return (
35
35
  <div className={containerClassName}>
36
- { label === undefined ? null : <div className={theme.label}>{label}</div> }
36
+ { label === undefined
37
+ ? null : <div className={theme.label}>{label}</div> }
37
38
  <input
38
39
  checked={checked === undefined ? undefined : checked === true}
39
40
  className={checkboxClassName}
40
41
  data-testid={process.env.NODE_ENV === 'production' ? undefined : testId}
41
42
  disabled={disabled}
42
43
  onChange={onChange}
43
- onClick={(e) => e.stopPropagation()}
44
+ onClick={(e) => {
45
+ e.stopPropagation();
46
+ }}
44
47
  type="checkbox"
45
48
  />
46
49
  </div>
@@ -87,7 +87,8 @@ const GenericLink = ({
87
87
  * - It should be opened in a new tab;
88
88
  * - It is an absolte URL (starts with http:// or https://);
89
89
  * - It is anchor link (starts with #). */
90
- if (disabled || enforceA || openNewTab || (to as string)?.match(/^(#|(https?|mailto):)/)) {
90
+ if (disabled || enforceA || openNewTab
91
+ || (to as string).match(/^(#|(https?|mailto):)/)) {
91
92
  return (
92
93
  <a
93
94
  className={className}
@@ -96,8 +97,12 @@ const GenericLink = ({
96
97
  // styled as a link.
97
98
  // disabled={disabled}
98
99
  href={to as string}
99
- onClick={disabled ? (e) => e.preventDefault() : onClick}
100
- onMouseDown={disabled ? (e) => e.preventDefault() : onMouseDown}
100
+ onClick={disabled ? (e) => {
101
+ e.preventDefault();
102
+ } : onClick}
103
+ onMouseDown={disabled ? (e) => {
104
+ e.preventDefault();
105
+ } : onMouseDown}
101
106
  rel="noopener noreferrer"
102
107
  styleName="link"
103
108
  target={openNewTab ? '_blank' : ''}
@@ -114,9 +119,6 @@ const GenericLink = ({
114
119
  className={className}
115
120
  discover="none"
116
121
  // disabled
117
- onMouseDown={onMouseDown}
118
- replace={replace}
119
- to={to!}
120
122
  onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
121
123
  // Executes the user-provided event handler, if any.
122
124
  if (onClick) onClick(e);
@@ -124,7 +126,12 @@ const GenericLink = ({
124
126
  // By default, clicking the link scrolls the page to beginning.
125
127
  if (!keepScrollPosition) window.scroll(0, 0);
126
128
  }}
127
- {...rest} // eslint-disable-line react/jsx-props-no-spreading
129
+ onMouseDown={onMouseDown}
130
+ replace={replace}
131
+ to={to}
132
+ // TODO: Refactor it later.
133
+ // eslint-disable-next-line react/jsx-props-no-spreading
134
+ {...rest}
128
135
  >
129
136
  {children}
130
137
  </L>
@@ -37,7 +37,10 @@ const Input: FunctionComponent<PropsT> = ({
37
37
  className={theme.input}
38
38
  data-testid={process.env.NODE_ENV === 'production' ? undefined : testId}
39
39
  ref={ref}
40
- {...rest} // eslint-disable-line react/jsx-props-no-spreading
40
+
41
+ // TODO: Avoid the spreading later.
42
+ // eslint-disable-next-line react/jsx-props-no-spreading
43
+ {...rest}
41
44
  />
42
45
  </span>
43
46
  );
@@ -12,10 +12,14 @@ import GenericLink, { type PropsT as GenericLinkPropsT } from './GenericLink';
12
12
 
13
13
  type PropsT = Omit<GenericLinkPropsT, 'routerLinkType'> & LinkProps;
14
14
 
15
- const Link: React.FunctionComponent<PropsT> = (props) => (
16
- /* eslint-disable react/jsx-props-no-spreading */
17
- <GenericLink {...props} routerLinkType={RrLink} />
18
- /* eslint-enable react/jsx-props-no-spreading */
19
- );
15
+ const Link: React.FunctionComponent<PropsT>
16
+ = (props) => (
17
+ <GenericLink
18
+ // TODO: Avoid the spreading later.
19
+ // eslint-disable-next-line react/jsx-props-no-spreading
20
+ {...props}
21
+ routerLinkType={RrLink}
22
+ />
23
+ );
20
24
 
21
25
  export default Link;
@@ -1,3 +1,5 @@
1
+ // TODO: Move this component to React Helmet library.
2
+
1
3
  import {
2
4
  type Context as ContextT,
3
5
  type FunctionComponent,
@@ -46,8 +48,12 @@ const MetaTags: FunctionComponent<PropsT> & {
46
48
  title,
47
49
  url,
48
50
  }) => {
51
+ // NOTE: I guess, in this very case, we should prefer title and description
52
+ // also to empty social title and decscription?
53
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
49
54
  const socTitle = socialTitle || title;
50
55
  const socDesc = socialDescription || description;
56
+ /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */
51
57
 
52
58
  const context = useMemo(() => ({
53
59
  description,
@@ -74,8 +80,8 @@ const MetaTags: FunctionComponent<PropsT> & {
74
80
  extra.push(
75
81
  <meta
76
82
  content={content}
77
- name={name}
78
83
  key={`extra-meta-tag-${i}`}
84
+ name={name}
79
85
  />,
80
86
  );
81
87
  }
@@ -88,28 +94,28 @@ const MetaTags: FunctionComponent<PropsT> & {
88
94
  <title>
89
95
  {title}
90
96
  </title>
91
- <meta name="description" content={description} />
97
+ <meta content={description} name="description" />
92
98
 
93
99
  {/* Twitter cards. */}
94
- <meta name="twitter:card" content="summary_large_image" />
95
- <meta name="twitter:title" content={socTitle} />
96
- <meta name="twitter:description" content={socDesc} />
97
- { image ? <meta name="twitter:image" content={image} /> : null }
100
+ <meta content="summary_large_image" name="twitter:card" />
101
+ <meta content={socTitle} name="twitter:title" />
102
+ <meta content={socDesc} name="twitter:description" />
103
+ { image ? <meta content={image} name="twitter:image" /> : null }
98
104
  {
99
- siteName ? (
100
- <meta name="twitter:site" content={`@${siteName}`} />
101
- ) : null
105
+ siteName
106
+ ? <meta content={`@${siteName}`} name="twitter:site" />
107
+ : null
102
108
  }
103
109
 
104
110
  {/* Open Graph data. */}
105
- <meta name="og:title" content={socTitle} />
106
- { image ? <meta name="og:image" content={image} /> : null }
107
- { image ? <meta name="og:image:alt" content={socTitle} /> : null }
108
- <meta name="og:description" content={socDesc} />
111
+ <meta content={socTitle} name="og:title" />
112
+ { image ? <meta content={image} name="og:image" /> : null }
113
+ { image ? <meta content={socTitle} name="og:image:alt" /> : null }
114
+ <meta content={socDesc} name="og:description" />
109
115
  {
110
- siteName ? (<meta name="og:sitename" content={siteName} />) : null
116
+ siteName ? <meta content={siteName} name="og:sitename" /> : null
111
117
  }
112
- { url ? (<meta name="og:url" content={url} />) : null }
118
+ { url ? <meta content={url} name="og:url" /> : null }
113
119
  {extra}
114
120
  </Helmet>
115
121
  {
@@ -94,13 +94,14 @@ const BaseModal: FunctionComponent<PropsT> = ({
94
94
  const focusLast = useMemo(() => (
95
95
  <div
96
96
  onFocus={() => {
97
- const elems = containerRef.current?.querySelectorAll('*') as NodeListOf<HTMLElement>;
97
+ const elems = containerRef.current!.querySelectorAll('*');
98
98
  for (let i = elems.length - 1; i >= 0; --i) {
99
- elems[i]?.focus();
99
+ (elems[i] as HTMLElement).focus();
100
100
  if (document.activeElement === elems[i]) return;
101
101
  }
102
102
  overlayRef.current?.focus();
103
103
  }}
104
+ // TODO: Have a look at this later.
104
105
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
105
106
  tabIndex={0}
106
107
  />
@@ -149,30 +150,30 @@ const BaseModal: FunctionComponent<PropsT> = ({
149
150
  // (because visually and logically the modal dialog does not belong
150
151
  // to its parent container, where it technically belongs from
151
152
  // the HTML mark-up perpective).
152
- /* eslint-disable jsx-a11y/click-events-have-key-events,
153
- jsx-a11y/no-noninteractive-element-interactions */
154
153
  }
155
- <div
154
+ <div // eslint-disable-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
156
155
  aria-modal="true"
157
156
  className={theme.container}
158
157
  data-testid={process.env.NODE_ENV === 'production' ? undefined : testId}
159
- onClick={(e) => e.stopPropagation()}
160
- onWheel={(event) => event.stopPropagation()}
158
+ onClick={(e) => {
159
+ e.stopPropagation();
160
+ }}
161
+ onWheel={(event) => {
162
+ event.stopPropagation();
163
+ }}
161
164
  ref={containerRef}
162
165
  role="dialog"
163
166
  style={style ?? containerStyle}
164
167
  >
165
168
  {children}
166
169
  </div>
167
- {/* eslint-enable jsx-a11y/click-events-have-key-events,
168
- jsx-a11y/no-noninteractive-element-interactions */}
169
170
  <div
170
171
  onFocus={() => {
171
172
  overlayRef.current?.focus();
172
173
  }}
173
- /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
174
+ // TODO: Have a look at this later.
175
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
174
176
  tabIndex={0}
175
- /* eslint-enable jsx-a11y/no-noninteractive-tabindex */
176
177
  />
177
178
  {focusLast}
178
179
  </>
@@ -4,10 +4,15 @@ import GenericLink, { type PropsT as GenericLinkPropsT } from './GenericLink';
4
4
 
5
5
  type PropsT = Omit<GenericLinkPropsT, 'routerLinkType'> & NavLinkProps;
6
6
 
7
- const NavLink: React.FunctionComponent<PropsT> = (props) => (
8
- /* eslint-disable react/jsx-props-no-spreading */
9
- <GenericLink {...props} routerLinkType={RrNavLink} />
10
- /* eslint-enable react/jsx-props-no-spreading */
11
- );
7
+ const NavLink: React.FunctionComponent<PropsT>
8
+ = (props) => (
9
+ <GenericLink
10
+ // TODO: I guess, we better re-write it to avoid the props spreading,
11
+ // but no need to spend time on it right now.
12
+ // eslint-disable-next-line react/jsx-props-no-spreading
13
+ {...props}
14
+ routerLinkType={RrNavLink}
15
+ />
16
+ );
12
17
 
13
18
  export default NavLink;
@@ -1,5 +1,8 @@
1
1
  import {
2
+ type ChangeEventHandler,
2
3
  type FocusEventHandler,
4
+ type FunctionComponent,
5
+ type KeyboardEventHandler,
3
6
  useEffect,
4
7
  useRef,
5
8
  useState,
@@ -17,14 +20,14 @@ type ThemeKeyT =
17
20
  type Props = {
18
21
  disabled?: boolean;
19
22
  onBlur?: FocusEventHandler<HTMLTextAreaElement>;
20
- onChange?: React.ChangeEventHandler<HTMLTextAreaElement>;
21
- onKeyDown?: React.KeyboardEventHandler<HTMLTextAreaElement>;
23
+ onChange?: ChangeEventHandler<HTMLTextAreaElement>;
24
+ onKeyDown?: KeyboardEventHandler<HTMLTextAreaElement>;
22
25
  placeholder?: string;
23
26
  theme: Theme<ThemeKeyT>;
24
27
  value?: string;
25
28
  };
26
29
 
27
- const TextArea: React.FunctionComponent<Props> = ({
30
+ const TextArea: FunctionComponent<Props> = ({
28
31
  disabled,
29
32
  onBlur,
30
33
  onChange,
@@ -36,7 +39,7 @@ const TextArea: React.FunctionComponent<Props> = ({
36
39
  const hiddenAreaRef = useRef<HTMLTextAreaElement>(null);
37
40
  const [height, setHeight] = useState<number | undefined>();
38
41
 
39
- const [localValue, setLocalValue] = useState(value || '');
42
+ const [localValue, setLocalValue] = useState(value ?? '');
40
43
  if (value !== undefined && localValue !== value) setLocalValue(value);
41
44
 
42
45
  // This resizes text area's height when its width is changed for any reason.
@@ -64,27 +67,32 @@ const TextArea: React.FunctionComponent<Props> = ({
64
67
  return (
65
68
  <div className={theme.container}>
66
69
  <textarea
70
+ className={`${theme.textarea} ${theme.hidden}`}
71
+
67
72
  // This text area is hidden underneath the primary one below,
68
73
  // and it is used for text measurements, to implement auto-scaling
69
74
  // of the primary textarea's height.
70
75
  readOnly
71
76
  ref={hiddenAreaRef}
72
- className={`${theme.textarea} ${theme.hidden}`}
73
77
  value={localValue}
74
78
  />
75
79
  <textarea
80
+ className={theme.textarea}
76
81
  disabled={disabled}
77
82
  onBlur={onBlur}
83
+
78
84
  // When value is "undefined" the text area is not-managed, and we should
79
85
  // manage it internally for the measurement / resizing functionality
80
86
  // to work.
81
- onChange={value === undefined ? ((e) => {
82
- setLocalValue(e.target.value);
83
- }) : onChange}
87
+ onChange={
88
+ value === undefined
89
+ ? (e) => {
90
+ setLocalValue(e.target.value);
91
+ } : onChange
92
+ }
84
93
  onKeyDown={onKeyDown}
85
94
  placeholder={placeholder}
86
95
  style={{ height }}
87
- className={theme.textarea}
88
96
  value={localValue}
89
97
  />
90
98
  </div>