Skip to content

Commit 4c51dec

Browse files
authored
Merge pull request #3160 from Blargian/kb_tagged_search_mixed
KB: Improve KB search - search by title, description (tags)
2 parents 49cb829 + 9a4f7d2 commit 4c51dec

File tree

27 files changed

+2134
-49
lines changed

27 files changed

+2134
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ clickhouse-docs.code-workspace
2828
yarn.lock
2929
yarn.error-log
3030
.comments
31+
yarn-error.log
3132

3233
# Output files used by scripts to verify links
3334
sidebar_links.txt

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"build-api-doc": "node clickhouseapi.js",
1919
"build-swagger": "npx @redocly/cli build-docs https://api.clickhouse.cloud/v1 --output build/en/cloud/manage/api/swagger.html",
2020
"auto-generate-settings": "bash ./scripts/settings/autogenerate-settings.sh",
21-
"new-build": "yarn copy-clickhouse-repo-docs && yarn auto-generate-settings && yarn build-api-doc && yarn build && yarn build-swagger",
21+
"auto-generate-table-of-contents": "bash ./scripts/autogenerate-table-of-contents.sh",
22+
"new-build": "yarn copy-clickhouse-repo-docs && yarn auto-generate-settings && yarn auto-generate-table-of-contents && yarn build-api-doc && yarn build && yarn build-swagger",
2223
"start": "docusaurus start",
2324
"swizzle": "docusaurus swizzle",
2425
"write-heading-ids": "docusaurus write-heading-ids",
@@ -31,12 +32,14 @@
3132
"@docusaurus/theme-mermaid": "3.7.0",
3233
"@docusaurus/theme-search-algolia": "^3.7.0",
3334
"@mdx-js/react": "^3.1.0",
35+
"@yaireo/tagify": "^4.33.2",
3436
"@radix-ui/react-navigation-menu": "^1.2.4",
3537
"axios": "^1.7.9",
3638
"clsx": "^2.1.0",
3739
"docusaurus-plugin-sass": "^0.2.6",
3840
"esbuild": "^0.20.1",
3941
"esbuild-loader": "^4.0.3",
42+
"flexsearch": "^0.7.43",
4043
"gray-matter": "^4.0.3",
4144
"hast-util-is-element": "1.1.0",
4245
"http-proxy-middleware": "3.0.3",
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
3+
# This script is used for automatically generating any .json table of contents files
4+
# used for various things on the docs site, such as:
5+
# - landing pages
6+
# - indexing of the knowledgebase
7+
8+
# check if virtual environment exists
9+
if [ ! -d "venv" ]; then
10+
echo "Creating virtual environment..."
11+
python3 -m venv venv
12+
fi
13+
14+
source venv/bin/activate
15+
pip install -r scripts/table-of-contents-generator/requirements.txt
16+
17+
# Add runs of the script below for any table of contents files that need to be generated
18+
# You can run toc_gen.py --help for descriptions of the parameters
19+
python3 scripts/table-of-contents-generator/toc_gen.py --dir="knowledgebase" --single-toc --out="static" --ignore images
20+
21+
deactivate
22+
rm -r venv

scripts/settings/autogenerate-settings.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,4 @@ if [ -f ./clickhouse ]; then
8585
fi
8686

8787
echo "[$SCRIPT_NAME] Autogenerating settings completed"
88+

scripts/table-of-contents-generator/toc_gen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def write_to_file(json_items, directory, output=None):
9090
except OSError as e:
9191
if e.errno == 21:
9292
print(f"Directory already exists: {e}")
93-
else:
93+
elif e.errno != 17:
9494
print(f"An error occurred creating directory: {e}")
9595
def write_file(json_items, args, directory):
9696
print(args)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React, {useEffect} from 'react';
2+
import {useLocation} from '@docusaurus/router';
3+
import Link from '@docusaurus/Link';
4+
import styles from './styles.module.css'
5+
import HomeBreadcrumbItem from "@theme/DocBreadcrumbs/Items/Home";
6+
7+
function capitalizeFirstLetter(str) {
8+
9+
if (str === 'knowledgebase')
10+
return 'Knowledge Base'
11+
if (str.length === 0) {
12+
return str; // Handle empty string case
13+
}
14+
return str.charAt(0).toUpperCase() + str.slice(1);
15+
}
16+
17+
function pretty(str) {
18+
19+
if (str === 'knowledgebase')
20+
return 'Knowledge Base'
21+
22+
let spacedStr = str.replace(/[-_]/g, ' ');
23+
let words = spacedStr.split(' ');
24+
25+
let capitalizedWords = words.map(word => {
26+
// Handle already capitalized words e.g. TTL
27+
if (word === word.toUpperCase()) {
28+
return word; // Leave as is
29+
} else {
30+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); // convert other letters to lowercase
31+
}
32+
});
33+
34+
return capitalizedWords.join(' ');
35+
}
36+
37+
38+
const BlogBreadcrumbs = () => {
39+
40+
const location = useLocation();
41+
const location_paths = location.pathname.split("/")
42+
let cleaned_location_paths = location_paths.filter((path)=>{
43+
return !(path === '' || path === 'docs');
44+
})
45+
let accumulatedPath = '/docs/';
46+
47+
return(
48+
<div className={styles.BlogBreadcrumbsContainer}>
49+
<HomeBreadcrumbItem />
50+
<div className={styles.BlogBreadcrumbs}>
51+
{
52+
cleaned_location_paths.map((path, index)=>{
53+
accumulatedPath += path;
54+
const toPath = accumulatedPath;
55+
56+
if (index < cleaned_location_paths.length - 1) { // Check if it's not the last element
57+
accumulatedPath += '/'; // Add a slash if it's not the last element
58+
return (
59+
<div className={styles.breadCrumbLinkItem}>
60+
<Link className={styles.BreadcrumbLink} key={path} to={toPath}>
61+
{capitalizeFirstLetter(path)}
62+
</Link>
63+
<span className={styles.forwardSlash}>{"/"}</span>
64+
</div>
65+
);
66+
} else { // Last element
67+
return (
68+
<Link className={styles.BreadcrumbLinkBold} key={path} to={toPath}>
69+
{pretty(path)}
70+
</Link>
71+
);
72+
}
73+
})
74+
}
75+
</div>
76+
</div>
77+
)
78+
}
79+
80+
export default BlogBreadcrumbs
81+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
.BlogBreadcrumbsContainer {
2+
display: flex;
3+
margin-top: 32px;
4+
}
5+
6+
.BlogBreadcrumbs {
7+
font-size: 14px;
8+
margin-bottom: 16px;
9+
display: flex;
10+
}
11+
12+
.BreadcrumbLink {
13+
font-weight: 400;
14+
text-decoration: none;
15+
}
16+
17+
[data-theme='light'] .BreadcrumbLink {
18+
color: #8e8c8d !important;
19+
}
20+
21+
.BreadcrumbLink:hover {
22+
text-decoration: none;
23+
}
24+
25+
/* the link + "/" (which is in a span element) */
26+
.breadCrumbLinkItem {
27+
display: flex;
28+
}
29+
30+
.forwardSlash {
31+
margin-left: 10px;
32+
margin-right: 10px;
33+
}
34+
35+
[data-theme='dark'] .forwardSlash {
36+
color: #3c3c3c;
37+
}
38+
39+
[data-theme='light'] .forwardSlash {
40+
color: #8d8d8e;
41+
}
42+
43+
[data-theme='dark'] .BreadcrumbLink:hover {
44+
color: yellow !important;
45+
}
46+
47+
[data-theme='light'] .BreadcrumbLink:hover {
48+
color: #135BE6 !important;
49+
}
50+
51+
.BreadcrumbLinkBold {
52+
font-weight: 600;
53+
cursor: default;
54+
}
55+
56+
[data-theme='dark'] .BreadcrumbLinkBold {
57+
58+
color: white;
59+
60+
}
61+
62+
[data-theme='light'] .BreadcrumbLinkBold {
63+
color: black;
64+
}
65+
66+
.BreadcrumbLinkBold:hover {
67+
text-decoration: none;
68+
cursor: text;
69+
font-weight: 600;
70+
}
71+
72+
[data-theme='dark'] .BreadcrumbLinkBold:hover {
73+
color: white;
74+
}
75+
76+
[data-theme='light'] .BreadcrumbLinkBold:hover {
77+
color: black;
78+
}
79+
80+
[data-theme='dark'] .BlogBreadcrumbs .BreadcrumbLink {
81+
color: white;
82+
}
83+
84+
[data-theme='light'] .BlogBreadcrumbs .BreadcrumbLink {
85+
color: black;
86+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react';
2+
import { HTMLAttributes, ReactNode } from "react";
3+
import styles from './styles.module.css'
4+
5+
type ButtonGroupType = "default" | "borderless";
6+
7+
export interface ButtonGroupElementProps
8+
extends Omit<HTMLAttributes<HTMLButtonElement>, "children"> {
9+
value: string;
10+
label?: ReactNode;
11+
}
12+
13+
export interface ButtonGroupProps
14+
extends Omit<HTMLAttributes<HTMLDivElement>, "onClick"> {
15+
options: Array<ButtonGroupElementProps>;
16+
selected?: string;
17+
onClick?: (value: string) => void;
18+
fillWidth?: boolean;
19+
type?: ButtonGroupType;
20+
}
21+
22+
export const ButtonGroup = ({
23+
options,
24+
selected,
25+
fillWidth,
26+
onClick,
27+
type,
28+
...props
29+
}: ButtonGroupProps) => {
30+
const lastIndex = options.length - 1;
31+
const btns = options.map(({ value, label, ...rest }, index) => {
32+
const position: ButtonPosition =
33+
index === 0 ? "left" : index === lastIndex ? "right" : "center";
34+
const isActive = value === selected;
35+
36+
return (
37+
<button
38+
key={value}
39+
className={`${styles.button} ${isActive ? styles.active : ""} ${
40+
styles[position]
41+
} ${fillWidth ? styles.fillWidth : ""} ${
42+
type === "borderless" ? styles.borderless : ""
43+
}`}
44+
onClick={() => onClick?.(value)}
45+
role="button"
46+
aria-pressed={isActive}
47+
{...rest}
48+
>
49+
{label}
50+
</button>
51+
);
52+
});
53+
return (
54+
<div
55+
className={`${styles.buttonGroupWrapper} ${
56+
fillWidth ? styles.fillWidth : ""
57+
} ${type === "borderless" ? styles.borderless : ""}`}
58+
{...props}
59+
>
60+
{btns}
61+
</div>
62+
);
63+
};
64+
65+
type ButtonPosition = "left" | "center" | "right";
66+
67+
export default ButtonGroup;
68+

0 commit comments

Comments
 (0)