cozy-ui-plus 12.2.1 → 12.3.0
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 +6 -0
- package/dist/Intent/IntentDialogOpener/index.js +17 -2
- package/dist/Intent/IntentIframe/index.js +13 -3
- package/dist/src/Intent/IntentDialogOpener/index.d.ts +5 -0
- package/package.json +3 -3
- package/src/Intent/IntentDialogOpener/Readme.md +5 -0
- package/src/Intent/IntentDialogOpener/index.jsx +33 -21
- package/src/Intent/IntentIframe/Readme.md +5 -1
- package/src/Intent/IntentIframe/index.jsx +26 -18
- package/src/Intent/IntentIframe/index.spec.jsx +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [12.3.0](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@12.2.1...cozy-ui-plus@12.3.0) (2026-07-28)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- **intent:** Support forced themes ([b207c1c](https://github.com/cozy/cozy-libs/commit/b207c1c488d26cfc60b1408ad356983a8aa2cd66))
|
|
11
|
+
|
|
6
12
|
## [12.2.1](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@12.2.0...cozy-ui-plus@12.2.1) (2026-07-28)
|
|
7
13
|
|
|
8
14
|
**Note:** Version bump only for package cozy-ui-plus
|
|
@@ -21,6 +21,8 @@ var _CozyDialogs = require("cozy-ui/transpiled/react/CozyDialogs");
|
|
|
21
21
|
|
|
22
22
|
var _Dialog = _interopRequireDefault(require("cozy-ui/transpiled/react/Dialog"));
|
|
23
23
|
|
|
24
|
+
var _CozyTheme = _interopRequireDefault(require("cozy-ui/transpiled/react/providers/CozyTheme"));
|
|
25
|
+
|
|
24
26
|
var _IntentIframe = _interopRequireWildcard(require("../IntentIframe"));
|
|
25
27
|
|
|
26
28
|
var _excluded = ["options", "action", "doctype", "children", "closable", "showCloseButton", "create", "tag", "onComplete", "onDismiss", "onReadyToUse", "waitForReadyToUse", "iframeProps", "Component"];
|
|
@@ -34,6 +36,8 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
34
36
|
* for the specified intent.
|
|
35
37
|
*/
|
|
36
38
|
var IntentDialogOpener = function IntentDialogOpener(props) {
|
|
39
|
+
var _options$theme;
|
|
40
|
+
|
|
37
41
|
var options = props.options,
|
|
38
42
|
action = props.action,
|
|
39
43
|
doctype = props.doctype,
|
|
@@ -77,14 +81,18 @@ var IntentDialogOpener = function IntentDialogOpener(props) {
|
|
|
77
81
|
|
|
78
82
|
var Tag = tag; // React needs uppercase element names
|
|
79
83
|
|
|
84
|
+
var themeType = options === null || options === void 0 ? void 0 : (_options$theme = options.theme) === null || _options$theme === void 0 ? void 0 : _options$theme.type;
|
|
85
|
+
var forcedTheme = ['light', 'dark'].includes(themeType) ? themeType : undefined;
|
|
80
86
|
var elements = [/*#__PURE__*/_react.default.cloneElement(children, {
|
|
81
87
|
key: 'opener',
|
|
82
88
|
onClick: openModal
|
|
83
89
|
})];
|
|
84
90
|
|
|
85
91
|
if (modalOpened) {
|
|
86
|
-
elements.push( /*#__PURE__*/_react.default.createElement(
|
|
92
|
+
elements.push( /*#__PURE__*/_react.default.createElement(_CozyTheme.default, {
|
|
87
93
|
key: "intent-modal",
|
|
94
|
+
type: forcedTheme
|
|
95
|
+
}, /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({
|
|
88
96
|
open: modalOpened,
|
|
89
97
|
onClose: closable && handleDismiss
|
|
90
98
|
}, componentProps), closable && showCloseButton && /*#__PURE__*/_react.default.createElement(_CozyDialogs.DialogCloseButton, {
|
|
@@ -99,7 +107,7 @@ var IntentDialogOpener = function IntentDialogOpener(props) {
|
|
|
99
107
|
onReadyToUse: onReadyToUse,
|
|
100
108
|
waitForReadyToUse: waitForReadyToUse,
|
|
101
109
|
iframeProps: iframeProps
|
|
102
|
-
})));
|
|
110
|
+
}))));
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
return /*#__PURE__*/_react.default.createElement(Tag, null, elements);
|
|
@@ -124,6 +132,13 @@ IntentDialogOpener.propTypes = {
|
|
|
124
132
|
/** Action you want to execute */
|
|
125
133
|
action: _propTypes.default.string.isRequired,
|
|
126
134
|
|
|
135
|
+
/** Options passed to the intent */
|
|
136
|
+
options: _propTypes.default.shape({
|
|
137
|
+
theme: _propTypes.default.shape({
|
|
138
|
+
type: _propTypes.default.oneOf(['light', 'dark'])
|
|
139
|
+
})
|
|
140
|
+
}),
|
|
141
|
+
|
|
127
142
|
/** Doctype on which you want to execute the action */
|
|
128
143
|
doctype: _propTypes.default.string.isRequired,
|
|
129
144
|
|
|
@@ -35,6 +35,8 @@ var _cozyInterapp = require("cozy-interapp");
|
|
|
35
35
|
|
|
36
36
|
var _Spinner = _interopRequireDefault(require("cozy-ui/transpiled/react/Spinner"));
|
|
37
37
|
|
|
38
|
+
var _CozyTheme = _interopRequireDefault(require("cozy-ui/transpiled/react/providers/CozyTheme"));
|
|
39
|
+
|
|
38
40
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
39
41
|
|
|
40
42
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
@@ -152,9 +154,11 @@ var IntentIframe = /*#__PURE__*/function (_React$Component) {
|
|
|
152
154
|
}, {
|
|
153
155
|
key: "render",
|
|
154
156
|
value: function render() {
|
|
155
|
-
var
|
|
157
|
+
var _data$theme,
|
|
158
|
+
_this3 = this;
|
|
156
159
|
|
|
157
160
|
var _this$props3 = this.props,
|
|
161
|
+
data = _this$props3.data,
|
|
158
162
|
iframeProps = _this$props3.iframeProps,
|
|
159
163
|
waitForReadyToUse = _this$props3.waitForReadyToUse;
|
|
160
164
|
var _this$state = this.state,
|
|
@@ -162,7 +166,13 @@ var IntentIframe = /*#__PURE__*/function (_React$Component) {
|
|
|
162
166
|
frameLoaded = _this$state.frameLoaded,
|
|
163
167
|
readyToUse = _this$state.readyToUse;
|
|
164
168
|
var loading = error === null && (!frameLoaded || waitForReadyToUse && !readyToUse);
|
|
165
|
-
|
|
169
|
+
var themeType = data === null || data === void 0 ? void 0 : (_data$theme = data.theme) === null || _data$theme === void 0 ? void 0 : _data$theme.type;
|
|
170
|
+
var forcedTheme = ['light', 'dark'].includes(themeType) ? themeType : null;
|
|
171
|
+
return /*#__PURE__*/_react.default.createElement(_CozyTheme.default, {
|
|
172
|
+
type: forcedTheme,
|
|
173
|
+
ignoreItself: false,
|
|
174
|
+
className: styles.intentContainer
|
|
175
|
+
}, /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({
|
|
166
176
|
ref: function ref(intentViewer) {
|
|
167
177
|
return _this3.intentViewer = intentViewer;
|
|
168
178
|
},
|
|
@@ -176,7 +186,7 @@ var IntentIframe = /*#__PURE__*/function (_React$Component) {
|
|
|
176
186
|
size: "xxlarge"
|
|
177
187
|
}, (0, _get.default)(iframeProps, 'spinnerProps')))), error && /*#__PURE__*/_react.default.createElement("div", {
|
|
178
188
|
className: styles.intentContainer__error
|
|
179
|
-
}, error.message));
|
|
189
|
+
}, error.message)));
|
|
180
190
|
}
|
|
181
191
|
}]);
|
|
182
192
|
return IntentIframe;
|
|
@@ -12,6 +12,11 @@ declare namespace IntentDialogOpener {
|
|
|
12
12
|
export let onReadyToUse: PropTypes.Requireable<(...args: any[]) => any>;
|
|
13
13
|
export let waitForReadyToUse: PropTypes.Requireable<boolean>;
|
|
14
14
|
export let action: PropTypes.Validator<string>;
|
|
15
|
+
export let options: PropTypes.Requireable<PropTypes.InferProps<{
|
|
16
|
+
theme: PropTypes.Requireable<PropTypes.InferProps<{
|
|
17
|
+
type: PropTypes.Requireable<string>;
|
|
18
|
+
}>>;
|
|
19
|
+
}>>;
|
|
15
20
|
export let doctype: PropTypes.Validator<string>;
|
|
16
21
|
export let closable: PropTypes.Validator<boolean>;
|
|
17
22
|
export let showCloseButton: PropTypes.Validator<boolean>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-ui-plus",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.0",
|
|
4
4
|
"description": "Cozy-ui-plus is an extension of cozy-ui and provides components based on cozy-client",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"cozy-intent": "^2.31.1",
|
|
39
39
|
"cozy-interapp": "^0.18.2",
|
|
40
40
|
"cozy-logger": "^1.18.1",
|
|
41
|
-
"cozy-sharing": "^37.2.
|
|
41
|
+
"cozy-sharing": "^37.2.6",
|
|
42
42
|
"cozy-stack-client": "^60.21.1",
|
|
43
43
|
"cozy-ui": "^138.1.0",
|
|
44
44
|
"jest": "30.3.0",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"react-dom": "^16 || ^17 || ^18",
|
|
79
79
|
"twake-i18n": ">=0.3.0"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "84d402f452f6acd67084d91bb58040ded4ca3254"
|
|
82
82
|
}
|
|
@@ -16,6 +16,7 @@ import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
|
|
|
16
16
|
fullWidth // extra props will be passed to the dialog
|
|
17
17
|
maxWidth="md" // extra props will be passed to the dialog
|
|
18
18
|
iframeProps={{ spinnerProps: { middle: true } }}
|
|
19
|
+
options={{ theme: { type: 'dark' } }}
|
|
19
20
|
waitForReadyToUse
|
|
20
21
|
onComplete={res => alert('intent has completed ! ' + JSON.stringify(res))}
|
|
21
22
|
onDismiss={() => alert('intent has been dismissed !')}
|
|
@@ -28,3 +29,7 @@ import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
|
|
|
28
29
|
Set `waitForReadyToUse` to keep the intent hidden behind the loading spinner
|
|
29
30
|
until its service sends the `readyToUse` signal. This option is disabled by
|
|
30
31
|
default because older intent services do not send this signal.
|
|
32
|
+
|
|
33
|
+
Set `options.theme.type` to `light` or `dark` to force that theme on the
|
|
34
|
+
dialog, close button and loading surface as well as passing it to the intent
|
|
35
|
+
service. Any other value or no value keeps the caller's theme.
|
|
@@ -3,6 +3,7 @@ import React, { useState } from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { DialogCloseButton } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
5
5
|
import Dialog from 'cozy-ui/transpiled/react/Dialog'
|
|
6
|
+
import CozyTheme from 'cozy-ui/transpiled/react/providers/CozyTheme'
|
|
6
7
|
|
|
7
8
|
import IntentIframe, { iframeProps } from '../IntentIframe'
|
|
8
9
|
|
|
@@ -48,33 +49,38 @@ const IntentDialogOpener = props => {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const Tag = tag // React needs uppercase element names
|
|
52
|
+
const themeType = options?.theme?.type
|
|
53
|
+
const forcedTheme = ['light', 'dark'].includes(themeType)
|
|
54
|
+
? themeType
|
|
55
|
+
: undefined
|
|
51
56
|
const elements = [
|
|
52
57
|
React.cloneElement(children, { key: 'opener', onClick: openModal })
|
|
53
58
|
]
|
|
54
59
|
|
|
55
60
|
if (modalOpened) {
|
|
56
61
|
elements.push(
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
<CozyTheme key="intent-modal" type={forcedTheme}>
|
|
63
|
+
<Component
|
|
64
|
+
open={modalOpened}
|
|
65
|
+
onClose={closable && handleDismiss}
|
|
66
|
+
{...componentProps}
|
|
67
|
+
>
|
|
68
|
+
{closable && showCloseButton && (
|
|
69
|
+
<DialogCloseButton onClick={handleDismiss} />
|
|
70
|
+
)}
|
|
71
|
+
<IntentIframe
|
|
72
|
+
action={action}
|
|
73
|
+
type={doctype}
|
|
74
|
+
data={options}
|
|
75
|
+
create={create}
|
|
76
|
+
onCancel={handleDismiss}
|
|
77
|
+
onTerminate={handleComplete}
|
|
78
|
+
onReadyToUse={onReadyToUse}
|
|
79
|
+
waitForReadyToUse={waitForReadyToUse}
|
|
80
|
+
iframeProps={iframeProps}
|
|
81
|
+
/>
|
|
82
|
+
</Component>
|
|
83
|
+
</CozyTheme>
|
|
78
84
|
)
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -94,6 +100,12 @@ IntentDialogOpener.propTypes = {
|
|
|
94
100
|
waitForReadyToUse: PropTypes.bool,
|
|
95
101
|
/** Action you want to execute */
|
|
96
102
|
action: PropTypes.string.isRequired,
|
|
103
|
+
/** Options passed to the intent */
|
|
104
|
+
options: PropTypes.shape({
|
|
105
|
+
theme: PropTypes.shape({
|
|
106
|
+
type: PropTypes.oneOf(['light', 'dark'])
|
|
107
|
+
})
|
|
108
|
+
}),
|
|
97
109
|
/** Doctype on which you want to execute the action */
|
|
98
110
|
doctype: PropTypes.string.isRequired,
|
|
99
111
|
/** Whether the dialog is closable by itself (not by the Intent) with a button or by clicking outside of it */
|
|
@@ -14,7 +14,7 @@ import utils from '../../docs/utils'
|
|
|
14
14
|
<IntentIframe
|
|
15
15
|
action="PICK"
|
|
16
16
|
create={utils.fakeIntentCreate}
|
|
17
|
-
data={{folder: '840af7e7be6c41bed3c5b76d03f66328'}}
|
|
17
|
+
data={{folder: '840af7e7be6c41bed3c5b76d03f66328', theme: { type: 'dark' }}}
|
|
18
18
|
type="io.cozy.files"
|
|
19
19
|
onCancel={() => alert('intent cancelled')}
|
|
20
20
|
onError={(error) => alert('intent has failed with error: ' + error.message)}
|
|
@@ -27,6 +27,10 @@ Set `waitForReadyToUse` to keep the intent hidden behind the loading spinner
|
|
|
27
27
|
until its service sends the `readyToUse` signal. This option is disabled by
|
|
28
28
|
default because older intent services do not send this signal.
|
|
29
29
|
|
|
30
|
+
Set `data.theme.type` to `light` or `dark` to force that theme on the
|
|
31
|
+
loading surface and pass it to the intent service. Any other value or no value
|
|
32
|
+
keeps the caller's theme.
|
|
33
|
+
|
|
30
34
|
### Within an application side controled Dialog
|
|
31
35
|
|
|
32
36
|
Sometimes you have to render an Intent inside a modal, and handle the modal opening state on the application side. You can then use IntentIframe wrapped in a Dialog for that use case, instead of using IntentDialogOpener component (that is useful to start a new intent modal from a click on a button).
|
|
@@ -7,6 +7,7 @@ import React from 'react'
|
|
|
7
7
|
import { withClient } from 'cozy-client'
|
|
8
8
|
import { Intents } from 'cozy-interapp'
|
|
9
9
|
import Spinner from 'cozy-ui/transpiled/react/Spinner'
|
|
10
|
+
import CozyTheme from 'cozy-ui/transpiled/react/providers/CozyTheme'
|
|
10
11
|
|
|
11
12
|
import styles from './styles.styl'
|
|
12
13
|
|
|
@@ -79,30 +80,37 @@ class IntentIframe extends React.Component {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
render() {
|
|
82
|
-
const { iframeProps, waitForReadyToUse } = this.props
|
|
83
|
+
const { data, iframeProps, waitForReadyToUse } = this.props
|
|
83
84
|
const { error, frameLoaded, readyToUse } = this.state
|
|
84
85
|
const loading =
|
|
85
86
|
error === null && (!frameLoaded || (waitForReadyToUse && !readyToUse))
|
|
86
|
-
|
|
87
|
+
const themeType = data?.theme?.type
|
|
88
|
+
const forcedTheme = ['light', 'dark'].includes(themeType) ? themeType : null
|
|
87
89
|
return (
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
+
<CozyTheme
|
|
91
|
+
type={forcedTheme}
|
|
92
|
+
ignoreItself={false}
|
|
90
93
|
className={styles.intentContainer}
|
|
91
|
-
aria-busy={loading}
|
|
92
|
-
data-iframe-loaded={frameLoaded}
|
|
93
|
-
data-waiting-for-ready-to-use={waitForReadyToUse && !readyToUse}
|
|
94
|
-
{...get(iframeProps, 'wrapperProps')}
|
|
95
94
|
>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
<div
|
|
96
|
+
ref={intentViewer => (this.intentViewer = intentViewer)}
|
|
97
|
+
className={styles.intentContainer}
|
|
98
|
+
aria-busy={loading}
|
|
99
|
+
data-iframe-loaded={frameLoaded}
|
|
100
|
+
data-waiting-for-ready-to-use={waitForReadyToUse && !readyToUse}
|
|
101
|
+
{...get(iframeProps, 'wrapperProps')}
|
|
102
|
+
>
|
|
103
|
+
{loading && (
|
|
104
|
+
<div className={styles.intentContainer__loader}>
|
|
105
|
+
<Spinner size="xxlarge" {...get(iframeProps, 'spinnerProps')} />
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
{error && (
|
|
109
|
+
<div className={styles.intentContainer__error}>{error.message}</div>
|
|
110
|
+
)}
|
|
111
|
+
{/* intent iframe will be appended here */}
|
|
112
|
+
</div>
|
|
113
|
+
</CozyTheme>
|
|
106
114
|
)
|
|
107
115
|
}
|
|
108
116
|
}
|
|
@@ -7,7 +7,7 @@ jest.mock('cozy-ui/transpiled/react/Spinner', () => () => (
|
|
|
7
7
|
<div data-testid="intent-spinner" />
|
|
8
8
|
))
|
|
9
9
|
|
|
10
|
-
function setup({ startError = null, waitForReadyToUse = false } = {}) {
|
|
10
|
+
function setup({ data, startError = null, waitForReadyToUse = false } = {}) {
|
|
11
11
|
let startOptions
|
|
12
12
|
const start = jest.fn((element, options) => {
|
|
13
13
|
startOptions = options
|
|
@@ -24,6 +24,7 @@ function setup({ startError = null, waitForReadyToUse = false } = {}) {
|
|
|
24
24
|
action="PICK"
|
|
25
25
|
client={{}}
|
|
26
26
|
create={create}
|
|
27
|
+
data={data}
|
|
27
28
|
iframeProps={{ setIsLoading }}
|
|
28
29
|
onCancel={jest.fn()}
|
|
29
30
|
onError={onError}
|