@12-apps/payments-frontend 3.44.0 → 3.45.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/payments-frontend",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.45.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Browser half of the vendor-agnostic payments platform: plug-and-play MUI components for the per-provider settings page (credential form from each provider's schema, masked hints, verify/enable) and the checkout page (PIX QR + polling, card tokenization, hosted-checkout redirect), plus the headless hooks and fetch clients they build on. Talks only to the host's payments HTTP surface — never to a provider directly. Microfrontend-ready: no app coupling, host injects theme and auth.",
|
|
@@ -85,6 +85,12 @@ function OnlineOptionRow({ row, props }: { row: OnlineRow; props: RowProps }): J
|
|
|
85
85
|
function DoorOptionRow({ option, props }: { option: CheckoutDoorOption; props: RowProps }): JSX.Element {
|
|
86
86
|
const { choice } = props.state;
|
|
87
87
|
const chosen = choice?.kind === "door" && choice.id === option.id;
|
|
88
|
+
const choose = (): void => {
|
|
89
|
+
if (!chosen) props.state.choose({ kind: "door", id: option.id });
|
|
90
|
+
};
|
|
91
|
+
// A function content gets the row's own controls (FUT-3715): a chip inside
|
|
92
|
+
// it can choose the row in the tap that answers it.
|
|
93
|
+
const content = typeof option.content === "function" ? option.content({ chosen, choose }) : option.content;
|
|
88
94
|
return (
|
|
89
95
|
<OptionRow
|
|
90
96
|
name={props.name}
|
|
@@ -92,10 +98,11 @@ function DoorOptionRow({ option, props }: { option: CheckoutDoorOption; props: R
|
|
|
92
98
|
chosen={chosen}
|
|
93
99
|
label={option.label}
|
|
94
100
|
hint={chosen && option.hideHintWhileChosen ? undefined : option.hint}
|
|
95
|
-
onChoose={
|
|
101
|
+
onChoose={choose}
|
|
96
102
|
testId={`list-door-${option.id}`}
|
|
103
|
+
open={option.showContentWhileIdle === true}
|
|
97
104
|
>
|
|
98
|
-
{
|
|
105
|
+
{content}
|
|
99
106
|
</OptionRow>
|
|
100
107
|
);
|
|
101
108
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Radio } from "@mui/material";
|
|
1
|
+
import { Box, Radio, type SxProps } from "@mui/material";
|
|
2
2
|
import { alpha, type Theme } from "@mui/material/styles";
|
|
3
3
|
import type { JSX, ReactNode } from "react";
|
|
4
4
|
|
|
@@ -35,6 +35,48 @@ const FIELDS_ON_SURFACE = {
|
|
|
35
35
|
},
|
|
36
36
|
} as const;
|
|
37
37
|
|
|
38
|
+
/** The row's label and its muted hint, beside the radio. */
|
|
39
|
+
function RowWords({ label, hint }: { label: string; hint?: string }): JSX.Element {
|
|
40
|
+
const { Text } = useCheckoutComponents();
|
|
41
|
+
return (
|
|
42
|
+
<Box sx={{ display: "flex", flexDirection: "column", minWidth: 0, flex: 1 }}>
|
|
43
|
+
<Text variant="body" size="md" as="span">
|
|
44
|
+
{label}
|
|
45
|
+
</Text>
|
|
46
|
+
{hint ? (
|
|
47
|
+
<Text variant="body" size="sm" color="secondary" as="span">
|
|
48
|
+
{hint}
|
|
49
|
+
</Text>
|
|
50
|
+
) : null}
|
|
51
|
+
</Box>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** What opens inside a row, its fields standing on the surface. */
|
|
56
|
+
function RowContent({ testId, children }: { testId: string; children: ReactNode }): JSX.Element {
|
|
57
|
+
return (
|
|
58
|
+
<Box data-testid={`${testId}-content`} sx={{ display: "flex", flexDirection: "column", gap: 1.25, pb: 1.5, ...FIELDS_ON_SURFACE }}>
|
|
59
|
+
{children}
|
|
60
|
+
</Box>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The row's frame: tinted and outlined while chosen, dimmed while disabled. */
|
|
65
|
+
function rowFrameSx(chosen: boolean, disabled: boolean): SxProps<Theme> {
|
|
66
|
+
return {
|
|
67
|
+
border: "1px solid",
|
|
68
|
+
borderColor: chosen ? "primary.main" : "divider",
|
|
69
|
+
bgcolor: chosen ? chosenTint : "background.paper",
|
|
70
|
+
borderRadius: 2.5,
|
|
71
|
+
px: 1.5,
|
|
72
|
+
opacity: disabled ? 0.6 : 1,
|
|
73
|
+
// The keyboard's place in the group, drawn on the whole row (5.1 item 6)
|
|
74
|
+
// — for the row's own radio only: a field opened inside draws its own
|
|
75
|
+
// ring, and a second one around the row doubled it.
|
|
76
|
+
"&:has(> label input[type='radio']:focus-visible)": { outline: (t) => `${t.spacing(0.25)} solid ${t.palette.primary.main}`, outlineOffset: (t) => t.spacing(0.25) },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
38
80
|
export function OptionRow({
|
|
39
81
|
name,
|
|
40
82
|
value,
|
|
@@ -44,6 +86,7 @@ export function OptionRow({
|
|
|
44
86
|
hint,
|
|
45
87
|
onChoose,
|
|
46
88
|
testId,
|
|
89
|
+
open = false,
|
|
47
90
|
children,
|
|
48
91
|
}: {
|
|
49
92
|
/** The group's name, shared by every row. */
|
|
@@ -55,26 +98,16 @@ export function OptionRow({
|
|
|
55
98
|
hint?: string;
|
|
56
99
|
onChoose: () => void;
|
|
57
100
|
testId: string;
|
|
58
|
-
/**
|
|
101
|
+
/** Render `children` while NOT chosen too (a door option's `showContentWhileIdle`). */
|
|
102
|
+
open?: boolean;
|
|
103
|
+
/** Rendered only while chosen, unless `open`. */
|
|
59
104
|
children?: ReactNode;
|
|
60
105
|
}): JSX.Element {
|
|
61
|
-
const { Text } = useCheckoutComponents();
|
|
62
106
|
return (
|
|
63
107
|
<Box
|
|
64
108
|
data-testid={testId}
|
|
65
109
|
data-chosen={chosen ? "true" : "false"}
|
|
66
|
-
sx={
|
|
67
|
-
border: "1px solid",
|
|
68
|
-
borderColor: chosen ? "primary.main" : "divider",
|
|
69
|
-
bgcolor: chosen ? chosenTint : "background.paper",
|
|
70
|
-
borderRadius: 2.5,
|
|
71
|
-
px: 1.5,
|
|
72
|
-
opacity: disabled ? 0.6 : 1,
|
|
73
|
-
// The keyboard's place in the group, drawn on the whole row (5.1 item 6)
|
|
74
|
-
// — for the row's own radio only: a field opened inside draws its own
|
|
75
|
-
// ring, and a second one around the row doubled it.
|
|
76
|
-
"&:has(> label input[type='radio']:focus-visible)": { outline: (t) => `${t.spacing(0.25)} solid ${t.palette.primary.main}`, outlineOffset: (t) => t.spacing(0.25) },
|
|
77
|
-
}}
|
|
110
|
+
sx={rowFrameSx(chosen, disabled)}
|
|
78
111
|
>
|
|
79
112
|
<Box
|
|
80
113
|
component="label"
|
|
@@ -94,23 +127,10 @@ export function OptionRow({
|
|
|
94
127
|
sx={{ p: 0, flex: "none" }}
|
|
95
128
|
inputProps={{ "data-testid": `${testId}-radio` } as Record<string, string>}
|
|
96
129
|
/>
|
|
97
|
-
<
|
|
98
|
-
<Text variant="body" size="md" as="span">
|
|
99
|
-
{label}
|
|
100
|
-
</Text>
|
|
101
|
-
{hint ? (
|
|
102
|
-
<Text variant="body" size="sm" color="secondary" as="span">
|
|
103
|
-
{hint}
|
|
104
|
-
</Text>
|
|
105
|
-
) : null}
|
|
106
|
-
</Box>
|
|
130
|
+
<RowWords label={label} hint={hint} />
|
|
107
131
|
</Box>
|
|
108
132
|
{/* Only a row with something to open draws the box for it — no empty band. */}
|
|
109
|
-
{chosen && children ?
|
|
110
|
-
<Box data-testid={`${testId}-content`} sx={{ display: "flex", flexDirection: "column", gap: 1.25, pb: 1.5, ...FIELDS_ON_SURFACE }}>
|
|
111
|
-
{children}
|
|
112
|
-
</Box>
|
|
113
|
-
) : null}
|
|
133
|
+
{(chosen || open) && children ? <RowContent testId={testId}>{children}</RowContent> : null}
|
|
114
134
|
</Box>
|
|
115
135
|
);
|
|
116
136
|
}
|
|
@@ -20,6 +20,22 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { ReactNode } from "react";
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* What a door option's `content` function is handed (FUT-3715).
|
|
25
|
+
*
|
|
26
|
+
* `choose` is exactly what the row's radio does; calling it on the chosen row
|
|
27
|
+
* changes nothing. The list reports the choice through `onChoiceChange` as it
|
|
28
|
+
* does for the radio — from an effect, after the render — so a host that
|
|
29
|
+
* answers in the same tap keeps its answer across that render. The list never
|
|
30
|
+
* moves keyboard focus here: where focus goes is the host's call.
|
|
31
|
+
*/
|
|
32
|
+
export interface CheckoutDoorRowControls {
|
|
33
|
+
/** This option is the list's current choice. */
|
|
34
|
+
chosen: boolean;
|
|
35
|
+
/** Choose this option, as its radio would. */
|
|
36
|
+
choose: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
/**
|
|
24
40
|
* One of the host's ways to pay at the door — "Dinheiro", "Maquininha".
|
|
25
41
|
*
|
|
@@ -41,10 +57,25 @@ export interface CheckoutDoorOption {
|
|
|
41
57
|
hideHintWhileChosen?: boolean;
|
|
42
58
|
/**
|
|
43
59
|
* What opens INSIDE the row when it is chosen (the troco question, the
|
|
44
|
-
* tender chips, the brand lines). Rendered only while chosen
|
|
45
|
-
* indent: it starts where the row's own
|
|
60
|
+
* tender chips, the brand lines). Rendered only while chosen — unless
|
|
61
|
+
* `showContentWhileIdle` — with no left indent: it starts where the row's own
|
|
62
|
+
* content starts.
|
|
63
|
+
*
|
|
64
|
+
* As a function it is handed the row's own controls, so a control inside the
|
|
65
|
+
* content can choose the row in the same tap that answers it (the troco
|
|
66
|
+
* chips, FUT-3715). A plain node behaves exactly as before. The function is
|
|
67
|
+
* called during the row's render, not mounted as a component: keep it one
|
|
68
|
+
* shape for the option's life, and return a component of your own if the
|
|
69
|
+
* content needs hooks.
|
|
70
|
+
*/
|
|
71
|
+
content?: ReactNode | ((row: CheckoutDoorRowControls) => ReactNode);
|
|
72
|
+
/**
|
|
73
|
+
* Render `content` while the option is NOT chosen too — for an option whose
|
|
74
|
+
* answers are themselves the way to choose it (FUT-3715). The unchosen row
|
|
75
|
+
* keeps its unchosen frame; the content keeps its box and place in both
|
|
76
|
+
* states, so choosing the row moves nothing.
|
|
46
77
|
*/
|
|
47
|
-
|
|
78
|
+
showContentWhileIdle?: boolean;
|
|
48
79
|
/** The bar button's verb while this option is chosen — "Enviar pedido". */
|
|
49
80
|
action: string;
|
|
50
81
|
/** The amount beside the verb. Absent ⇒ the cart's own total. */
|
package/src/index.ts
CHANGED
|
@@ -56,7 +56,7 @@ export { PT_BR_CHECKOUT_PAYMENT_COPY } from './components/checkout-payment-pt-BR
|
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
export { CheckoutFlow, type CheckoutCartView, type CheckoutFlowProps } from './components/checkout/checkout-flow';
|
|
58
58
|
// The Pagamento step as ONE list (FUT-3591/3592) — opt-in via `CheckoutFlowProps.paymentList`.
|
|
59
|
-
export type { CheckoutDoorOption, CheckoutListChoice, CheckoutListHold, CheckoutOnlineChoice, CheckoutPaymentList, CheckoutPixBank, PaymentListCopy, PixBankCopy, PixBankId, PixBankName } from './components/checkout/list/types';
|
|
59
|
+
export type { CheckoutDoorOption, CheckoutDoorRowControls, CheckoutListChoice, CheckoutListHold, CheckoutOnlineChoice, CheckoutPaymentList, CheckoutPixBank, PaymentListCopy, PixBankCopy, PixBankId, PixBankName } from './components/checkout/list/types';
|
|
60
60
|
export { PT_BR_PAYMENT_LIST_COPY } from './components/checkout/list/pt-BR';
|
|
61
61
|
export type { BackActionEmphasis } from './components/checkout/payment-status-parts';
|
|
62
62
|
export type { CheckoutDeliverySummary, CheckoutSummaryLine } from './components/checkout/payment-summary';
|