@adatechnology/conversations-ui 0.1.0-rc.7 → 0.1.0-rc.8
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.
|
@@ -394,6 +394,7 @@ function Lightbox({ imageUrl, caption, onClose, labels }) {
|
|
|
394
394
|
// src/InteractiveMessage.tsx
|
|
395
395
|
import { useState as useState3 } from "react";
|
|
396
396
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
397
|
+
var FORMATTING_CLASSES = "[&_strong]:font-bold [&_em]:italic [&_del]:line-through";
|
|
397
398
|
var DEFAULT_INTERACTIVE_MESSAGE_LABELS = {
|
|
398
399
|
openList: "Ver op\xE7\xF5es"
|
|
399
400
|
};
|
|
@@ -411,9 +412,18 @@ function InteractiveMessage({ payload, onSelect, labels, className }) {
|
|
|
411
412
|
const hasRows = collectRows(payload).length > 0;
|
|
412
413
|
const isInteractable = onSelect !== void 0;
|
|
413
414
|
return /* @__PURE__ */ jsxs5("div", { className: cn("flex flex-col gap-1", className), children: [
|
|
414
|
-
payload.header?.text ? /* @__PURE__ */ jsx7("p", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: payload.header.text }) : null,
|
|
415
|
-
payload.body?.text ? /* @__PURE__ */ jsx7(
|
|
416
|
-
|
|
415
|
+
payload.header?.text ? /* @__PURE__ */ jsx7("p", { className: cn("text-sm font-semibold text-gray-900 dark:text-gray-100", FORMATTING_CLASSES), children: parseWhatsAppFormatting(payload.header.text) }) : null,
|
|
416
|
+
payload.body?.text ? /* @__PURE__ */ jsx7(
|
|
417
|
+
"p",
|
|
418
|
+
{
|
|
419
|
+
className: cn(
|
|
420
|
+
"whitespace-pre-wrap break-words text-sm text-gray-900 dark:text-gray-100",
|
|
421
|
+
FORMATTING_CLASSES
|
|
422
|
+
),
|
|
423
|
+
children: parseWhatsAppFormatting(payload.body.text)
|
|
424
|
+
}
|
|
425
|
+
) : null,
|
|
426
|
+
payload.footer?.text ? /* @__PURE__ */ jsx7("p", { className: cn("text-xs text-gray-500 dark:text-gray-400", FORMATTING_CLASSES), children: parseWhatsAppFormatting(payload.footer.text) }) : null,
|
|
417
427
|
buttons.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "mt-1 flex flex-col gap-1 border-t border-gray-200 pt-1 dark:border-gray-700", children: buttons.map((button) => /* @__PURE__ */ jsx7(
|
|
418
428
|
"button",
|
|
419
429
|
{
|
package/dist/index.js
CHANGED
package/dist/preview/index.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
|
+
import { renderToStaticMarkup } from 'react-dom/server'
|
|
3
|
+
|
|
4
|
+
import { InteractiveMessage } from './InteractiveMessage'
|
|
5
|
+
import type { InteractivePayload } from './types'
|
|
6
|
+
|
|
7
|
+
function render(payload: InteractivePayload): string {
|
|
8
|
+
return renderToStaticMarkup(<InteractiveMessage payload={payload} />)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('InteractiveMessage', () => {
|
|
12
|
+
it('formata negrito no corpo, como o aparelho faz', () => {
|
|
13
|
+
const markup = render({ type: 'button', body: { text: 'Qual o *prazo desejado*?' } })
|
|
14
|
+
|
|
15
|
+
expect(markup).toContain('<strong>prazo desejado</strong>')
|
|
16
|
+
expect(markup).not.toContain('*prazo desejado*')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('formata cabeçalho e rodapé também', () => {
|
|
20
|
+
const markup = render({
|
|
21
|
+
type: 'list',
|
|
22
|
+
header: { text: '*Menu*' },
|
|
23
|
+
body: { text: 'corpo' },
|
|
24
|
+
footer: { text: '_Selecione_' },
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
expect(markup).toContain('<strong>Menu</strong>')
|
|
28
|
+
expect(markup).toContain('<em>Selecione</em>')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('deixa título de opção literal, porque o WhatsApp não formata ali', () => {
|
|
32
|
+
const markup = render({
|
|
33
|
+
type: 'button',
|
|
34
|
+
body: { text: 'corpo' },
|
|
35
|
+
action: { buttons: [{ reply: { id: 'sim', title: '*Sim*' } }] },
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
expect(markup).toContain('*Sim*')
|
|
39
|
+
expect(markup).not.toContain('<strong>Sim</strong>')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
import { useState } from 'react'
|
|
14
14
|
import type { InteractiveOption, InteractivePayload, InteractiveSelection } from './types'
|
|
15
15
|
import { cn } from './lib/cn'
|
|
16
|
+
import { parseWhatsAppFormatting } from './lib/whatsapp-formatting'
|
|
17
|
+
|
|
18
|
+
/** `parseWhatsAppFormatting` emite `<strong>`/`<em>`/`<del>`, que o reset do Tailwind zera. */
|
|
19
|
+
const FORMATTING_CLASSES = '[&_strong]:font-bold [&_em]:italic [&_del]:line-through'
|
|
16
20
|
|
|
17
21
|
export interface InteractiveMessageLabels {
|
|
18
22
|
/** Fallback do rótulo do botão que abre a lista, quando o payload não traz um. */
|
|
@@ -51,18 +55,31 @@ export function InteractiveMessage({ payload, onSelect, labels, className }: Int
|
|
|
51
55
|
|
|
52
56
|
return (
|
|
53
57
|
<div className={cn('flex flex-col gap-1', className)}>
|
|
58
|
+
{/* Cabeçalho, corpo e rodapé passam pela formatação do WhatsApp — o aparelho renderiza
|
|
59
|
+
`*negrito*` neles como em qualquer texto. Título e descrição de opção NÃO: ali o WhatsApp
|
|
60
|
+
mostra os asteriscos literais, e formatá-los aqui deixaria o simulador mais bonito que o
|
|
61
|
+
aparelho, que é o tipo de divergência que só aparece com o cliente na frente. */}
|
|
54
62
|
{payload.header?.text ? (
|
|
55
|
-
<p className=
|
|
63
|
+
<p className={cn('text-sm font-semibold text-gray-900 dark:text-gray-100', FORMATTING_CLASSES)}>
|
|
64
|
+
{parseWhatsAppFormatting(payload.header.text)}
|
|
65
|
+
</p>
|
|
56
66
|
) : null}
|
|
57
67
|
|
|
58
68
|
{payload.body?.text ? (
|
|
59
|
-
<p
|
|
60
|
-
{
|
|
69
|
+
<p
|
|
70
|
+
className={cn(
|
|
71
|
+
'whitespace-pre-wrap break-words text-sm text-gray-900 dark:text-gray-100',
|
|
72
|
+
FORMATTING_CLASSES,
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
{parseWhatsAppFormatting(payload.body.text)}
|
|
61
76
|
</p>
|
|
62
77
|
) : null}
|
|
63
78
|
|
|
64
79
|
{payload.footer?.text ? (
|
|
65
|
-
<p className=
|
|
80
|
+
<p className={cn('text-xs text-gray-500 dark:text-gray-400', FORMATTING_CLASSES)}>
|
|
81
|
+
{parseWhatsAppFormatting(payload.footer.text)}
|
|
82
|
+
</p>
|
|
66
83
|
) : null}
|
|
67
84
|
|
|
68
85
|
{buttons.length > 0 ? (
|