Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Add: Remove search result highlights with Escape key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Fix: Search highlights apply highlights when user is already on the resulting page.
14 changes: 11 additions & 3 deletions znai-reactjs/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import "./App.css";
import "./layout/DocumentationLayout.css";
import "./doc-elements/search/Search.css";

import React, { Component } from "react";
import React, { Component, useEffect } from "react";

import { ComponentViewer, DropDowns, Registries } from "react-component-viewer";
import { tabsDemo } from "./doc-elements/tabs/Tabs.demo";
Expand Down Expand Up @@ -243,9 +243,17 @@ window.znaiSearchIdx = createLocalSearchIndex();
populateLocalSearchIndexWithData(window.znaiSearchIdx, window.znaiSearchData);
registries
.add("end to end")
.registerAsMiniApp("full documentation navigation", /\/preview/, { root: "/preview" }, () => (
.registerAsMiniApp("test documentation page", /\/preview\/testpage/, { root: "/preview/testpage" }, () => (
<Documentation {...testDocumentation} />
));
))
.registerAsMiniApp("full documentation navigation", /\/preview/, { root: "/preview" }, () => {
useEffect(() => {
const pageId = documentationNavigation.currentPageLocation();
documentationNavigation.navigateToPage(pageId);
}, []);

return <Documentation {...testDocumentation} />;
});

const dropDowns = new DropDowns();
dropDowns.add("Theme").addItem("Default", "Alt 1").addItem("Dark", "Alt 2").onSelect(selectTheme);
Expand Down
22 changes: 16 additions & 6 deletions znai-reactjs/src/doc-elements/Documentation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export class Documentation extends Component {
page: Documentation.processPage(page),
toc: tableOfContents.toc,

searchResult: null,

// previous version put footer inside props
// we check props for backward compatibility with deployed docs
// should be safe to remove props.footer after October 2021
Expand Down Expand Up @@ -115,8 +117,6 @@ export class Documentation extends Component {
this.keyDownHandler = this.keyDownHandler.bind(this);
this.mouseClickHandler = this.mouseClickHandler.bind(this);

this.searchResult = null;

documentationNavigation.addUrlChangeListener(this.onUrlChange.bind(this));
}

Expand All @@ -139,12 +139,20 @@ export class Documentation extends Component {
}
}

removeSearchResult = () => {
this.setState({ searchResult: null });
};

componentDidUpdate(prevProps, prevState) {
const isTocItemChanged = !areTocItemEquals(this.state.page.tocItem, prevState.page.tocItem);

// reset searchResultId but only when navigating to a different page
if (this.searchResult && isTocItemChanged && !areTocItemEquals(this.state.page.tocItem, this.searchResult.id)) {
this.searchResult = null;
if (
this.state.searchResult &&
isTocItemChanged &&
!areTocItemEquals(this.state.page.tocItem, this.state.searchResult.id)
) {
this.removeSearchResult();
}
}

Expand All @@ -159,6 +167,7 @@ export class Documentation extends Component {
tocCollapsed,
isSearchActive,
pageGenError,
searchResult,
} = this.state;

const theme = this.theme;
Expand All @@ -179,7 +188,8 @@ export class Documentation extends Component {
const renderedPage = (
<elementsLibrary.Page
{...page}
searchResult={this.searchResult}
searchResult={searchResult}
removeSearchResult={this.removeSearchResult}
docMeta={docMeta}
onPresentationOpen={this.onPresentationOpen}
prevPageTocItem={this.prevPageTocItem}
Expand Down Expand Up @@ -525,7 +535,7 @@ export class Documentation extends Component {

onSearchSelection(query, id, snippetsToHighlight) {
this.onSearchClose();
this.searchResult = { id, snippetsToHighlight };
this.setState({ searchResult: { id, snippetsToHighlight } });
documentationTracking.onSearchResultSelect(query, id);
documentationNavigation.navigateToPage(id);
}
Expand Down
16 changes: 14 additions & 2 deletions znai-reactjs/src/doc-elements/default-elements/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,21 @@ interface Props extends DocElementProps {
}

function Section({ id, title, highlight, ...props }: Props) {
const className = "section" + (highlight ? " highlight" : "");
const [isHighlighted, setIsHighlighted] = React.useState(false);

React.useEffect(() => {
if (highlight) {
setIsHighlighted(true);
}
}, [highlight]);

const handleAnimationEnd = () => {
setIsHighlighted(false);
};

const className = "section" + (isHighlighted ? " highlight" : "");
return (
<div className={className} key={title}>
<div className={className} onAnimationEnd={handleAnimationEnd} key={title}>
<props.elementsLibrary.SectionTitle
level={1}
id={id}
Expand Down
1 change: 0 additions & 1 deletion znai-reactjs/src/doc-elements/images/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ interface MarkdownImageProps extends WithElementsLibrary {
}

const Image = ({ destination, title, inlined, fit, width = 0, height = 0, timestamp }: MarkdownImageProps) => {
console.log("@@ Image fit", fit);
return (
<AnnotatedImage
imageSrc={destination}
Expand Down
22 changes: 19 additions & 3 deletions znai-reactjs/src/doc-elements/page/default/DefaultPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ import { afterTitleId } from "../../../layout/classNamesAndIds";
import { DocElementProps } from "../../default-elements/DocElement";
import { SearchResultId } from "../../search/SearchResultId";
import { TocItem } from "../../../structure/TocItem";
import { highlightSearchResultAndMaybeScroll } from "../../search/searchResultHighlighter";
import { highlightSearchResultAndMaybeScroll, removeSearchHighlight } from "../../search/searchResultHighlighter";

interface Props extends DocElementProps {
tocItem: TocItem;
searchResult: { id: SearchResultId; snippetsToHighlight: string[] };
contentRootDom: HTMLElement;
removeSearchResult: () => void;
}

export function DefaultPageContent(props: Props) {
const { elementsLibrary, content, searchResult, tocItem, contentRootDom } = props;
const { elementsLibrary, content, searchResult, tocItem, contentRootDom, removeSearchResult } = props;
const { PageTitle } = elementsLibrary;

const searchResultId = searchResult?.id;
Expand All @@ -42,7 +43,22 @@ export function DefaultPageContent(props: Props) {
if (searchSnippetsToHighlight && isSearchResultOnThisPage && contentRootDom) {
highlightSearchResultAndMaybeScroll(contentRootDom, searchSnippetsToHighlight, false);
}
}, []);
}, [searchSnippetsToHighlight]);

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape" && isSearchResultOnThisPage) {
removeSearchHighlight(contentRootDom);
removeSearchResult();
event.stopPropagation();
}
};

window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [isSearchResultOnThisPage]);

const renderedSections = content!.map((section) => {
// @ts-ignore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ export function highlightSearchResultAndMaybeScroll(root: HTMLElement, snippets:
},
});
}

export function removeSearchHighlight(root: HTMLElement) {
const mark = new Mark(root);
mark.unmark({});
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright 2025 znai maintainers
* Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -14,60 +15,56 @@
* limitations under the License.
*/

import * as React from 'react'
import * as React from "react";

import DocumentationPreparation from './DocumentationPreparation'
import {socketUrl} from '../../utils/socket'
import DocumentationPreparation from "./DocumentationPreparation";
import { socketUrl } from "../../utils/socket";

import './DocumentationPreparationScreen.css'
import "./DocumentationPreparationScreen.css";

export class DocumentationPreparationScreen extends React.Component {
constructor(props) {
super(props)
this.state = props;
}
constructor(props) {
super(props);
this.state = props;
}

render() {
return (
<div className="documentation-preparation-screen">
<DocumentationPreparation {...this.state}/>
</div>
)
}
render() {
return (
<div className="documentation-preparation-screen">
<DocumentationPreparation {...this.state} />
</div>
);
}

componentDidMount() {
this._connect()
}
componentDidMount() {
this._connect();
}

componentWillUnmount() {
this._disconnect()
}
componentWillUnmount() {
this._disconnect();
}

_connect() {
this.ws = new WebSocket(socketUrl("_doc-update/" + this.props.docId))
_connect() {
this.ws = new WebSocket(socketUrl("_doc-update/" + this.props.docId));

this.ws.onopen = () => {
console.log('@@ open')
}
this.ws.onopen = () => {};

this.ws.onclose = () => {
console.log('@@ close')
}
this.ws.onclose = () => {};

this.ws.onmessage = (message) => {
const data = JSON.parse(message.data)
this._update(data)
};
}
this.ws.onmessage = (message) => {
const data = JSON.parse(message.data);
this._update(data);
};
}

_disconnect() {
this.ws.close()
}
_disconnect() {
this.ws.close();
}

_update({message, keyValues, progress}) {
this.setState({statusMessage: message, keyValues: keyValues || [], progressPercent: progress})
if (progress >= 100) {
setTimeout(() => window.location.reload(), 100)
}
_update({ message, keyValues, progress }) {
this.setState({ statusMessage: message, keyValues: keyValues || [], progressPercent: progress });
if (progress >= 100) {
setTimeout(() => window.location.reload(), 100);
}
}
}