Skip to content

Commit 4ea3d1f

Browse files
authored
Merge pull request #9 from CrashBytes/contentful-richtext-support
Added support for contentful richtext comparison; updated storybooks
2 parents 44ad1b3 + 5393421 commit 4ea3d1f

File tree

9 files changed

+941
-17
lines changed

9 files changed

+941
-17
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Publish to NPM
22

33
on:
4-
# This workflow runs on every push to main and can also be triggered manually
4+
# This workflow runs on every merge to main and can also be triggered manually
55
push:
66
branches:
77
- main

README.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The Storybook provides interactive examples for all comparison scenarios. You ca
1818
- 🎯 **Precise Highlighting**: Only highlights the actual differences, not entire lines
1919
- 📝 **String Comparison**: Word-level diff for text content
2020
- 📋 **Array Comparison**: Item-level diff for arrays of strings
21+
- 📄 **Contentful Rich Text**: Compare Contentful documents with text or structure modes
2122
- 🎨 **Clean Visual Design**: Clear red/green highlighting for changes
2223
- 📱 **Responsive**: Works on desktop and mobile devices
2324
-**TypeScript Support**: Full TypeScript definitions included
@@ -78,16 +79,52 @@ const modifiedArray = [
7879
/>
7980
```
8081

82+
### Contentful Rich Text Comparison
83+
84+
The component now supports comparing Contentful Rich Text documents with two different modes:
85+
86+
```tsx
87+
import { Compare } from '@crashbytes/react-version-compare';
88+
import { Document } from '@contentful/rich-text-types';
89+
90+
// Text mode - extracts plain text for comparison
91+
<Compare
92+
original={contentfulDoc1}
93+
modified={contentfulDoc2}
94+
compareMode="text"
95+
viewMode="side-by-side"
96+
/>
97+
98+
// Structure mode - compares document structure
99+
<Compare
100+
original={contentfulDoc1}
101+
modified={contentfulDoc2}
102+
compareMode="structure"
103+
viewMode="side-by-side"
104+
/>
105+
```
106+
107+
**Structure Mode** preserves the semantic meaning of the document by comparing:
108+
- Headings (with levels)
109+
- Paragraphs
110+
- Lists and list items
111+
- Block quotes
112+
- And other rich text elements
113+
114+
**Text Mode** extracts plain text content and performs word-level comparison, similar to string comparison.
115+
81116
## API Reference
82117

83118
### Props
84119

85120
| Prop | Type | Default | Description |
86121
|------|------|---------|-------------|
87-
| `original` | `string \| string[]` | **required** | The original content |
88-
| `modified` | `string \| string[]` | **required** | The modified content |
122+
| `original` | `string \| string[] \| Document` | **required** | The original content |
123+
| `modified` | `string \| string[] \| Document` | **required** | The modified content |
89124
| `viewMode` | `'side-by-side' \| 'inline'` | `'side-by-side'` | How to display the comparison |
90125
| `className` | `string` | `''` | Custom CSS class name |
126+
| `caseSensitive` | `boolean` | `true` | Whether comparison is case sensitive |
127+
| `compareMode` | `'text' \| 'structure'` | `'text'` | Comparison mode for Contentful documents |
91128

92129
## Examples
93130

@@ -149,13 +186,16 @@ The component uses CSS classes that you can customize:
149186
- **Content management**: Show edits in articles or posts
150187
- **Data comparison**: Compare lists or arrays of items
151188
- **Translation work**: Compare original and translated text
189+
- **Contentful CMS**: Compare rich text content versions and track editorial changes
190+
- **Documentation**: Track changes in structured content with semantic meaning
152191

153192
## How It Works
154193

155194
- **String comparison**: Uses word-level diffing to identify precise changes
156195
- **Array comparison**: Compares items by position and content
196+
- **Contentful comparison**: Extracts plain text or compares structural elements
157197
- **Smart highlighting**: Only highlights actual differences, not entire lines
158-
- **Type safety**: Ensures both inputs are the same type (both strings or both arrays)
198+
- **Type safety**: Ensures both inputs are the same type (strings, arrays, or Contentful documents)
159199

160200
## License
161201

examples/ContentfulExamples.tsx

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import React from 'react';
2+
import { Compare } from '../src/components/Compare';
3+
import { Document, BLOCKS, MARKS } from '@contentful/rich-text-types';
4+
5+
// Sample Contentful documents for demonstration
6+
const sampleContentfulDoc1: Document = {
7+
nodeType: BLOCKS.DOCUMENT,
8+
data: {},
9+
content: [
10+
{
11+
nodeType: BLOCKS.HEADING_1,
12+
data: {},
13+
content: [
14+
{
15+
nodeType: 'text',
16+
value: 'Welcome to Our Blog',
17+
marks: [],
18+
data: {},
19+
},
20+
],
21+
},
22+
{
23+
nodeType: BLOCKS.PARAGRAPH,
24+
data: {},
25+
content: [
26+
{
27+
nodeType: 'text',
28+
value: 'This is a sample blog post with ',
29+
marks: [],
30+
data: {},
31+
},
32+
{
33+
nodeType: 'text',
34+
value: 'bold text',
35+
marks: [{ type: MARKS.BOLD }],
36+
data: {},
37+
},
38+
{
39+
nodeType: 'text',
40+
value: ' and some regular content.',
41+
marks: [],
42+
data: {},
43+
},
44+
],
45+
},
46+
{
47+
nodeType: BLOCKS.UL_LIST,
48+
data: {},
49+
content: [
50+
{
51+
nodeType: BLOCKS.LIST_ITEM,
52+
data: {},
53+
content: [
54+
{
55+
nodeType: BLOCKS.PARAGRAPH,
56+
data: {},
57+
content: [
58+
{
59+
nodeType: 'text',
60+
value: 'First item',
61+
marks: [],
62+
data: {},
63+
},
64+
],
65+
},
66+
],
67+
},
68+
{
69+
nodeType: BLOCKS.LIST_ITEM,
70+
data: {},
71+
content: [
72+
{
73+
nodeType: BLOCKS.PARAGRAPH,
74+
data: {},
75+
content: [
76+
{
77+
nodeType: 'text',
78+
value: 'Second item',
79+
marks: [],
80+
data: {},
81+
},
82+
],
83+
},
84+
],
85+
},
86+
],
87+
},
88+
],
89+
};
90+
91+
const sampleContentfulDoc2: Document = {
92+
nodeType: BLOCKS.DOCUMENT,
93+
data: {},
94+
content: [
95+
{
96+
nodeType: BLOCKS.HEADING_1,
97+
data: {},
98+
content: [
99+
{
100+
nodeType: 'text',
101+
value: 'Welcome to Our Updated Blog',
102+
marks: [],
103+
data: {},
104+
},
105+
],
106+
},
107+
{
108+
nodeType: BLOCKS.PARAGRAPH,
109+
data: {},
110+
content: [
111+
{
112+
nodeType: 'text',
113+
value: 'This is a sample blog post with ',
114+
marks: [],
115+
data: {},
116+
},
117+
{
118+
nodeType: 'text',
119+
value: 'bold text',
120+
marks: [{ type: MARKS.BOLD }],
121+
data: {},
122+
},
123+
{
124+
nodeType: 'text',
125+
value: ' and some ',
126+
marks: [],
127+
data: {},
128+
},
129+
{
130+
nodeType: 'text',
131+
value: 'updated',
132+
marks: [{ type: MARKS.ITALIC }],
133+
data: {},
134+
},
135+
{
136+
nodeType: 'text',
137+
value: ' content.',
138+
marks: [],
139+
data: {},
140+
},
141+
],
142+
},
143+
{
144+
nodeType: BLOCKS.UL_LIST,
145+
data: {},
146+
content: [
147+
{
148+
nodeType: BLOCKS.LIST_ITEM,
149+
data: {},
150+
content: [
151+
{
152+
nodeType: BLOCKS.PARAGRAPH,
153+
data: {},
154+
content: [
155+
{
156+
nodeType: 'text',
157+
value: 'First item',
158+
marks: [],
159+
data: {},
160+
},
161+
],
162+
},
163+
],
164+
},
165+
{
166+
nodeType: BLOCKS.LIST_ITEM,
167+
data: {},
168+
content: [
169+
{
170+
nodeType: BLOCKS.PARAGRAPH,
171+
data: {},
172+
content: [
173+
{
174+
nodeType: 'text',
175+
value: 'Modified second item',
176+
marks: [],
177+
data: {},
178+
},
179+
],
180+
},
181+
],
182+
},
183+
{
184+
nodeType: BLOCKS.LIST_ITEM,
185+
data: {},
186+
content: [
187+
{
188+
nodeType: BLOCKS.PARAGRAPH,
189+
data: {},
190+
content: [
191+
{
192+
nodeType: 'text',
193+
value: 'Third item (new)',
194+
marks: [],
195+
data: {},
196+
},
197+
],
198+
},
199+
],
200+
},
201+
],
202+
},
203+
],
204+
};
205+
206+
const ContentfulTextComparisonExample: React.FC = () => {
207+
return (
208+
<div style={{ margin: '20px' }}>
209+
<h2>Contentful Document Comparison - Text Mode</h2>
210+
<p>Comparing two Contentful rich text documents in text mode (plain text comparison):</p>
211+
212+
<Compare
213+
original={sampleContentfulDoc1}
214+
modified={sampleContentfulDoc2}
215+
compareMode="text"
216+
viewMode="side-by-side"
217+
/>
218+
</div>
219+
);
220+
};
221+
222+
const ContentfulStructureComparisonExample: React.FC = () => {
223+
return (
224+
<div style={{ margin: '20px' }}>
225+
<h2>Contentful Document Comparison - Structure Mode</h2>
226+
<p>Comparing two Contentful rich text documents in structure mode (preserving document structure):</p>
227+
228+
<Compare
229+
original={sampleContentfulDoc1}
230+
modified={sampleContentfulDoc2}
231+
compareMode="structure"
232+
viewMode="side-by-side"
233+
/>
234+
</div>
235+
);
236+
};
237+
238+
const ContentfulInlineComparisonExample: React.FC = () => {
239+
return (
240+
<div style={{ margin: '20px' }}>
241+
<h2>Contentful Document Comparison - Inline View</h2>
242+
<p>Comparing two Contentful rich text documents in inline view:</p>
243+
244+
<Compare
245+
original={sampleContentfulDoc1}
246+
modified={sampleContentfulDoc2}
247+
compareMode="structure"
248+
viewMode="inline"
249+
/>
250+
</div>
251+
);
252+
};
253+
254+
export { ContentfulTextComparisonExample, ContentfulStructureComparisonExample, ContentfulInlineComparisonExample };

examples/main.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,35 @@ import ComplexParagraphExample from './ComplexParagraphExample';
55
import ArrayExample from './ArrayExample';
66
import DisorderedArray from './DisorderedArray';
77
import CaseInsensitiveExample from './CaseInsensitiveExample';
8+
import {
9+
ContentfulTextComparisonExample,
10+
ContentfulStructureComparisonExample,
11+
ContentfulInlineComparisonExample
12+
} from './ContentfulExamples';
813

914
const App = () => (
10-
<div>
11-
<SimpleParagraphExample />
12-
<ComplexParagraphExample />
13-
<ArrayExample />
14-
<DisorderedArray />
15-
<CaseInsensitiveExample/>
15+
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
16+
<h1>React Version Compare Examples</h1>
17+
18+
<section>
19+
<h2>String Comparisons</h2>
20+
<SimpleParagraphExample />
21+
<ComplexParagraphExample />
22+
<CaseInsensitiveExample />
23+
</section>
24+
25+
<section>
26+
<h2>Array Comparisons</h2>
27+
<ArrayExample />
28+
<DisorderedArray />
29+
</section>
30+
31+
<section>
32+
<h2>Contentful Rich Text Comparisons</h2>
33+
<ContentfulTextComparisonExample />
34+
<ContentfulStructureComparisonExample />
35+
<ContentfulInlineComparisonExample />
36+
</section>
1637
</div>
1738
);
1839

0 commit comments

Comments
 (0)