so these MDX body resets don't override its mb-0.5
- // ([&_p]:!mb-1.5 beats !mb-0.5 on specificity: class+element vs class).
- '[&_p:not([data-admonition-title])]:!mt-0 [&_p:not([data-admonition-title])]:!mb-1.5 [&_p:not([data-admonition-title]):last-child]:!mb-0',
- '[&_ul]:!my-1.5 [&_ol]:!my-1.5 [&_li]:!my-0.5',
- childProps?.description?.className
- )}
- >
+
{
expect(result).toStrictEqual('function')
})
+
+ it('flattens nested element children including icons without props.children', () => {
+ const value = [
+ 'See ',
+ {
+ props: {
+ children: [
+ 'pg_prewarm',
+ // lucide icons (and similar) are objects without useful text children
+ { $$typeof: Symbol.for('react.element'), type: 'svg', props: undefined },
+ ],
+ },
+ },
+ ]
+
+ const result = getAnchor(value)
+
+ expect(result).toStrictEqual('see-pgprewarm')
+ })
})
describe('when value is a string', () => {
diff --git a/packages/ui/src/components/CustomHTMLElements/CustomHTMLElements.utils.ts b/packages/ui/src/components/CustomHTMLElements/CustomHTMLElements.utils.ts
index 244210c945a..082e2a1c7f6 100644
--- a/packages/ui/src/components/CustomHTMLElements/CustomHTMLElements.utils.ts
+++ b/packages/ui/src/components/CustomHTMLElements/CustomHTMLElements.utils.ts
@@ -1,3 +1,20 @@
+/**
+ * Flatten React node trees to plain text for heading anchor ids.
+ * MDX headings often mix strings with inline nodes (`code`, links, icons);
+ * older code assumed every non-string had `props.children` and crashed when
+ * that was missing (e.g. lucide icons inside docs MDX links).
+ */
+const flattenNodeText = (node: unknown): string => {
+ if (node == null || typeof node === 'boolean') return ''
+ if (typeof node === 'string' || typeof node === 'number') return String(node)
+ if (Array.isArray(node)) return node.map(flattenNodeText).join('')
+ if (typeof node === 'object' && node !== null && 'props' in node) {
+ const props = (node as { props?: { children?: unknown } }).props
+ return flattenNodeText(props?.children)
+ }
+ return ''
+}
+
// Check if heading has custom anchor first, before forming the anchor based on the title
export const getAnchor = (text: any, { id }: { id?: string } = {}): string | undefined => {
if (id) {
@@ -15,27 +32,22 @@ export const getAnchor = (text: any, { id }: { id?: string } = {}): string | und
const formattedText = text
.map((x) => {
- if (typeof x !== 'string') {
- return x.props.children
+ if (typeof x === 'string') {
+ return x.trim()
}
-
- return x.trim()
+ return flattenNodeText(x)
})
.map((x) => {
if (typeof x !== 'string') {
return x
}
-
return slugify(x)
})
return formattedText.join('-').toLowerCase()
} else {
- const anchor = text.props.children
- if (typeof anchor === 'string') {
- return slugify(anchor)
- }
- return anchor
+ const flattened = flattenNodeText(text).trim()
+ return flattened ? slugify(flattened) : undefined
}
} else if (typeof text === 'string') {
if (hasCustomAnchor(text)) {
diff --git a/packages/ui/src/components/shadcn/ui/alert.tsx b/packages/ui/src/components/shadcn/ui/alert.tsx
index 56e00a1b8fc..e1590d9857e 100644
--- a/packages/ui/src/components/shadcn/ui/alert.tsx
+++ b/packages/ui/src/components/shadcn/ui/alert.tsx
@@ -31,40 +31,54 @@ const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
& VariantProps
>(({ className, variant, ...props }, ref) => (
-
+
))
Alert.displayName = 'Alert'
-const AlertTitle = React.forwardRef>(
- ({ className, ...props }, ref) =>
-)
-AlertTitle.displayName = 'AlertTitle'
-
-const AlertDescription = React.forwardRef<
+const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
->(({ className, children, ...props }, ref) => {
- // Automatically wrap primitive text nodes (string/number) in tags for semantic HTML
- const content =
- typeof children === 'string' || typeof children === 'number' ?
{children}
: children
+>(({ className, ...props }, ref) => (
+
+))
+AlertTitle.displayName = 'AlertTitle'
- return (
-
- {content}
-
- )
-})
+const AlertDescription = React.forwardRef>(
+ ({ className, children, ...props }, ref) => {
+ // Automatically wrap primitive text nodes (string/number) in tags for semantic HTML
+ const content =
+ typeof children === 'string' || typeof children === 'number' ?
{children}
: children
+
+ return (
+
+ {content}
+
+ )
+ }
+)
AlertDescription.displayName = 'AlertDescription'
export { Alert, AlertDescription, AlertTitle }