From 8fb69a8ee200d56f7b0e84f4b59aad68b0026c5a Mon Sep 17 00:00:00 2001 From: MykolaGolubyev Date: Sun, 14 Sep 2025 22:18:54 -0400 Subject: [PATCH 1/2] spoiler: handle text highlight from slack --- .../src/doc-elements/spoiler/Spoiler.css | 5 +- .../src/doc-elements/spoiler/Spoiler.demo.jsx | 125 ---------------- .../src/doc-elements/spoiler/Spoiler.demo.tsx | 134 ++++++++++++++++++ .../src/doc-elements/spoiler/Spoiler.jsx | 52 ------- .../src/doc-elements/spoiler/Spoiler.tsx | 56 ++++++++ .../src/doc-elements/testDocumentation.jsx | 56 ++++++++ .../text-selection/textHighlighter.css | 4 +- 7 files changed, 249 insertions(+), 183 deletions(-) delete mode 100644 znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.jsx create mode 100644 znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.tsx delete mode 100644 znai-reactjs/src/doc-elements/spoiler/Spoiler.jsx create mode 100644 znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx diff --git a/znai-reactjs/src/doc-elements/spoiler/Spoiler.css b/znai-reactjs/src/doc-elements/spoiler/Spoiler.css index 596c6985f..f148b160f 100644 --- a/znai-reactjs/src/doc-elements/spoiler/Spoiler.css +++ b/znai-reactjs/src/doc-elements/spoiler/Spoiler.css @@ -17,6 +17,7 @@ .znai-spoiler { display: grid; + background-color: var(--znai-spoiler-background-color); border: solid 1px var(--znai-spoiler-border-color); cursor: pointer; @@ -25,10 +26,6 @@ margin-bottom: 16px; } -.znai-spoiler { - background-color: var(--znai-spoiler-background-color); -} - .znai-spoiler-content { visibility: hidden; grid-column: 1; diff --git a/znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.jsx b/znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.jsx deleted file mode 100644 index c2d96f483..000000000 --- a/znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.jsx +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2019 TWO SIGMA OPEN SOURCE, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as React from 'react' -import {Spoiler} from './Spoiler' -import {elementsLibrary} from '../DefaultElementsLibrary' - -export function spoilerDemo(registry) { - registry - .add('regular text', () => - surroundWithText( - )) - - .add('regular text small box', () => -
- {surroundWithText( - ) - } -
) - - .add('code snippet', () => surroundWithText()) - .add('code snippet with bullets', () => surroundWithText()) - .add('tabs', () => surroundWithText()) -} - -function surroundWithText(rendered) { - return ( - - - {rendered} - - - ) -} - -function paragraphContent() { - return [ - { - "type": "Paragraph", - "content": [ - { - "text": "line of text line of text line of text line of text line of text line of text line of text " + - "line of text line of text line of text line of text", - "type": "SimpleText" - } - ] - }, - { - "type": "Paragraph", - "content": [ - { - "text": "another of text another of text another of text another of text another of text another of " + - "text another of text another of text another of text another of text another of text", - "type": "SimpleText" - } - ] - }] -} - -function codeContent() { - return [ - { - "type": "Snippet", - "snippet": 'class Hello {\n ...\n}' - } - ] -} - -function codeWithBulletsContent() { - return [ - { - "type": "Snippet", - "snippet": "class InternationalPriceService implements PriceService {\n" + - " private static void main(String... args) {\n" + - " ... // code goes here\n" + - " } // code stops here\n" + - "}\n", - "commentsType": "inline" - } - ] -} - -function tabsContent() { - return [ - { - type: "Tabs", - tabsContent: [ - { - "name": "cpp", - "content": [ - { - "lang": "cpp", - "snippet": "code snippet \n", - "lineNumber": "", - "type": "Snippet" - }, - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.tsx b/znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.tsx new file mode 100644 index 000000000..519cc8345 --- /dev/null +++ b/znai-reactjs/src/doc-elements/spoiler/Spoiler.demo.tsx @@ -0,0 +1,134 @@ +/* + * Copyright 2025 znai maintainers + * Copyright 2019 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; +import { Spoiler } from "./Spoiler"; +import { elementsLibrary } from "../DefaultElementsLibrary"; + +export function spoilerDemo(registry: any) { + registry + .add("regular text", () => + surroundWithText( + + ) + ) + + .add("regular text small box", () => ( +
+ {surroundWithText( + + )} +
+ )) + + .add("code snippet", () => + surroundWithText() + ) + .add("code snippet with bullets", () => + surroundWithText() + ) + .add("tabs", () => + surroundWithText() + ); +} + +function surroundWithText(rendered: React.ReactNode) { + const DocElement = elementsLibrary.DocElement as any; + return ( + + + {rendered} + + + ); +} + +function paragraphContent() { + return [ + { + type: "Paragraph", + content: [ + { + text: + "line of text line of text line of text line of text line of text line of text line of text " + + "line of text line of text line of text line of text", + type: "SimpleText", + }, + ], + }, + { + type: "Paragraph", + content: [ + { + text: + "another of text another of text another of text another of text another of text another of " + + "text another of text another of text another of text another of text another of text", + type: "SimpleText", + }, + ], + }, + ]; +} + +function codeContent() { + return [ + { + type: "Snippet", + snippet: "class Hello {\n ...\n}", + }, + ]; +} + +function codeWithBulletsContent() { + return [ + { + type: "Snippet", + snippet: + "class InternationalPriceService implements PriceService {\n" + + " private static void main(String... args) {\n" + + " ... // code goes here\n" + + " } // code stops here\n" + + "}\n", + commentsType: "inline", + }, + ]; +} + +function tabsContent() { + return [ + { + type: "Tabs", + tabsContent: [ + { + name: "cpp", + content: [ + { + lang: "cpp", + snippet: "code snippet \n", + lineNumber: "", + type: "Snippet", + }, + ], + }, + ], + }, + ]; +} diff --git a/znai-reactjs/src/doc-elements/spoiler/Spoiler.jsx b/znai-reactjs/src/doc-elements/spoiler/Spoiler.jsx deleted file mode 100644 index 84c714407..000000000 --- a/znai-reactjs/src/doc-elements/spoiler/Spoiler.jsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2019 TWO SIGMA OPEN SOURCE, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as React from 'react' - -import {documentationTracking} from '../tracking/DocumentationTracking' - -import './Spoiler.css' - -export class Spoiler extends React.Component { - state = { - active: true - } - - render() { - const {title, content, elementsLibrary} = this.props - const {active} = this.state - - if (active) { - return ( -
-
{title}
-
- -
-
- ) - } - - return - } - - reveal = () => { - const {title} = this.props - - this.setState({active: false}) - documentationTracking.onInteraction('spoiler', title) - } -} \ No newline at end of file diff --git a/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx b/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx new file mode 100644 index 000000000..e65580ddc --- /dev/null +++ b/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2025 znai maintainers + * Copyright 2019 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useRef, useState } from "react"; +import { documentationTracking } from "../tracking/DocumentationTracking"; +import { useHighlightOfHiddenElement } from "../text-selection/componentsHighlightUtils"; +import "./Spoiler.css"; + +interface SpoilerProps { + title: string; + content: any; + elementsLibrary: any; +} + +export function Spoiler({ title, content, elementsLibrary }: SpoilerProps) { + const [active, setActive] = useState(true); + const containerRef = useRef(null); + const hasHiddenHighlightedElement = useHighlightOfHiddenElement(containerRef, containerRef, !active); + + const DocElement = elementsLibrary.DocElement; + + const reveal = () => { + setActive(false); + documentationTracking.onInteraction("spoiler", title); + }; + + if (active) { + // TODO function to augment class name with highlight? + const titleClassName = "znai-spoiler-title" + (hasHiddenHighlightedElement ? " znai-highlight single" : ""); + console.log("hasHiddenHighlightedElement", hasHiddenHighlightedElement); + return ( +
+
{title}
+
+ +
+
+ ); + } + + return ; +} diff --git a/znai-reactjs/src/doc-elements/testDocumentation.jsx b/znai-reactjs/src/doc-elements/testDocumentation.jsx index b0ee75719..c1b0e8cb0 100644 --- a/znai-reactjs/src/doc-elements/testDocumentation.jsx +++ b/znai-reactjs/src/doc-elements/testDocumentation.jsx @@ -65,12 +65,68 @@ export const testDocumentation = { }, ], }, + { wide: true, mermaid: "sequenceDiagram\n par Alice to Bob\n Alice->>Bob: Go help John\n and Alice to John\n Alice->>John: I want this done today\n par John to Charlie\n John->>Charlie: Can we do this today?\n and John to Diana\n John->>Diana: Can you help us today?\n and Alice to Carl\n Alice->>Carl: I also want this done today\n end\nend\n", type: "Mermaid", }, + { + title: "Are you ready to see what's inside?", + content: [ + { + delimiter: ".", + startNumber: 1, + type: "OrderedList", + content: [ + { + type: "ListItem", + content: [ + { + type: "Paragraph", + content: [ + { + text: "Line One", + type: "SimpleText", + }, + ], + }, + ], + }, + { + type: "ListItem", + content: [ + { + type: "Paragraph", + content: [ + { + text: "Line Two", + type: "SimpleText", + }, + ], + }, + ], + }, + { + type: "ListItem", + content: [ + { + type: "Paragraph", + content: [ + { + text: "Line Three", + type: "SimpleText", + }, + ], + }, + ], + }, + ], + }, + ], + type: "Spoiler", + }, { diagram: { isInvertedTextColorByStyleId: {}, diff --git a/znai-reactjs/src/doc-elements/text-selection/textHighlighter.css b/znai-reactjs/src/doc-elements/text-selection/textHighlighter.css index 7e5df7335..300d92ebf 100644 --- a/znai-reactjs/src/doc-elements/text-selection/textHighlighter.css +++ b/znai-reactjs/src/doc-elements/text-selection/textHighlighter.css @@ -15,8 +15,8 @@ */ .znai-highlight { - background-color: #ffeb3b; - color: black; + background-color: #ffeb3b !important; + color: black !important; transition: background-color 0.2s; } From 50f4c2ddf178c332204b7fb403699bf4c346c42e Mon Sep 17 00:00:00 2001 From: MykolaGolubyev Date: Sun, 14 Sep 2025 22:31:20 -0400 Subject: [PATCH 2/2] spoiler: handle text highlight from slack --- .../1.80/add-2025-09-14-highlight-inside-spoiler.md | 1 + znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx | 1 - .../src/doc-elements/text-selection/HighlightedText.tsx | 6 ++++-- .../doc-elements/text-selection/componentsHighlightUtils.ts | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 znai-docs/znai/release-notes/1.80/add-2025-09-14-highlight-inside-spoiler.md diff --git a/znai-docs/znai/release-notes/1.80/add-2025-09-14-highlight-inside-spoiler.md b/znai-docs/znai/release-notes/1.80/add-2025-09-14-highlight-inside-spoiler.md new file mode 100644 index 000000000..e286609b7 --- /dev/null +++ b/znai-docs/znai/release-notes/1.80/add-2025-09-14-highlight-inside-spoiler.md @@ -0,0 +1 @@ +* Add: highlight Slack questions inside spoiler block \ No newline at end of file diff --git a/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx b/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx index e65580ddc..6f6419e1c 100644 --- a/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx +++ b/znai-reactjs/src/doc-elements/spoiler/Spoiler.tsx @@ -41,7 +41,6 @@ export function Spoiler({ title, content, elementsLibrary }: SpoilerProps) { if (active) { // TODO function to augment class name with highlight? const titleClassName = "znai-spoiler-title" + (hasHiddenHighlightedElement ? " znai-highlight single" : ""); - console.log("hasHiddenHighlightedElement", hasHiddenHighlightedElement); return (
{title}
diff --git a/znai-reactjs/src/doc-elements/text-selection/HighlightedText.tsx b/znai-reactjs/src/doc-elements/text-selection/HighlightedText.tsx index 464a8f05f..ef5e70ff0 100644 --- a/znai-reactjs/src/doc-elements/text-selection/HighlightedText.tsx +++ b/znai-reactjs/src/doc-elements/text-selection/HighlightedText.tsx @@ -26,7 +26,7 @@ import { removeTrailingSlashFromQueryParam } from "./queryParamUtils"; import "./HighlightedText.css"; export interface HighlightedTextListener { - onUserDrivenTextHighlight(firstElement: HTMLElement): void; + onUserDrivenTextHighlight(firstElement: HTMLElement, hideBubble: () => void): void; } const highlightedTextListeners: HighlightedTextListener[] = []; @@ -174,7 +174,9 @@ export function HighlightedText({ } scrollToBubbleIfRequired(firstHighlightedElement); - highlightedTextListeners.forEach((listener) => listener.onUserDrivenTextHighlight(firstHighlightedElement)); + highlightedTextListeners.forEach((listener) => + listener.onUserDrivenTextHighlight(firstHighlightedElement, hideBubble) + ); } addTextMenuListener(textMenuListener); diff --git a/znai-reactjs/src/doc-elements/text-selection/componentsHighlightUtils.ts b/znai-reactjs/src/doc-elements/text-selection/componentsHighlightUtils.ts index fb040a170..677573219 100644 --- a/znai-reactjs/src/doc-elements/text-selection/componentsHighlightUtils.ts +++ b/znai-reactjs/src/doc-elements/text-selection/componentsHighlightUtils.ts @@ -35,7 +35,7 @@ export function useHighlightOfHiddenElement( useEffect(() => { const listener = { - onUserDrivenTextHighlight: (firstHighlightElement: HTMLElement) => { + onUserDrivenTextHighlight: (firstHighlightElement: HTMLElement, hideBubble: () => void) => { if ( containerRef.current && hiddenContainerRef.current && @@ -46,6 +46,7 @@ export function useHighlightOfHiddenElement( firstHighlightElement, containerRef.current ); + hideBubble(); setHasHiddenHighlightedElement(true); onlyOnce.current = true; }