@simplybusiness/mobius 10.0.0 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/components/AddressLookup/AddressLookup.js +9 -4
- package/dist/cjs/components/AddressLookup/AddressLookup.js.map +2 -2
- package/dist/cjs/components/AddressLookup/index.js +9 -4
- package/dist/cjs/components/AddressLookup/index.js.map +2 -2
- package/dist/cjs/components/Checkbox/Checkbox.js +9 -4
- package/dist/cjs/components/Checkbox/Checkbox.js.map +2 -2
- package/dist/cjs/components/Checkbox/CheckboxGroup.js +9 -4
- package/dist/cjs/components/Checkbox/CheckboxGroup.js.map +2 -2
- package/dist/cjs/components/Checkbox/index.js +9 -4
- package/dist/cjs/components/Checkbox/index.js.map +2 -2
- package/dist/cjs/components/Combobox/Combobox.js +9 -4
- package/dist/cjs/components/Combobox/Combobox.js.map +2 -2
- package/dist/cjs/components/Combobox/index.js +9 -4
- package/dist/cjs/components/Combobox/index.js.map +2 -2
- package/dist/cjs/components/DateField/DateField.js +9 -4
- package/dist/cjs/components/DateField/DateField.js.map +2 -2
- package/dist/cjs/components/DateField/index.js +9 -4
- package/dist/cjs/components/DateField/index.js.map +2 -2
- package/dist/cjs/components/ErrorMessage/ErrorMessage.js +9 -4
- package/dist/cjs/components/ErrorMessage/ErrorMessage.js.map +2 -2
- package/dist/cjs/components/ErrorMessage/index.js +9 -4
- package/dist/cjs/components/ErrorMessage/index.js.map +2 -2
- package/dist/cjs/components/ExpandableText/ExpandableText.js +9 -4
- package/dist/cjs/components/ExpandableText/ExpandableText.js.map +2 -2
- package/dist/cjs/components/ExpandableText/index.js +9 -4
- package/dist/cjs/components/ExpandableText/index.js.map +2 -2
- package/dist/cjs/components/MaskedField/MaskedField.js +9 -4
- package/dist/cjs/components/MaskedField/MaskedField.js.map +2 -2
- package/dist/cjs/components/MaskedField/index.js +9 -4
- package/dist/cjs/components/MaskedField/index.js.map +2 -2
- package/dist/cjs/components/NumberField/NumberField.js +9 -4
- package/dist/cjs/components/NumberField/NumberField.js.map +2 -2
- package/dist/cjs/components/NumberField/index.js +9 -4
- package/dist/cjs/components/NumberField/index.js.map +2 -2
- package/dist/cjs/components/PasswordField/PasswordField.js +9 -4
- package/dist/cjs/components/PasswordField/PasswordField.js.map +2 -2
- package/dist/cjs/components/PasswordField/ShowHideButton.js +9 -4
- package/dist/cjs/components/PasswordField/ShowHideButton.js.map +2 -2
- package/dist/cjs/components/PasswordField/index.js +9 -4
- package/dist/cjs/components/PasswordField/index.js.map +2 -2
- package/dist/cjs/components/Radio/Radio.js +9 -4
- package/dist/cjs/components/Radio/Radio.js.map +2 -2
- package/dist/cjs/components/Radio/RadioGroup.js +9 -4
- package/dist/cjs/components/Radio/RadioGroup.js.map +2 -2
- package/dist/cjs/components/Radio/index.js +9 -4
- package/dist/cjs/components/Radio/index.js.map +2 -2
- package/dist/cjs/components/Select/Select.js +9 -4
- package/dist/cjs/components/Select/Select.js.map +2 -2
- package/dist/cjs/components/Select/index.js +9 -4
- package/dist/cjs/components/Select/index.js.map +2 -2
- package/dist/cjs/components/TextArea/TextArea.js +9 -4
- package/dist/cjs/components/TextArea/TextArea.js.map +2 -2
- package/dist/cjs/components/TextArea/index.js +9 -4
- package/dist/cjs/components/TextArea/index.js.map +2 -2
- package/dist/cjs/components/TextField/TextField.js +9 -4
- package/dist/cjs/components/TextField/TextField.js.map +2 -2
- package/dist/cjs/components/TextField/index.js +9 -4
- package/dist/cjs/components/TextField/index.js.map +2 -2
- package/dist/cjs/components/TextOrHTML/TextOrHTML.js +8 -3
- package/dist/cjs/components/TextOrHTML/TextOrHTML.js.map +2 -2
- package/dist/cjs/components/TextOrHTML/index.js +8 -3
- package/dist/cjs/components/TextOrHTML/index.js.map +2 -2
- package/dist/cjs/components/index.js +9 -4
- package/dist/cjs/components/index.js.map +2 -2
- package/dist/cjs/index.js +9 -4
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/meta.json +100 -100
- package/dist/esm/{chunk-ZU3XMAWQ.js → chunk-C4BILMFX.js} +2 -2
- package/dist/esm/{chunk-HHJ4Y5JQ.js → chunk-QRHDVVRK.js} +9 -4
- package/dist/esm/chunk-QRHDVVRK.js.map +7 -0
- package/dist/esm/components/AddressLookup/AddressLookup.js +2 -2
- package/dist/esm/components/AddressLookup/index.js +2 -2
- package/dist/esm/components/Checkbox/Checkbox.js +2 -2
- package/dist/esm/components/Checkbox/CheckboxGroup.js +2 -2
- package/dist/esm/components/Checkbox/index.js +2 -2
- package/dist/esm/components/Combobox/Combobox.js +2 -2
- package/dist/esm/components/Combobox/index.js +2 -2
- package/dist/esm/components/DateField/DateField.js +2 -2
- package/dist/esm/components/DateField/index.js +2 -2
- package/dist/esm/components/ErrorMessage/ErrorMessage.js +2 -2
- package/dist/esm/components/ErrorMessage/index.js +2 -2
- package/dist/esm/components/ExpandableText/ExpandableText.js +2 -2
- package/dist/esm/components/ExpandableText/index.js +2 -2
- package/dist/esm/components/MaskedField/MaskedField.js +2 -2
- package/dist/esm/components/MaskedField/index.js +2 -2
- package/dist/esm/components/NumberField/NumberField.js +2 -2
- package/dist/esm/components/NumberField/index.js +2 -2
- package/dist/esm/components/PasswordField/PasswordField.js +2 -2
- package/dist/esm/components/PasswordField/ShowHideButton.js +2 -2
- package/dist/esm/components/PasswordField/index.js +2 -2
- package/dist/esm/components/Radio/Radio.js +2 -2
- package/dist/esm/components/Radio/RadioGroup.js +2 -2
- package/dist/esm/components/Radio/index.js +2 -2
- package/dist/esm/components/Select/Select.js +2 -2
- package/dist/esm/components/Select/index.js +2 -2
- package/dist/esm/components/TextArea/TextArea.js +2 -2
- package/dist/esm/components/TextArea/index.js +2 -2
- package/dist/esm/components/TextField/TextField.js +2 -2
- package/dist/esm/components/TextField/index.js +2 -2
- package/dist/esm/components/TextOrHTML/TextOrHTML.js +1 -1
- package/dist/esm/components/TextOrHTML/index.js +1 -1
- package/dist/esm/components/index.js +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/meta.json +73 -73
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/components/TextOrHTML/TextOrHTML.d.ts +3 -1
- package/package.json +1 -1
- package/src/components/TextOrHTML/TextOrHTML.test.tsx +95 -0
- package/src/components/TextOrHTML/TextOrHTML.tsx +13 -3
- package/dist/esm/chunk-HHJ4Y5JQ.js.map +0 -7
- /package/dist/esm/{chunk-ZU3XMAWQ.js.map → chunk-C4BILMFX.js.map} +0 -0
|
@@ -10,9 +10,11 @@ export interface TextOrHTMLProps extends Omit<TextProps, "children">, RefAttribu
|
|
|
10
10
|
htmlElementType?: HTMLElementType;
|
|
11
11
|
/** If true, wraps the dangerous HTML element inside a Text component */
|
|
12
12
|
textWrapper?: boolean;
|
|
13
|
+
/** If true, auto-detects whether text is HTML or plain text to determine wrapping and element type */
|
|
14
|
+
autoDetect?: boolean;
|
|
13
15
|
}
|
|
14
16
|
declare const TextOrHTML: {
|
|
15
|
-
({ ref, text, htmlClassName, htmlElementType, textWrapper, ...textProps }: TextOrHTMLProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
({ ref, text, htmlClassName, htmlElementType, textWrapper, autoDetect, ...textProps }: TextOrHTMLProps): import("react/jsx-runtime").JSX.Element;
|
|
16
18
|
displayName: string;
|
|
17
19
|
};
|
|
18
20
|
export { TextOrHTML };
|
package/package.json
CHANGED
|
@@ -127,6 +127,101 @@ describe("TextOrHTML", () => {
|
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
describe("autoDetect prop", () => {
|
|
131
|
+
it("detects HTML content and renders as a div without Text wrapper", () => {
|
|
132
|
+
const { container } = render(
|
|
133
|
+
<TextOrHTML text="<strong>Bold text</strong>" autoDetect />,
|
|
134
|
+
);
|
|
135
|
+
expect(container.firstChild?.nodeName).toBe("DIV");
|
|
136
|
+
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
|
|
137
|
+
const div = container.querySelector("div");
|
|
138
|
+
expect(div?.innerHTML).toBe("<strong>Bold text</strong>");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("detects plain text and wraps in a Text component with a span", () => {
|
|
142
|
+
render(
|
|
143
|
+
<TextOrHTML text="Just plain text" autoDetect data-testid="test" />,
|
|
144
|
+
);
|
|
145
|
+
const element = screen.getByTestId("test");
|
|
146
|
+
expect(element).toHaveClass("mobius");
|
|
147
|
+
expect(element).toHaveClass(CLASS_NAME);
|
|
148
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
149
|
+
const span = element.querySelector("span");
|
|
150
|
+
expect(span?.innerHTML).toBe("Just plain text");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("detects HTML with leading whitespace", () => {
|
|
154
|
+
const { container } = render(
|
|
155
|
+
<TextOrHTML text=" <div>Indented HTML</div>" autoDetect />,
|
|
156
|
+
);
|
|
157
|
+
expect(container.firstChild?.nodeName).toBe("DIV");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("treats text not starting with a tag as plain text", () => {
|
|
161
|
+
render(<TextOrHTML text="Hello world" autoDetect data-testid="test" />);
|
|
162
|
+
expect(screen.getByTestId("test")).toHaveClass(CLASS_NAME);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("does not auto-detect when autoDetect is false", () => {
|
|
166
|
+
const { container } = render(<TextOrHTML text="Plain text" />);
|
|
167
|
+
// Without autoDetect, defaults to span with no Text wrapper
|
|
168
|
+
expect(container.firstChild?.nodeName).toBe("SPAN");
|
|
169
|
+
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
|
|
170
|
+
expect(container.querySelector(`.${CLASS_NAME}`)).toBeNull();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("respects explicit htmlElementType even with autoDetect", () => {
|
|
174
|
+
const { container } = render(
|
|
175
|
+
<TextOrHTML
|
|
176
|
+
htmlElementType="span"
|
|
177
|
+
text="<strong>HTML</strong>"
|
|
178
|
+
autoDetect
|
|
179
|
+
/>,
|
|
180
|
+
);
|
|
181
|
+
expect(container.firstChild?.nodeName).toBe("SPAN");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("wraps HTML in Text when both autoDetect and textWrapper are true", () => {
|
|
185
|
+
render(
|
|
186
|
+
<TextOrHTML
|
|
187
|
+
text="<strong>Bold</strong>"
|
|
188
|
+
htmlElementType="span"
|
|
189
|
+
autoDetect
|
|
190
|
+
textWrapper
|
|
191
|
+
data-testid="test"
|
|
192
|
+
/>,
|
|
193
|
+
);
|
|
194
|
+
const element = screen.getByTestId("test");
|
|
195
|
+
expect(element).toHaveClass(CLASS_NAME);
|
|
196
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
197
|
+
const span = element.querySelector("span");
|
|
198
|
+
expect(span?.innerHTML).toBe("<strong>Bold</strong>");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("passes className to Text wrapper when autoDetect wraps plain text", () => {
|
|
202
|
+
render(
|
|
203
|
+
<TextOrHTML
|
|
204
|
+
text="Plain text"
|
|
205
|
+
autoDetect
|
|
206
|
+
className="custom-class"
|
|
207
|
+
data-testid="test"
|
|
208
|
+
/>,
|
|
209
|
+
);
|
|
210
|
+
const element = screen.getByTestId("test");
|
|
211
|
+
expect(element).toHaveClass(CLASS_NAME);
|
|
212
|
+
expect(element).toHaveClass("custom-class");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("does not wrap in Text when autoDetect is off and textWrapper is omitted", () => {
|
|
216
|
+
const { container } = render(
|
|
217
|
+
<TextOrHTML text="Plain text, no wrapper" />,
|
|
218
|
+
);
|
|
219
|
+
expect(container.firstChild?.nodeName).toBe("SPAN");
|
|
220
|
+
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
|
|
221
|
+
expect(container.querySelector(`.${CLASS_NAME}`)).toBeNull();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
130
225
|
// See: https://github.com/facebook/react/issues/31660
|
|
131
226
|
describe("React 19 dangerouslySetInnerHTML regression", () => {
|
|
132
227
|
it("should not replace innerHTML on re-render when text prop hasn't changed", async () => {
|
|
@@ -5,6 +5,8 @@ import { Text } from "../Text/Text";
|
|
|
5
5
|
|
|
6
6
|
export type HTMLElementType = "span" | "div";
|
|
7
7
|
|
|
8
|
+
const isHTML = (text: string) => /^\s*<[a-z]/i.test(text);
|
|
9
|
+
|
|
8
10
|
export interface TextOrHTMLProps
|
|
9
11
|
extends Omit<TextProps, "children">, RefAttributes<TextElementType> {
|
|
10
12
|
/** HTML string to be rendered with dangerouslySetInnerHTML */
|
|
@@ -15,17 +17,25 @@ export interface TextOrHTMLProps
|
|
|
15
17
|
htmlElementType?: HTMLElementType;
|
|
16
18
|
/** If true, wraps the dangerous HTML element inside a Text component */
|
|
17
19
|
textWrapper?: boolean;
|
|
20
|
+
/** If true, auto-detects whether text is HTML or plain text to determine wrapping and element type */
|
|
21
|
+
autoDetect?: boolean;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
const TextOrHTML = ({
|
|
21
25
|
ref,
|
|
22
26
|
text,
|
|
23
27
|
htmlClassName,
|
|
24
|
-
htmlElementType
|
|
28
|
+
htmlElementType,
|
|
25
29
|
textWrapper = false,
|
|
30
|
+
autoDetect = false,
|
|
26
31
|
...textProps
|
|
27
32
|
}: TextOrHTMLProps) => {
|
|
28
|
-
const
|
|
33
|
+
const textIsHTML = autoDetect && isHTML(text);
|
|
34
|
+
const shouldWrapInText = autoDetect
|
|
35
|
+
? textWrapper || !textIsHTML
|
|
36
|
+
: textWrapper;
|
|
37
|
+
const resolvedElementType = htmlElementType ?? (textIsHTML ? "div" : "span");
|
|
38
|
+
const DangerousComponent = resolvedElementType;
|
|
29
39
|
|
|
30
40
|
// Memoize the dangerouslySetInnerHTML object to prevent unnecessary re-renders
|
|
31
41
|
// See: https://github.com/facebook/react/issues/31660
|
|
@@ -38,7 +48,7 @@ const TextOrHTML = ({
|
|
|
38
48
|
/>
|
|
39
49
|
);
|
|
40
50
|
|
|
41
|
-
if (
|
|
51
|
+
if (shouldWrapInText) {
|
|
42
52
|
return (
|
|
43
53
|
<Text ref={ref} {...textProps}>
|
|
44
54
|
{dangerousElement}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/components/TextOrHTML/TextOrHTML.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { RefAttributes } from \"react\";\nimport { useMemo } from \"react\";\nimport type { TextElementType, TextProps } from \"../Text/Text\";\nimport { Text } from \"../Text/Text\";\n\nexport type HTMLElementType = \"span\" | \"div\";\n\nexport interface TextOrHTMLProps\n extends Omit<TextProps, \"children\">, RefAttributes<TextElementType> {\n /** HTML string to be rendered with dangerouslySetInnerHTML */\n text: string;\n /** Custom class name for the dangerous HTML element */\n htmlClassName?: string;\n /** HTML element type for the dangerous HTML element */\n htmlElementType?: HTMLElementType;\n /** If true, wraps the dangerous HTML element inside a Text component */\n textWrapper?: boolean;\n}\n\nconst TextOrHTML = ({\n ref,\n text,\n htmlClassName,\n htmlElementType = \"span\",\n textWrapper = false,\n ...textProps\n}: TextOrHTMLProps) => {\n const DangerousComponent = htmlElementType;\n\n // Memoize the dangerouslySetInnerHTML object to prevent unnecessary re-renders\n // See: https://github.com/facebook/react/issues/31660\n const dangerousHTML = useMemo(() => ({ __html: text }), [text]);\n\n const dangerousElement = (\n <DangerousComponent\n className={htmlClassName}\n dangerouslySetInnerHTML={dangerousHTML}\n />\n );\n\n if (textWrapper) {\n return (\n <Text ref={ref} {...textProps}>\n {dangerousElement}\n </Text>\n );\n }\n\n return dangerousElement;\n};\n\nTextOrHTML.displayName = \"TextOrHTML\";\nexport { TextOrHTML };\n"],
|
|
5
|
-
"mappings": ";;;;;AACA,SAAS,eAAe;AAiCpB;AAfJ,IAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,GAAG;AACL,MAAuB;AACrB,QAAM,qBAAqB;AAI3B,QAAM,gBAAgB,QAAQ,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC;AAE9D,QAAM,mBACJ;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,MACX,yBAAyB;AAAA;AAAA,EAC3B;AAGF,MAAI,aAAa;AACf,WACE,oBAAC,QAAK,KAAW,GAAG,WACjB,4BACH;AAAA,EAEJ;AAEA,SAAO;AACT;AAEA,WAAW,cAAc;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
File without changes
|