@oneblink/apps-react 6.10.0-beta.1 → 6.10.0-beta.10

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 (52) hide show
  1. package/dist/components/ConfirmDialog.d.ts +22 -0
  2. package/dist/components/ConfirmDialog.js +40 -0
  3. package/dist/components/ConfirmDialog.js.map +1 -0
  4. package/dist/components/InputClear.d.ts +8 -0
  5. package/dist/components/InputClear.js +11 -0
  6. package/dist/components/InputClear.js.map +1 -0
  7. package/dist/components/InputField.d.ts +7 -0
  8. package/dist/components/InputField.js +17 -0
  9. package/dist/components/InputField.js.map +1 -0
  10. package/dist/components/SuccessSnackbar.d.ts +8 -0
  11. package/dist/components/SuccessSnackbar.js +19 -0
  12. package/dist/components/SuccessSnackbar.js.map +1 -0
  13. package/dist/components/calendar-bookings/CalendarBookingsCancelForm.d.ts +4 -0
  14. package/dist/components/calendar-bookings/CalendarBookingsCancelForm.js +28 -0
  15. package/dist/components/calendar-bookings/CalendarBookingsCancelForm.js.map +1 -0
  16. package/dist/components/calendar-bookings/CalendarBookingsContainer.d.ts +7 -0
  17. package/dist/components/calendar-bookings/CalendarBookingsContainer.js +34 -0
  18. package/dist/components/calendar-bookings/CalendarBookingsContainer.js.map +1 -0
  19. package/dist/components/calendar-bookings/CalendarBookingsForm.d.ts +7 -0
  20. package/dist/components/calendar-bookings/CalendarBookingsForm.js +131 -0
  21. package/dist/components/calendar-bookings/CalendarBookingsForm.js.map +1 -0
  22. package/dist/components/calendar-bookings/CalendarBookingsProvider.d.ts +13 -0
  23. package/dist/components/calendar-bookings/CalendarBookingsProvider.js +64 -0
  24. package/dist/components/calendar-bookings/CalendarBookingsProvider.js.map +1 -0
  25. package/dist/components/calendar-bookings/CalendarBookingsReschedulingForm.d.ts +4 -0
  26. package/dist/components/calendar-bookings/CalendarBookingsReschedulingForm.js +37 -0
  27. package/dist/components/calendar-bookings/CalendarBookingsReschedulingForm.js.map +1 -0
  28. package/dist/components/calendar-bookings/ErrorModal.d.ts +10 -0
  29. package/dist/components/calendar-bookings/ErrorModal.js +48 -0
  30. package/dist/components/calendar-bookings/ErrorModal.js.map +1 -0
  31. package/dist/components/mfa/MfaDialog.d.ts +9 -0
  32. package/dist/components/mfa/MfaDialog.js +86 -0
  33. package/dist/components/mfa/MfaDialog.js.map +1 -0
  34. package/dist/components/mfa/MultiFactorAuthentication.d.ts +46 -0
  35. package/dist/components/mfa/MultiFactorAuthentication.js +112 -0
  36. package/dist/components/mfa/MultiFactorAuthentication.js.map +1 -0
  37. package/dist/form-elements/FormElementTextarea.js +6 -3
  38. package/dist/form-elements/FormElementTextarea.js.map +1 -1
  39. package/dist/hooks/useConditionalLogic.js +9 -2
  40. package/dist/hooks/useConditionalLogic.js.map +1 -1
  41. package/dist/hooks/useMfa.d.ts +99 -0
  42. package/dist/hooks/useMfa.js +220 -0
  43. package/dist/hooks/useMfa.js.map +1 -0
  44. package/dist/index.d.ts +5 -0
  45. package/dist/index.js +5 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/services/form-validation.js +2 -1
  48. package/dist/services/form-validation.js.map +1 -1
  49. package/dist/styles/calendar-booking-form.scss +7 -0
  50. package/dist/styles.css +8 -0
  51. package/dist/styles.scss +1 -0
  52. package/package.json +4 -2
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import clsx from 'clsx';
3
+ import { OneBlinkAppsError } from '@oneblink/apps';
4
+ import OneBlinkAppsErrorOriginalMessage from '../renderer/OneBlinkAppsErrorOriginalMessage';
5
+ import sanitizeHtml from '../../services/sanitize-html';
6
+ import Modal from '../renderer/Modal';
7
+ import MaterialIcon from '../MaterialIcon';
8
+ function ErrorModal({ error, closeButtonLabel, closeButtonClassName, onClose, }) {
9
+ const displayError = React.useMemo(() => {
10
+ if (!error)
11
+ return;
12
+ let displayError;
13
+ if (!(error instanceof OneBlinkAppsError)) {
14
+ displayError = new OneBlinkAppsError(error.message);
15
+ }
16
+ else {
17
+ displayError = error;
18
+ }
19
+ return displayError;
20
+ }, [error]);
21
+ const sanitizedHtml = React.useMemo(() => {
22
+ if (!error)
23
+ return '';
24
+ return sanitizeHtml(error.message);
25
+ }, [error]);
26
+ const handleClose = React.useCallback(async () => {
27
+ if (!displayError)
28
+ return;
29
+ onClose();
30
+ }, [displayError, onClose]);
31
+ if (!displayError) {
32
+ return null;
33
+ }
34
+ return (React.createElement(Modal, { isOpen: true, title: displayError.title, className: "cypress-error-modal", cardClassName: clsx({
35
+ 'has-text-centered': displayError.isOffline,
36
+ }), titleClassName: "cypress-error-title", bodyClassName: "cypress-error-message", actions: React.createElement(React.Fragment, null,
37
+ React.createElement("button", { type: "button", className: clsx('button ob-button cypress-close-error is-primary', closeButtonClassName), onClick: handleClose, autoFocus: true }, closeButtonLabel || 'Okay')) },
38
+ React.createElement(React.Fragment, null,
39
+ React.createElement("div", {
40
+ // eslint-disable-next-line react/no-danger
41
+ dangerouslySetInnerHTML: {
42
+ __html: sanitizedHtml,
43
+ } }),
44
+ displayError.isOffline && (React.createElement(MaterialIcon, { className: "has-text-warning icon-x-large" }, "wifi_off")),
45
+ React.createElement(OneBlinkAppsErrorOriginalMessage, { error: displayError.originalError }))));
46
+ }
47
+ export default React.memo(ErrorModal);
48
+ //# sourceMappingURL=ErrorModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorModal.js","sourceRoot":"","sources":["../../../src/components/calendar-bookings/ErrorModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClD,OAAO,gCAAgC,MAAM,8CAA8C,CAAA;AAC3F,OAAO,YAAY,MAAM,8BAA8B,CAAA;AACvD,OAAO,KAAK,MAAM,mBAAmB,CAAA;AACrC,OAAO,YAAY,MAAM,iBAAiB,CAAA;AAS1C,SAAS,UAAU,CAAC,EAClB,KAAK,EACL,gBAAgB,EAChB,oBAAoB,EACpB,OAAO,GACD;IACN,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACtC,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,IAAI,YAAY,CAAA;QAEhB,IAAI,CAAC,CAAC,KAAK,YAAY,iBAAiB,CAAC,EAAE,CAAC;YAC1C,YAAY,GAAG,IAAI,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrD,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,KAAK,CAAA;QACtB,CAAC;QACD,OAAO,YAAY,CAAA;IACrB,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAEX,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACvC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAA;QACrB,OAAO,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACpC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAEX,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE;QAC/C,IAAI,CAAC,YAAY;YAAE,OAAM;QAEzB,OAAO,EAAE,CAAA;IACX,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAA;IAE3B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,oBAAC,KAAK,IACJ,MAAM,QACN,KAAK,EAAE,YAAY,CAAC,KAAK,EACzB,SAAS,EAAC,qBAAqB,EAC/B,aAAa,EAAE,IAAI,CAAC;YAClB,mBAAmB,EAAE,YAAY,CAAC,SAAS;SAC5C,CAAC,EACF,cAAc,EAAC,qBAAqB,EACpC,aAAa,EAAC,uBAAuB,EACrC,OAAO,EACL;YACE,gCACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,IAAI,CACb,iDAAiD,EACjD,oBAAoB,CACrB,EACD,OAAO,EAAE,WAAW,EACpB,SAAS,UAER,gBAAgB,IAAI,MAAM,CACpB,CACR;QAGL;YACE;gBACE,2CAA2C;gBAC3C,uBAAuB,EAAE;oBACvB,MAAM,EAAE,aAAa;iBACtB,GACD;YACD,YAAY,CAAC,SAAS,IAAI,CACzB,oBAAC,YAAY,IAAC,SAAS,EAAC,+BAA+B,eAExC,CAChB;YACD,oBAAC,gCAAgC,IAAC,KAAK,EAAE,YAAY,CAAC,aAAa,GAAI,CACtE,CACG,CACT,CAAA;AACH,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAQ,UAAU,CAAC,CAAA","sourcesContent":["import * as React from 'react'\nimport clsx from 'clsx'\nimport { OneBlinkAppsError } from '@oneblink/apps'\nimport OneBlinkAppsErrorOriginalMessage from '../renderer/OneBlinkAppsErrorOriginalMessage'\nimport sanitizeHtml from '../../services/sanitize-html'\nimport Modal from '../renderer/Modal'\nimport MaterialIcon from '../MaterialIcon'\n\ntype Props = {\n error: OneBlinkAppsError | Error | null\n closeButtonLabel?: string\n closeButtonClassName?: string\n onClose: () => unknown\n}\n\nfunction ErrorModal({\n error,\n closeButtonLabel,\n closeButtonClassName,\n onClose,\n}: Props) {\n const displayError = React.useMemo(() => {\n if (!error) return\n let displayError\n\n if (!(error instanceof OneBlinkAppsError)) {\n displayError = new OneBlinkAppsError(error.message)\n } else {\n displayError = error\n }\n return displayError\n }, [error])\n\n const sanitizedHtml = React.useMemo(() => {\n if (!error) return ''\n return sanitizeHtml(error.message)\n }, [error])\n\n const handleClose = React.useCallback(async () => {\n if (!displayError) return\n\n onClose()\n }, [displayError, onClose])\n\n if (!displayError) {\n return null\n }\n\n return (\n <Modal\n isOpen\n title={displayError.title}\n className=\"cypress-error-modal\"\n cardClassName={clsx({\n 'has-text-centered': displayError.isOffline,\n })}\n titleClassName=\"cypress-error-title\"\n bodyClassName=\"cypress-error-message\"\n actions={\n <>\n <button\n type=\"button\"\n className={clsx(\n 'button ob-button cypress-close-error is-primary',\n closeButtonClassName,\n )}\n onClick={handleClose}\n autoFocus\n >\n {closeButtonLabel || 'Okay'}\n </button>\n </>\n }\n >\n <>\n <div\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{\n __html: sanitizedHtml,\n }}\n />\n {displayError.isOffline && (\n <MaterialIcon className=\"has-text-warning icon-x-large\">\n wifi_off\n </MaterialIcon>\n )}\n <OneBlinkAppsErrorOriginalMessage error={displayError.originalError} />\n </>\n </Modal>\n )\n}\n\nexport default React.memo<Props>(ErrorModal)\n"]}
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import { authService } from '@oneblink/apps';
3
+ declare function MfaDialog({ onClose, onCompleted, mfaSetup, }: {
4
+ onClose: () => void;
5
+ onCompleted: () => void;
6
+ mfaSetup: Awaited<ReturnType<typeof authService.setupMfa>> | undefined;
7
+ }): React.JSX.Element;
8
+ declare const _default: React.MemoExoticComponent<typeof MfaDialog>;
9
+ export default _default;
@@ -0,0 +1,86 @@
1
+ import * as React from 'react';
2
+ import { QRCodeSVG } from 'qrcode.react';
3
+ import { Box, Collapse, Dialog, Grid, Link, Typography, DialogContent, DialogActions, Button, } from '@mui/material';
4
+ import { authService } from '@oneblink/apps';
5
+ import useBooleanState from '../../hooks/useBooleanState';
6
+ import { CopyToClipBoardIconButton } from '../CopyToClipboardIconButton';
7
+ import SuccessSnackbar from '../SuccessSnackbar';
8
+ import InputField from '../InputField';
9
+ import { LoadingButton } from '@mui/lab';
10
+ function MfaDialog({ onClose, onCompleted, mfaSetup, }) {
11
+ const [code, setState] = React.useState('');
12
+ const [hasSuccessfullySaved, showSuccessfullySaved, hideSuccessfullySaved] = useBooleanState(false);
13
+ const [isShowingSecretCode, showSecretCode, hideSecretCode] = useBooleanState(false);
14
+ const qrcodeValue = React.useMemo(() => {
15
+ if (mfaSetup) {
16
+ return authService.generateMfaQrCodeUrl(mfaSetup);
17
+ }
18
+ }, [mfaSetup]);
19
+ const [isSaving, startSaving, stopSaving] = useBooleanState(false);
20
+ const handleSave = React.useCallback(async () => {
21
+ startSaving();
22
+ if (!code || !mfaSetup) {
23
+ return;
24
+ }
25
+ await mfaSetup.mfaCodeCallback(code);
26
+ onCompleted();
27
+ stopSaving();
28
+ showSuccessfullySaved();
29
+ }, [
30
+ code,
31
+ mfaSetup,
32
+ onCompleted,
33
+ showSuccessfullySaved,
34
+ startSaving,
35
+ stopSaving,
36
+ ]);
37
+ return (React.createElement(React.Fragment, null,
38
+ React.createElement(Dialog, { open: !!mfaSetup, onClose: onClose, title: "Complete MFA Setup" },
39
+ React.createElement(DialogContent, { dividers: true },
40
+ React.createElement(React.Fragment, null,
41
+ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Authenticator App"),
42
+ React.createElement(Typography, { variant: "body2", paragraph: true },
43
+ "Authenticator apps like",
44
+ ' ',
45
+ React.createElement(Link, { href: "https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2", target: "_blank", rel: "noopener noreferrer" }, "Google Authenticator"),
46
+ ' ',
47
+ "and",
48
+ ' ',
49
+ React.createElement(Link, { href: "https://www.microsoft.com/en-us/security/mobile-authenticator-app", target: "_blank", rel: "noopener noreferrer" }, "Microsoft Authenticator"),
50
+ ' ',
51
+ "generate one-time passwords that are used as a second factor to verify your identity when prompted during sign-in."),
52
+ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Scan the QR code"),
53
+ React.createElement(Typography, { variant: "body2", paragraph: true }, "Use an authenticator app or browser extension to scan the QR code below."),
54
+ React.createElement(Box, { marginBottom: 2 },
55
+ React.createElement(Grid, { container: true, spacing: 2, alignItems: "center" },
56
+ React.createElement(Grid, { item: true, xs: false },
57
+ React.createElement(Box, { border: 1, padding: 2, borderRadius: 1, borderColor: "divider", display: "inline-block" },
58
+ React.createElement(QRCodeSVG, { value: qrcodeValue || '' }))),
59
+ React.createElement(Grid, { item: true, xs: true },
60
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" },
61
+ "Having trouble scanning the QR code?",
62
+ ' ',
63
+ React.createElement(Link, { onClick: showSecretCode, component: "button" }, "Click here"),
64
+ ' ',
65
+ "to display the setup key which can be manually entered in your authenticator app.")))),
66
+ React.createElement(Collapse, { in: isShowingSecretCode },
67
+ React.createElement(Box, { marginBottom: 2 },
68
+ React.createElement(InputField, { label: "Setup Key", value: (mfaSetup === null || mfaSetup === void 0 ? void 0 : mfaSetup.secretCode) || '', fullWidth: true, InputProps: {
69
+ endAdornment: (React.createElement(CopyToClipBoardIconButton, { text: (mfaSetup === null || mfaSetup === void 0 ? void 0 : mfaSetup.secretCode) || '' })),
70
+ }, helperText: React.createElement(React.Fragment, null,
71
+ React.createElement(Link, { onClick: hideSecretCode, component: "button" }, "Click here"),
72
+ ' ',
73
+ "to hide the setup key") }))),
74
+ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Verify App"),
75
+ React.createElement(Typography, { variant: "body2", paragraph: true }, "Enter the 6-digit code found in your authenticator app."),
76
+ React.createElement(InputField, { autoFocus: true, margin: "none", name: "code", label: "Code", fullWidth: true, placeholder: "XXXXXX", variant: "outlined", value: code, onChange: (event) => {
77
+ const newValue = event.target.value;
78
+ setState(() => newValue);
79
+ }, disabled: isSaving, "data-cypress": "mfa-dialog-code" }))),
80
+ React.createElement(DialogActions, null,
81
+ React.createElement(Button, { onClick: onClose, disabled: isSaving }, "Cancel"),
82
+ React.createElement(LoadingButton, { variant: "contained", color: "primary", loading: isSaving, onClick: handleSave }, "Save"))),
83
+ React.createElement(SuccessSnackbar, { open: hasSuccessfullySaved, onClose: hideSuccessfullySaved }, "MFA has been successfully setup.")));
84
+ }
85
+ export default React.memo(MfaDialog);
86
+ //# sourceMappingURL=MfaDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaDialog.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaDialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport { QRCodeSVG } from 'qrcode.react'\nimport {\n Box,\n Collapse,\n Dialog,\n Grid,\n Link,\n Typography,\n DialogContent,\n DialogActions,\n Button,\n} from '@mui/material'\nimport { authService } from '@oneblink/apps'\nimport useBooleanState from '../../hooks/useBooleanState'\nimport { CopyToClipBoardIconButton } from '../CopyToClipboardIconButton'\nimport SuccessSnackbar from '../SuccessSnackbar'\nimport InputField from '../InputField'\nimport { LoadingButton } from '@mui/lab'\n\nfunction MfaDialog({\n onClose,\n onCompleted,\n mfaSetup,\n}: {\n onClose: () => void\n onCompleted: () => void\n mfaSetup: Awaited<ReturnType<typeof authService.setupMfa>> | undefined\n}) {\n const [code, setState] = React.useState('')\n const [hasSuccessfullySaved, showSuccessfullySaved, hideSuccessfullySaved] =\n useBooleanState(false)\n const [isShowingSecretCode, showSecretCode, hideSecretCode] =\n useBooleanState(false)\n\n const qrcodeValue = React.useMemo(() => {\n if (mfaSetup) {\n return authService.generateMfaQrCodeUrl(mfaSetup)\n }\n }, [mfaSetup])\n\n const [isSaving, startSaving, stopSaving] = useBooleanState(false)\n const handleSave = React.useCallback(async () => {\n startSaving()\n if (!code || !mfaSetup) {\n return\n }\n\n await mfaSetup.mfaCodeCallback(code)\n onCompleted()\n stopSaving()\n showSuccessfullySaved()\n }, [\n code,\n mfaSetup,\n onCompleted,\n showSuccessfullySaved,\n startSaving,\n stopSaving,\n ])\n\n return (\n <React.Fragment>\n <Dialog open={!!mfaSetup} onClose={onClose} title=\"Complete MFA Setup\">\n <DialogContent dividers>\n <>\n <Typography variant=\"subtitle2\" gutterBottom>\n Authenticator App\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Authenticator apps like{' '}\n <Link\n href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Google Authenticator\n </Link>{' '}\n and{' '}\n <Link\n href=\"https://www.microsoft.com/en-us/security/mobile-authenticator-app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Microsoft Authenticator\n </Link>{' '}\n generate one-time passwords that are used as a second factor to\n verify your identity when prompted during sign-in.\n </Typography>\n <Typography variant=\"subtitle2\" gutterBottom>\n Scan the QR code\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Use an authenticator app or browser extension to scan the QR code\n below.\n </Typography>\n\n <Box marginBottom={2}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs={false}>\n <Box\n border={1}\n padding={2}\n borderRadius={1}\n borderColor=\"divider\"\n display=\"inline-block\"\n >\n <QRCodeSVG value={qrcodeValue || ''} />\n </Box>\n </Grid>\n <Grid item xs>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Having trouble scanning the QR code?{' '}\n <Link onClick={showSecretCode} component=\"button\">\n Click here\n </Link>{' '}\n to display the setup key which can be manually entered in\n your authenticator app.\n </Typography>\n </Grid>\n </Grid>\n </Box>\n\n <Collapse in={isShowingSecretCode}>\n <Box marginBottom={2}>\n <InputField\n label=\"Setup Key\"\n value={mfaSetup?.secretCode || ''}\n fullWidth\n InputProps={{\n endAdornment: (\n <CopyToClipBoardIconButton\n text={mfaSetup?.secretCode || ''}\n />\n ),\n }}\n helperText={\n <>\n <Link onClick={hideSecretCode} component=\"button\">\n Click here\n </Link>{' '}\n to hide the setup key\n </>\n }\n />\n </Box>\n </Collapse>\n\n <Typography variant=\"subtitle2\" gutterBottom>\n Verify App\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Enter the 6-digit code found in your authenticator app.\n </Typography>\n\n <InputField\n autoFocus\n margin=\"none\"\n name=\"code\"\n label=\"Code\"\n fullWidth\n placeholder=\"XXXXXX\"\n variant=\"outlined\"\n value={code}\n onChange={(event) => {\n const newValue = event.target.value\n setState(() => newValue)\n }}\n disabled={isSaving}\n data-cypress=\"mfa-dialog-code\"\n />\n </>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={isSaving}>\n Cancel\n </Button>\n <LoadingButton\n variant=\"contained\"\n color=\"primary\"\n loading={isSaving}\n onClick={handleSave}\n >\n Save\n </LoadingButton>\n </DialogActions>\n </Dialog>\n\n <SuccessSnackbar\n open={hasSuccessfullySaved}\n onClose={hideSuccessfullySaved}\n >\n MFA has been successfully setup.\n </SuccessSnackbar>\n </React.Fragment>\n )\n}\n\nexport default React.memo(MfaDialog)\n"]}
@@ -0,0 +1,46 @@
1
+ import * as React from 'react';
2
+ export declare const LargeIcon: import("@emotion/styled").StyledComponent<import("@mui/material").IconOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & {
3
+ ref?: ((instance: HTMLSpanElement | null) => void) | React.RefObject<HTMLSpanElement> | null | undefined;
4
+ }, "className" | "style" | "children" | "classes" | "sx" | "color" | "fontSize" | "baseClassName"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, {}, {}>;
5
+ type Props = {
6
+ ssoSetupUrl?: string;
7
+ isExternalIdentityProviderUser?: boolean;
8
+ };
9
+ /**
10
+ * React Component that provides a mechanism for app users to configure Multi
11
+ * Factor Authentication. `<MfaProvider />` must be provided above this
12
+ * component in the component tree.
13
+ *
14
+ * #### Example
15
+ *
16
+ * ```js
17
+ * import * as React from 'react'
18
+ * import {
19
+ * MfaProvider,
20
+ * MultiFactorAuthentication,
21
+ * } from '@oneblink/apps-react'
22
+ *
23
+ * function Component() {
24
+ * return <MultiFactorAuthentication />
25
+ * }
26
+ *
27
+ * function App() {
28
+ * return (
29
+ * <MfaProvider>
30
+ * <Component />
31
+ * </MfaProvider>
32
+ * )
33
+ * }
34
+ *
35
+ * const root = document.getElementById('root')
36
+ * if (root) {
37
+ * ReactDOM.render(<App />, root)
38
+ * }
39
+ * ```
40
+ *
41
+ * @param props
42
+ * @returns
43
+ * @group Components
44
+ */
45
+ export default function MultiFactorAuthentication({ ssoSetupUrl, isExternalIdentityProviderUser, }: Props): React.JSX.Element;
46
+ export {};
@@ -0,0 +1,112 @@
1
+ import * as React from 'react';
2
+ import { Box, Button, Chip, CircularProgress, Divider, Grid, Icon, Paper, Tooltip, Typography, styled, } from '@mui/material';
3
+ import { LoadingButton } from '@mui/lab';
4
+ import ConfirmDialog from '../ConfirmDialog';
5
+ import MfaDialog from './MfaDialog';
6
+ import ErrorSnackbar from '../ErrorSnackbar';
7
+ import MaterialIcon from '../MaterialIcon';
8
+ import useMfa from '../../hooks/useMfa';
9
+ import ErrorMessage from '../messages/ErrorMessage';
10
+ export const LargeIcon = styled(Icon)(({ theme }) => ({
11
+ fontSize: `${theme.typography.h4.fontSize} !important`,
12
+ }));
13
+ function MfaStatus({ isExternalIdentityProviderUser, isLoading, loadingError, loadMfa, isMfaEnabled, }) {
14
+ if (isExternalIdentityProviderUser)
15
+ return null;
16
+ if (isLoading) {
17
+ return (React.createElement(Box, { padding: 3 },
18
+ React.createElement(Grid, { container: true, justifyContent: "center" },
19
+ React.createElement(CircularProgress, null))));
20
+ }
21
+ if (loadingError) {
22
+ return (React.createElement("div", null,
23
+ React.createElement(ErrorMessage, { title: "Error Loading Multi Factor Authentication Configuration", onTryAgain: loadMfa }, loadingError.message)));
24
+ }
25
+ if (isMfaEnabled) {
26
+ return (React.createElement(Chip, { label: "Enabled", icon: React.createElement(MaterialIcon, { color: "success" }, "verified_user") }));
27
+ }
28
+ return (React.createElement(Chip, { label: "Disabled", icon: React.createElement(MaterialIcon, { color: "warning" }, "remove_moderator") }));
29
+ }
30
+ function MfaSetup({ ssoSetupUrl, isExternalIdentityProviderUser, }) {
31
+ const { setupError, isMfaEnabled, isDisablingMfa, isSettingUpMfa, mfaSetup, beginMfaSetup, cancelMfaSetup, completeMfaSetup, clearMfaSetupError, beginDisablingMfa, completeDisablingMfa, cancelDisablingMfa, } = useMfa();
32
+ if (ssoSetupUrl) {
33
+ return (React.createElement(Grid, { item: true },
34
+ React.createElement(Button, { variant: "outlined", size: "small", component: "a", href: ssoSetupUrl, target: "_blank", rel: "noopener noreferrer", "data-cypress": "configure-mfa-button" }, "Configure MFA")));
35
+ }
36
+ if (isExternalIdentityProviderUser) {
37
+ return (React.createElement(Grid, { item: true },
38
+ React.createElement(Tooltip, { title: "MFA must be configured in your login provider." },
39
+ React.createElement("span", null,
40
+ React.createElement(Button, { variant: "outlined", size: "small", disabled: isExternalIdentityProviderUser, "data-cypress": "configure-mfa-button" }, "Configure MFA")))));
41
+ }
42
+ return (React.createElement(React.Fragment, null,
43
+ React.createElement(Grid, { item: true },
44
+ React.createElement(Button, { variant: "outlined", size: "small", disabled: !isMfaEnabled, "data-cypress": "disable-mfa-button", onClick: beginDisablingMfa }, "Disable MFA"),
45
+ React.createElement(ConfirmDialog, { isOpen: isDisablingMfa, onClose: cancelDisablingMfa, onConfirm: completeDisablingMfa, title: "Please Confirm", confirmButtonText: "Disable MFA", confirmButtonIcon: React.createElement(MaterialIcon, null, "remove_moderator"), cypress: {
46
+ dialog: 'disable-mfa-dialog',
47
+ confirmButton: 'disable-mfa-dialog-confirm-button',
48
+ cancelButton: 'disable-mfa-dialog-cancel-button',
49
+ error: 'disable-mfa-dialog-error-message',
50
+ } },
51
+ React.createElement(Typography, { variant: "body2" }, "Are you sure want to disable multi factor authentication (MFA)?"))),
52
+ React.createElement(Grid, { item: true },
53
+ React.createElement(LoadingButton, { variant: "contained", size: "small", loading: isSettingUpMfa, disabled: isMfaEnabled, onClick: beginMfaSetup, "data-cypress": "setup-mfa-button" }, "Setup MFA"),
54
+ React.createElement(MfaDialog, { mfaSetup: mfaSetup, onClose: cancelMfaSetup, onCompleted: completeMfaSetup })),
55
+ React.createElement(ErrorSnackbar, { open: !!setupError, onClose: clearMfaSetupError },
56
+ React.createElement("span", { "data-cypress": "mfa-setup-error-message" }, setupError === null || setupError === void 0 ? void 0 : setupError.message))));
57
+ }
58
+ /**
59
+ * React Component that provides a mechanism for app users to configure Multi
60
+ * Factor Authentication. `<MfaProvider />` must be provided above this
61
+ * component in the component tree.
62
+ *
63
+ * #### Example
64
+ *
65
+ * ```js
66
+ * import * as React from 'react'
67
+ * import {
68
+ * MfaProvider,
69
+ * MultiFactorAuthentication,
70
+ * } from '@oneblink/apps-react'
71
+ *
72
+ * function Component() {
73
+ * return <MultiFactorAuthentication />
74
+ * }
75
+ *
76
+ * function App() {
77
+ * return (
78
+ * <MfaProvider>
79
+ * <Component />
80
+ * </MfaProvider>
81
+ * )
82
+ * }
83
+ *
84
+ * const root = document.getElementById('root')
85
+ * if (root) {
86
+ * ReactDOM.render(<App />, root)
87
+ * }
88
+ * ```
89
+ *
90
+ * @param props
91
+ * @returns
92
+ * @group Components
93
+ */
94
+ export default function MultiFactorAuthentication({ ssoSetupUrl, isExternalIdentityProviderUser, }) {
95
+ const { loadingError, isLoading, isMfaEnabled, loadMfa } = useMfa();
96
+ return (React.createElement(Grid, { item: true, xs: true, lg: 8 },
97
+ React.createElement(Box, { padding: 3 },
98
+ React.createElement(Paper, null,
99
+ React.createElement(Box, { padding: 3 },
100
+ React.createElement(Grid, { container: true, spacing: 2, alignItems: "center" },
101
+ React.createElement(Grid, { item: true, xs: true },
102
+ React.createElement(Typography, { variant: "h4", fontWeight: "light" },
103
+ "Multi Factor Authentication",
104
+ ' ',
105
+ React.createElement(MfaStatus, { loadMfa: loadMfa, isLoading: isLoading, loadingError: loadingError, isMfaEnabled: isMfaEnabled, isExternalIdentityProviderUser: !!isExternalIdentityProviderUser })),
106
+ React.createElement(Box, { marginY: 1 },
107
+ React.createElement(Divider, null)),
108
+ React.createElement(Typography, { variant: "body2", paragraph: true }, "Multi factor authentication (MFA), also known as two factor authentication (2FA), is a best practice that requires a second authentication factor in addition to user name and password sign-in credentials. We strongly recommend enabling MFA to enhance your account security."),
109
+ React.createElement(Grid, { container: true, justifyContent: "flex-end", spacing: 1 },
110
+ React.createElement(MfaSetup, { isExternalIdentityProviderUser: !!isExternalIdentityProviderUser, ssoSetupUrl: ssoSetupUrl || '' })))))))));
111
+ }
112
+ //# sourceMappingURL=MultiFactorAuthentication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiFactorAuthentication.js","sourceRoot":"","sources":["../../../src/components/mfa/MultiFactorAuthentication.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport {\n Box,\n Button,\n Chip,\n CircularProgress,\n Divider,\n Grid,\n Icon,\n Paper,\n Tooltip,\n Typography,\n styled,\n} from '@mui/material'\nimport { LoadingButton } from '@mui/lab'\nimport ConfirmDialog from '../ConfirmDialog'\nimport MfaDialog from './MfaDialog'\nimport ErrorSnackbar from '../ErrorSnackbar'\nimport MaterialIcon from '../MaterialIcon'\nimport useMfa from '../../hooks/useMfa'\nimport ErrorMessage from '../messages/ErrorMessage'\n\nexport const LargeIcon = styled(Icon)(({ theme }) => ({\n fontSize: `${theme.typography.h4.fontSize} !important`,\n}))\n\ntype Props = {\n ssoSetupUrl?: string\n isExternalIdentityProviderUser?: boolean\n}\n\ntype MfaStatusProps = {\n isExternalIdentityProviderUser: boolean\n isLoading: boolean\n isMfaEnabled: boolean\n loadMfa: () => void\n loadingError?: Error\n}\n\nfunction MfaStatus({\n isExternalIdentityProviderUser,\n isLoading,\n loadingError,\n loadMfa,\n isMfaEnabled,\n}: MfaStatusProps) {\n if (isExternalIdentityProviderUser) return null\n\n if (isLoading) {\n return (\n <Box padding={3}>\n <Grid container justifyContent=\"center\">\n <CircularProgress />\n </Grid>\n </Box>\n )\n }\n\n if (loadingError) {\n return (\n <div>\n <ErrorMessage\n title=\"Error Loading Multi Factor Authentication Configuration\"\n onTryAgain={loadMfa}\n >\n {loadingError.message}\n </ErrorMessage>\n </div>\n )\n }\n\n if (isMfaEnabled) {\n return (\n <Chip\n label=\"Enabled\"\n icon={<MaterialIcon color=\"success\">verified_user</MaterialIcon>}\n />\n )\n }\n\n return (\n <Chip\n label=\"Disabled\"\n icon={<MaterialIcon color=\"warning\">remove_moderator</MaterialIcon>}\n />\n )\n}\n\nfunction MfaSetup({\n ssoSetupUrl,\n isExternalIdentityProviderUser,\n}: {\n ssoSetupUrl: string\n isExternalIdentityProviderUser: boolean\n}) {\n const {\n setupError,\n isMfaEnabled,\n isDisablingMfa,\n isSettingUpMfa,\n mfaSetup,\n beginMfaSetup,\n cancelMfaSetup,\n completeMfaSetup,\n clearMfaSetupError,\n beginDisablingMfa,\n completeDisablingMfa,\n cancelDisablingMfa,\n } = useMfa()\n\n if (ssoSetupUrl) {\n return (\n <Grid item>\n <Button\n variant=\"outlined\"\n size=\"small\"\n component=\"a\"\n href={ssoSetupUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </Grid>\n )\n }\n\n if (isExternalIdentityProviderUser) {\n return (\n <Grid item>\n <Tooltip title=\"MFA must be configured in your login provider.\">\n <span>\n <Button\n variant=\"outlined\"\n size=\"small\"\n disabled={isExternalIdentityProviderUser}\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </span>\n </Tooltip>\n </Grid>\n )\n }\n\n return (\n <>\n <Grid item>\n <Button\n variant=\"outlined\"\n size=\"small\"\n disabled={!isMfaEnabled}\n data-cypress=\"disable-mfa-button\"\n onClick={beginDisablingMfa}\n >\n Disable MFA\n </Button>\n <ConfirmDialog\n isOpen={isDisablingMfa}\n onClose={cancelDisablingMfa}\n onConfirm={completeDisablingMfa}\n title=\"Please Confirm\"\n confirmButtonText=\"Disable MFA\"\n confirmButtonIcon={<MaterialIcon>remove_moderator</MaterialIcon>}\n cypress={{\n dialog: 'disable-mfa-dialog',\n confirmButton: 'disable-mfa-dialog-confirm-button',\n cancelButton: 'disable-mfa-dialog-cancel-button',\n error: 'disable-mfa-dialog-error-message',\n }}\n >\n <Typography variant=\"body2\">\n Are you sure want to disable multi factor authentication (MFA)?\n </Typography>\n </ConfirmDialog>\n </Grid>\n\n <Grid item>\n <LoadingButton\n variant=\"contained\"\n size=\"small\"\n loading={isSettingUpMfa}\n disabled={isMfaEnabled}\n onClick={beginMfaSetup}\n data-cypress=\"setup-mfa-button\"\n >\n Setup MFA\n </LoadingButton>\n <MfaDialog\n mfaSetup={mfaSetup}\n onClose={cancelMfaSetup}\n onCompleted={completeMfaSetup}\n />\n </Grid>\n <ErrorSnackbar open={!!setupError} onClose={clearMfaSetupError}>\n <span data-cypress=\"mfa-setup-error-message\">\n {setupError?.message}\n </span>\n </ErrorSnackbar>\n </>\n )\n}\n/**\n * React Component that provides a mechanism for app users to configure Multi\n * Factor Authentication. `<MfaProvider />` must be provided above this\n * component in the component tree.\n *\n * #### Example\n *\n * ```js\n * import * as React from 'react'\n * import {\n * MfaProvider,\n * MultiFactorAuthentication,\n * } from '@oneblink/apps-react'\n *\n * function Component() {\n * return <MultiFactorAuthentication />\n * }\n *\n * function App() {\n * return (\n * <MfaProvider>\n * <Component />\n * </MfaProvider>\n * )\n * }\n *\n * const root = document.getElementById('root')\n * if (root) {\n * ReactDOM.render(<App />, root)\n * }\n * ```\n *\n * @param props\n * @returns\n * @group Components\n */\nexport default function MultiFactorAuthentication({\n ssoSetupUrl,\n isExternalIdentityProviderUser,\n}: Props) {\n const { loadingError, isLoading, isMfaEnabled, loadMfa } =\n useMfa()\n\n return (\n <Grid item xs={true} lg={8}>\n <Box padding={3}>\n <Paper>\n <Box padding={3}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs>\n <Typography variant=\"h4\" fontWeight=\"light\">\n Multi Factor Authentication{' '}\n <MfaStatus\n loadMfa={loadMfa}\n isLoading={isLoading}\n loadingError={loadingError}\n isMfaEnabled={isMfaEnabled}\n isExternalIdentityProviderUser={\n !!isExternalIdentityProviderUser\n }\n />\n </Typography>\n <Box marginY={1}>\n <Divider />\n </Box>\n <Typography variant=\"body2\" paragraph>\n Multi factor authentication (MFA), also known as two factor\n authentication (2FA), is a best practice that requires a\n second authentication factor in addition to user name and\n password sign-in credentials. We strongly recommend enabling\n MFA to enhance your account security.\n </Typography>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <MfaSetup\n isExternalIdentityProviderUser={\n !!isExternalIdentityProviderUser\n }\n ssoSetupUrl={ssoSetupUrl || ''}\n />\n </Grid>\n </Grid>\n </Grid>\n </Box>\n </Paper>\n </Box>\n </Grid>\n )\n}\n"]}
@@ -1,11 +1,14 @@
1
1
  import * as React from 'react';
2
2
  import clsx from 'clsx';
3
- import { TextareaAutosize } from '@mui/material';
3
+ import { TextareaAutosize, styled } from '@mui/material';
4
4
  import CopyToClipboardButton from '../components/renderer/CopyToClipboardButton';
5
5
  import LookupButton from '../components/renderer/LookupButton';
6
6
  import FormElementLabelContainer from '../components/renderer/FormElementLabelContainer';
7
7
  import { LookupNotificationContext } from '../hooks/useLookupNotification';
8
8
  import useElementAriaDescribedby from '../hooks/useElementAriaDescribedby';
9
+ const StyledTextareaAutosize = styled(TextareaAutosize)(() => ({
10
+ resize: 'vertical',
11
+ }));
9
12
  function FormElementTextarea({ id, element, value, onChange, validationMessage, displayValidationMessage, isDirty, setIsDirty, autocompleteAttributes, }) {
10
13
  const ariaDescribedby = useElementAriaDescribedby(id, element);
11
14
  const text = typeof value === 'string' ? value : '';
@@ -16,9 +19,9 @@ function FormElementTextarea({ id, element, value, onChange, validationMessage,
16
19
  return (React.createElement("div", { className: "cypress-textarea-element" },
17
20
  React.createElement(FormElementLabelContainer, { className: "ob-textarea", id: id, element: element, required: element.required },
18
21
  React.createElement("div", { className: "control" },
19
- React.createElement(TextareaAutosize, { placeholder: element.placeholderValue, id: id, name: element.name, className: "textarea input ob-input cypress-textarea-control", value: text, onChange: (e) => onChange(element, {
22
+ React.createElement(StyledTextareaAutosize, { placeholder: element.placeholderValue, id: id, name: element.name, className: "input ob-input cypress-textarea-control", value: text, onChange: (e) => onChange(element, {
20
23
  value: e.target.value || undefined,
21
- }), required: element.required, disabled: element.readOnly, onBlur: setIsDirty, "aria-describedby": ariaDescribedby, autoComplete: autocompleteAttributes })),
24
+ }), required: element.required, disabled: element.readOnly, onBlur: setIsDirty, "aria-describedby": ariaDescribedby, autoComplete: autocompleteAttributes, minRows: 4 })),
22
25
  (isDisplayingValidationMessage || !!element.maxLength) && (React.createElement("div", { role: "alert", className: "has-margin-top-8" },
23
26
  React.createElement("div", { className: "is-flex is-justify-content-space-between" },
24
27
  isDisplayingValidationMessage ? (React.createElement("div", { className: "has-text-danger ob-error__text cypress-validation-message" }, validationMessage)) : (React.createElement("div", null)),
@@ -1 +1 @@
1
- {"version":3,"file":"FormElementTextarea.js","sourceRoot":"","sources":["../../src/form-elements/FormElementTextarea.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport clsx from 'clsx'\nimport { FormTypes } from '@oneblink/types'\nimport { TextareaAutosize } from '@mui/material'\n\nimport CopyToClipboardButton from '../components/renderer/CopyToClipboardButton'\nimport LookupButton from '../components/renderer/LookupButton'\nimport FormElementLabelContainer from '../components/renderer/FormElementLabelContainer'\nimport { FormElementValueChangeHandler, IsDirtyProps } from '../types/form'\nimport { LookupNotificationContext } from '../hooks/useLookupNotification'\nimport useElementAriaDescribedby from '../hooks/useElementAriaDescribedby'\n\ntype Props = {\n id: string\n element: FormTypes.TextareaElement\n value: unknown\n onChange: FormElementValueChangeHandler<string>\n displayValidationMessage: boolean\n validationMessage: string | undefined\n autocompleteAttributes?: string\n} & IsDirtyProps\n\nfunction FormElementTextarea({\n id,\n element,\n value,\n onChange,\n validationMessage,\n displayValidationMessage,\n isDirty,\n setIsDirty,\n autocompleteAttributes,\n}: Props) {\n const ariaDescribedby = useElementAriaDescribedby(id, element)\n const text = typeof value === 'string' ? value : ''\n const isDisplayingCopyButton = !!element.readOnly && !!value\n const isDisplayingLookupButton =\n !!element.isDataLookup || !!element.isElementLookup\n\n const { isLookingUp } = React.useContext(LookupNotificationContext)\n const isDisplayingValidationMessage =\n (isDirty || displayValidationMessage) && !!validationMessage && !isLookingUp\n\n return (\n <div className=\"cypress-textarea-element\">\n <FormElementLabelContainer\n className=\"ob-textarea\"\n id={id}\n element={element}\n required={element.required}\n >\n <div className=\"control\">\n <TextareaAutosize\n placeholder={element.placeholderValue}\n id={id}\n name={element.name}\n className=\"textarea input ob-input cypress-textarea-control\"\n value={text}\n onChange={(e) =>\n onChange(element, {\n value: e.target.value || undefined,\n })\n }\n required={element.required}\n disabled={element.readOnly}\n onBlur={setIsDirty}\n aria-describedby={ariaDescribedby}\n autoComplete={autocompleteAttributes}\n />\n </div>\n\n {(isDisplayingValidationMessage || !!element.maxLength) && (\n <div role=\"alert\" className=\"has-margin-top-8\">\n <div className=\"is-flex is-justify-content-space-between\">\n {isDisplayingValidationMessage ? (\n <div className=\"has-text-danger ob-error__text cypress-validation-message\">\n {validationMessage}\n </div>\n ) : (\n <div />\n )}\n {!!element.maxLength && (\n <div\n className={clsx(\n 'ob-max-length__text cypress-max-length-message',\n {\n 'has-text-danger': text.length > element.maxLength,\n },\n )}\n >\n {text.length} / {element.maxLength}\n </div>\n )}\n </div>\n </div>\n )}\n\n {(isDisplayingLookupButton || isDisplayingCopyButton) && (\n <div className=\"buttons ob-buttons has-margin-top-8\">\n {isDisplayingCopyButton && (\n <CopyToClipboardButton\n className=\"button ob-button cypress-copy-to-clipboard-button\"\n text={text}\n />\n )}\n {isDisplayingLookupButton && (\n <LookupButton\n value={value}\n validationMessage={validationMessage}\n lookupButtonConfig={element.lookupButton}\n />\n )}\n </div>\n )}\n </FormElementLabelContainer>\n </div>\n )\n}\n\nexport default React.memo(FormElementTextarea)\n"]}
1
+ {"version":3,"file":"FormElementTextarea.js","sourceRoot":"","sources":["../../src/form-elements/FormElementTextarea.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport clsx from 'clsx'\nimport { FormTypes } from '@oneblink/types'\nimport { TextareaAutosize, styled } from '@mui/material'\n\nimport CopyToClipboardButton from '../components/renderer/CopyToClipboardButton'\nimport LookupButton from '../components/renderer/LookupButton'\nimport FormElementLabelContainer from '../components/renderer/FormElementLabelContainer'\nimport { FormElementValueChangeHandler, IsDirtyProps } from '../types/form'\nimport { LookupNotificationContext } from '../hooks/useLookupNotification'\nimport useElementAriaDescribedby from '../hooks/useElementAriaDescribedby'\n\nconst StyledTextareaAutosize = styled(TextareaAutosize)(() => ({\n resize: 'vertical',\n}))\n\ntype Props = {\n id: string\n element: FormTypes.TextareaElement\n value: unknown\n onChange: FormElementValueChangeHandler<string>\n displayValidationMessage: boolean\n validationMessage: string | undefined\n autocompleteAttributes?: string\n} & IsDirtyProps\n\nfunction FormElementTextarea({\n id,\n element,\n value,\n onChange,\n validationMessage,\n displayValidationMessage,\n isDirty,\n setIsDirty,\n autocompleteAttributes,\n}: Props) {\n const ariaDescribedby = useElementAriaDescribedby(id, element)\n const text = typeof value === 'string' ? value : ''\n const isDisplayingCopyButton = !!element.readOnly && !!value\n const isDisplayingLookupButton =\n !!element.isDataLookup || !!element.isElementLookup\n\n const { isLookingUp } = React.useContext(LookupNotificationContext)\n const isDisplayingValidationMessage =\n (isDirty || displayValidationMessage) && !!validationMessage && !isLookingUp\n\n return (\n <div className=\"cypress-textarea-element\">\n <FormElementLabelContainer\n className=\"ob-textarea\"\n id={id}\n element={element}\n required={element.required}\n >\n <div className=\"control\">\n <StyledTextareaAutosize\n placeholder={element.placeholderValue}\n id={id}\n name={element.name}\n className=\"input ob-input cypress-textarea-control\"\n value={text}\n onChange={(e) =>\n onChange(element, {\n value: e.target.value || undefined,\n })\n }\n required={element.required}\n disabled={element.readOnly}\n onBlur={setIsDirty}\n aria-describedby={ariaDescribedby}\n autoComplete={autocompleteAttributes}\n minRows={4}\n />\n </div>\n\n {(isDisplayingValidationMessage || !!element.maxLength) && (\n <div role=\"alert\" className=\"has-margin-top-8\">\n <div className=\"is-flex is-justify-content-space-between\">\n {isDisplayingValidationMessage ? (\n <div className=\"has-text-danger ob-error__text cypress-validation-message\">\n {validationMessage}\n </div>\n ) : (\n <div />\n )}\n {!!element.maxLength && (\n <div\n className={clsx(\n 'ob-max-length__text cypress-max-length-message',\n {\n 'has-text-danger': text.length > element.maxLength,\n },\n )}\n >\n {text.length} / {element.maxLength}\n </div>\n )}\n </div>\n </div>\n )}\n\n {(isDisplayingLookupButton || isDisplayingCopyButton) && (\n <div className=\"buttons ob-buttons has-margin-top-8\">\n {isDisplayingCopyButton && (\n <CopyToClipboardButton\n className=\"button ob-button cypress-copy-to-clipboard-button\"\n text={text}\n />\n )}\n {isDisplayingLookupButton && (\n <LookupButton\n value={value}\n validationMessage={validationMessage}\n lookupButtonConfig={element.lookupButton}\n />\n )}\n </div>\n )}\n </FormElementLabelContainer>\n </div>\n )\n}\n\nexport default React.memo(FormElementTextarea)\n"]}
@@ -1,6 +1,7 @@
1
1
  import { Sentry } from '@oneblink/apps';
2
2
  import * as React from 'react';
3
3
  import { conditionalLogicService } from '@oneblink/sdk-core';
4
+ import cleanFormSubmissionModel from '../services/cleanFormSubmissionModel';
4
5
  export default function useConditionalLogic(definition, submission) {
5
6
  const [conditionalLogicError, setConditionalLogicError] = React.useState();
6
7
  const errorCallback = React.useCallback((error) => {
@@ -20,14 +21,20 @@ export default function useConditionalLogic(definition, submission) {
20
21
  return true;
21
22
  }
22
23
  const { requiresAllConditionalPredicates, conditionalPredicates } = definition.enableSubmission;
24
+ const { model } = cleanFormSubmissionModel(submission, definition.elements, formElementsConditionallyShown, true);
23
25
  return conditionalLogicService.evaluateConditionalPredicates({
24
26
  isConditional: true,
25
27
  requiresAllConditionalPredicates,
26
28
  conditionalPredicates,
27
29
  formElements: definition.elements,
28
- submission,
30
+ submission: model,
29
31
  });
30
- }, [definition.elements, definition.enableSubmission, submission]);
32
+ }, [
33
+ definition.elements,
34
+ definition.enableSubmission,
35
+ formElementsConditionallyShown,
36
+ submission,
37
+ ]);
31
38
  return {
32
39
  conditionalLogicError,
33
40
  formElementsConditionallyShown,
@@ -1 +1 @@
1
- {"version":3,"file":"useConditionalLogic.js","sourceRoot":"","sources":["../../src/hooks/useConditionalLogic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAI5D,MAAM,CAAC,OAAO,UAAU,mBAAmB,CACzC,UAA0B,EAC1B,UAA0D;IAE1D,MAAM,CAAC,qBAAqB,EAAE,wBAAwB,CAAC,GAAG,KAAK,CAAC,QAAQ,EAErE,CAAA;IAEH,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,KAAY,EAAE,EAAE;QACvD,OAAO,CAAC,IAAI,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAA;QAC7D,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC9B,wBAAwB,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,8BAA8B,GAClC,KAAK,CAAC,OAAO,CAAiC,GAAG,EAAE;QACjD,OAAO,uBAAuB,CAAC,sCAAsC,CAAC;YACpE,YAAY,EAAE,UAAU,CAAC,QAAQ;YACjC,UAAU;YACV,aAAa;SACd,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAA;IAEtD,MAAM,8BAA8B,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACxD,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC;YACjC,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,EAAE,gCAAgC,EAAE,qBAAqB,EAAE,GAC/D,UAAU,CAAC,gBAAgB,CAAA;QAC7B,OAAO,uBAAuB,CAAC,6BAA6B,CAAC;YAC3D,aAAa,EAAE,IAAI;YACnB,gCAAgC;YAChC,qBAAqB;YACrB,YAAY,EAAE,UAAU,CAAC,QAAQ;YACjC,UAAU;SACX,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC,CAAA;IAElE,OAAO;QACL,qBAAqB;QACrB,8BAA8B;QAC9B,8BAA8B;KAC/B,CAAA;AACH,CAAC","sourcesContent":["import { Sentry } from '@oneblink/apps'\nimport { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport * as React from 'react'\nimport { conditionalLogicService } from '@oneblink/sdk-core'\n\nimport { FormElementsConditionallyShown } from '../types/form'\n\nexport default function useConditionalLogic(\n definition: FormTypes.Form,\n submission: SubmissionTypes.S3SubmissionData['submission'],\n) {\n const [conditionalLogicError, setConditionalLogicError] = React.useState<\n Error | undefined\n >()\n\n const errorCallback = React.useCallback((error: Error) => {\n console.warn('Error while checking conditional logic', error)\n Sentry.captureException(error)\n setConditionalLogicError(error)\n }, [])\n\n const formElementsConditionallyShown =\n React.useMemo<FormElementsConditionallyShown>(() => {\n return conditionalLogicService.generateFormElementsConditionallyShown({\n formElements: definition.elements,\n submission,\n errorCallback,\n })\n }, [definition.elements, submission, errorCallback])\n\n const submissionConditionallyEnabled = React.useMemo(() => {\n if (!definition.enableSubmission) {\n return true\n }\n const { requiresAllConditionalPredicates, conditionalPredicates } =\n definition.enableSubmission\n return conditionalLogicService.evaluateConditionalPredicates({\n isConditional: true,\n requiresAllConditionalPredicates,\n conditionalPredicates,\n formElements: definition.elements,\n submission,\n })\n }, [definition.elements, definition.enableSubmission, submission])\n\n return {\n conditionalLogicError,\n formElementsConditionallyShown,\n submissionConditionallyEnabled,\n }\n}\n"]}
1
+ {"version":3,"file":"useConditionalLogic.js","sourceRoot":"","sources":["../../src/hooks/useConditionalLogic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAG5D,OAAO,wBAAwB,MAAM,sCAAsC,CAAA;AAE3E,MAAM,CAAC,OAAO,UAAU,mBAAmB,CACzC,UAA0B,EAC1B,UAA0D;IAE1D,MAAM,CAAC,qBAAqB,EAAE,wBAAwB,CAAC,GAAG,KAAK,CAAC,QAAQ,EAErE,CAAA;IAEH,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,KAAY,EAAE,EAAE;QACvD,OAAO,CAAC,IAAI,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAA;QAC7D,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC9B,wBAAwB,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,8BAA8B,GAClC,KAAK,CAAC,OAAO,CAAiC,GAAG,EAAE;QACjD,OAAO,uBAAuB,CAAC,sCAAsC,CAAC;YACpE,YAAY,EAAE,UAAU,CAAC,QAAQ;YACjC,UAAU;YACV,aAAa;SACd,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAA;IAEtD,MAAM,8BAA8B,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACxD,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC;YACjC,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,EAAE,gCAAgC,EAAE,qBAAqB,EAAE,GAC/D,UAAU,CAAC,gBAAgB,CAAA;QAC7B,MAAM,EAAE,KAAK,EAAE,GAAG,wBAAwB,CACxC,UAAU,EACV,UAAU,CAAC,QAAQ,EACnB,8BAA8B,EAC9B,IAAI,CACL,CAAA;QACD,OAAO,uBAAuB,CAAC,6BAA6B,CAAC;YAC3D,aAAa,EAAE,IAAI;YACnB,gCAAgC;YAChC,qBAAqB;YACrB,YAAY,EAAE,UAAU,CAAC,QAAQ;YACjC,UAAU,EAAE,KAAK;SAClB,CAAC,CAAA;IACJ,CAAC,EAAE;QACD,UAAU,CAAC,QAAQ;QACnB,UAAU,CAAC,gBAAgB;QAC3B,8BAA8B;QAC9B,UAAU;KACX,CAAC,CAAA;IAEF,OAAO;QACL,qBAAqB;QACrB,8BAA8B;QAC9B,8BAA8B;KAC/B,CAAA;AACH,CAAC","sourcesContent":["import { Sentry } from '@oneblink/apps'\nimport { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport * as React from 'react'\nimport { conditionalLogicService } from '@oneblink/sdk-core'\n\nimport { FormElementsConditionallyShown } from '../types/form'\nimport cleanFormSubmissionModel from '../services/cleanFormSubmissionModel'\n\nexport default function useConditionalLogic(\n definition: FormTypes.Form,\n submission: SubmissionTypes.S3SubmissionData['submission'],\n) {\n const [conditionalLogicError, setConditionalLogicError] = React.useState<\n Error | undefined\n >()\n\n const errorCallback = React.useCallback((error: Error) => {\n console.warn('Error while checking conditional logic', error)\n Sentry.captureException(error)\n setConditionalLogicError(error)\n }, [])\n\n const formElementsConditionallyShown =\n React.useMemo<FormElementsConditionallyShown>(() => {\n return conditionalLogicService.generateFormElementsConditionallyShown({\n formElements: definition.elements,\n submission,\n errorCallback,\n })\n }, [definition.elements, submission, errorCallback])\n\n const submissionConditionallyEnabled = React.useMemo(() => {\n if (!definition.enableSubmission) {\n return true\n }\n const { requiresAllConditionalPredicates, conditionalPredicates } =\n definition.enableSubmission\n const { model } = cleanFormSubmissionModel(\n submission,\n definition.elements,\n formElementsConditionallyShown,\n true,\n )\n return conditionalLogicService.evaluateConditionalPredicates({\n isConditional: true,\n requiresAllConditionalPredicates,\n conditionalPredicates,\n formElements: definition.elements,\n submission: model,\n })\n }, [\n definition.elements,\n definition.enableSubmission,\n formElementsConditionallyShown,\n submission,\n ])\n\n return {\n conditionalLogicError,\n formElementsConditionallyShown,\n submissionConditionallyEnabled,\n }\n}\n"]}
@@ -0,0 +1,99 @@
1
+ import * as React from 'react';
2
+ import { authService } from '@oneblink/apps';
3
+ type MfaState = {
4
+ isExternalIdentityProviderUser: boolean;
5
+ isLoading: boolean;
6
+ isMfaEnabled: boolean;
7
+ loadingError?: Error;
8
+ isSettingUpMfa: boolean;
9
+ isDisablingMfa: boolean;
10
+ setupError?: Error;
11
+ mfaSetup?: Awaited<ReturnType<typeof authService.setupMfa>>;
12
+ };
13
+ export declare const MfaContext: React.Context<MfaState & {
14
+ beginMfaSetup: () => void;
15
+ cancelMfaSetup: () => void;
16
+ completeMfaSetup: () => void;
17
+ beginDisablingMfa: () => void;
18
+ cancelDisablingMfa: () => void;
19
+ completeDisablingMfa: () => void;
20
+ clearMfaSetupError: () => void;
21
+ loadMfa: () => void;
22
+ }>;
23
+ /**
24
+ * React Component that provides the context for the
25
+ * `useUserMeetsMfaRequirement()` hook and `<MultiFactorAuthentication />`
26
+ * component, to be used by components further down your component tree. **It
27
+ * should only be included in your component tree once and ideally at the root
28
+ * of the application.**
29
+ *
30
+ * #### Example
31
+ *
32
+ * ```js
33
+ * import * as React from 'react'
34
+ * import {
35
+ * MfaProvider,
36
+ * useUserMeetsMfaRequirement,
37
+ * } from '@oneblink/apps-react'
38
+ *
39
+ * function Component() {
40
+ * const { isLoading, userMeetsMfaRequirement } =
41
+ * useUserMeetsMfaRequirement(true)
42
+ * // use MFA Requirement details here
43
+ * }
44
+ *
45
+ * function App() {
46
+ * return (
47
+ * <MfaProvider isExternalIdentityProviderUser={false}>
48
+ * <Component />
49
+ * </MfaProvider>
50
+ * )
51
+ * }
52
+ *
53
+ * const root = document.getElementById('root')
54
+ * if (root) {
55
+ * ReactDOM.render(<App />, root)
56
+ * }
57
+ * ```
58
+ *
59
+ * @param props
60
+ * @returns
61
+ * @group Components
62
+ */
63
+ export declare function MfaProvider({ children, isExternalIdentityProviderUser, }: {
64
+ children: React.ReactNode;
65
+ isExternalIdentityProviderUser: boolean;
66
+ }): React.JSX.Element;
67
+ export default function useMfa(): MfaState & {
68
+ beginMfaSetup: () => void;
69
+ cancelMfaSetup: () => void;
70
+ completeMfaSetup: () => void;
71
+ beginDisablingMfa: () => void;
72
+ cancelDisablingMfa: () => void;
73
+ completeDisablingMfa: () => void;
74
+ clearMfaSetupError: () => void;
75
+ loadMfa: () => void;
76
+ };
77
+ /**
78
+ * React hook to check if the logged in user meets the MFA requirement of your
79
+ * application. Will throw an Error if used outside of the `<MfaProvider />`
80
+ * component.
81
+ *
82
+ * Example
83
+ *
84
+ * ```js
85
+ * import { useUserMeetsMfaRequirement } from '@oneblink/apps-react'
86
+ *
87
+ * const isMfaRequired = true
88
+ *
89
+ * function Component() {
90
+ * const userMeetsMfaRequirement =
91
+ * useUserMeetsMfaRequirement(isMfaRequired)
92
+ * }
93
+ * ```
94
+ *
95
+ * @returns
96
+ * @group Hooks
97
+ */
98
+ export declare function useUserMeetsMfaRequirement(isMfaRequired: boolean): boolean;
99
+ export {};