@telus-uds/components-base 1.14.0 → 1.14.3

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 CHANGED
@@ -1,12 +1,38 @@
1
1
  # Change Log - @telus-uds/components-base
2
2
 
3
- This log was last generated on Wed, 17 Aug 2022 18:43:08 GMT and should not be manually modified.
3
+ This log was last generated on Thu, 01 Sep 2022 15:38:33 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 1.14.3
8
+
9
+ Thu, 01 Sep 2022 15:38:33 GMT
10
+
11
+ ### Patches
12
+
13
+ - add input validation to select component (evander.owusu@telus.com)
14
+ - Refactor ssr support to match ssrStyles approach proven in ds-allium (alan.slater@nearform.com)
15
+
16
+ ## 1.14.2
17
+
18
+ Tue, 23 Aug 2022 19:32:37 GMT
19
+
20
+ ### Patches
21
+
22
+ - fix: remove extra icons from pagination (ruslan.bredikhin@nearform.com)
23
+ - Bump @telus-uds/system-theme-tokens to v2.4.0
24
+
25
+ ## 1.14.1
26
+
27
+ Wed, 17 Aug 2022 20:50:59 GMT
28
+
29
+ ### Patches
30
+
31
+ - Bump @telus-uds/system-theme-tokens to v2.3.0
32
+
7
33
  ## 1.14.0
8
34
 
9
- Wed, 17 Aug 2022 18:43:08 GMT
35
+ Wed, 17 Aug 2022 18:45:01 GMT
10
36
 
11
37
  ### Minor changes
12
38
 
@@ -1157,6 +1157,16 @@
1157
1157
  true
1158
1158
  ],
1159
1159
  "type": "state"
1160
+ },
1161
+ "width": {
1162
+ "description": "The width of the modal",
1163
+ "values": [
1164
+ "s",
1165
+ "m",
1166
+ "l",
1167
+ "xl"
1168
+ ],
1169
+ "type": "state"
1160
1170
  }
1161
1171
  },
1162
1172
  "Notification": {
@@ -33,12 +33,22 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
33
33
 
34
34
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
35
35
 
36
- const selectIconTokens = (_ref, direction) => {
36
+ // We need to drop the icon here since it gets rendered via children and not
37
+ // `ButtonBase` in order to tap into the state of the button
38
+ const selectButtonTokens = _ref => {
39
+ let {
40
+ icon: _,
41
+ ...rest
42
+ } = _ref;
43
+ return (0, _utils.selectTokens)('Button', rest);
44
+ };
45
+
46
+ const selectIconTokens = (_ref2, direction) => {
37
47
  let {
38
48
  color,
39
49
  iconSize,
40
50
  iconDisplace
41
- } = _ref;
51
+ } = _ref2;
42
52
  return {
43
53
  color,
44
54
  size: iconSize,
@@ -50,7 +60,7 @@ const directionToSide = {
50
60
  previous: 'left',
51
61
  next: 'right'
52
62
  };
53
- const SideButton = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
63
+ const SideButton = /*#__PURE__*/(0, _react.forwardRef)((_ref3, ref) => {
54
64
  let {
55
65
  direction = 'previous',
56
66
  onPress,
@@ -59,7 +69,7 @@ const SideButton = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
59
69
  variant,
60
70
  tokens,
61
71
  ...props
62
- } = _ref2;
72
+ } = _ref3;
63
73
  const viewport = (0, _ViewportProvider.useViewport)();
64
74
  const buttonVariant = {
65
75
  direction,
@@ -75,7 +85,7 @@ const SideButton = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
75
85
  icon
76
86
  } = getTokens(tokens, buttonVariant);
77
87
 
78
- const getButtonTokens = buttonState => (0, _utils.selectTokens)('Button', getTokens(buttonState));
88
+ const getButtonTokens = buttonState => selectButtonTokens(getTokens(buttonState));
79
89
 
80
90
  const getIconTokens = buttonState => selectIconTokens(getTokens(buttonState), direction);
81
91
 
@@ -101,11 +111,11 @@ const SideButton = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
101
111
  ref: ref,
102
112
  ...buttonProps,
103
113
  tokens: getButtonTokens,
104
- children: _ref3 => {
114
+ children: _ref4 => {
105
115
  let {
106
116
  textStyles,
107
117
  ...buttonState
108
- } = _ref3;
118
+ } = _ref4;
109
119
  const iconProps = {
110
120
  tokens: getIconTokens(buttonState)
111
121
  };
@@ -258,6 +258,7 @@ const Select = /*#__PURE__*/(0, _react.forwardRef)((_ref7, ref) => {
258
258
  } = (0, _ThemeProvider.useTheme)();
259
259
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_InputSupports.default, { ...supportsProps,
260
260
  ...selectedProps,
261
+ validation: validation,
261
262
  children: _ref8 => {
262
263
  let {
263
264
  inputId,
package/lib/utils/ssr.js CHANGED
@@ -3,10 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getSSRStyles = exports.getReactNativeWebSSRStyles = void 0;
6
+ exports.ssrStyles = exports.getSSRStyles = exports.getReactNativeWebSSRStyles = exports.default = void 0;
7
+
8
+ var _react = _interopRequireDefault(require("react"));
7
9
 
8
10
  var _AppRegistry = _interopRequireDefault(require("react-native-web/dist/cjs/exports/AppRegistry"));
9
11
 
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+
10
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
15
 
12
16
  /** @typedef {import('react').ComponentType} ReactComponent */
@@ -14,6 +18,112 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
14
18
  /** @typedef {import('react').ReactElement} ReactElement */
15
19
 
16
20
  /**
21
+ * Returns object with `renderApp` and `getStyles` functions.
22
+ * Weave these into your app's server-side render process:
23
+ *
24
+ * - Call `renderApp` first to do the actual server-side render
25
+ * - After the render is complete, call `getStyles`
26
+ * - Include the style tags returned by `getStyles` in the SSR <head>
27
+ *
28
+ * @param {string} [appName] - optional unique identifier if ssrStyles is called multiple times for multiple apps
29
+ * @param {object} [options] -
30
+ * - `styleGetters`: optional array of additional style getter functions to call after render
31
+ * - `collectStyles`: optional function, takes the rendered app, returns either the same or a new app element
32
+ * @param {boolean} [options.styleGetters]
33
+ * @param {(ReactElement) => ReactElement} [options.collectStyles]
34
+ */
35
+ const ssrStyles = function () {
36
+ let appName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'UDS app';
37
+ let {
38
+ styleGetters = [],
39
+ collectStyles
40
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
41
+ let hasAppRendered = false;
42
+ return {
43
+ /**
44
+ * Server-side-renders the provided app in a way that supports collecting
45
+ * styles for Styled Components and React Native Web stylesheets.
46
+ *
47
+ * @param {ReactComponent} App - the root component for the app
48
+ * @param {object} [props] - props for this render e.g. page routing props
49
+ * @param {object} [options] -
50
+ * - `renderedByRNW`: pass as true if the main render is by AppRegistry.runApplication from React Native
51
+ * - `WrapperComponent`: Component rendered with no props around app content and inside root tag
52
+ * @param {boolean} [options.renderedByRNW]
53
+ * @param {ReactComponent} [options.WrapperComponent]
54
+ *
55
+ * @returns {ReactElement} - the rendered app output
56
+ */
57
+ renderApp: function (App, props) {
58
+ let {
59
+ renderedByRNW = false,
60
+ WrapperComponent
61
+ } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
62
+
63
+ _AppRegistry.default.registerComponent(appName, () => App); // AppRegistry.getApplication renders the app in a container, and collects styles.
64
+
65
+
66
+ const {
67
+ element,
68
+ getStyleElement
69
+ } = _AppRegistry.default.getApplication(appName, {
70
+ WrapperComponent,
71
+ initialProps: props
72
+ });
73
+
74
+ let renderedApp = // React Native Web's AppRegistry.getApplication assumes the app is rendered using
75
+ // AppRegistry.runApplication and wraps it in <AppContainer>, which wraps the entire app
76
+ // in two outer containers resembling <View style={{ flex: 1 }} pointerEvents="box-none">.
77
+ // So, use that IF user says AppRegistry.runApplication will do the client-side render.
78
+ renderedByRNW && element || // If the live app is not rendered using AppRegistry.runApplication, we need to
79
+ // re-render it without the <AppContainer> wrapper, to avoid SSR mismatch errors.
80
+ // Default to this as many platforms (e.g. NextJS) will use their own renderers.
81
+ WrapperComponent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(WrapperComponent, {
82
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(App, { ...props
83
+ })
84
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(App, { ...props
85
+ });
86
+
87
+ const getRNWStyle = () => getStyleElement({
88
+ key: 'react-native-stylesheet'
89
+ });
90
+
91
+ styleGetters.push(getRNWStyle);
92
+
93
+ if (typeof collectStyles === 'function') {
94
+ renderedApp = collectStyles(renderedApp);
95
+ }
96
+
97
+ hasAppRendered = true;
98
+ return renderedApp;
99
+ },
100
+
101
+ /**
102
+ * Turns styles collected during renderApp into an array of React elements of
103
+ * HTML <style> tags ready for insertion into the SSR HTML's <head>.
104
+ *
105
+ * Must be called after `renderApp` has completed.
106
+ *
107
+ * @param {...ReactElement} existingStyles - any existing style tag elements to merge in
108
+ * @returns {ReactElement[]} - flat array of <style> React elements
109
+ */
110
+ getStyles: function () {
111
+ if (!hasAppRendered) throw new Error('Called getStyles before renderApp in ssrStyles');
112
+
113
+ for (var _len = arguments.length, existingStyles = new Array(_len), _key = 0; _key < _len; _key++) {
114
+ existingStyles[_key] = arguments[_key];
115
+ }
116
+
117
+ return [...existingStyles, ...styleGetters.flatMap(getter => getter())];
118
+ }
119
+ };
120
+ };
121
+
122
+ exports.ssrStyles = ssrStyles;
123
+ var _default = ssrStyles;
124
+ /**
125
+ * @deprecated - use ssrStyles instead
126
+ *
17
127
  * Registers the app's root component with React Native Web and generates
18
128
  * the main <style> tag containing React Native Web stylesheet styles.
19
129
  *
@@ -21,6 +131,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
21
131
  * @param {string} [appName]
22
132
  * @returns {ReactElement[]}
23
133
  */
134
+
135
+ exports.default = _default;
136
+
24
137
  const getReactNativeWebSSRStyles = function (AppRoot) {
25
138
  let appName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'app';
26
139
 
@@ -33,6 +146,8 @@ const getReactNativeWebSSRStyles = function (AppRoot) {
33
146
  return [getStyleElement()];
34
147
  };
35
148
  /**
149
+ * @deprecated - use ssrStyles instead
150
+ *
36
151
  * Gets style tags for each currently supported CSS-in-JS library and returns
37
152
  * them alongside any existing style tags.
38
153
  *
@@ -7,15 +7,25 @@ import { useThemeTokensCallback } from '../ThemeProvider';
7
7
  import { useViewport } from '../ViewportProvider';
8
8
  import { copyPropTypes, hrefAttrsProp, linkProps, selectTokens, withLinkRouter } from '../utils';
9
9
  import dictionary from './dictionary';
10
- import useCopy from '../utils/useCopy';
10
+ import useCopy from '../utils/useCopy'; // We need to drop the icon here since it gets rendered via children and not
11
+ // `ButtonBase` in order to tap into the state of the button
12
+
11
13
  import { jsx as _jsx } from "react/jsx-runtime";
12
14
 
13
- const selectIconTokens = (_ref, direction) => {
15
+ const selectButtonTokens = _ref => {
16
+ let {
17
+ icon: _,
18
+ ...rest
19
+ } = _ref;
20
+ return selectTokens('Button', rest);
21
+ };
22
+
23
+ const selectIconTokens = (_ref2, direction) => {
14
24
  let {
15
25
  color,
16
26
  iconSize,
17
27
  iconDisplace
18
- } = _ref;
28
+ } = _ref2;
19
29
  return {
20
30
  color,
21
31
  size: iconSize,
@@ -27,7 +37,7 @@ const directionToSide = {
27
37
  previous: 'left',
28
38
  next: 'right'
29
39
  };
30
- const SideButton = /*#__PURE__*/forwardRef((_ref2, ref) => {
40
+ const SideButton = /*#__PURE__*/forwardRef((_ref3, ref) => {
31
41
  let {
32
42
  direction = 'previous',
33
43
  onPress,
@@ -36,7 +46,7 @@ const SideButton = /*#__PURE__*/forwardRef((_ref2, ref) => {
36
46
  variant,
37
47
  tokens,
38
48
  ...props
39
- } = _ref2;
49
+ } = _ref3;
40
50
  const viewport = useViewport();
41
51
  const buttonVariant = {
42
52
  direction,
@@ -52,7 +62,7 @@ const SideButton = /*#__PURE__*/forwardRef((_ref2, ref) => {
52
62
  icon
53
63
  } = getTokens(tokens, buttonVariant);
54
64
 
55
- const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
65
+ const getButtonTokens = buttonState => selectButtonTokens(getTokens(buttonState));
56
66
 
57
67
  const getIconTokens = buttonState => selectIconTokens(getTokens(buttonState), direction);
58
68
 
@@ -76,11 +86,11 @@ const SideButton = /*#__PURE__*/forwardRef((_ref2, ref) => {
76
86
  ref: ref,
77
87
  ...buttonProps,
78
88
  tokens: getButtonTokens,
79
- children: _ref3 => {
89
+ children: _ref4 => {
80
90
  let {
81
91
  textStyles,
82
92
  ...buttonState
83
- } = _ref3;
93
+ } = _ref4;
84
94
  const iconProps = {
85
95
  tokens: getIconTokens(buttonState)
86
96
  };
@@ -234,6 +234,7 @@ const Select = /*#__PURE__*/forwardRef((_ref7, ref) => {
234
234
  } = useTheme();
235
235
  return /*#__PURE__*/_jsx(InputSupports, { ...supportsProps,
236
236
  ...selectedProps,
237
+ validation: validation,
237
238
  children: _ref8 => {
238
239
  let {
239
240
  inputId,
@@ -1,9 +1,113 @@
1
+ import React from 'react';
1
2
  import AppRegistry from "react-native-web/dist/exports/AppRegistry";
2
3
  /** @typedef {import('react').ComponentType} ReactComponent */
3
4
 
4
5
  /** @typedef {import('react').ReactElement} ReactElement */
5
6
 
6
7
  /**
8
+ * Returns object with `renderApp` and `getStyles` functions.
9
+ * Weave these into your app's server-side render process:
10
+ *
11
+ * - Call `renderApp` first to do the actual server-side render
12
+ * - After the render is complete, call `getStyles`
13
+ * - Include the style tags returned by `getStyles` in the SSR <head>
14
+ *
15
+ * @param {string} [appName] - optional unique identifier if ssrStyles is called multiple times for multiple apps
16
+ * @param {object} [options] -
17
+ * - `styleGetters`: optional array of additional style getter functions to call after render
18
+ * - `collectStyles`: optional function, takes the rendered app, returns either the same or a new app element
19
+ * @param {boolean} [options.styleGetters]
20
+ * @param {(ReactElement) => ReactElement} [options.collectStyles]
21
+ */
22
+
23
+ import { jsx as _jsx } from "react/jsx-runtime";
24
+ export const ssrStyles = function () {
25
+ let appName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'UDS app';
26
+ let {
27
+ styleGetters = [],
28
+ collectStyles
29
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
30
+ let hasAppRendered = false;
31
+ return {
32
+ /**
33
+ * Server-side-renders the provided app in a way that supports collecting
34
+ * styles for Styled Components and React Native Web stylesheets.
35
+ *
36
+ * @param {ReactComponent} App - the root component for the app
37
+ * @param {object} [props] - props for this render e.g. page routing props
38
+ * @param {object} [options] -
39
+ * - `renderedByRNW`: pass as true if the main render is by AppRegistry.runApplication from React Native
40
+ * - `WrapperComponent`: Component rendered with no props around app content and inside root tag
41
+ * @param {boolean} [options.renderedByRNW]
42
+ * @param {ReactComponent} [options.WrapperComponent]
43
+ *
44
+ * @returns {ReactElement} - the rendered app output
45
+ */
46
+ renderApp: function (App, props) {
47
+ let {
48
+ renderedByRNW = false,
49
+ WrapperComponent
50
+ } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
51
+ AppRegistry.registerComponent(appName, () => App); // AppRegistry.getApplication renders the app in a container, and collects styles.
52
+
53
+ const {
54
+ element,
55
+ getStyleElement
56
+ } = AppRegistry.getApplication(appName, {
57
+ WrapperComponent,
58
+ initialProps: props
59
+ });
60
+ let renderedApp = // React Native Web's AppRegistry.getApplication assumes the app is rendered using
61
+ // AppRegistry.runApplication and wraps it in <AppContainer>, which wraps the entire app
62
+ // in two outer containers resembling <View style={{ flex: 1 }} pointerEvents="box-none">.
63
+ // So, use that IF user says AppRegistry.runApplication will do the client-side render.
64
+ renderedByRNW && element || // If the live app is not rendered using AppRegistry.runApplication, we need to
65
+ // re-render it without the <AppContainer> wrapper, to avoid SSR mismatch errors.
66
+ // Default to this as many platforms (e.g. NextJS) will use their own renderers.
67
+ WrapperComponent ? /*#__PURE__*/_jsx(WrapperComponent, {
68
+ children: /*#__PURE__*/_jsx(App, { ...props
69
+ })
70
+ }) : /*#__PURE__*/_jsx(App, { ...props
71
+ });
72
+
73
+ const getRNWStyle = () => getStyleElement({
74
+ key: 'react-native-stylesheet'
75
+ });
76
+
77
+ styleGetters.push(getRNWStyle);
78
+
79
+ if (typeof collectStyles === 'function') {
80
+ renderedApp = collectStyles(renderedApp);
81
+ }
82
+
83
+ hasAppRendered = true;
84
+ return renderedApp;
85
+ },
86
+
87
+ /**
88
+ * Turns styles collected during renderApp into an array of React elements of
89
+ * HTML <style> tags ready for insertion into the SSR HTML's <head>.
90
+ *
91
+ * Must be called after `renderApp` has completed.
92
+ *
93
+ * @param {...ReactElement} existingStyles - any existing style tag elements to merge in
94
+ * @returns {ReactElement[]} - flat array of <style> React elements
95
+ */
96
+ getStyles: function () {
97
+ if (!hasAppRendered) throw new Error('Called getStyles before renderApp in ssrStyles');
98
+
99
+ for (var _len = arguments.length, existingStyles = new Array(_len), _key = 0; _key < _len; _key++) {
100
+ existingStyles[_key] = arguments[_key];
101
+ }
102
+
103
+ return [...existingStyles, ...styleGetters.flatMap(getter => getter())];
104
+ }
105
+ };
106
+ };
107
+ export default ssrStyles;
108
+ /**
109
+ * @deprecated - use ssrStyles instead
110
+ *
7
111
  * Registers the app's root component with React Native Web and generates
8
112
  * the main <style> tag containing React Native Web stylesheet styles.
9
113
  *
@@ -21,6 +125,8 @@ export const getReactNativeWebSSRStyles = function (AppRoot) {
21
125
  return [getStyleElement()];
22
126
  };
23
127
  /**
128
+ * @deprecated - use ssrStyles instead
129
+ *
24
130
  * Gets style tags for each currently supported CSS-in-JS library and returns
25
131
  * them alongside any existing style tags.
26
132
  *
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "airbnb-prop-types": "^2.16.0",
11
11
  "@telus-uds/system-constants": "^1.0.4",
12
- "@telus-uds/system-theme-tokens": "^2.2.0",
12
+ "@telus-uds/system-theme-tokens": "^2.4.0",
13
13
  "lodash.debounce": "^4.0.8",
14
14
  "lodash.merge": "^4.6.2",
15
15
  "prop-types": "^15.7.2",
@@ -71,5 +71,5 @@
71
71
  "standard-engine": {
72
72
  "skip": true
73
73
  },
74
- "version": "1.14.0"
74
+ "version": "1.14.3"
75
75
  }
@@ -12,6 +12,9 @@ import { copyPropTypes, hrefAttrsProp, linkProps, selectTokens, withLinkRouter }
12
12
  import dictionary from './dictionary'
13
13
  import useCopy from '../utils/useCopy'
14
14
 
15
+ // We need to drop the icon here since it gets rendered via children and not
16
+ // `ButtonBase` in order to tap into the state of the button
17
+ const selectButtonTokens = ({ icon: _, ...rest }) => selectTokens('Button', rest)
15
18
  const selectIconTokens = ({ color, iconSize, iconDisplace }, direction) => {
16
19
  return {
17
20
  color,
@@ -35,7 +38,7 @@ const SideButton = forwardRef(
35
38
 
36
39
  const { icon } = getTokens(tokens, buttonVariant)
37
40
 
38
- const getButtonTokens = (buttonState) => selectTokens('Button', getTokens(buttonState))
41
+ const getButtonTokens = (buttonState) => selectButtonTokens(getTokens(buttonState))
39
42
  const getIconTokens = (buttonState) => selectIconTokens(getTokens(buttonState), direction)
40
43
 
41
44
  const label = direction === 'previous' ? getCopy('previousText') : getCopy('nextText')
@@ -215,7 +215,7 @@ const Select = forwardRef(
215
215
  const { themeOptions } = useTheme()
216
216
 
217
217
  return (
218
- <InputSupports {...supportsProps} {...selectedProps}>
218
+ <InputSupports {...supportsProps} {...selectedProps} validation={validation}>
219
219
  {({ inputId, ...props }) => (
220
220
  <View style={selectOuterBorderStyles(themeTokens)}>
221
221
  <Picker
@@ -0,0 +1,124 @@
1
+ import React from 'react'
2
+ import { AppRegistry } from 'react-native'
3
+ /** @typedef {import('react').ComponentType} ReactComponent */
4
+ /** @typedef {import('react').ReactElement} ReactElement */
5
+
6
+ /**
7
+ * Returns object with `renderApp` and `getStyles` functions.
8
+ * Weave these into your app's server-side render process:
9
+ *
10
+ * - Call `renderApp` first to do the actual server-side render
11
+ * - After the render is complete, call `getStyles`
12
+ * - Include the style tags returned by `getStyles` in the SSR <head>
13
+ *
14
+ * @param {string} [appName] - optional unique identifier if ssrStyles is called multiple times for multiple apps
15
+ * @param {object} [options] -
16
+ * - `styleGetters`: optional array of additional style getter functions to call after render
17
+ * - `collectStyles`: optional function, takes the rendered app, returns either the same or a new app element
18
+ * @param {boolean} [options.styleGetters]
19
+ * @param {(ReactElement) => ReactElement} [options.collectStyles]
20
+ */
21
+ export const ssrStyles = (appName = 'UDS app', { styleGetters = [], collectStyles } = {}) => {
22
+ let hasAppRendered = false
23
+ return {
24
+ /**
25
+ * Server-side-renders the provided app in a way that supports collecting
26
+ * styles for Styled Components and React Native Web stylesheets.
27
+ *
28
+ * @param {ReactComponent} App - the root component for the app
29
+ * @param {object} [props] - props for this render e.g. page routing props
30
+ * @param {object} [options] -
31
+ * - `renderedByRNW`: pass as true if the main render is by AppRegistry.runApplication from React Native
32
+ * - `WrapperComponent`: Component rendered with no props around app content and inside root tag
33
+ * @param {boolean} [options.renderedByRNW]
34
+ * @param {ReactComponent} [options.WrapperComponent]
35
+ *
36
+ * @returns {ReactElement} - the rendered app output
37
+ */
38
+ renderApp: (App, props, { renderedByRNW = false, WrapperComponent } = {}) => {
39
+ AppRegistry.registerComponent(appName, () => App)
40
+ // AppRegistry.getApplication renders the app in a container, and collects styles.
41
+ const { element, getStyleElement } = AppRegistry.getApplication(appName, {
42
+ WrapperComponent,
43
+ initialProps: props
44
+ })
45
+
46
+ let renderedApp =
47
+ // React Native Web's AppRegistry.getApplication assumes the app is rendered using
48
+ // AppRegistry.runApplication and wraps it in <AppContainer>, which wraps the entire app
49
+ // in two outer containers resembling <View style={{ flex: 1 }} pointerEvents="box-none">.
50
+ // So, use that IF user says AppRegistry.runApplication will do the client-side render.
51
+ (renderedByRNW && element) ||
52
+ // If the live app is not rendered using AppRegistry.runApplication, we need to
53
+ // re-render it without the <AppContainer> wrapper, to avoid SSR mismatch errors.
54
+ // Default to this as many platforms (e.g. NextJS) will use their own renderers.
55
+ WrapperComponent ? (
56
+ <WrapperComponent>
57
+ <App {...props} />
58
+ </WrapperComponent>
59
+ ) : (
60
+ <App {...props} />
61
+ )
62
+
63
+ const getRNWStyle = () => getStyleElement({ key: 'react-native-stylesheet' })
64
+ styleGetters.push(getRNWStyle)
65
+
66
+ if (typeof collectStyles === 'function') {
67
+ renderedApp = collectStyles(renderedApp)
68
+ }
69
+ hasAppRendered = true
70
+ return renderedApp
71
+ },
72
+ /**
73
+ * Turns styles collected during renderApp into an array of React elements of
74
+ * HTML <style> tags ready for insertion into the SSR HTML's <head>.
75
+ *
76
+ * Must be called after `renderApp` has completed.
77
+ *
78
+ * @param {...ReactElement} existingStyles - any existing style tag elements to merge in
79
+ * @returns {ReactElement[]} - flat array of <style> React elements
80
+ */
81
+ getStyles: (...existingStyles) => {
82
+ if (!hasAppRendered) throw new Error('Called getStyles before renderApp in ssrStyles')
83
+ return [...existingStyles, ...styleGetters.flatMap((getter) => getter())]
84
+ }
85
+ }
86
+ }
87
+
88
+ export default ssrStyles
89
+
90
+ /**
91
+ * @deprecated - use ssrStyles instead
92
+ *
93
+ * Registers the app's root component with React Native Web and generates
94
+ * the main <style> tag containing React Native Web stylesheet styles.
95
+ *
96
+ * @param {ReactComponent} AppRoot
97
+ * @param {string} [appName]
98
+ * @returns {ReactElement[]}
99
+ */
100
+
101
+ export const getReactNativeWebSSRStyles = (AppRoot, appName = 'app') => {
102
+ AppRegistry.registerComponent(appName, () => AppRoot)
103
+ const { getStyleElement } = AppRegistry.getApplication(appName)
104
+ return [getStyleElement()]
105
+ }
106
+
107
+ /**
108
+ * @deprecated - use ssrStyles instead
109
+ *
110
+ * Gets style tags for each currently supported CSS-in-JS library and returns
111
+ * them alongside any existing style tags.
112
+ *
113
+ * @param {ReactComponent} AppRoot
114
+ * @param {string} [appName]
115
+ * @param {ReactElement[]} [existingStyles]
116
+ * @returns {ReactElement[]}
117
+ */
118
+ export const getSSRStyles = (AppRoot, appName = 'app', existingStyles = []) => {
119
+ return [
120
+ ...existingStyles,
121
+ ...getReactNativeWebSSRStyles(AppRoot, appName)
122
+ // if any other CSS-in-JS is added e.g. styled-components generate and add its styles here
123
+ ]
124
+ }
package/src/utils/ssr.js DELETED
@@ -1,35 +0,0 @@
1
- import { AppRegistry } from 'react-native'
2
- /** @typedef {import('react').ComponentType} ReactComponent */
3
- /** @typedef {import('react').ReactElement} ReactElement */
4
-
5
- /**
6
- * Registers the app's root component with React Native Web and generates
7
- * the main <style> tag containing React Native Web stylesheet styles.
8
- *
9
- * @param {ReactComponent} AppRoot
10
- * @param {string} [appName]
11
- * @returns {ReactElement[]}
12
- */
13
-
14
- export const getReactNativeWebSSRStyles = (AppRoot, appName = 'app') => {
15
- AppRegistry.registerComponent(appName, () => AppRoot)
16
- const { getStyleElement } = AppRegistry.getApplication(appName)
17
- return [getStyleElement()]
18
- }
19
-
20
- /**
21
- * Gets style tags for each currently supported CSS-in-JS library and returns
22
- * them alongside any existing style tags.
23
- *
24
- * @param {ReactComponent} AppRoot
25
- * @param {string} [appName]
26
- * @param {ReactElement[]} [existingStyles]
27
- * @returns {ReactElement[]}
28
- */
29
- export const getSSRStyles = (AppRoot, appName = 'app', existingStyles = []) => {
30
- return [
31
- ...existingStyles,
32
- ...getReactNativeWebSSRStyles(AppRoot, appName)
33
- // if any other CSS-in-JS is added e.g. styled-components generate and add its styles here
34
- ]
35
- }