Skip to content

Commit 1989331

Browse files
committed
Update documentation
1 parent f245677 commit 1989331

File tree

4 files changed

+165
-13
lines changed

4 files changed

+165
-13
lines changed

README.md

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,41 @@ Here is a table for the available options, usage questions, troubleshooting & gu
103103
### templateMiddleware (Function) [optional]
104104

105105
A function that will be called whenever a match in the template is found.
106-
It gets passed the current property name, property value, and the template.
106+
It gets passed the current property name, property value, template, query, and match information.
107107
If the function returns a non-undefined value, it gets replaced in the template.
108108

109-
This can be potentially useful for manipulating URLs etc.
109+
**New Interface:**
110+
```js
111+
templateMiddleware(prop, value, template, query?, matchInfo?)
112+
```
110113
111-
Example:
114+
- `prop`: The property name being processed from the JSON data.
115+
- `value`: The property value
116+
- `template`: The template string
117+
- `query`: The search query (optional)
118+
- `matchInfo`: Array of match information objects with start/end positions and match types (optional)
112119
120+
This can be useful for manipulating URLs, highlighting search terms, or custom formatting.
121+
122+
**Basic Example:**
113123
```js
114124
SimpleJekyllSearch({
115125
// ...other config
116-
templateMiddleware: function(prop, value, template) {
117-
if (prop === 'bar') {
118-
return value.replace(/^\//, '')
126+
searchResultTemplate: '<li>{title}</li>',
127+
templateMiddleware: function(prop, value, template, query, matchInfo) {
128+
if (prop === 'title') {
129+
return value.toUpperCase()
119130
}
120131
},
121132
})
122133
```
123134
135+
**How it works:**
136+
- Template: `'<li>{title}</li>'`
137+
- When processing `{title}`: `prop = 'title'`, `value = 'my post'` → returns `'MY POST'`
138+
- Final result: `'<li>MY POST</li>'`
139+
140+
124141
### sortMiddleware (Function) [optional]
125142
126143
A function that will be used to sort the filtered results.
@@ -139,3 +156,33 @@ SimpleJekyllSearch({
139156
},
140157
})
141158
```
159+
160+
### Built-in Highlight Middleware (Function) [optional]
161+
162+
Simple-Jekyll-Search now includes built-in highlighting functionality that can be easily integrated:
163+
164+
```js
165+
import { createHighlightTemplateMiddleware } from 'simple-jekyll-search/middleware';
166+
167+
SimpleJekyllSearch({
168+
// ...other config
169+
templateMiddleware: createHighlightTemplateMiddleware({
170+
className: 'search-highlight', // CSS class for highlighted text
171+
maxLength: 200, // Maximum length of highlighted content
172+
contextLength: 30 // Characters of context around matches
173+
}),
174+
})
175+
```
176+
177+
**Highlight Options:**
178+
- `className`: CSS class name for highlighted spans (default: 'search-highlight')
179+
- `maxLength`: Maximum length of content to display (truncates with ellipsis)
180+
- `contextLength`: Number of characters to show around matches when truncating
181+
182+
**CSS Styling:**
183+
```css
184+
.search-highlight {
185+
background-color: yellow;
186+
font-weight: bold;
187+
}
188+
```

docs/get-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Customize SimpleJekyllSearch by passing in your configuration options:
6565
searchResultTemplate: '<li><a href="{url}?query={query}" title="{desc}">{title}</a></li>',
6666
noResultsText: 'No results found',
6767
limit: 10,
68-
fuzzy: true,
68+
strategy: 'fuzzy',
6969
exclude: ['Welcome']
7070
})
7171
</script>

tests/Repository.test.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
import { Repository } from '../src/Repository';
3-
import { SearchResult } from '../src/utils/types';
43

54
interface TestElement {
65
title: string;
@@ -95,7 +94,7 @@ describe('Repository', () => {
9594
expect(repository.search('almostbar')).toEqual([]);
9695
});
9796

98-
it('excludes items from search #2', () => {
97+
it('sorts search results alphabetically by title', () => {
9998
repository.setOptions({
10099
sortMiddleware: (a: TestElement, b: TestElement) => {
101100
return a.title.localeCompare(b.title);
@@ -108,23 +107,90 @@ describe('Repository', () => {
108107
expect(results[2]).toMatchObject(loremElement);
109108
});
110109

110+
it('uses default NoSort when no sortMiddleware provided', () => {
111+
const results = repository.search('r');
112+
expect(results).toHaveLength(3);
113+
expect(results[0]).toMatchObject(barElement);
114+
expect(results[1]).toMatchObject(almostBarElement);
115+
expect(results[2]).toMatchObject(loremElement);
116+
});
117+
118+
it('demonstrates README example: custom sorting by section and caption', () => {
119+
const testData = [
120+
{ section: 'Getting Started', caption: 'Installation', title: 'How to install' },
121+
{ section: 'API Reference', caption: 'Methods', title: 'Available methods' },
122+
{ section: 'Getting Started', caption: 'Configuration', title: 'How to configure' },
123+
{ section: 'API Reference', caption: 'Properties', title: 'Object properties' }
124+
];
125+
126+
repository.put(testData);
127+
repository.setOptions({
128+
sortMiddleware: (a: any, b: any) => {
129+
const astr = String(a.section) + "-" + String(a.caption);
130+
const bstr = String(b.section) + "-" + String(b.caption);
131+
return astr.localeCompare(bstr);
132+
},
133+
});
134+
135+
const results = repository.search('How');
136+
expect(results).toHaveLength(2);
137+
// Should be sorted by section first, then caption
138+
expect(results[0].section).toBe('Getting Started');
139+
expect(results[0].caption).toBe('Configuration');
140+
expect(results[1].section).toBe('Getting Started');
141+
expect(results[1].caption).toBe('Installation');
142+
});
143+
111144
it('search results should be a clone and not a reference to repository data', () => {
112145
const query = 'Developer';
113-
repository.put(
146+
const testData = [
114147
{ name: 'Alice', role: 'Developer' },
115-
{ name: 'Bob', role: 'Designer' },
116-
);
148+
{ name: 'Bob', role: 'Designer' }
149+
];
150+
repository.put(testData);
117151

118152
const results = repository.search(query);
119153
expect(results).toHaveLength(1);
120154
expect(results[0]).toMatchObject({ name: 'Alice', role: 'Developer' });
121155

122-
(results as SearchResult[]).forEach(result => {
156+
(results as any[]).forEach(result => {
123157
result.role = 'Modified Role';
124158
});
125159

126160
const originalData = repository.search(query);
127161
expect(originalData).toHaveLength(1);
128162
expect(originalData[0]).toMatchObject({ name: 'Alice', role: 'Developer' });
129163
});
164+
165+
it('demonstrates README sortMiddleware example exactly', () => {
166+
// This test matches the exact example from the README
167+
const testData = [
168+
{ section: 'API Reference', caption: 'Properties', title: 'Object properties' },
169+
{ section: 'Getting Started', caption: 'Installation', title: 'How to install' },
170+
{ section: 'API Reference', caption: 'Methods', title: 'Available methods' },
171+
{ section: 'Getting Started', caption: 'Configuration', title: 'How to configure' }
172+
];
173+
174+
repository.put(testData);
175+
repository.setOptions({
176+
sortMiddleware: function(a: any, b: any) {
177+
var astr = String(a.section) + "-" + String(a.caption);
178+
var bstr = String(b.section) + "-" + String(b.caption);
179+
return astr.localeCompare(bstr);
180+
},
181+
});
182+
183+
const results = repository.search('a'); // Search for 'a' to get all results
184+
expect(results).toHaveLength(4);
185+
186+
// Should be sorted by section first, then caption alphabetically
187+
expect(results[0].section).toBe('API Reference');
188+
expect(results[0].caption).toBe('Methods');
189+
expect(results[1].section).toBe('API Reference');
190+
expect(results[1].caption).toBe('Properties');
191+
expect(results[2].section).toBe('Getting Started');
192+
expect(results[2].caption).toBe('Configuration');
193+
expect(results[3].section).toBe('Getting Started');
194+
expect(results[3].caption).toBe('Installation');
195+
});
130196
});

tests/Templater.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,43 @@ describe('Templater', () => {
137137
const compiled = templater.compile({ foo: 'bar' });
138138
expect(compiled).toBe('bar');
139139
});
140+
141+
it('demonstrates README example: uppercase title middleware', () => {
142+
templater.setOptions({
143+
template: '<li>{title}</li>',
144+
middleware(prop: string, value: string) {
145+
if (prop === 'title') {
146+
return value.toUpperCase();
147+
}
148+
return undefined
149+
}
150+
});
151+
152+
const data = { title: 'my post' };
153+
const compiled = templater.compile(data);
154+
expect(compiled).toBe('<li>MY POST</li>');
155+
});
156+
157+
it('demonstrates multiple property processing with different transformations', () => {
158+
templater.setOptions({
159+
template: '<li><a href="{url}">{title}</a><p>{desc}</p></li>',
160+
middleware(prop: string, value: string) {
161+
if (prop === 'url') {
162+
return value.replace(/^\//, ''); // Remove leading slash
163+
}
164+
if (prop === 'title') {
165+
return value.toUpperCase();
166+
}
167+
return undefined;
168+
}
169+
});
170+
171+
const data = {
172+
url: '/blog/post',
173+
title: 'my post',
174+
desc: 'description'
175+
};
176+
const compiled = templater.compile(data);
177+
expect(compiled).toBe('<li><a href="blog/post">MY POST</a><p>description</p></li>');
178+
});
140179
});

0 commit comments

Comments
 (0)