diff --git a/docs/userGuide/syntax/cardstacks.md b/docs/userGuide/syntax/cardstacks.md index e392fa304c..2f063ab4c5 100644 --- a/docs/userGuide/syntax/cardstacks.md +++ b/docs/userGuide/syntax/cardstacks.md @@ -14,7 +14,7 @@ Each `card` contains `tag` and `keyword` field: -The search feature searches the `card` components of `cardstack` by header, tags and keywords specified within each card component. +The search feature searches the `card` components of `cardstack` by header, body text, tags, and keywords specified within each card component. Specifying them can help improve searchability of the `cardstack` component! @@ -60,7 +60,7 @@ For example, if a card is about "Machine Learning," you might tag it as `AI` and As shown in the above example, - a `card` can be given a `header` attribute (optional). - tags can be added to cards using the `tag` attribute, which can then be used to filter cards. -- the `searchable` attribute can be used to make the Card Stack searchable based on tags and headers. +- the `searchable` attribute can be used to make the Card Stack searchable based on card content, tags, and headers. In the example given below, a Card Stack is used to show a list of questions and answers, by including `question` components inside `card` components. diff --git a/packages/core/src/Page/index.ts b/packages/core/src/Page/index.ts index e76a343a28..d32085a540 100644 --- a/packages/core/src/Page/index.ts +++ b/packages/core/src/Page/index.ts @@ -113,6 +113,10 @@ export class Page { * https://markbind.org/userGuide/makingTheSiteSearchable.html#keywords */ this.keywords = {}; + /** + * Normalized page body text used for search indexing. + */ + this.body = ''; /** * The title of the page. * This is initially set to the title specified in the site configuration, @@ -291,6 +295,13 @@ export class Page { */ collectHeadingsAndKeywords(pageContent: string) { this.collectHeadingsAndKeywordsInContent(pageContent, null, false, []); + this.collectBodyText(pageContent); + } + + collectBodyText(content: string) { + const $ = cheerio.load(content); + $('modal, panel, script, style, noscript').remove(); + this.body = $.root().text().replace(/\s+/g, ' ').trim(); } /** diff --git a/packages/vue-components/src/cardstack/Card.vue b/packages/vue-components/src/cardstack/Card.vue index ff82d48d23..881f5be571 100644 --- a/packages/vue-components/src/cardstack/Card.vue +++ b/packages/vue-components/src/cardstack/Card.vue @@ -11,7 +11,7 @@ -
+

@@ -61,6 +61,7 @@ export default { exposedTags: [], headerText: '', hasHeader: true, + bodyText: '', cardStack: null, }; }, @@ -125,7 +126,8 @@ export default { const matchesTags = this.computeTags.some(tag => selectedTags.includes(tag)); // Check if the card mateches the search terms - const searchTarget = (this.computeTags.join(' ') + this.keywords + this.headerText).toLowerCase(); + const searchTarget = (this.computeTags.join(' ') + + this.keywords + this.headerText + this.bodyText).toLowerCase(); const matchesSearch = searchTerms.length === 0 || searchTerms.every(term => searchTarget.toLowerCase().includes(term.toLowerCase())); @@ -141,6 +143,7 @@ export default { this.isMounted = true; this.headerText = this.computeHeaders; this.hasHeader = this.headerText !== ''; + this.bodyText = this.$refs.content?.innerText || ''; this.cardStack.updateRawTags(this.computeTags); this.cardStack.updateTagMapping(); diff --git a/packages/vue-components/src/cardstack/CardStack.vue b/packages/vue-components/src/cardstack/CardStack.vue index 7f537596d0..074d6d1bb1 100644 --- a/packages/vue-components/src/cardstack/CardStack.vue +++ b/packages/vue-components/src/cardstack/CardStack.vue @@ -119,7 +119,8 @@ function createCardStackRef(props) { const rawTags = child.computeTags; const keywords = child.computeKeywords; const header = child.headerText; - const searchTarget = rawTags.join(' ') + keywords + header; + const body = child.bodyText || ''; + const searchTarget = rawTags.join(' ') + keywords + header + body; primitiveMap.set(searchTarget, child); }); @@ -186,7 +187,7 @@ export default { if (child.disabled) return; const searchTerms = this.cardStackRef.searchTerms || []; const searchTarget = (child.computeTags.join(' ') - + child.keywords + child.headerText).toLowerCase(); + + child.keywords + child.headerText + (child.bodyText || '')).toLowerCase(); const matchesSearch = searchTerms.length === 0 || searchTerms.every(term => searchTarget.includes(term.toLowerCase())); if (matchesSearch) {