@oxyhq/bloom 0.30.10 → 0.31.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/lib/commonjs/alert-dialog/AlertDialog.js +37 -61
- package/lib/commonjs/alert-dialog/AlertDialog.js.map +1 -1
- package/lib/module/alert-dialog/AlertDialog.js +38 -62
- package/lib/module/alert-dialog/AlertDialog.js.map +1 -1
- package/lib/typescript/commonjs/alert-dialog/AlertDialog.d.ts +8 -2
- package/lib/typescript/commonjs/alert-dialog/AlertDialog.d.ts.map +1 -1
- package/lib/typescript/module/alert-dialog/AlertDialog.d.ts +8 -2
- package/lib/typescript/module/alert-dialog/AlertDialog.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/AlertDialog.test.tsx +105 -15
- package/src/__tests__/confirm-store.test.ts +16 -0
- package/src/alert-dialog/AlertDialog.tsx +40 -72
|
@@ -5,16 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.createAlertDialog = createAlertDialog;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
-
var _reactNative = require("react-native");
|
|
9
|
-
var _button = require("../button");
|
|
10
|
-
var _useTheme = require("../theme/use-theme.js");
|
|
11
|
-
var _index = require("../typography/index.js");
|
|
12
|
-
var _tokens = require("../styles/tokens.js");
|
|
13
8
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
9
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
|
-
/** Centered alert-card max width (px). Matches the legacy AlertDialog card. */
|
|
16
|
-
const ALERT_DIALOG_MAX_WIDTH = 440;
|
|
17
|
-
|
|
18
10
|
/**
|
|
19
11
|
* Build the `AlertDialog` component bound to a platform `Dialog`.
|
|
20
12
|
*
|
|
@@ -24,10 +16,16 @@ const ALERT_DIALOG_MAX_WIDTH = 440;
|
|
|
24
16
|
* (`index.ts` / `index.web.ts`) inject the correct `Dialog` here. The body
|
|
25
17
|
* below is single-source, no duplication.
|
|
26
18
|
*
|
|
27
|
-
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that
|
|
28
|
-
*
|
|
19
|
+
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that maps
|
|
20
|
+
* the confirm/cancel props onto the Dialog's declarative `actions` row — the
|
|
21
|
+
* SAME battle-tested `ActionRow`/`ActionButton` primitive every other bloom
|
|
22
|
+
* confirm surface uses (e.g. Mention's `ConfirmPrompt`), so the button
|
|
23
|
+
* component, layout, and palette stay identical across apps. Reach for the
|
|
29
24
|
* imperative `confirm()` helper + `<AlertDialogHost />` to trigger a confirm
|
|
30
25
|
* from an event handler without owning visible state.
|
|
26
|
+
*
|
|
27
|
+
* The centered card uses the Dialog default `maxWidth` (480px) for full visual
|
|
28
|
+
* parity with the shared confirm surface.
|
|
31
29
|
*/
|
|
32
30
|
function createAlertDialog(Dialog) {
|
|
33
31
|
const AlertDialogComponent = function AlertDialog({
|
|
@@ -45,73 +43,51 @@ function createAlertDialog(Dialog) {
|
|
|
45
43
|
cardStyle,
|
|
46
44
|
testID
|
|
47
45
|
}) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
// Confirm resolves + closes SYNCHRONOUSLY, in this order, and must NOT go
|
|
47
|
+
// through the Dialog's auto-close (`shouldCloseOnPress: false`). In
|
|
48
|
+
// controlled mode `close()` fires `onClose` FIRST, and the `confirm()` host
|
|
49
|
+
// wires `onClose` to resolve the promise as *cancelled* (`false`). Running
|
|
50
|
+
// `onConfirm` (resolve `true`, which removes the queue entry) before
|
|
51
|
+
// `onClose` (now a no-op for that entry) is what makes the confirm button
|
|
52
|
+
// resolve `true`.
|
|
53
53
|
const handleConfirm = (0, _react.useCallback)(() => {
|
|
54
54
|
onConfirm?.();
|
|
55
55
|
onClose();
|
|
56
56
|
}, [onConfirm, onClose]);
|
|
57
|
-
|
|
57
|
+
const actions = [{
|
|
58
|
+
label: confirmLabel,
|
|
59
|
+
onPress: handleConfirm,
|
|
60
|
+
color: destructive ? 'destructive' : 'default',
|
|
61
|
+
shouldCloseOnPress: false
|
|
62
|
+
}];
|
|
63
|
+
if (!hideCancel) {
|
|
64
|
+
// The `cancel` color auto-dismisses via the Dialog's `close()`, which in
|
|
65
|
+
// controlled mode fires `onClose` (the host resolves `false`). `onCancel`
|
|
66
|
+
// runs after the surface finishes closing.
|
|
67
|
+
actions.push({
|
|
68
|
+
label: cancelLabel,
|
|
69
|
+
onPress: onCancel,
|
|
70
|
+
color: 'cancel'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Dialog, {
|
|
58
74
|
open: visible,
|
|
59
75
|
onClose: onClose,
|
|
60
76
|
placement: "center"
|
|
61
|
-
// Alert dialogs are blocking by default — the backdrop only
|
|
62
|
-
// when the caller opts in via `dismissible`.
|
|
77
|
+
// Alert dialogs are blocking by default — the backdrop / Escape only
|
|
78
|
+
// dismiss when the caller opts in via `dismissible`.
|
|
63
79
|
,
|
|
64
80
|
dismissOnBackdrop: dismissible,
|
|
65
|
-
maxWidth: ALERT_DIALOG_MAX_WIDTH,
|
|
66
81
|
title: title,
|
|
82
|
+
description: description,
|
|
83
|
+
actions: actions,
|
|
67
84
|
label: title,
|
|
68
85
|
style: cardStyle,
|
|
69
|
-
testID: testID
|
|
70
|
-
children: [description ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index.Text, {
|
|
71
|
-
style: {
|
|
72
|
-
fontSize: _tokens.fontSize.md,
|
|
73
|
-
lineHeight: _tokens.fontSize.md * 1.4,
|
|
74
|
-
color: theme.colors.textSecondary,
|
|
75
|
-
paddingBottom: _tokens.space.lg
|
|
76
|
-
},
|
|
77
|
-
children: description
|
|
78
|
-
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
79
|
-
style: styles.actions,
|
|
80
|
-
children: [!hideCancel ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
81
|
-
variant: "secondary",
|
|
82
|
-
size: "medium",
|
|
83
|
-
onPress: handleCancel,
|
|
84
|
-
style: styles.action,
|
|
85
|
-
accessibilityLabel: cancelLabel,
|
|
86
|
-
children: cancelLabel
|
|
87
|
-
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
88
|
-
variant: "primary",
|
|
89
|
-
size: "medium",
|
|
90
|
-
onPress: handleConfirm,
|
|
91
|
-
style: _reactNative.StyleSheet.flatten([styles.action, destructive && {
|
|
92
|
-
backgroundColor: theme.colors.negative
|
|
93
|
-
}]),
|
|
94
|
-
textStyle: destructive ? {
|
|
95
|
-
color: theme.colors.negativeForeground
|
|
96
|
-
} : undefined,
|
|
97
|
-
accessibilityLabel: confirmLabel,
|
|
98
|
-
children: confirmLabel
|
|
99
|
-
})]
|
|
100
|
-
})]
|
|
86
|
+
testID: testID
|
|
101
87
|
});
|
|
102
88
|
};
|
|
103
89
|
const AlertDialog = /*#__PURE__*/(0, _react.memo)(AlertDialogComponent);
|
|
104
90
|
AlertDialog.displayName = 'AlertDialog';
|
|
105
91
|
return AlertDialog;
|
|
106
92
|
}
|
|
107
|
-
const styles = _reactNative.StyleSheet.create({
|
|
108
|
-
actions: {
|
|
109
|
-
flexDirection: 'row',
|
|
110
|
-
justifyContent: 'flex-end',
|
|
111
|
-
gap: _tokens.space.sm
|
|
112
|
-
},
|
|
113
|
-
action: {
|
|
114
|
-
minWidth: 96
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
93
|
//# sourceMappingURL=AlertDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","createAlertDialog","Dialog","AlertDialogComponent","AlertDialog","visible","onClose","title","description","confirmLabel","cancelLabel","onConfirm","onCancel","destructive","hideCancel","dismissible","cardStyle","testID","handleConfirm","useCallback","actions","label","onPress","color","shouldCloseOnPress","push","jsx","open","placement","dismissOnBackdrop","style","memo","displayName"],"sourceRoot":"../../../src","sources":["alert-dialog/AlertDialog.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React, { memo, useCallback } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import { Button } from '../button';
|
|
6
|
-
import { useTheme } from "../theme/use-theme.js";
|
|
7
|
-
import { Text } from "../typography/index.js";
|
|
8
|
-
import { fontSize, space } from "../styles/tokens.js";
|
|
9
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
-
/** Centered alert-card max width (px). Matches the legacy AlertDialog card. */
|
|
11
|
-
const ALERT_DIALOG_MAX_WIDTH = 440;
|
|
12
|
-
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
5
|
/**
|
|
14
6
|
* Build the `AlertDialog` component bound to a platform `Dialog`.
|
|
15
7
|
*
|
|
@@ -19,10 +11,16 @@ const ALERT_DIALOG_MAX_WIDTH = 440;
|
|
|
19
11
|
* (`index.ts` / `index.web.ts`) inject the correct `Dialog` here. The body
|
|
20
12
|
* below is single-source, no duplication.
|
|
21
13
|
*
|
|
22
|
-
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that
|
|
23
|
-
*
|
|
14
|
+
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that maps
|
|
15
|
+
* the confirm/cancel props onto the Dialog's declarative `actions` row — the
|
|
16
|
+
* SAME battle-tested `ActionRow`/`ActionButton` primitive every other bloom
|
|
17
|
+
* confirm surface uses (e.g. Mention's `ConfirmPrompt`), so the button
|
|
18
|
+
* component, layout, and palette stay identical across apps. Reach for the
|
|
24
19
|
* imperative `confirm()` helper + `<AlertDialogHost />` to trigger a confirm
|
|
25
20
|
* from an event handler without owning visible state.
|
|
21
|
+
*
|
|
22
|
+
* The centered card uses the Dialog default `maxWidth` (480px) for full visual
|
|
23
|
+
* parity with the shared confirm surface.
|
|
26
24
|
*/
|
|
27
25
|
export function createAlertDialog(Dialog) {
|
|
28
26
|
const AlertDialogComponent = function AlertDialog({
|
|
@@ -40,73 +38,51 @@ export function createAlertDialog(Dialog) {
|
|
|
40
38
|
cardStyle,
|
|
41
39
|
testID
|
|
42
40
|
}) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
// Confirm resolves + closes SYNCHRONOUSLY, in this order, and must NOT go
|
|
42
|
+
// through the Dialog's auto-close (`shouldCloseOnPress: false`). In
|
|
43
|
+
// controlled mode `close()` fires `onClose` FIRST, and the `confirm()` host
|
|
44
|
+
// wires `onClose` to resolve the promise as *cancelled* (`false`). Running
|
|
45
|
+
// `onConfirm` (resolve `true`, which removes the queue entry) before
|
|
46
|
+
// `onClose` (now a no-op for that entry) is what makes the confirm button
|
|
47
|
+
// resolve `true`.
|
|
48
48
|
const handleConfirm = useCallback(() => {
|
|
49
49
|
onConfirm?.();
|
|
50
50
|
onClose();
|
|
51
51
|
}, [onConfirm, onClose]);
|
|
52
|
-
|
|
52
|
+
const actions = [{
|
|
53
|
+
label: confirmLabel,
|
|
54
|
+
onPress: handleConfirm,
|
|
55
|
+
color: destructive ? 'destructive' : 'default',
|
|
56
|
+
shouldCloseOnPress: false
|
|
57
|
+
}];
|
|
58
|
+
if (!hideCancel) {
|
|
59
|
+
// The `cancel` color auto-dismisses via the Dialog's `close()`, which in
|
|
60
|
+
// controlled mode fires `onClose` (the host resolves `false`). `onCancel`
|
|
61
|
+
// runs after the surface finishes closing.
|
|
62
|
+
actions.push({
|
|
63
|
+
label: cancelLabel,
|
|
64
|
+
onPress: onCancel,
|
|
65
|
+
color: 'cancel'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return /*#__PURE__*/_jsx(Dialog, {
|
|
53
69
|
open: visible,
|
|
54
70
|
onClose: onClose,
|
|
55
71
|
placement: "center"
|
|
56
|
-
// Alert dialogs are blocking by default — the backdrop only
|
|
57
|
-
// when the caller opts in via `dismissible`.
|
|
72
|
+
// Alert dialogs are blocking by default — the backdrop / Escape only
|
|
73
|
+
// dismiss when the caller opts in via `dismissible`.
|
|
58
74
|
,
|
|
59
75
|
dismissOnBackdrop: dismissible,
|
|
60
|
-
maxWidth: ALERT_DIALOG_MAX_WIDTH,
|
|
61
76
|
title: title,
|
|
77
|
+
description: description,
|
|
78
|
+
actions: actions,
|
|
62
79
|
label: title,
|
|
63
80
|
style: cardStyle,
|
|
64
|
-
testID: testID
|
|
65
|
-
children: [description ? /*#__PURE__*/_jsx(Text, {
|
|
66
|
-
style: {
|
|
67
|
-
fontSize: fontSize.md,
|
|
68
|
-
lineHeight: fontSize.md * 1.4,
|
|
69
|
-
color: theme.colors.textSecondary,
|
|
70
|
-
paddingBottom: space.lg
|
|
71
|
-
},
|
|
72
|
-
children: description
|
|
73
|
-
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
74
|
-
style: styles.actions,
|
|
75
|
-
children: [!hideCancel ? /*#__PURE__*/_jsx(Button, {
|
|
76
|
-
variant: "secondary",
|
|
77
|
-
size: "medium",
|
|
78
|
-
onPress: handleCancel,
|
|
79
|
-
style: styles.action,
|
|
80
|
-
accessibilityLabel: cancelLabel,
|
|
81
|
-
children: cancelLabel
|
|
82
|
-
}) : null, /*#__PURE__*/_jsx(Button, {
|
|
83
|
-
variant: "primary",
|
|
84
|
-
size: "medium",
|
|
85
|
-
onPress: handleConfirm,
|
|
86
|
-
style: StyleSheet.flatten([styles.action, destructive && {
|
|
87
|
-
backgroundColor: theme.colors.negative
|
|
88
|
-
}]),
|
|
89
|
-
textStyle: destructive ? {
|
|
90
|
-
color: theme.colors.negativeForeground
|
|
91
|
-
} : undefined,
|
|
92
|
-
accessibilityLabel: confirmLabel,
|
|
93
|
-
children: confirmLabel
|
|
94
|
-
})]
|
|
95
|
-
})]
|
|
81
|
+
testID: testID
|
|
96
82
|
});
|
|
97
83
|
};
|
|
98
84
|
const AlertDialog = /*#__PURE__*/memo(AlertDialogComponent);
|
|
99
85
|
AlertDialog.displayName = 'AlertDialog';
|
|
100
86
|
return AlertDialog;
|
|
101
87
|
}
|
|
102
|
-
const styles = StyleSheet.create({
|
|
103
|
-
actions: {
|
|
104
|
-
flexDirection: 'row',
|
|
105
|
-
justifyContent: 'flex-end',
|
|
106
|
-
gap: space.sm
|
|
107
|
-
},
|
|
108
|
-
action: {
|
|
109
|
-
minWidth: 96
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
88
|
//# sourceMappingURL=AlertDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","memo","useCallback","
|
|
1
|
+
{"version":3,"names":["React","memo","useCallback","jsx","_jsx","createAlertDialog","Dialog","AlertDialogComponent","AlertDialog","visible","onClose","title","description","confirmLabel","cancelLabel","onConfirm","onCancel","destructive","hideCancel","dismissible","cardStyle","testID","handleConfirm","actions","label","onPress","color","shouldCloseOnPress","push","open","placement","dismissOnBackdrop","style","displayName"],"sourceRoot":"../../../src","sources":["alert-dialog/AlertDialog.tsx"],"mappings":";;AAAA,OAAOA,KAAK,IAAIC,IAAI,EAAEC,WAAW,QAAQ,OAAO;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAOjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAACC,MAAuB,EAAE;EACzD,MAAMC,oBAAoB,GAAG,SAASC,WAAWA,CAAC;IAChDC,OAAO;IACPC,OAAO;IACPC,KAAK;IACLC,WAAW;IACXC,YAAY,GAAG,SAAS;IACxBC,WAAW,GAAG,QAAQ;IACtBC,SAAS;IACTC,QAAQ;IACRC,WAAW,GAAG,KAAK;IACnBC,UAAU,GAAG,KAAK;IAClBC,WAAW,GAAG,KAAK;IACnBC,SAAS;IACTC;EACgB,CAAC,EAAE;IACnB;IACA;IACA;IACA;IACA;IACA;IACA;IACA,MAAMC,aAAa,GAAGpB,WAAW,CAAC,MAAM;MACtCa,SAAS,GAAG,CAAC;MACbL,OAAO,CAAC,CAAC;IACX,CAAC,EAAE,CAACK,SAAS,EAAEL,OAAO,CAAC,CAAC;IAExB,MAAMa,OAAuB,GAAG,CAC9B;MACEC,KAAK,EAAEX,YAAY;MACnBY,OAAO,EAAEH,aAAa;MACtBI,KAAK,EAAET,WAAW,GAAG,aAAa,GAAG,SAAS;MAC9CU,kBAAkB,EAAE;IACtB,CAAC,CACF;IACD,IAAI,CAACT,UAAU,EAAE;MACf;MACA;MACA;MACAK,OAAO,CAACK,IAAI,CAAC;QACXJ,KAAK,EAAEV,WAAW;QAClBW,OAAO,EAAET,QAAQ;QACjBU,KAAK,EAAE;MACT,CAAC,CAAC;IACJ;IAEA,oBACEtB,IAAA,CAACE,MAAM;MACLuB,IAAI,EAAEpB,OAAQ;MACdC,OAAO,EAAEA,OAAQ;MACjBoB,SAAS,EAAC;MACV;MACA;MAAA;MACAC,iBAAiB,EAAEZ,WAAY;MAC/BR,KAAK,EAAEA,KAAM;MACbC,WAAW,EAAEA,WAAY;MACzBW,OAAO,EAAEA,OAAQ;MACjBC,KAAK,EAAEb,KAAM;MACbqB,KAAK,EAAEZ,SAAU;MACjBC,MAAM,EAAEA;IAAO,CAChB,CAAC;EAEN,CAAC;EAED,MAAMb,WAAW,gBAAGP,IAAI,CAACM,oBAAoB,CAAC;EAC9CC,WAAW,CAACyB,WAAW,GAAG,aAAa;EACvC,OAAOzB,WAAW;AACpB","ignoreList":[]}
|
|
@@ -11,10 +11,16 @@ type DialogComponent = React.ComponentType<DialogProps>;
|
|
|
11
11
|
* (`index.ts` / `index.web.ts`) inject the correct `Dialog` here. The body
|
|
12
12
|
* below is single-source, no duplication.
|
|
13
13
|
*
|
|
14
|
-
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that
|
|
15
|
-
*
|
|
14
|
+
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that maps
|
|
15
|
+
* the confirm/cancel props onto the Dialog's declarative `actions` row — the
|
|
16
|
+
* SAME battle-tested `ActionRow`/`ActionButton` primitive every other bloom
|
|
17
|
+
* confirm surface uses (e.g. Mention's `ConfirmPrompt`), so the button
|
|
18
|
+
* component, layout, and palette stay identical across apps. Reach for the
|
|
16
19
|
* imperative `confirm()` helper + `<AlertDialogHost />` to trigger a confirm
|
|
17
20
|
* from an event handler without owning visible state.
|
|
21
|
+
*
|
|
22
|
+
* The centered card uses the Dialog default `maxWidth` (480px) for full visual
|
|
23
|
+
* parity with the shared confirm surface.
|
|
18
24
|
*/
|
|
19
25
|
export declare function createAlertDialog(Dialog: DialogComponent): React.MemoExoticComponent<({ visible, onClose, title, description, confirmLabel, cancelLabel, onConfirm, onCancel, destructive, hideCancel, dismissible, cardStyle, testID, }: AlertDialogProps) => import("react/jsx-runtime").JSX.Element>;
|
|
20
26
|
export type AlertDialogType = ReturnType<typeof createAlertDialog>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../../src/alert-dialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../../src/alert-dialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,KAAK,eAAe,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,kLAepD,gBAAgB,8CAqDpB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
|
|
@@ -11,10 +11,16 @@ type DialogComponent = React.ComponentType<DialogProps>;
|
|
|
11
11
|
* (`index.ts` / `index.web.ts`) inject the correct `Dialog` here. The body
|
|
12
12
|
* below is single-source, no duplication.
|
|
13
13
|
*
|
|
14
|
-
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that
|
|
15
|
-
*
|
|
14
|
+
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that maps
|
|
15
|
+
* the confirm/cancel props onto the Dialog's declarative `actions` row — the
|
|
16
|
+
* SAME battle-tested `ActionRow`/`ActionButton` primitive every other bloom
|
|
17
|
+
* confirm surface uses (e.g. Mention's `ConfirmPrompt`), so the button
|
|
18
|
+
* component, layout, and palette stay identical across apps. Reach for the
|
|
16
19
|
* imperative `confirm()` helper + `<AlertDialogHost />` to trigger a confirm
|
|
17
20
|
* from an event handler without owning visible state.
|
|
21
|
+
*
|
|
22
|
+
* The centered card uses the Dialog default `maxWidth` (480px) for full visual
|
|
23
|
+
* parity with the shared confirm surface.
|
|
18
24
|
*/
|
|
19
25
|
export declare function createAlertDialog(Dialog: DialogComponent): React.MemoExoticComponent<({ visible, onClose, title, description, confirmLabel, cancelLabel, onConfirm, onCancel, destructive, hideCancel, dismissible, cardStyle, testID, }: AlertDialogProps) => import("react/jsx-runtime").JSX.Element>;
|
|
20
26
|
export type AlertDialogType = ReturnType<typeof createAlertDialog>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../../src/alert-dialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../../src/alert-dialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,KAAK,eAAe,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,kLAepD,gBAAgB,8CAqDpB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { act, fireEvent, render } from '@testing-library/react-native';
|
|
3
3
|
|
|
4
|
-
import { AlertDialog } from '../alert-dialog';
|
|
4
|
+
import { AlertDialog, AlertDialogHost, confirm } from '../alert-dialog';
|
|
5
|
+
import { getConfirmQueue, resolveConfirm } from '../alert-dialog/confirm-store';
|
|
5
6
|
import { BloomThemeProvider } from '../theme/BloomThemeProvider';
|
|
6
7
|
|
|
7
8
|
function renderWithTheme(ui: React.ReactElement) {
|
|
@@ -12,11 +13,23 @@ function renderWithTheme(ui: React.ReactElement) {
|
|
|
12
13
|
);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
// `AlertDialog` is
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
16
|
+
// `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that maps
|
|
17
|
+
// its confirm/cancel props onto the Dialog's declarative `actions` row (the
|
|
18
|
+
// shared `ActionRow`/`ActionButton` primitive). These tests assert the
|
|
19
|
+
// public-API contract it owns — controlled visibility, the title/description
|
|
20
|
+
// copy, the confirm/cancel actions, and — most importantly — the exact
|
|
21
|
+
// `confirm()` promise-resolution contract every ecosystem call site relies on:
|
|
22
|
+
// the confirm button resolves `true`; the cancel button (or Escape / backdrop,
|
|
23
|
+
// which route through the Dialog's `onClose`) resolves `false`.
|
|
19
24
|
describe('AlertDialog (built on Dialog)', () => {
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
// Drain any confirm() entries a test left pending so the module-scoped
|
|
27
|
+
// queue never bleeds across tests.
|
|
28
|
+
for (const entry of [...getConfirmQueue()]) {
|
|
29
|
+
resolveConfirm(entry.id, false);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
20
33
|
it('renders nothing when not visible', () => {
|
|
21
34
|
const { queryByText } = renderWithTheme(
|
|
22
35
|
<AlertDialog
|
|
@@ -48,8 +61,9 @@ describe('AlertDialog (built on Dialog)', () => {
|
|
|
48
61
|
});
|
|
49
62
|
|
|
50
63
|
it('runs onConfirm then onClose when the confirm button is pressed', () => {
|
|
51
|
-
const
|
|
52
|
-
const
|
|
64
|
+
const calls: string[] = [];
|
|
65
|
+
const onConfirm = jest.fn(() => calls.push('confirm'));
|
|
66
|
+
const onClose = jest.fn(() => calls.push('close'));
|
|
53
67
|
const { getByText } = renderWithTheme(
|
|
54
68
|
<AlertDialog
|
|
55
69
|
visible
|
|
@@ -64,10 +78,11 @@ describe('AlertDialog (built on Dialog)', () => {
|
|
|
64
78
|
});
|
|
65
79
|
expect(onConfirm).toHaveBeenCalledTimes(1);
|
|
66
80
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
81
|
+
// Order matters: onConfirm must resolve the promise BEFORE onClose runs.
|
|
82
|
+
expect(calls).toEqual(['confirm', 'close']);
|
|
67
83
|
});
|
|
68
84
|
|
|
69
|
-
it('
|
|
70
|
-
const onCancel = jest.fn();
|
|
85
|
+
it('requests close (resolves cancel) when the cancel button is pressed', () => {
|
|
71
86
|
const onClose = jest.fn();
|
|
72
87
|
const { getByText } = renderWithTheme(
|
|
73
88
|
<AlertDialog
|
|
@@ -75,16 +90,37 @@ describe('AlertDialog (built on Dialog)', () => {
|
|
|
75
90
|
onClose={onClose}
|
|
76
91
|
title="Confirm?"
|
|
77
92
|
cancelLabel="No"
|
|
78
|
-
onCancel={onCancel}
|
|
79
93
|
/>,
|
|
80
94
|
);
|
|
81
95
|
act(() => {
|
|
82
96
|
fireEvent.press(getByText('No'));
|
|
83
97
|
});
|
|
84
|
-
|
|
98
|
+
// The `cancel` action dismisses via the Dialog's `close()`, which in
|
|
99
|
+
// controlled mode fires `onClose` — the confirm() host's resolve-false.
|
|
85
100
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
86
101
|
});
|
|
87
102
|
|
|
103
|
+
it('invokes onCancel after the cancel button dismisses the dialog', () => {
|
|
104
|
+
const onCancel = jest.fn();
|
|
105
|
+
function Harness() {
|
|
106
|
+
const [open, setOpen] = useState(true);
|
|
107
|
+
return (
|
|
108
|
+
<AlertDialog
|
|
109
|
+
visible={open}
|
|
110
|
+
onClose={() => setOpen(false)}
|
|
111
|
+
title="Confirm?"
|
|
112
|
+
cancelLabel="No"
|
|
113
|
+
onCancel={onCancel}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const { getByText } = renderWithTheme(<Harness />);
|
|
118
|
+
act(() => {
|
|
119
|
+
fireEvent.press(getByText('No'));
|
|
120
|
+
});
|
|
121
|
+
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
122
|
+
});
|
|
123
|
+
|
|
88
124
|
it('hides the cancel button when hideCancel is set', () => {
|
|
89
125
|
const { queryByText, getByText } = renderWithTheme(
|
|
90
126
|
<AlertDialog
|
|
@@ -112,11 +148,65 @@ describe('AlertDialog (built on Dialog)', () => {
|
|
|
112
148
|
onConfirm={onConfirm}
|
|
113
149
|
/>,
|
|
114
150
|
);
|
|
115
|
-
const
|
|
116
|
-
expect(
|
|
151
|
+
const confirmButton = getByText('Sign out');
|
|
152
|
+
expect(confirmButton).toBeTruthy();
|
|
117
153
|
act(() => {
|
|
118
|
-
fireEvent.press(
|
|
154
|
+
fireEvent.press(confirmButton);
|
|
119
155
|
});
|
|
120
156
|
expect(onConfirm).toHaveBeenCalledTimes(1);
|
|
121
157
|
});
|
|
158
|
+
|
|
159
|
+
// The authoritative ecosystem contract: `confirm()` + `<AlertDialogHost />`.
|
|
160
|
+
// Every consuming call site does `const ok = await confirm({...})`, so these
|
|
161
|
+
// assert the exact promise resolution against the REAL rendered action row.
|
|
162
|
+
describe('confirm() + AlertDialogHost promise contract', () => {
|
|
163
|
+
it('resolves true when the confirm button is pressed', async () => {
|
|
164
|
+
const { getByText } = renderWithTheme(<AlertDialogHost />);
|
|
165
|
+
let result!: Promise<boolean>;
|
|
166
|
+
act(() => {
|
|
167
|
+
result = confirm({
|
|
168
|
+
title: 'Delete app?',
|
|
169
|
+
confirmLabel: 'Delete',
|
|
170
|
+
cancelLabel: 'Keep',
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
act(() => {
|
|
174
|
+
fireEvent.press(getByText('Delete'));
|
|
175
|
+
});
|
|
176
|
+
await expect(result).resolves.toBe(true);
|
|
177
|
+
expect(getConfirmQueue()).toHaveLength(0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('resolves false when the cancel button is pressed', async () => {
|
|
181
|
+
const { getByText } = renderWithTheme(<AlertDialogHost />);
|
|
182
|
+
let result!: Promise<boolean>;
|
|
183
|
+
act(() => {
|
|
184
|
+
result = confirm({
|
|
185
|
+
title: 'Discard changes?',
|
|
186
|
+
confirmLabel: 'Discard',
|
|
187
|
+
cancelLabel: 'Keep editing',
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
act(() => {
|
|
191
|
+
fireEvent.press(getByText('Keep editing'));
|
|
192
|
+
});
|
|
193
|
+
await expect(result).resolves.toBe(false);
|
|
194
|
+
expect(getConfirmQueue()).toHaveLength(0);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('resolves true even though confirm fires both onConfirm and onClose', async () => {
|
|
198
|
+
// The confirm button runs onConfirm (resolve true, drains the entry) then
|
|
199
|
+
// onClose (resolve false — a no-op once the entry is gone). The net must
|
|
200
|
+
// be `true`; this guards the double-resolution path explicitly.
|
|
201
|
+
const { getByText } = renderWithTheme(<AlertDialogHost />);
|
|
202
|
+
let result!: Promise<boolean>;
|
|
203
|
+
act(() => {
|
|
204
|
+
result = confirm({ title: 'Proceed?', confirmLabel: 'Proceed' });
|
|
205
|
+
});
|
|
206
|
+
act(() => {
|
|
207
|
+
fireEvent.press(getByText('Proceed'));
|
|
208
|
+
});
|
|
209
|
+
await expect(result).resolves.toBe(true);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
122
212
|
});
|
|
@@ -42,6 +42,22 @@ describe('confirm-store', () => {
|
|
|
42
42
|
await expect(promise).resolves.toBe(false);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
it('resolves once — a second resolve for the same id is a no-op', async () => {
|
|
46
|
+
// The AlertDialog confirm path calls onConfirm (resolve true) THEN onClose
|
|
47
|
+
// (resolve false) for the SAME entry. The first resolve drains the entry,
|
|
48
|
+
// so the second is ignored and the promise keeps its `true` value. This is
|
|
49
|
+
// the guard the whole confirm() button contract relies on.
|
|
50
|
+
const promise = confirm({ title: 'Proceed?' });
|
|
51
|
+
const id = getConfirmQueue()[0]?.id;
|
|
52
|
+
expect(id).toBeTruthy();
|
|
53
|
+
if (id) {
|
|
54
|
+
resolveConfirm(id, true);
|
|
55
|
+
resolveConfirm(id, false);
|
|
56
|
+
}
|
|
57
|
+
await expect(promise).resolves.toBe(true);
|
|
58
|
+
expect(getConfirmQueue()).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
45
61
|
it('drains queued entries to a late subscriber', () => {
|
|
46
62
|
void confirm({ title: 'Queued before subscribe' });
|
|
47
63
|
let delivered: number | null = null;
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import React, { memo, useCallback } from 'react';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import type { DialogProps } from '../dialog';
|
|
6
|
-
import { useTheme } from '../theme/use-theme';
|
|
7
|
-
import { Text } from '../typography';
|
|
8
|
-
import { fontSize, space } from '../styles/tokens';
|
|
3
|
+
import type { DialogAction, DialogProps } from '../dialog';
|
|
9
4
|
import type { AlertDialogProps } from './types';
|
|
10
5
|
|
|
11
6
|
type DialogComponent = React.ComponentType<DialogProps>;
|
|
12
7
|
|
|
13
|
-
/** Centered alert-card max width (px). Matches the legacy AlertDialog card. */
|
|
14
|
-
const ALERT_DIALOG_MAX_WIDTH = 440;
|
|
15
|
-
|
|
16
8
|
/**
|
|
17
9
|
* Build the `AlertDialog` component bound to a platform `Dialog`.
|
|
18
10
|
*
|
|
@@ -22,10 +14,16 @@ const ALERT_DIALOG_MAX_WIDTH = 440;
|
|
|
22
14
|
* (`index.ts` / `index.web.ts`) inject the correct `Dialog` here. The body
|
|
23
15
|
* below is single-source, no duplication.
|
|
24
16
|
*
|
|
25
|
-
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that
|
|
26
|
-
*
|
|
17
|
+
* `AlertDialog` is a thin wrapper over `<Dialog placement="center">` that maps
|
|
18
|
+
* the confirm/cancel props onto the Dialog's declarative `actions` row — the
|
|
19
|
+
* SAME battle-tested `ActionRow`/`ActionButton` primitive every other bloom
|
|
20
|
+
* confirm surface uses (e.g. Mention's `ConfirmPrompt`), so the button
|
|
21
|
+
* component, layout, and palette stay identical across apps. Reach for the
|
|
27
22
|
* imperative `confirm()` helper + `<AlertDialogHost />` to trigger a confirm
|
|
28
23
|
* from an event handler without owning visible state.
|
|
24
|
+
*
|
|
25
|
+
* The centered card uses the Dialog default `maxWidth` (480px) for full visual
|
|
26
|
+
* parity with the shared confirm surface.
|
|
29
27
|
*/
|
|
30
28
|
export function createAlertDialog(Dialog: DialogComponent) {
|
|
31
29
|
const AlertDialogComponent = function AlertDialog({
|
|
@@ -43,71 +41,52 @@ export function createAlertDialog(Dialog: DialogComponent) {
|
|
|
43
41
|
cardStyle,
|
|
44
42
|
testID,
|
|
45
43
|
}: AlertDialogProps) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
// Confirm resolves + closes SYNCHRONOUSLY, in this order, and must NOT go
|
|
45
|
+
// through the Dialog's auto-close (`shouldCloseOnPress: false`). In
|
|
46
|
+
// controlled mode `close()` fires `onClose` FIRST, and the `confirm()` host
|
|
47
|
+
// wires `onClose` to resolve the promise as *cancelled* (`false`). Running
|
|
48
|
+
// `onConfirm` (resolve `true`, which removes the queue entry) before
|
|
49
|
+
// `onClose` (now a no-op for that entry) is what makes the confirm button
|
|
50
|
+
// resolve `true`.
|
|
53
51
|
const handleConfirm = useCallback(() => {
|
|
54
52
|
onConfirm?.();
|
|
55
53
|
onClose();
|
|
56
54
|
}, [onConfirm, onClose]);
|
|
57
55
|
|
|
56
|
+
const actions: DialogAction[] = [
|
|
57
|
+
{
|
|
58
|
+
label: confirmLabel,
|
|
59
|
+
onPress: handleConfirm,
|
|
60
|
+
color: destructive ? 'destructive' : 'default',
|
|
61
|
+
shouldCloseOnPress: false,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
if (!hideCancel) {
|
|
65
|
+
// The `cancel` color auto-dismisses via the Dialog's `close()`, which in
|
|
66
|
+
// controlled mode fires `onClose` (the host resolves `false`). `onCancel`
|
|
67
|
+
// runs after the surface finishes closing.
|
|
68
|
+
actions.push({
|
|
69
|
+
label: cancelLabel,
|
|
70
|
+
onPress: onCancel,
|
|
71
|
+
color: 'cancel',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
return (
|
|
59
76
|
<Dialog
|
|
60
77
|
open={visible}
|
|
61
78
|
onClose={onClose}
|
|
62
79
|
placement="center"
|
|
63
|
-
// Alert dialogs are blocking by default — the backdrop only
|
|
64
|
-
// when the caller opts in via `dismissible`.
|
|
80
|
+
// Alert dialogs are blocking by default — the backdrop / Escape only
|
|
81
|
+
// dismiss when the caller opts in via `dismissible`.
|
|
65
82
|
dismissOnBackdrop={dismissible}
|
|
66
|
-
maxWidth={ALERT_DIALOG_MAX_WIDTH}
|
|
67
83
|
title={title}
|
|
84
|
+
description={description}
|
|
85
|
+
actions={actions}
|
|
68
86
|
label={title}
|
|
69
87
|
style={cardStyle}
|
|
70
88
|
testID={testID}
|
|
71
|
-
|
|
72
|
-
{description ? (
|
|
73
|
-
<Text
|
|
74
|
-
style={{
|
|
75
|
-
fontSize: fontSize.md,
|
|
76
|
-
lineHeight: fontSize.md * 1.4,
|
|
77
|
-
color: theme.colors.textSecondary,
|
|
78
|
-
paddingBottom: space.lg,
|
|
79
|
-
}}
|
|
80
|
-
>
|
|
81
|
-
{description}
|
|
82
|
-
</Text>
|
|
83
|
-
) : null}
|
|
84
|
-
<View style={styles.actions}>
|
|
85
|
-
{!hideCancel ? (
|
|
86
|
-
<Button
|
|
87
|
-
variant="secondary"
|
|
88
|
-
size="medium"
|
|
89
|
-
onPress={handleCancel}
|
|
90
|
-
style={styles.action}
|
|
91
|
-
accessibilityLabel={cancelLabel}
|
|
92
|
-
>
|
|
93
|
-
{cancelLabel}
|
|
94
|
-
</Button>
|
|
95
|
-
) : null}
|
|
96
|
-
<Button
|
|
97
|
-
variant="primary"
|
|
98
|
-
size="medium"
|
|
99
|
-
onPress={handleConfirm}
|
|
100
|
-
style={StyleSheet.flatten([
|
|
101
|
-
styles.action,
|
|
102
|
-
destructive && { backgroundColor: theme.colors.negative },
|
|
103
|
-
])}
|
|
104
|
-
textStyle={destructive ? { color: theme.colors.negativeForeground } : undefined}
|
|
105
|
-
accessibilityLabel={confirmLabel}
|
|
106
|
-
>
|
|
107
|
-
{confirmLabel}
|
|
108
|
-
</Button>
|
|
109
|
-
</View>
|
|
110
|
-
</Dialog>
|
|
89
|
+
/>
|
|
111
90
|
);
|
|
112
91
|
};
|
|
113
92
|
|
|
@@ -117,14 +96,3 @@ export function createAlertDialog(Dialog: DialogComponent) {
|
|
|
117
96
|
}
|
|
118
97
|
|
|
119
98
|
export type AlertDialogType = ReturnType<typeof createAlertDialog>;
|
|
120
|
-
|
|
121
|
-
const styles = StyleSheet.create({
|
|
122
|
-
actions: {
|
|
123
|
-
flexDirection: 'row',
|
|
124
|
-
justifyContent: 'flex-end',
|
|
125
|
-
gap: space.sm,
|
|
126
|
-
},
|
|
127
|
-
action: {
|
|
128
|
-
minWidth: 96,
|
|
129
|
-
},
|
|
130
|
-
});
|