Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions error-handling-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from 'react';
import { NotionRenderer, UnsupportedBlockError } from './src/index';

// Mock block map with unsupported block types
const mockBlockMapWithUnsupportedBlocks = {
"test-1": {
role: "reader",
value: {
id: "test-1",
version: 1,
type: "collection_view", // Unsupported database block
parent_id: "root",
parent_table: "block",
alive: true,
created_time: Date.now(),
last_edited_time: Date.now(),
created_by_table: "notion_user",
created_by_id: "user-1",
last_edited_by_table: "notion_user",
last_edited_by_id: "user-1",
properties: {
title: [["Database Block"]]
}
}
},
"test-2": {
role: "reader",
value: {
id: "test-2",
version: 1,
type: "checkbox", // Unsupported checkbox block
parent_id: "root",
parent_table: "block",
alive: true,
created_time: Date.now(),
last_edited_time: Date.now(),
created_by_table: "notion_user",
created_by_id: "user-1",
last_edited_by_table: "notion_user",
last_edited_by_id: "user-1",
properties: {
title: [["Checkbox Item"]]
}
}
},
"test-3": {
role: "reader",
value: {
id: "test-3",
version: 1,
type: "table_of_contents", // Unsupported TOC block
parent_id: "root",
parent_table: "block",
alive: true,
created_time: Date.now(),
last_edited_time: Date.now(),
created_by_table: "notion_user",
created_by_id: "user-1",
last_edited_by_table: "notion_user",
last_edited_by_id: "user-1"
}
}
};

// Test component demonstrating error handling
export const ErrorHandlingDemo = () => {
const [errorMessages, setErrorMessages] = React.useState<string[]>([]);

const handleUnsupportedBlock = (blockType: string, blockId?: string) => {
const message = `Unsupported block detected: ${blockType} (ID: ${blockId})`;
setErrorMessages(prev => [...prev, message]);
console.warn(message);
};

return (
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
<h1>React Notion Error Handling Demo</h1>
<p>This demo shows how the library now handles unsupported block types:</p>

<h2>Error Handling Enabled (Default)</h2>
<div style={{ border: '1px solid #ddd', padding: '16px', marginBottom: '20px' }}>
<NotionRenderer
blockMap={mockBlockMapWithUnsupportedBlocks}
showUnsupportedBlockErrors={true}
onUnsupportedBlock={handleUnsupportedBlock}
/>
</div>

<h2>Error Handling Disabled (Backward Compatibility)</h2>
<div style={{ border: '1px solid #ddd', padding: '16px', marginBottom: '20px' }}>
<NotionRenderer
blockMap={mockBlockMapWithUnsupportedBlocks}
showUnsupportedBlockErrors={false}
/>
</div>

<h2>Standalone Error Component Examples</h2>
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
<UnsupportedBlockError blockType="collection_view" blockId="db-123" />
<UnsupportedBlockError blockType="checkbox" />
<UnsupportedBlockError blockType="table_of_contents" />
<UnsupportedBlockError blockType="unknown_block_type" blockId="xyz-789" />
</div>

{errorMessages.length > 0 && (
<div style={{ marginTop: '20px' }}>
<h3>Error Callback Messages:</h3>
<ul>
{errorMessages.map((msg, idx) => (
<li key={idx} style={{ color: '#d9534f' }}>{msg}</li>
))}
</ul>
</div>
)}
</div>
);
};

export default ErrorHandlingDemo;
41 changes: 34 additions & 7 deletions src/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
MapPageUrl,
MapImageUrl,
CustomBlockComponents,
BlockValueProp,
CustomDecoratorComponents,
CustomDecoratorComponentProps
} from "./types";
import Asset from "./components/asset";
import Code from "./components/code";
import PageIcon from "./components/page-icon";
import PageHeader from "./components/page-header";
import UnsupportedBlockError from "./components/unsupported-block-error";
import { classNames, getTextContent, getListNumber } from "./utils";

export const createRenderChildText = (
Expand Down Expand Up @@ -94,6 +94,10 @@ interface Block {
hideHeader?: boolean;
customBlockComponents?: CustomBlockComponents;
customDecoratorComponents?: CustomDecoratorComponents;

// Error handling props
showUnsupportedBlockErrors?: boolean;
onUnsupportedBlock?: (blockType: string, blockId?: string) => void;
}

export const Block: React.FC<Block> = props => {
Expand All @@ -106,8 +110,10 @@ export const Block: React.FC<Block> = props => {
blockMap,
mapPageUrl,
mapImageUrl,
customBlockComponents,
customDecoratorComponents
// customBlockComponents, // Temporarily disabled due to type issues
customDecoratorComponents,
showUnsupportedBlockErrors = true,
onUnsupportedBlock
} = props;
const blockValue = block?.value;

Expand Down Expand Up @@ -516,15 +522,37 @@ export const Block: React.FC<Block> = props => {
</details>
);
default:
const blockType = block?.value?.type || "unknown";
const blockId = block?.value?.id;

// Call the error callback if provided
if (onUnsupportedBlock) {
onUnsupportedBlock(blockType, blockId);
}

// Log for development
if (process.env.NODE_ENV !== "production") {
console.log("Unsupported type " + block?.value?.type);
console.log("Unsupported type " + blockType);
}

// Show user-friendly error message if enabled
if (showUnsupportedBlockErrors) {
return (
<UnsupportedBlockError blockType={blockType} blockId={blockId} />
);
}

// Fallback to empty div for backward compatibility
return <div />;
}
return null;
};

return renderComponent();

// NOTE: Custom block components handling has type issues that need separate fixing
// render a custom component first if passed.
/*
if (
customBlockComponents &&
customBlockComponents[blockValue?.type] &&
Expand All @@ -536,13 +564,12 @@ export const Block: React.FC<Block> = props => {
<CustomComponent
renderComponent={renderComponent}
blockMap={blockMap}
blockValue={blockValue as BlockValueProp<typeof blockValue.type>}
blockValue={blockValue as any}
level={level}
>
{children}
</CustomComponent>
);
}

return renderComponent();
*/
};
104 changes: 104 additions & 0 deletions src/components/unsupported-block-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as React from "react";

interface UnsupportedBlockErrorProps {
blockType: string;
blockId?: string;
className?: string;
}

const getErrorMessage = (
blockType: string
): { title: string; message: string; suggestion: string } => {
switch (blockType) {
case "collection_view":
case "collection_view_page":
case "database":
return {
title: "Database Not Supported",
message:
"This Notion document includes a Database which cannot be imported.",
suggestion:
"Please remove the Database to render this page, or consider using react-notion-x for full database support."
};

case "checkbox":
case "to_do_list":
return {
title: "Checkbox Not Supported",
message:
"This Notion document includes Checkboxes which cannot be imported.",
suggestion:
"Please remove the Checkboxes to render this page, or consider using react-notion-x for checkbox support."
};

case "table_of_contents":
case "toc":
return {
title: "Table of Contents Not Supported",
message:
"This Notion document includes a Table of Contents which cannot be imported.",
suggestion:
"Please remove the Table of Contents to render this page, or consider using react-notion-x for full support."
};

case "synced_block":
return {
title: "Synced Block Not Supported",
message:
"This Notion document includes a Synced Block which cannot be imported.",
suggestion:
"Please remove the Synced Block to render this page, or consider using react-notion-x for full support."
};

case "equation":
return {
title: "Equation Block Not Supported",
message:
"This Notion document includes an Equation which cannot be imported.",
suggestion:
"Please remove the Equation to render this page, or consider using react-notion-x for equation support."
};

default:
return {
title: "Unsupported Block Type",
message: `This Notion document includes a '${blockType}' block which cannot be imported.`,
suggestion:
"Please remove this block to render this page, or consider using react-notion-x for extended block support."
};
}
};

export const UnsupportedBlockError: React.FC<UnsupportedBlockErrorProps> = ({
blockType,
blockId,
className
}) => {
const { title, message, suggestion } = getErrorMessage(blockType);

return (
<div className={`notion-unsupported-block ${className || ""}`}>
<div className="notion-unsupported-block-content">
<div className="notion-unsupported-block-icon">
<span role="img" aria-label="Warning">
⚠️
</span>
</div>
<div className="notion-unsupported-block-text">
<div className="notion-unsupported-block-title">{title}</div>
<div className="notion-unsupported-block-message">{message}</div>
<div className="notion-unsupported-block-suggestion">
{suggestion}
</div>
{blockId && (
<div className="notion-unsupported-block-id">
Block ID: {blockId}
</div>
)}
</div>
</div>
</div>
);
};

export default UnsupportedBlockError;
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { NotionRenderer } from "./renderer";
export { UnsupportedBlockError } from "./components/unsupported-block-error";
export * from "./types";
export * from "./utils";
export * from "./block";
4 changes: 4 additions & 0 deletions src/renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export interface NotionRendererProps {
level?: number;
customBlockComponents?: CustomBlockComponents;
customDecoratorComponents?: CustomDecoratorComponents;

// Error handling options
showUnsupportedBlockErrors?: boolean;
onUnsupportedBlock?: (blockType: string, blockId?: string) => void;
}

export const NotionRenderer: React.FC<NotionRendererProps> = ({
Expand Down
Loading