Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: i18n for search context labels and force ignore noIndex #386

Merged
merged 2 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 29 additions & 28 deletions README.md

Large diffs are not rendered by default.

74 changes: 46 additions & 28 deletions docusaurus-search-local/src/client/theme/SearchBar/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ import {
indexDocs,
searchContextByPaths,
hideSearchBarWithNoSearchContext,
useAllContextsWithNoSearchContext,
} from "../../utils/proxiedGenerated";
import LoadingRing from "../LoadingRing/LoadingRing";

import styles from "./SearchBar.module.css";
import { normalizeContextByPath } from "../../utils/normalizeContextByPath";

async function fetchAutoCompleteJS(): Promise<any> {
const autoCompleteModule = await import("@easyops-cn/autocomplete.js");
Expand Down Expand Up @@ -62,6 +64,7 @@ export default function SearchBar({
const isBrowser = useIsBrowser();
const {
siteConfig: { baseUrl },
i18n: { currentLocale },
} = useDocusaurusContext();

// It returns undefined for non-docs pages
Expand Down Expand Up @@ -161,39 +164,51 @@ export default function SearchBar({
const a = document.createElement("a");
const params = new URLSearchParams();

const seeAllResultsText = translate({
id: "theme.SearchBar.seeAll",
message: "See all results",
});

const seeAllResultsOutsideContextText = translate(
{
id: "theme.SearchBar.seeAllOutsideContext",
message: "See results outside {context}",
},
{ context: searchContext }
);

const seeAllResultsInContextText = translate(
{
id: "theme.SearchBar.searchInContext",
message: "See all results in {context}",
},
{ context: searchContext }
);

params.set("q", query);

let linkText;
if (searchContext && isEmpty) {
linkText = seeAllResultsOutsideContextText;
} else if (searchContext) {
linkText = seeAllResultsInContextText;
if (searchContext) {
const detailedSearchContext =
searchContext && Array.isArray(searchContextByPaths)
? searchContextByPaths.find((item) =>
typeof item === "string"
? item === searchContext
: item.path === searchContext
)
: searchContext;
const translatedSearchContext = detailedSearchContext
? normalizeContextByPath(detailedSearchContext, currentLocale).label
: searchContext;

if (useAllContextsWithNoSearchContext && isEmpty) {
linkText = translate(
{
id: "theme.SearchBar.seeAllOutsideContext",
message: "See results outside {context}",
},
{ context: translatedSearchContext }
);
} else {
linkText = translate(
{
id: "theme.SearchBar.searchInContext",
message: "See all results in {context}",
},
{ context: translatedSearchContext }
);
}
} else {
linkText = seeAllResultsText;
linkText = translate({
id: "theme.SearchBar.seeAll",
message: "See all results",
});
}

if (Array.isArray(searchContextByPaths) && !isEmpty) {
if (
searchContext &&
Array.isArray(searchContextByPaths) &&
(!useAllContextsWithNoSearchContext || !isEmpty)
) {
params.set("ctx", searchContext);
}

Expand Down Expand Up @@ -250,7 +265,10 @@ export default function SearchBar({
suggestion: SuggestionTemplate,
empty: EmptyTemplate,
footer: ({ query, isEmpty }: any) => {
if (isEmpty && !searchContext) {
if (
isEmpty &&
(!searchContext || !useAllContextsWithNoSearchContext)
) {
return;
}
const a = searchFooterLinkElement({ query, isEmpty });
Expand Down
37 changes: 18 additions & 19 deletions docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "../../utils/proxiedGenerated";

import styles from "./SearchPage.module.css";
import { normalizeContextByPath } from "../../utils/normalizeContextByPath";

export default function SearchPage(): React.ReactElement {
return (
Expand All @@ -35,6 +36,7 @@ export default function SearchPage(): React.ReactElement {
function SearchPageContent(): React.ReactElement {
const {
siteConfig: { baseUrl },
i18n: { currentLocale },
} = useDocusaurusContext();

const { selectMessage } = usePluralForm();
Expand Down Expand Up @@ -106,10 +108,10 @@ function SearchPageContent(): React.ReactElement {

useEffect(() => {
async function doFetchIndexes() {
const { wrappedIndexes, zhDictionary } = await fetchIndexes(
versionUrl,
searchContext
);
const { wrappedIndexes, zhDictionary } =
searchContext || useAllContextsWithNoSearchContext
? await fetchIndexes(versionUrl, searchContext)
: { wrappedIndexes: [], zhDictionary: [] };
setSearchSource(() =>
SearchSourceFactory(wrappedIndexes, zhDictionary, 100)
);
Expand Down Expand Up @@ -166,22 +168,19 @@ function SearchPageContent(): React.ReactElement {
value={searchContext}
onChange={(e) => updateSearchContext(e.target.value)}
>
<option value="">
{useAllContextsWithNoSearchContext
? translate({
id: "theme.SearchPage.searchContext.everywhere",
message: "everywhere",
})
: ""}
</option>
{useAllContextsWithNoSearchContext && (
<option value="">
{translate({
id: "theme.SearchPage.searchContext.everywhere",
message: "everywhere",
})}
</option>
)}
{searchContextByPaths.map((context) => {
let label: string;
let path: string;
if (typeof context === "string") {
label = path = context;
} else {
({ label, path } = context);
}
const { label, path } = normalizeContextByPath(
context,
currentLocale
);
return (
<option key={path} value={path}>
{label}
Expand Down
18 changes: 14 additions & 4 deletions docusaurus-search-local/src/client/theme/hooks/useSearchQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { useHistory, useLocation } from "@docusaurus/router";
import useIsBrowser from "@docusaurus/useIsBrowser";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { searchContextByPaths } from "../../utils/proxiedGenerated";

const SEARCH_PARAM_QUERY = "q";
const SEARCH_PARAM_CONTEXT = "ctx";
Expand All @@ -34,21 +35,30 @@ function useSearchQuery(): any {
searchParams.delete(SEARCH_PARAM_QUERY);
}
return searchParams;
}
};

return {
searchValue,
searchContext,
searchContext:
searchContext &&
Array.isArray(searchContextByPaths) &&
searchContextByPaths.some((item) =>
typeof item === "string"
? item === searchContext
: item.path === searchContext
)
? searchContext
: "",
searchVersion,
updateSearchPath: (searchValue: string) => {
const searchParams = getSearchParams(searchValue);
history.replace({
search: searchParams.toString(),
});
},
updateSearchContext: (searchContext: string) => {
updateSearchContext: (value: string) => {
const searchParams = new URLSearchParams(location.search);
searchParams.set(SEARCH_PARAM_CONTEXT, searchContext);
searchParams.set(SEARCH_PARAM_CONTEXT, value);
history.replace({
search: searchParams.toString(),
});
Expand Down
36 changes: 36 additions & 0 deletions docusaurus-search-local/src/client/utils/normalizeContextByPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
interface DetailedItem {
label: string | Record<string, string>;
path: string;
}

interface NormalizedItem {
path: string;
label: string;
}

export function normalizeContextByPath(
context: string | DetailedItem,
currentLocale: string
): NormalizedItem {
if (typeof context === "string") {
return {
label: context,
path: context,
};
} else {
const { label, path } = context;
if (typeof label === "string") {
return { label, path };
}
if (Object.prototype.hasOwnProperty.call(label, currentLocale)) {
return {
label: label[currentLocale],
path,
};
}
return {
label: path,
path,
};
}
}
3 changes: 2 additions & 1 deletion docusaurus-search-local/src/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ declare module "*/generated.js" {
export const indexDocs: boolean;
export const searchContextByPaths: (
| string
| { label: string; path: string }
| { label: string | Record<string, string>; path: string }
)[];
export const hideSearchBarWithNoSearchContext: boolean;
export const useAllContextsWithNoSearchContext: boolean;
export const forceIgnoreNoIndex: boolean;
// These below are for mocking only.
export const __setLanguage: (value: string[]) => void;
export const __setRemoveDefaultStopWordFilter: (value: boolean) => void;
Expand Down
12 changes: 11 additions & 1 deletion docusaurus-search-local/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,10 @@ export interface PluginOptions {
* Provide an list of sub-paths as separate search context, E.g.: `["docs", "community", "legacy/resources"]`.
* It will create multiple search indexes by these paths.
*/
searchContextByPaths?: (string | { label: string; path: string })[];
searchContextByPaths?: (
| string
| { label: string | Record<string, string>; path: string }
)[];

/**
* Whether to hide the search bar when no search context was matched.
Expand All @@ -189,4 +192,11 @@ export interface PluginOptions {
* @default false
*/
useAllContextsWithNoSearchContext?: boolean;

/**
* Force enable search index even if noIndex: true is set, this also affects unlisted articles.
*
* @default false
*/
forceIgnoreNoIndex?: boolean;
}
4 changes: 2 additions & 2 deletions docusaurus-search-local/src/server/utils/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ export function parse(
html: string,
type: "docs" | "blog" | "page",
url: string,
{ ignoreCssSelectors }: ProcessedPluginOptions
{ ignoreCssSelectors, forceIgnoreNoIndex }: ProcessedPluginOptions
): ParsedDocument | null {
const $ = cheerio.load(html);

const robotsMeta = $('meta[name="robots"]');
if (robotsMeta.attr("content")?.includes("noindex")) {
if (!forceIgnoreNoIndex && robotsMeta.attr("content")?.includes("noindex")) {
// Unlisted content
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe("validateOptions", () => {
searchBarShortcut: true,
searchBarShortcutHint: true,
searchBarPosition: "auto",
forceIgnoreNoIndex: false,
},
],
[
Expand Down Expand Up @@ -83,6 +84,7 @@ describe("validateOptions", () => {
searchBarShortcut: true,
searchBarShortcutHint: true,
searchBarPosition: "auto",
forceIgnoreNoIndex: false,
},
],
[
Expand Down Expand Up @@ -110,6 +112,7 @@ describe("validateOptions", () => {
searchBarShortcut: true,
searchBarShortcutHint: true,
searchBarPosition: "auto",
forceIgnoreNoIndex: false,
},
],
[
Expand Down Expand Up @@ -137,6 +140,7 @@ describe("validateOptions", () => {
searchBarShortcut: true,
searchBarShortcutHint: true,
searchBarPosition: "auto",
forceIgnoreNoIndex: false,
},
],
[
Expand All @@ -151,6 +155,7 @@ describe("validateOptions", () => {
explicitSearchResultPath: false,
searchResultContextMaxLength: 30,
searchBarShortcut: false,
forceIgnoreNoIndex: true,
},
{
blogRouteBasePath: ["blog"],
Expand All @@ -175,6 +180,7 @@ describe("validateOptions", () => {
searchBarShortcut: false,
searchBarShortcutHint: true,
searchBarPosition: "auto",
forceIgnoreNoIndex: true,
},
],
[
Expand Down Expand Up @@ -207,6 +213,7 @@ describe("validateOptions", () => {
searchBarShortcut: true,
searchBarShortcutHint: false,
searchBarPosition: "auto",
forceIgnoreNoIndex: false,
},
],
[
Expand Down Expand Up @@ -245,6 +252,7 @@ describe("validateOptions", () => {
searchBarPosition: "left",
docsPluginIdForPreferredVersion: "product",
searchContextByPaths: ["docs", "community"],
forceIgnoreNoIndex: false,
},
],
[
Expand Down Expand Up @@ -282,6 +290,7 @@ describe("validateOptions", () => {
searchBarPosition: "left",
docsPluginIdForPreferredVersion: "product",
searchContextByPaths: ["docs", "community"],
forceIgnoreNoIndex: false,
},
],
])("validateOptions(...) should work", (options, config) => {
Expand Down