Skip to content

Commit 3c2003e

Browse files
ux: ask in slack and generate link selection menu (#1336)
1 parent d9ef058 commit 3c2003e

32 files changed

+2244
-3596
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Add: Initial menu for selected text. Generate link with comment and send to slack.

znai-reactjs/src/App.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@
194194
--znai-attention-recommendation-color: var(--znai-attention-question-color);
195195
--znai-attention-recommendation-background-color: var(--znai-attention-question-background-color);
196196

197+
--znai-text-selection-menu-background-color: var(--znai-brand-primary-color);
198+
--znai-text-selection-menu-text-color: #fff;
199+
197200
--znai-text-badge-background-color: #06437c;
198201
--znai-text-badge-text-color: #eee;
199202

znai-reactjs/src/App.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ const docMeta = {
112112
type: "User Guide",
113113
title: "Znai",
114114
previewEnabled: true,
115-
hipchatRoom: "Test Room",
115+
slackChannel: "help-domain-name",
116+
sendToSlackUrl: "http://localhost:5111/ask-in-slack",
116117
viewOn: {
117118
link: "https://github.com/testingisdocumenting/znai/znai-cli/documentation",
118119
title: "View On GitHub",
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
.znai-notification {
2+
position: fixed;
3+
top: 0;
4+
left: 0;
5+
right: 0;
6+
z-index: 10000;
7+
padding: 12px 16px;
8+
font-size: 14px;
9+
font-family: inherit;
10+
opacity: 0;
11+
transform: translateY(-100%);
12+
transition: all 0.3s ease-in-out;
13+
}
14+
15+
.znai-notification.visible {
16+
opacity: 1;
17+
transform: translateY(0);
18+
}
19+
20+
.znai-notification.fade-out {
21+
opacity: 0;
22+
transform: translateY(-100%);
23+
}
24+
25+
.znai-notification-success {
26+
background-color: #d4edda;
27+
color: #155724;
28+
border-bottom: 2px solid #28a745;
29+
}
30+
31+
.znai-notification-error {
32+
background-color: #f8d7da;
33+
color: #721c24;
34+
border-bottom: 2px solid #dc3545;
35+
}
36+
37+
.znai-notification-content {
38+
display: flex;
39+
justify-content: space-between;
40+
align-items: center;
41+
max-width: 1200px;
42+
margin: 0 auto;
43+
padding-left: 16px;
44+
padding-right: 16px;
45+
}
46+
47+
.znai-notification-message {
48+
flex: 1;
49+
padding-right: 16px;
50+
}
51+
52+
.znai-notification-close {
53+
background: none;
54+
border: none;
55+
font-size: 18px;
56+
cursor: pointer;
57+
padding: 4px;
58+
width: 24px;
59+
height: 24px;
60+
display: flex;
61+
align-items: center;
62+
justify-content: center;
63+
border-radius: 4px;
64+
transition: background-color 0.2s ease;
65+
flex-shrink: 0;
66+
}
67+
68+
.znai-notification-success .znai-notification-close {
69+
color: #155724;
70+
}
71+
72+
.znai-notification-error .znai-notification-close {
73+
color: #721c24;
74+
}
75+
76+
.znai-notification-close:hover {
77+
background-color: rgba(0, 0, 0, 0.1);
78+
}
79+
80+
.znai-notification-close:focus {
81+
outline: 2px solid currentColor;
82+
outline-offset: 2px;
83+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2025 znai maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import React, { useEffect, useState } from "react";
18+
import "./Notification.css";
19+
20+
export interface NotificationProps {
21+
type: "success" | "error";
22+
message: string;
23+
onClose: () => void;
24+
}
25+
26+
export function Notification({ type, message, onClose }: NotificationProps) {
27+
const [visible, setVisible] = useState(true);
28+
29+
useEffect(() => {
30+
if (type === "success") {
31+
const timer = setTimeout(() => {
32+
setVisible(false);
33+
setTimeout(onClose, 300); // Wait for fade out animation
34+
}, 2000);
35+
36+
return () => clearTimeout(timer);
37+
}
38+
}, [type, onClose]);
39+
40+
const handleClose = () => {
41+
setVisible(false);
42+
setTimeout(onClose, 300); // Wait for fade out animation
43+
};
44+
45+
return (
46+
<div className={`znai-notification znai-notification-${type} ${visible ? "visible" : "fade-out"}`}>
47+
<div className="znai-notification-content">
48+
<span className="znai-notification-message">{message}</span>
49+
<button className="znai-notification-close" onClick={handleClose} aria-label="Close notification">
50+
51+
</button>
52+
</div>
53+
</div>
54+
);
55+
}

znai-reactjs/src/doc-elements/Documentation.jsx

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import SearchPopup from './search/SearchPopup'
2424
import {getSearchPromise} from './search/searchPromise'
2525
import {documentationNavigation} from '../structure/DocumentationNavigation'
2626
import {documentationTracking} from './tracking/DocumentationTracking'
27-
import {textSelection} from './selected-text-extensions/TextSelection'
2827
import {tableOfContents} from '../structure/toc/TableOfContents'
2928
import {getAllPagesPromise} from './allPages'
3029

@@ -115,11 +114,9 @@ export class Documentation extends Component {
115114
this.onPageGenError = this.onPageGenError.bind(this)
116115
this.updateCurrentPageSection = this.updateCurrentPageSection.bind(this)
117116
this.keyDownHandler = this.keyDownHandler.bind(this)
118-
this.mouseUpHandler = this.mouseUpHandler.bind(this)
119117
this.mouseClickHandler = this.mouseClickHandler.bind(this)
120118

121119
documentationNavigation.addUrlChangeListener(this.onUrlChange.bind(this))
122-
textSelection.addListener(this.onTextSelection.bind(this))
123120
}
124121

125122
get theme() {
@@ -224,7 +221,6 @@ export class Documentation extends Component {
224221
onTocItemPageSectionClick={this.onTocItemPageSectionClick}
225222
onNextPage={this.onNextPage}
226223
onPrevPage={this.onPrevPage}
227-
textSelection={textSelection}
228224
scrollToTop={this.scrollTop}
229225
scrollToPageSection={this.scrollToPageSection}
230226
pageGenError={pageGenError}/>
@@ -265,8 +261,6 @@ export class Documentation extends Component {
265261
this.onPageLoad()
266262

267263
document.addEventListener('keydown', this.keyDownHandler)
268-
document.addEventListener('mouseup', this.mouseUpHandler)
269-
document.addEventListener('click', this.mouseClickHandler)
270264

271265
presentationModeListeners.addListener(this)
272266
}
@@ -275,15 +269,14 @@ export class Documentation extends Component {
275269
this.disableScrollListener()
276270

277271
document.removeEventListener('keydown', this.keyDownHandler)
278-
document.removeEventListener('mouseup', this.mouseUpHandler)
279-
document.removeEventListener('click', this.mouseClickHandler)
280272

281273
presentationModeListeners.removeListener(this)
282274
}
283275

284276
keyDownHandler(e) {
285277
const {isSearchActive, mode} = this.state
286-
if (e.code === "Slash" && !isSearchActive && mode === DocumentationModes.DEFAULT) {
278+
const isFromInputElement = e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA'
279+
if (e.code === "Slash" && !isSearchActive && mode === DocumentationModes.DEFAULT && !isFromInputElement) {
287280
e.preventDefault()
288281
this.setState({isSearchActive: true})
289282
} else if (mode === DocumentationModes.DEFAULT && e.code === 'KeyP' && e.altKey) {
@@ -301,32 +294,6 @@ export class Documentation extends Component {
301294
}
302295
}
303296

304-
mouseUpHandler() {
305-
const {isSearchActive, mode} = this.state
306-
if (mode !== DocumentationModes.DEFAULT || isSearchActive) {
307-
return
308-
}
309-
310-
const selection = window.getSelection()
311-
if (!selection.rangeCount) {
312-
return
313-
}
314-
315-
const rangeAt = selection.getRangeAt(0)
316-
const text = selection.toString()
317-
318-
if (!text || selection.isCollapsed) {
319-
textSelection.clear()
320-
} else {
321-
const {page} = this.state
322-
323-
const tocItem = page.tocItem
324-
const startNode = selection.isCollapsed ? null : rangeAt.startContainer.parentNode
325-
326-
textSelection.endSelection({tocItem, startNode, text})
327-
}
328-
}
329-
330297
mouseClickHandler() {
331298
}
332299

@@ -361,7 +328,7 @@ export class Documentation extends Component {
361328
if (isMobile) {
362329
window.removeEventListener("scroll", this.updateCurrentPageSection)
363330
} else {
364-
this.mainPanelDom.addEventListener("scroll", this.updateCurrentPageSection)
331+
this.mainPanelDom.removeEventListener("scroll", this.updateCurrentPageSection)
365332
}
366333
}
367334

@@ -637,9 +604,6 @@ export class Documentation extends Component {
637604
return funcToUpdatePage()
638605
}
639606

640-
onTextSelection(selectedText) {
641-
}
642-
643607
onUrlChange(url, urlHistoryState) {
644608
return this.getAllPagesPromise().then((allPages) => {
645609
const currentPageLocation = documentationNavigation.extractPageLocation(url)

znai-reactjs/src/doc-elements/default-elements/PresentationHeading.jsx renamed to znai-reactjs/src/doc-elements/default-elements/PresentationHeading.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,22 @@
1515
* limitations under the License.
1616
*/
1717

18-
import React from 'react'
18+
import React from "react";
1919

20-
import './PresentationHeading.css'
20+
import "./PresentationHeading.css";
2121

22-
export function PresentationHeading({level, title, className}) {
23-
const Element = `h${level}`
22+
interface Props {
23+
level: number;
24+
title: string;
25+
className?: string;
26+
}
27+
28+
export function PresentationHeading({ level, title, className }: Props) {
29+
const Element = `h${level}` as keyof JSX.IntrinsicElements;
2430

25-
return (
26-
<div className="presentation-heading-wrapper">
27-
<Element className={className}>{title}</Element>
28-
</div>
29-
)
31+
return (
32+
<div className="presentation-heading-wrapper">
33+
<Element className={className}>{title}</Element>
34+
</div>
35+
);
3036
}

znai-reactjs/src/doc-elements/default-elements/Section.jsx

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)